pax_global_header00006660000000000000000000000064150615546740014527gustar00rootroot0000000000000052 comment=dbdaa4c1e20ac594c4b96733c7be92d575d0f9e5 avogadrolibs-1.101.0/000077500000000000000000000000001506155467400143435ustar00rootroot00000000000000avogadrolibs-1.101.0/.clang-format000066400000000000000000000053401506155467400167200ustar00rootroot00000000000000--- # This was based on the Mozilla style from clang-format 3.8. # Effectively creating a snapshot to produce more consistent results with later # versions of clang-format. Language: Cpp AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlinesLeft: false AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: true AfterControlStatement: false AfterEnum: true AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: true AfterUnion: true BeforeCatch: false BeforeElse: false IndentBraces: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Mozilla BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 2 Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IndentCaseLabels: true IndentWidth: 2 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: false PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: false SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 8 UseTab: Never ... avogadrolibs-1.101.0/.clang-tidy000066400000000000000000000023351506155467400164020ustar00rootroot00000000000000Checks: 'bugprone*, clang-analyzer*, cppcoreguidelines*, misc-uniqueptr-reset-release, misc-unused*, misc-redundant-expression, modernize-avoid-bind,modernize-concat-nested-namespaces, modernize-loop-convert,modernize-macro-to-enum,modernize-use-bool-literals, modernize-use-auto,modernize-use-nullptr,modernize-use-override, modernize-raw-string-literal,modernize-use-emplace,modernize-pass-by-value, modernize-use-using, performance-avoid-endl, performance-faster*, performance-for-range-copy, performance-inefficient*,performance-type-promotion*, performance-unnecessary-copy-initialization, performance-move-const-arg, readability-const-return-type, readability-container*, readability-duplicate-include, readability-delete-null-pointer, readability-qualified-auto, readability-redundant*, readability-simplify*, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-owning-memory, -readability-else-after-return, -portability-restrict-system-includes, -modernize-use-trailing-return-type' FormatStyle: file avogadrolibs-1.101.0/.clang-tidy-ignore000066400000000000000000000000351506155467400176560ustar00rootroot00000000000000thirdparty/* core/graph.cpp avogadrolibs-1.101.0/.codacy.yaml000066400000000000000000000001161506155467400165450ustar00rootroot00000000000000--- engines: cppcheck: language: c++ exclude_paths: - "thirdparty/**" avogadrolibs-1.101.0/.github/000077500000000000000000000000001506155467400157035ustar00rootroot00000000000000avogadrolibs-1.101.0/.github/FUNDING.yml000066400000000000000000000001721506155467400175200ustar00rootroot00000000000000# These are supported funding model platforms open_collective: avogadro # Replace with a single Open Collective username avogadrolibs-1.101.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001506155467400200665ustar00rootroot00000000000000avogadrolibs-1.101.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000015311506155467400225600ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve --- **Avogadro version: (please complete the following information from the About box):** - Avogadrolibs: (e.g. 1.90.0-316-g6d14770) - Qt: (e.g., 5.9.5) **Desktop version: (please complete the following information):** - OS: [e.g. MacOS] - Version [e.g. 10.12.4] - Compiler (if you built from source) **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. **Please consider uploading or linking test files.** avogadrolibs-1.101.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000014251506155467400236150ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I wish Avogadro calculated XYQ moments to render with .. **Describe the solution you'd like** A clear and concise description of what you want to happen. Please be detailed. How might this feature work (e.g., first a window would open and you'd click to …) **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. Please consider opening a topic on https://discuss.avogadro.cc/ to allow further public discussion avogadrolibs-1.101.0/.github/PULL_REQUEST_TEMPLATE000066400000000000000000000026151506155467400211110ustar00rootroot00000000000000Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 1 Letterman Drive Suite D4700 San Francisco, CA, 94129 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. avogadrolibs-1.101.0/.github/codeql-config.yml000066400000000000000000000002531506155467400211400ustar00rootroot00000000000000query-filters: - exclude: id: cpp/integer-multiplication-cast-to-long paths-ignore: - '**/moc_*.*' - '**/ui_*.h' - 'thirdparty/**/*.*' - 'utilities/**/*.*'avogadrolibs-1.101.0/.github/config.yml000066400000000000000000000017701506155467400177000ustar00rootroot00000000000000# Configuration for welcome - https://github.com/behaviorbot/welcome # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome # Comment to be posted to on first time issues newIssueWelcomeComment: > Thanks for opening your first issue here! Please try to include example files and screenshots if possible. If you're looking for support, please post on our forum: https://discuss.avogadro.cc/ # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome # Comment to be posted to on PRs from first time contributors in your repository newPRWelcomeComment: > Thanks for opening this pull request! Please check out our contributing guidelines and check for the automated tests. # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge # Comment to be posted to on pull requests merged by a first time user firstPRMergeComment: > Congrats on merging your first pull request! 🎉 Thanks for making Avogadro better for everyone! avogadrolibs-1.101.0/.github/dependabot.yml000066400000000000000000000003321506155467400205310ustar00rootroot00000000000000# Set update schedule for GitHub Actions version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: # Check for updates to GitHub Actions every weekday interval: "daily" avogadrolibs-1.101.0/.github/release-drafter.yml000066400000000000000000000021071506155467400214730ustar00rootroot00000000000000name-template: 'Avogadro $RESOLVED_VERSION' tag-template: '$RESOLVED_VERSION' categories: - title: '✨ Features' labels: - 'feature' - 'enhancement' - title: '🐛 Bug Fixes' labels: - 'fix' - 'bugfix' - 'bug' - title: '🚀 Performance Improvements' labels: - 'speed' - title: '🧰 Maintenance' labels: - 'chore' - 'build' - title: '📚 Translations' labels: - 'i18n' autolabeler: - label: 'build' branch: - '/build\/.+/' title: - '/build/i' - label: 'bug' branch: - '/fix\/.+/' title: - '/fix/i' - label: 'i18n' branch: - '/weblate\/.+/' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. version-resolver: major: labels: - 'major' minor: labels: - 'minor' patch: labels: - 'patch' default: minor template: | ## Changes $CHANGES ## Credits Thanks to many contributors, including: $CONTRIBUTORS avogadrolibs-1.101.0/.github/workflows/000077500000000000000000000000001506155467400177405ustar00rootroot00000000000000avogadrolibs-1.101.0/.github/workflows/add_artifact_urls.yml000066400000000000000000000012631506155467400241370ustar00rootroot00000000000000name: add artifact links to pull request and related issues on: workflow_run: workflows: ['Qt5 Build Matrix'] types: [completed] jobs: artifacts-url-comments: name: add artifact links to pull request and related issues job runs-on: windows-latest steps: - name: add artifact links to pull request and related issues step uses: tonyhallett/artifacts-url-comments@v1.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: prefix: Here are the build results suffix: Artifacts will only be retained for 90 days. format: name addTo: pullandissues continue-on-error: true avogadrolibs-1.101.0/.github/workflows/build_cmake.yml000066400000000000000000000276401506155467400227330ustar00rootroot00000000000000name: Qt5 Build Matrix # Many thanks to Cristian Adam for examples # e.g. https://github.com/cristianadam/HelloWorld/blob/master/.github/workflows/build_cmake.yml # https://cristianadam.eu/20191222/using-github-actions-with-c-plus-plus-and-cmake/ on: [push, pull_request, workflow_dispatch] env: QT_VERSION: 5.15.2 # this is different from MACOSX_DEPLOYMENT_TARGET to prevent build problems # we set MACOSX_DEPLOYMENT_TARGET later MACOS_TARGET: 10.12 FEATURES: -DBUILD_GPL_PLUGINS=ON -DWITH_COORDGEN=OFF -DUSE_3DCONNEXION=ON -DQT_VERSION=5 CACHE: -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache SCCACHE_GHA_ENABLED: "true" concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: - { name: "macOS Clang", artifact: "macOS.dmg", os: macos-13, cc: "clang", cxx: "clang++", build_type: "Release", cmake_flags: "-G Ninja -DBUILD_MOLEQUEUE=OFF -DUSE_VTK=ON", cpack_flags: "-G DragNDrop", } - { name: "Windows Latest MSVC", artifact: "Win64.exe", os: windows-latest, cc: "cl", cxx: "cl", build_type: "Release", cmake_flags: "-DBUILD_MOLEQUEUE=OFF", build_flags: "-j 2", cpack_flags: "-G NSIS", ssl_env: "D:\\a\\avogadrolibs\\Qt\\Tools\\OpenSSLv3\\Win_x64", } - { name: "Ubuntu Address Sanitizer", artifact: "", os: ubuntu-22.04, cc: "gcc", cxx: "g++", build_type: "asan", cmake_flags: "-G Ninja -DENABLE_TESTING=ON -DUSE_VTK=ON -DTEST_QTGL=OFF -USE_SYSTEM_ZLIB=ON", cpack: "", } - { name: "Ubuntu Undefined Behavior Sanitizer", artifact: "", os: ubuntu-22.04, cc: "gcc", cxx: "g++", build_type: "ubsan", cmake_flags: "-G Ninja -DENABLE_TESTING=ON -DUSE_VTK=ON -DTEST_QTGL=OFF -USE_SYSTEM_ZLIB=ON", cpack: "", } steps: - name: Install Dependencies (macOS) if: runner.os == 'macOS' run: | if uname -p | grep -q "arm" ; then export PATH=/opt/homebrew/bin:$PATH else # not self-hosted runner brew install ninja eigen glew fi - name: Install Dependencies (Windows) if: runner.os == 'Windows' run: | choco install ninja nsis vcpkg install zlib - name: Install Dependencies (Linux) if: runner.os == 'Linux' run: | sudo add-apt-repository -y universe sudo apt-get -qq update sudo apt-get -qq install ninja-build libeigen3-dev libboost-all-dev libglew-dev libxml2-dev sudo apt-get -qq install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5x11extras5-dev libqt5svg5-dev sudo apt-get -qq install libgcc-10-dev libgcc-9-dev sudo apt-get -qq install libfuse2 - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: recursive - name: Checkout molecules uses: actions/checkout@v5 with: repository: openchemistry/molecules path: molecules - name: Checkout fragments uses: actions/checkout@v5 with: repository: openchemistry/fragments path: fragments - name: Checkout crystals uses: actions/checkout@v5 with: repository: openchemistry/crystals path: crystals - name: Checkout i18n uses: actions/checkout@v5 with: repository: openchemistry/avogadro-i18n path: avogadro-i18n - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: avogadroapp fetch-depth: 0 - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: avogadrolibs fetch-depth: 0 - name: Install Qt uses: jurplel/install-qt-action@v3 with: aqtversion: '==3.1.*' cache: true version: ${{ env.QT_VERSION }} - name: Install OpenSSL (Win64) if: runner.os == 'Windows' uses: jurplel/install-qt-action@v3 with: aqtversion: '==3.1.*' cache: true host: 'windows' target: 'desktop' tools-only: 'true' tools: 'tools_opensslv3_x64' - name: Configure MSVC Command Prompt if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 with: arch: x64 - name: Grab cache files uses: actions/cache@v4 if: runner.os != 'Windows' with: path: | ${{ runner.workspace }}/build/Downloads key: ${{ matrix.config.name }}-downloads - name: Run sccache-cache uses: mozilla-actions/sccache-action@main - name: Configure run: | if [ ! -d "${{ runner.workspace }}/build" ]; then mkdir "${{ runner.workspace }}/build"; fi cd "${{ runner.workspace }}/build" # won't have any effect except on Mac echo "MACOSX_DEPLOYMENT_TARGET=${{ env.MACOS_TARGET }}" >> $GITHUB_ENV CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake $GITHUB_WORKSPACE ${{env.FEATURES}} ${{env.CACHE}} -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ${{matrix.config.cmake_flags}} shell: bash - name: Build run: | CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} CMAKE_C_COMPILER_LAUNCHER=sccache CMAKE_CXX_COMPILER_LAUNCHER=sccache cmake --build . --config ${{matrix.config.build_type}} ${{matrix.config.build_flags}} shell: bash working-directory: ${{ runner.workspace }}/build - name: Run tests if: (matrix.config.build_type == 'asan') || (matrix.config.build_type == 'ubsan') shell: cmake -P {0} run: | include(ProcessorCount) ProcessorCount(N) set(ENV{CTEST_OUTPUT_ON_FAILURE} "ON") set(ENV{ASAN_OPTIONS} "new_delete_type_mismatch=0") execute_process( COMMAND ctest -j ${N} WORKING_DIRECTORY ${{ runner.workspace }}/build/avogadrolibs RESULT_VARIABLE result ) if (NOT result EQUAL 0) message(FATAL_ERROR "Running tests failed!") endif() - name: Fix Mac plugins if: runner.os == 'macOS' working-directory: ${{ runner.workspace }}/build/prefix/lib/openbabel run: | for plugin in *.so; do for libpath in `otool -L ${plugin} | grep '/Users/runner/work' | awk '{print $1}'`; do export lib=`echo $libpath | cut -d '/' -f 9`; echo "Fixing $plugin $lib $libpath" install_name_tool -change $libpath @executable_path/../Frameworks/$lib $plugin done done cd .. # build/prefix/lib for plugin in libinchi.?.?.?.dylib libopenbabel.?.?.?.dylib; do for libpath in `otool -L ${plugin} | grep '/Users/runner/work' | awk '{print $1}'`; do export lib=`echo $libpath | cut -d '/' -f 9`; echo "Fixing $plugin $lib $libpath" install_name_tool -change $libpath @executable_path/../Frameworks/$lib $plugin done done otool -L libinchi.?.?.?.dylib otool -L libopenbabel.?.?.?.dylib cp -p libinchi* ../Avogadro2.app/Contents/Frameworks/ cp -p libopenbabel* ../Avogadro2.app/Contents/Frameworks/ # finally, fixup the binaries cd ../bin for exe in obabel obmm eht_bind genXrdPattern; do for libpath in `otool -L ${exe} | grep '/Users/runner/work' | awk '{print $1}'`; do export lib=`echo $libpath | cut -d '/' -f 9`; echo "Fixing $exe $lib $libpath" install_name_tool -change $libpath @executable_path/../Frameworks/$lib $exe done done - name: Install the Apple certificate # From GitHub docs: https://docs.github.com/en/actions/guides/installing-an-apple-certificate-on-macos-runners-for-xcode-development if: runner.os == 'macOS' working-directory: ${{ runner.workspace }}/build env: BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} NOTARIZE_USERNAME: ${{ secrets.AC_USERNAME }} NOTARIZE_PASSWORD: ${{ secrets.AC_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.CODESIGN_ID }} PRODUCT_BUNDLE_IDENTIFIER: cc.avogadro run: | # be sure the DMG can be created easily # https://github.com/actions/runner-images/issues/7522#issuecomment-1556766641 echo killing...; sudo pkill -9 XProtect >/dev/null || true; echo waiting...; while pgrep XProtect; do sleep 3; done; # create variables if [ -n "${P12_PASSWORD}" ]; then CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db # import certificate and provisioning profile from secrets echo -n "$BUILD_CERTIFICATE_BASE64" | base64 -d -o $CERTIFICATE_PATH # create temporary keychain if the cert is non-zero if [ -s $CERTIFICATE_PATH ]; then security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH # import certificate to keychain security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH # signing occurs via avogadroapp cpack instructions fi # certificate exists fi # password exists - name: Create Mac and Windows Packages if: runner.os == 'Windows' || runner.os == 'macOS' shell: bash run: | if [ -z "${P12_PASSWORD}" ]; then unset CODESIGN_IDENTITY # to prevent cpack failing when trying to sign fi if [ -z "${OPENSSL_ROOT_DIR}" ]; then unset OPENSSL_ROOT_DIR fi [[ ! "${GITHUB_REF}" =~ "tags" ]] && export SNAPSHOT_DATE=`date "+%d-%m-%y"` cpack ${{ matrix.config.cpack_flags }} working-directory: ${{ runner.workspace }}/build/avogadroapp continue-on-error: true env: P12_PASSWORD: ${{ secrets.P12_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.CODESIGN_ID }} OPENSSL_ROOT_DIR: ${{ matrix.config.ssl_env }} - name: Notarize Mac DMG if: runner.os == 'macOS' run: | # check current directory (failing signing) echo `pwd` ls -ld # check if we have the password and the username if [ -n "${NOTARIZE_PASSWORD}" ] && [ -n "${NOTARIZE_USERNAME}" ]; then codesign -s "$CODESIGN_IDENTITY" --timestamp Avogadro2*.dmg xcrun notarytool submit Avogadro2*.dmg --apple-id "$NOTARIZE_USERNAME" --team-id "$NOTARIZE_TEAM" --password "$NOTARIZE_PASSWORD" --verbose --wait xcrun stapler staple -v Avogadro2*.dmg fi working-directory: ${{ runner.workspace }}/build/avogadroapp env: NOTARIZE_TEAM: ${{ secrets.AC_TEAM }} NOTARIZE_USERNAME: ${{ secrets.AC_USERNAME }} NOTARIZE_PASSWORD: ${{ secrets.AC_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.CODESIGN_ID }} PRODUCT_BUNDLE_IDENTIFIER: cc.avogadro continue-on-error: true - name: Upload if: matrix.config.artifact != 0 id: upload-artifact uses: actions/upload-artifact@v4 with: path: ${{ runner.workspace }}/build/avogadroapp/Avogadro*.* name: ${{ matrix.config.artifact }} - name: Cleanup if: ${{ always() }} # To ensure this step runs even when earlier steps fail shell: bash run: | ls -la ./ rm -rf ./* || true rm -rf ./.??* || true ls -la ./ avogadrolibs-1.101.0/.github/workflows/build_flatpak.yml000066400000000000000000000060411506155467400232650ustar00rootroot00000000000000name: Build Flatpak on: [push, pull_request, workflow_dispatch] jobs: flatpak: name: "Flatpak" runs-on: ubuntu-latest steps: - name: Install dependencies run: sudo apt update -qq && sudo apt install -y -qq flatpak - name: Configure flatpak run: flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo - name: Install flatpak-builder run: flatpak --user install --or-update --noninteractive flathub org.flatpak.Builder - name: Configure git run: git config --global protocol.file.allow always # Have to do this because for a while git's handling of submodules was broken # See https://github.com/flatpak/flatpak-builder/issues/495 - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: false path: openchemistry - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: openchemistry/avogadroapp - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: openchemistry/avogadrolibs - name: Checkout i18n uses: actions/checkout@v5 with: repository: openchemistry/avogadro-i18n path: openchemistry/avogadro-i18n - name: Checkout avogadrogenerators uses: actions/checkout@v5 with: repository: openchemistry/avogenerators path: openchemistry/avogadrogenerators - name: Checkout crystals uses: actions/checkout@v5 with: repository: openchemistry/crystals path: openchemistry/crystals - name: Checkout fragments uses: actions/checkout@v5 with: repository: openchemistry/fragments path: openchemistry/fragments - name: Checkout molecules uses: actions/checkout@v5 with: repository: openchemistry/molecules path: openchemistry/molecules - name: Checkout Flathub shared-modules uses: actions/checkout@v5 with: repository: flathub/shared-modules path: shared-modules - name: Set up tmate session if: failure() uses: mxschmitt/action-tmate@v3 - name: Move manifest run: mv openchemistry/avogadroapp/flatpak/org.openchemistry.Avogadro2.yaml ./ - name: Build with flatpak-builder run: dbus-run-session flatpak run org.flatpak.Builder --force-clean --user --install-deps-from=flathub --arch=x86_64 --default-branch=test --repo=repo builddir org.openchemistry.Avogadro2.yaml - name: Create bundle run: flatpak build-bundle repo Avogadro2.flatpak org.openchemistry.Avogadro2 test - name: Upload bundle uses: actions/upload-artifact@v4 with: path: Avogadro2.flatpak - name: Cleanup if: ${{ always() }} # To ensure this step runs even when earlier steps fail shell: bash run: | ls -la ./ rm -rf ./* || true rm -rf ./.??* || true ls -la ./ avogadrolibs-1.101.0/.github/workflows/build_linux.yml000066400000000000000000000151621506155467400230060ustar00rootroot00000000000000name: Linux Build Matrix # Many thanks to Cristian Adam for examples # e.g. https://github.com/cristianadam/HelloWorld/blob/master/.github/workflows/build_cmake.yml # https://cristianadam.eu/20191222/using-github-actions-with-c-plus-plus-and-cmake/ on: [push, pull_request, workflow_dispatch] env: QT_VERSION: 6.8.1 FEATURES: -DUSE_VTK=ON -DBUILD_GPL_PLUGINS=ON -DBUILD_MOLEQUEUE=OFF -DWITH_COORDGEN=OFF -DQT_VERSION=6 concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: - { name: "Ubuntu x86", artifact: "", os: ubuntu-latest, cc: "gcc", cxx: "g++", build_type: "Release", cmake_flags: "-G Ninja -DUSE_SYSTEM_EIGEN=TRUE", } #- { # name: "Ubuntu ARM", artifact: "", # os: ubuntu-24.04-arm, # cc: "gcc", cxx: "g++", # build_type: "Release", # cmake_flags: "-G Ninja -DUSE_SYSTEM_EIGEN=TRUE", # } - { name: "AppImage x86", artifact: "Avogadro2-x86_64.AppImage", os: ubuntu-22.04, cc: "gcc", cxx: "g++", build_type: "Release", cmake_flags: "-G Ninja -DUSE_SYSTEM_EIGEN=TRUE -DINSTALL_BUNDLE_FILES=ON", } #- { # name: "AppImage ARM", artifact: "Avogadro2-arm64.AppImage", # os: ubuntu-22.04-arm, # cc: "gcc", cxx: "g++", # build_type: "Release", # cmake_flags: "-G Ninja -DINSTALL_BUNDLE_FILES=ON", # } - { name: "Ubuntu Address Sanitizer", artifact: "", os: ubuntu-latest, cc: "gcc", cxx: "g++", build_type: "asan", cmake_flags: "-G Ninja -DUSE_SYSTEM_EIGEN=TRUE -DENABLE_TESTING=ON -DTEST_QTGL=OFF -USE_SYSTEM_ZLIB=ON", } - { name: "Ubuntu Undefined Behavior Sanitizer", artifact: "", os: ubuntu-latest, cc: "gcc", cxx: "g++", build_type: "ubsan", cmake_flags: "-G Ninja -DUSE_SYSTEM_EIGEN=TRUE -DENABLE_TESTING=ON -DTEST_QTGL=OFF -USE_SYSTEM_ZLIB=ON", } steps: - name: Install Dependencies run: | sudo apt-get -qq update sudo apt-get -qq install ninja-build libeigen3-dev libboost-all-dev libglew-dev libxml2-dev sudo apt-get -qq install libfuse2 - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: recursive path: openchemistry - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: openchemistry/avogadroapp - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: openchemistry/avogadrolibs - name: Checkout i18n uses: actions/checkout@v5 with: repository: openchemistry/avogadro-i18n path: openchemistry/avogadro-i18n - name: Checkout avogadrogenerators uses: actions/checkout@v5 with: repository: openchemistry/avogenerators path: openchemistry/avogadrogenerators - name: Checkout crystals uses: actions/checkout@v5 with: repository: openchemistry/crystals path: openchemistry/crystals - name: Checkout fragments uses: actions/checkout@v5 with: repository: openchemistry/fragments path: openchemistry/fragments - name: Checkout molecules uses: actions/checkout@v5 with: repository: openchemistry/molecules path: openchemistry/molecules - name: Install Qt uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} - name: Configure run: | if [ ! -d "${{ runner.workspace }}/build" ]; then mkdir "${{ runner.workspace }}/build"; fi cd "${{ runner.workspace }}/build" CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake $GITHUB_WORKSPACE/openchemistry ${{env.FEATURES}} -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ${{matrix.config.cmake_flags}} shell: bash - name: Build run: | CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake --build . --config ${{matrix.config.build_type}} ${{matrix.config.build_flags}} shell: bash working-directory: ${{ runner.workspace }}/build - name: Run tests if: (matrix.config.build_type == 'asan') || (matrix.config.build_type == 'ubsan') shell: cmake -P {0} run: | include(ProcessorCount) ProcessorCount(N) set(ENV{CTEST_OUTPUT_ON_FAILURE} "ON") set(ENV{ASAN_OPTIONS} "new_delete_type_mismatch=0") execute_process( COMMAND ctest -j ${N} WORKING_DIRECTORY ${{ runner.workspace }}/build/avogadrolibs RESULT_VARIABLE result ) if (NOT result EQUAL 0) message(FATAL_ERROR "Running tests failed!") endif() - name: Package AppImage if: matrix.config.name == 'AppImage x86' shell: bash run: | mkdir appdir mv prefix appdir/usr export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:appdir/usr/lib wget -c -nv "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" wget -c -nv "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" wget -c -nv "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" chmod a+x *.AppImage # Though it is not great security practice, bundle libssl and libcrypto ./linuxdeploy-x86_64.AppImage -d appdir/usr/share/applications/*.desktop --plugin qt --library /lib/x86_64-linux-gnu/libssl.so.3 --library /lib/x86_64-linux-gnu/libcrypto.so.3 --appdir appdir # add the custom AppRun rm appdir/AppRun cp ../avogadrolibs/openchemistry/avogadrolibs/scripts/AppImage.sh appdir/AppRun chmod a+x appdir/AppRun ./appimagetool-x86_64.AppImage appdir mv Avogadro*.AppImage avogadroapp/Avogadro2-x86_64.AppImage # for upload working-directory: ${{ runner.workspace }}/build - name: Upload if: matrix.config.artifact != 0 uses: actions/upload-artifact@v4 with: path: ${{ runner.workspace }}/build/avogadroapp/Avogadro2*.* name: ${{ matrix.config.artifact }} - name: Setup tmate session if: ${{ failure() }} uses: mxschmitt/action-tmate@v3 avogadrolibs-1.101.0/.github/workflows/build_m1.yml000066400000000000000000000173441506155467400221700ustar00rootroot00000000000000name: CMake Self-Hosted on: [push, pull_request, workflow_dispatch] env: # this is different from MACOSX_DEPLOYMENT_TARGET to prevent build problems # we set MACOSX_DEPLOYMENT_TARGET later MACOS_TARGET: 10.12 FEATURES: -DUSE_VTK=ON -DBUILD_GPL_PLUGINS=ON -DBUILD_MOLEQUEUE=OFF -DWITH_COORDGEN=OFF -DUSE_3DCONNEXION=ON concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: - { name: "macOS ARM", artifact: "macOS-arm64.dmg", os: [self-hosted, macOS], cc: "clang", cxx: "clang++", build_type: "Release", cmake_flags: "-G Ninja -DCMAKE_PREFIX_PATH=/opt/homebrew/Cellar/qt@5/5.15.5_1/lib/cmake/Qt5", cpack_flags: "-G DragNDrop", } steps: - name: Install Dependencies (macOS) if: runner.os == 'macOS' run: | if uname -p | grep -q "arm" ; then export PATH=/opt/homebrew/bin:$PATH else # not self-hosted runner brew install ninja eigen glew fi - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: recursive - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: avogadroapp - name: Checkout molecules uses: actions/checkout@v5 with: repository: openchemistry/molecules path: molecules - name: Checkout fragments uses: actions/checkout@v5 with: repository: openchemistry/fragments path: fragments - name: Checkout crystals uses: actions/checkout@v5 with: repository: openchemistry/crystals path: crystals - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: avogadrolibs - name: Checkout i18n uses: actions/checkout@v5 with: repository: openchemistry/avogadro-i18n path: avogadro-i18n - name: Configure run: | if [ ! -d "${{ runner.workspace }}/build" ]; then mkdir "${{ runner.workspace }}/build"; fi cd "${{ runner.workspace }}/build" echo "MACOSX_DEPLOYMENT_TARGET=${{ env.MACOS_TARGET }}" >> $GITHUB_ENV CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake $GITHUB_WORKSPACE ${{env.FEATURES}} -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ${{matrix.config.cmake_flags}} shell: bash - name: Build run: | CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake --build . --config ${{matrix.config.build_type}} ${{matrix.config.build_flags}} shell: bash working-directory: ${{ runner.workspace }}/build - name: Fix Mac plugins if: runner.os == 'macOS' working-directory: ${{ runner.workspace }}/build/prefix/lib/openbabel run: | for plugin in *.so; do for libpath in `otool -L ${plugin} | grep '/Users/runner' | awk '{print $1}'`; do export lib=`echo $libpath | cut -d '/' -f 10`; echo "Fixing $plugin $lib $libpath" install_name_tool -change $libpath @executable_path/../Frameworks/$lib $plugin done done cd .. # build/prefix/lib for plugin in libinchi.?.?.?.dylib; do for libpath in `otool -L ${plugin} | grep '/Users/runner' | awk '{print $1}'`; do export lib=`echo $libpath | cut -d '/' -f 10`; echo "Fixing $plugin $lib $libpath" install_name_tool -change $libpath @executable_path/../Frameworks/$lib $plugin done done otool -L libinchi.?.?.?.dylib cp -p libinchi* ../Avogadro2.app/Contents/Frameworks/ # finally, fixup the binaries #cd ../bin #for exe in obabel obmm eht_bind genXrdPattern; do # for libpath in `otool -L ${exe} | grep '/Users/runner' | awk '{print $1}'`; do # export lib=`echo $libpath | cut -d '/' -f 10`; # echo "Fixing $exe $lib $libpath" # install_name_tool -change $libpath @executable_path/../Frameworks/$lib $exe # done #done - name: Install the Apple certificate # From GitHub docs: https://docs.github.com/en/actions/guides/installing-an-apple-certificate-on-macos-runners-for-xcode-development if: runner.os == 'macOS' working-directory: ${{ runner.workspace }}/build env: BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} NOTARIZE_USERNAME: ${{ secrets.AC_USERNAME }} NOTARIZE_PASSWORD: ${{ secrets.AC_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.CODESIGN_ID }} PRODUCT_BUNDLE_IDENTIFIER: cc.avogadro run: | # create variables if [ -n "${P12_PASSWORD}" ]; then CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db # import certificate and provisioning profile from secrets echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH # create temporary keychain if the cert is non-zero if [ -s $CERTIFICATE_PATH ]; then security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH # import certificate to keychain security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH # signing occurs via avogadroapp cpack instructions fi # certificate exists fi # password exists # remove any previous DMG in case they're still around rm -f avogadroapp/*.dmg - name: Create Mac and Windows Packages shell: bash run: | if [ -z "${P12_PASSWORD}" ]; then unset CODESIGN_IDENTITY # to prevent cpack failing when trying to sign fi [[ ! "${GITHUB_REF}" =~ "tags" ]] && export SNAPSHOT_DATE=`date -j "+%d-%m-%y"` cpack ${{ matrix.config.cpack_flags }} working-directory: ${{ runner.workspace }}/build/avogadroapp env: P12_PASSWORD: ${{ secrets.P12_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.CODESIGN_ID }} - name: Notarize Mac DMG if: runner.os == 'macOS' run: | # check if we have the password and the username if [ -n "${NOTARIZE_PASSWORD}" ] && [ -n "${NOTARIZE_USERNAME}" ]; then codesign -s "$CODESIGN_IDENTITY" --timestamp Avogadro2*.dmg xcrun notarytool submit Avogadro2*.dmg --apple-id "$NOTARIZE_USERNAME" --team-id "$NOTARIZE_TEAM" --password "$NOTARIZE_PASSWORD" --verbose --wait xcrun stapler staple -v Avogadro2*.dmg fi working-directory: ${{ runner.workspace }}/build/avogadroapp env: NOTARIZE_TEAM: ${{ secrets.AC_TEAM }} NOTARIZE_USERNAME: ${{ secrets.AC_USERNAME }} NOTARIZE_PASSWORD: ${{ secrets.AC_PASSWORD }} CODESIGN_IDENTITY: ${{ secrets.CODESIGN_ID }} continue-on-error: true - name: Upload if: matrix.config.artifact != 0 uses: actions/upload-artifact@v4 with: path: ${{ runner.workspace }}/build/avogadroapp/Avogadro2*.* name: ${{ matrix.config.artifact }} - name: Cleanup if: ${{ always() }} # To ensure this step runs even when earlier steps fail shell: bash run: | ls -la ./ rm -rf ./* || true rm -rf ./.??* || true ls -la ./ avogadrolibs-1.101.0/.github/workflows/build_qt6.yml000066400000000000000000000063711506155467400223630ustar00rootroot00000000000000name: Qt6 Build Matrix # Many thanks to Cristian Adam for examples # e.g. https://github.com/cristianadam/HelloWorld/blob/master/.github/workflows/build_cmake.yml # https://cristianadam.eu/20191222/using-github-actions-with-c-plus-plus-and-cmake/ on: [push, pull_request, workflow_dispatch] env: QT_VERSION: 6.8.3 # this is different from MACOSX_DEPLOYMENT_TARGET to prevent build problems # we set MACOSX_DEPLOYMENT_TARGET later MACOS_TARGET: 12 FEATURES: -DBUILD_GPL_PLUGINS=ON -DBUILD_MOLEQUEUE=OFF -DWITH_COORDGEN=OFF -DUSE_VTK=ON -DQT_VERSION=6 jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: - { name: "macOS Qt6", artifact: "", os: macos-latest, cc: "clang", cxx: "clang++", build_type: "Release", cmake_flags: "-G Ninja -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64'", cpack_flags: "-G DragNDrop", } # for now disable Windows for Qt6 builds # - { # name: "Windows Qt6", artifact: "", # os: windows-latest, # cc: "cl", cxx: "cl", # build_type: "Release", # cmake_flags: "", # build_flags: "-j 2", # cpack_flags: "-G NSIS", # } steps: - name: Install Dependencies (macOS) if: runner.os == 'macOS' run: | brew install ninja eigen glew - name: Install Dependencies (Windows) if: runner.os == 'Windows' run: choco install ninja - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: recursive - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: avogadroapp - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: avogadrolibs - name: Install Qt uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} - name: Configure MSVC Command Prompt if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 with: arch: x64 - name: Configure run: | if [ ! -d "${{ runner.workspace }}/build" ]; then mkdir "${{ runner.workspace }}/build"; fi cd "${{ runner.workspace }}/build" # won't have any effect except on Mac echo "MACOSX_DEPLOYMENT_TARGET=${{ env.MACOS_TARGET }}" >> $GITHUB_ENV CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake $GITHUB_WORKSPACE ${{env.FEATURES}} -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ${{matrix.config.cmake_flags}} shell: bash - name: Build run: | CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake --build . --config ${{matrix.config.build_type}} ${{matrix.config.build_flags}} shell: bash working-directory: ${{ runner.workspace }}/build - name: Upload if: matrix.config.artifact != 0 uses: actions/upload-artifact@v4 with: path: ${{ runner.workspace }}/build/avogadroapp/Avogadro2*.* name: ${{ matrix.config.artifact }} - name: Setup tmate session if: ${{ failure() }} uses: mxschmitt/action-tmate@v3 avogadrolibs-1.101.0/.github/workflows/build_wheels.yml000066400000000000000000000044141506155467400231340ustar00rootroot00000000000000name: Build Wheels on: [push, pull_request] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: # builds to skip (e.g. skip 32-bit windows) CIBW_SKIP: "cp38-macosx_arm64 *-manylinux_i686 *-win32" # Need to do some setup before repairing the wheel on linux... CIBW_REPAIR_WHEEL_COMMAND_LINUX: bash scripts/github-actions/repair_command_linux.sh CIBW_BEFORE_ALL_LINUX: bash scripts/github-actions/repair-linux.sh # Specify eigen location for windows CIBW_ENVIRONMENT_WINDOWS: "EXTRA_CMAKE_ARGS=-DEIGEN3_INCLUDE_DIR:PATH=/c/eigen" # On Mac build both x64 and arm64 CIBW_ARCHS_MACOS: "x86_64 arm64" CIBW_TEST_REQUIRES: pytest # Run a very simple test to make sure the wheels are working CIBW_TEST_COMMAND: pytest {project}/scripts/github-actions/simple_test.py # We can't currently test Mac arm64 builds on x64 CIBW_TEST_SKIP: "*_arm64" # Use bash by default for the run command defaults: run: shell: bash jobs: build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14] steps: - uses: actions/checkout@v5 with: # Grab the whole history so that setuptools-scm can see the tags and # give it a correct version even on non-tag push. fetch-depth: 0 - name: Install dependencies run: . ./scripts/github-actions/install.sh - name: Build wheels uses: pypa/cibuildwheel@v3.1.4 - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl upload_pypi: needs: build_wheels name: Upload wheels to PyPI runs-on: ubuntu-latest # upload to PyPI on every tag push if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/') steps: - uses: actions/download-artifact@v5 with: # unpacks all CIBW artifacts into dist/ pattern: cibw-* path: dist merge-multiple: true - uses: pypa/gh-action-pypi-publish@master with: user: __token__ password: ${{ secrets.pypi_api_token }} avogadrolibs-1.101.0/.github/workflows/build_windows.yml000066400000000000000000000125431506155467400233410ustar00rootroot00000000000000name: CMake Windows Build # Many thanks to Cristian Adam for examples # e.g. https://github.com/cristianadam/HelloWorld/blob/master/.github/workflows/build_cmake.yml # https://cristianadam.eu/20191222/using-github-actions-with-c-plus-plus-and-cmake/ # This workflow will build and sign on Windows # .. since SignPath requires only a Windows build in the action # .. to successfully sign on: [push, workflow_dispatch] env: QT_VERSION: 5.15.2 FEATURES: -DBUILD_GPL_PLUGINS=ON -DWITH_COORDGEN=OFF -DUSE_VTK=ON -DUSE_3DCONNEXION=ON concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: - { name: "Windows Latest MSVC", artifact: "Win64.exe", os: windows-latest, cc: "cl", cxx: "cl", build_type: "Release", cmake_flags: "-DBUILD_MOLEQUEUE=OFF", build_flags: "", cpack_flags: "-G NSIS", ssl_env: "D:\\a\\avogadrolibs\\Qt\\Tools\\OpenSSLv3\\Win_x64", } steps: - name: Install Dependencies (Windows) if: runner.os == 'Windows' run: choco install ninja nsis - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: recursive - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: avogadroapp - name: Checkout molecules uses: actions/checkout@v5 with: repository: openchemistry/molecules path: molecules - name: Checkout fragments uses: actions/checkout@v5 with: repository: openchemistry/fragments path: fragments - name: Checkout crystals uses: actions/checkout@v5 with: repository: openchemistry/crystals path: crystals - name: Checkout i18n uses: actions/checkout@v5 with: repository: openchemistry/avogadro-i18n path: avogadro-i18n - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: avogadrolibs - name: Install Qt uses: jurplel/install-qt-action@v3 with: aqtversion: '==3.1.*' cache: true version: ${{ env.QT_VERSION }} - name: Configure MSVC Command Prompt if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 with: arch: x64 - name: Install OpenSSL (Win64) if: runner.os == 'Windows' uses: jurplel/install-qt-action@v3 with: aqtversion: '==3.1.*' cache: true host: 'windows' target: 'desktop' tools-only: 'true' tools: 'tools_opensslv3_x64' - name: Grab cache files uses: actions/cache@v4 if: runner.os != 'Windows' with: path: | ${{ runner.workspace }}/build/Downloads key: ${{ matrix.config.name }}-downloads - name: Configure run: | if [ ! -d "${{ runner.workspace }}/build" ]; then mkdir "${{ runner.workspace }}/build"; fi cd "${{ runner.workspace }}/build" CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake $GITHUB_WORKSPACE ${{env.FEATURES}} -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ${{matrix.config.cmake_flags}} shell: bash - name: Build run: | CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake --build . --config ${{matrix.config.build_type}} ${{matrix.config.build_flags}} shell: bash working-directory: ${{ runner.workspace }}/build - name: Create Windows Package if: matrix.config.os == 'windows-latest' shell: bash run: | [[ ! "${GITHUB_REF}" =~ "tags" ]] && export SNAPSHOT_DATE=`date -j "+%d-%m-%y"` cpack ${{ matrix.config.cpack_flags }} working-directory: ${{ runner.workspace }}/build/avogadroapp continue-on-error: true env: OPENSSL_ROOT_DIR: ${{ matrix.config.ssl_env }} - name: Upload if: matrix.config.artifact != 0 id: upload-artifact uses: actions/upload-artifact@v4 with: path: ${{ runner.workspace }}/build/avogadroapp/Avogadro*.* name: ${{ matrix.config.artifact }} - name: Sign Windows release if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/') uses: signpath/github-action-submit-signing-request@v1 with: api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' organization-id: '${{ secrets.SIGNPATH_ORG_ID }}' project-slug: 'avogadrolibs' signing-policy-slug: 'release-signing' github-artifact-id: '${{ steps.upload-artifact.outputs.artifact-id }}' wait-for-completion: true output-artifact-directory: '../build/' - name: Upload if: matrix.config.artifact != 0 id: upload-signed-artifact uses: actions/upload-artifact@v4 with: path: ${{ runner.workspace }}/build/Avogadro2*.* name: 'Win64-signed.exe' - name: Setup tmate session if: failure() uses: mxschmitt/action-tmate@v3 - name: Cleanup if: ${{ always() }} # To ensure this step runs even when earlier steps fail shell: bash run: | ls -la ./ rm -rf ./* || true rm -rf ./.??* || true ls -la ./ avogadrolibs-1.101.0/.github/workflows/clang-format-check.yml000066400000000000000000000044451506155467400241170ustar00rootroot00000000000000name: clang-format Check on: [push, pull_request] jobs: formatting-check: name: Formatting Check runs-on: ubuntu-latest if: ${{ github.ref != 'refs/heads/master' }} steps: - name: 'Install clang-format' run: sudo apt-get -qq install clang-format - uses: actions/checkout@v5 - name: 'Run clang-format-diff' run: | git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/openchemistry/avogadrolibs git fetch origin master:master echo `which clang-format-diff` export MASTER_SHA=`cat .git/refs/heads/master` echo MASTER_SHA=${MASTER_SHA} git diff `cat .git/refs/heads/master` --name-only DIFF=`git diff -U0 ${MASTER_SHA} -- '*.h' '*.cpp' | clang-format-diff -p1` if [ -z "$DIFF" ]; then printf "clang-format-diff reports no problems" exit 0 else git diff -U0 ${MASTER_SHA} -- '*.h' '*.cpp' | clang-format-diff -p1 >${{ runner.workspace }}/clang-format.diff exit 1 fi - name: Upload patch if: failure() uses: actions/upload-artifact@v4 with: path: ${{ runner.workspace }}/clang-format.diff name: clang-format.diff - name: Comment on diff if: failure() run: | export NAME=`curl "https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" | jq '.artifacts[].name'` export DL=`curl "https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" | jq '.artifacts[].archive_download_url'` export URL="https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" echo URL=${URL} echo NAME=${NAME} echo DL=${DL} curl "https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" jq -nc "{\"body\": \"ERROR: clang-format-diff detected formatting issues. See the artifact for a patch or run clang-format on your branch.\"}" | \ curl -sL -X POST -d @- \ -H "Content-Type: application/json" \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ "https://api.github.com/repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/comments" avogadrolibs-1.101.0/.github/workflows/clang-tidy.yml000066400000000000000000000070011506155467400225140ustar00rootroot00000000000000name: Clang-Tidy Static Analysis on: workflow_dispatch: env: FEATURES: -DUSE_VTK=ON -DBUILD_GPL_PLUGINS=ON -DWITH_COORDGEN=OFF -DUSE_YAEHMOP=ON BUILD_TYPE: RelWithDebInfo QT_VERSION: 5.15.2 jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: - { name: "Ubuntu Analysis", os: ubuntu-latest, cc: "clang", cxx: "clang++", cmake_flags: "-G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DUSE_SYSTEM_LIBXML2=ON -USE_SYSTEM_ZLIB=ON", cpack: "", } steps: - name: Install Dependencies (Linux) if: runner.os == 'Linux' run: | sudo apt-get -qq update sudo apt-get -qq install ninja-build bear libeigen3-dev libboost-all-dev libglew-dev libxml2-dev sudo apt-get -qq install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5x11extras5-dev libqt5svg5-dev sudo apt-get -qq install clang-tidy - name: Install Qt uses: jurplel/install-qt-action@v3 with: cache: true version: ${{ env.QT_VERSION }} - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: recursive - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: avogadroapp - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: avogadrolibs fetch-depth: 2 - name: Grab cache files uses: actions/cache@v4 if: runner.os != 'Windows' with: path: | ${{ runner.workspace }}/build/thirdparty ${{ runner.workspace }}/build/Downloads key: ${{ matrix.config.name }}-thirdparty - name: Configure run: | if [ ! -d "${{ runner.workspace }}/build" ]; then mkdir "${{ runner.workspace }}/build"; fi cd "${{ runner.workspace }}/build" CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} cmake $GITHUB_WORKSPACE ${{env.FEATURES}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{matrix.config.cmake_flags}} shell: bash - name: Build run: | CC=${{matrix.config.cc}} CXX=${{matrix.config.cxx}} ninja # only re-compile avogadrolibs (cd avogadrolibs; ninja clean) bear -- ninja shell: bash working-directory: ${{ runner.workspace }}/build - name: Create results directory run: | mkdir ${{ runner.workspace }}/clang-tidy-result - name: Analyze run: | # generate the fixes and we'll make a diff run-clang-tidy -p ../build -fix cd avogadrolibs pwd echo "Generating diff" git diff >${{ runner.workspace }}/clang-tidy-result/tidy.patch working-directory: ${{ runner.workspace }}/avogadrolibs - name: Save PR metadata run: | echo ${{ github.event.number }} > ${{ runner.workspace }}/clang-tidy-result/pr-id.txt echo ${{ github.event.pull_request.head.repo.full_name }} > ${{ runner.workspace }}/clang-tidy-result/pr-head-repo.txt echo ${{ github.event.pull_request.head.ref }} > ${{ runner.workspace }}/clang-tidy-result/pr-head-ref.txt - uses: actions/upload-artifact@v4 with: name: clang-tidy-result path: ${{ runner.workspace }}/clang-tidy-result/ - name: Setup tmate session if: ${{ failure() }} uses: mxschmitt/action-tmate@v3 avogadrolibs-1.101.0/.github/workflows/codacy.yml000066400000000000000000000051351506155467400217310ustar00rootroot00000000000000# This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. # This workflow checks out code, performs a Codacy security scan # and integrates the results with the # GitHub Advanced Security code scanning feature. For more information on # the Codacy security scan action usage and parameters, see # https://github.com/codacy/codacy-analysis-cli-action. # For more information on Codacy Analysis CLI in general, see # https://github.com/codacy/codacy-analysis-cli. name: Codacy Security Scan on: workflow_dispatch: # push: # branches: [ master ] # pull_request: # # The branches below must be a subset of the branches above # branches: [ master ] # schedule: # - cron: '28 10 * * 6' permissions: contents: read jobs: codacy-security-scan: name: Codacy Security Scan permissions: contents: read # for actions/checkout to fetch code security-events: write # for github/codeql-action/upload-sarif to upload SARIF results runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true steps: # Checkout the repository to the GitHub Actions runner - name: Checkout code uses: actions/checkout@v5 # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - name: Run Codacy Analysis CLI uses: codacy/codacy-analysis-cli-action@master with: # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository # You can also omit the token and run the tools that support default configurations project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} verbose: true output: results.sarif format: sarif # Adjust severity of non-security issues gh-code-scanning-compat: true # Force 0 exit code to allow SARIF file generation # This will handover control about PR rejection to the GitHub side max-allowed-issues: 2147483647 - name: Filter out MISRA run: | pip install globber python3 scripts/github-actions/filter_sarif.py --input results.sarif --output filtered.sarif --split-lines -- "-**/*.*:cppcheck_misra*" # Upload the SARIF file generated in the previous step - name: Upload SARIF results file uses: github/codeql-action/upload-sarif@v3 with: sarif_file: filtered.sarif avogadrolibs-1.101.0/.github/workflows/codeql.yml000066400000000000000000000065041506155467400217370ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ "master" ] pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] schedule: - cron: '42 6 * * 6' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: QT_VERSION: 5.15.2 FEATURES: -DBUILD_GPL_PLUGINS=ON -DWITH_COORDGEN=OFF jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'cpp' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Use only 'java' to analyze code written in Java, Kotlin or both # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout openchemistry uses: actions/checkout@v5 with: repository: openchemistry/openchemistry submodules: recursive - name: Checkout avogadroapp uses: actions/checkout@v5 with: repository: openchemistry/avogadroapp path: avogadroapp - name: Checkout avogadrolibs uses: actions/checkout@v5 with: path: avogadrolibs - name: Install Dependencies (Linux) if: runner.os == 'Linux' run: | sudo apt-get -qq update sudo apt-get -qq install ninja-build libeigen3-dev libboost-all-dev libglew-dev libxml2-dev sudo apt-get -qq install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5x11extras5-dev libqt5svg5-dev - name: Install Qt uses: jurplel/install-qt-action@v3 with: cache: True version: ${{ env.QT_VERSION }} - name: Grab cache files uses: actions/cache@v4 with: path: | ${{ runner.workspace }}/build/thirdparty ${{ runner.workspace }}/build/Downloads key: ${{ matrix.config.name }}-thirdparty - name: Configure run: | cmake $GITHUB_WORKSPACE ${{env.FEATURES}} -DCMAKE_BUILD_TYPE=RelWithDebInfo ${{matrix.config.cmake_flags}} shell: bash # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} source-root: ${{ github.workspace }}/avogadrolibs queries: security-extended,security-and-quality config-file: ${{ github.workspace }}/avogadrolibs/.github/codeql-config.yml - name: Build run: | cmake --build . -j 4 --config RelWithDebInfo shell: bash - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" checkout_path: ${{ github.workspace }}/avogadrolibs avogadrolibs-1.101.0/.github/workflows/misspell-fixer.yml000066400000000000000000000005001506155467400234210ustar00rootroot00000000000000name: misspell-fixer on: [workflow_dispatch] jobs: spelling-check: name: Spelling Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: misspell-fixer check for code and comments uses: sobolevn/misspell-fixer-action@master with: options: '-rsnuRVd avogadro/' avogadrolibs-1.101.0/.github/workflows/release-drafter.yml000066400000000000000000000015151506155467400235320ustar00rootroot00000000000000name: Release Drafter # https://github.com/release-drafter/release-drafter on: push: # branches to consider in the event; optional, defaults to all branches: - master # pull_request event is required only for autolabeler pull_request: # Only following types are handled by the action, but one can default to all as well types: [opened, reopened, synchronize] jobs: update_release_draft: runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" - uses: release-drafter/release-drafter@v6 # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml # with: # config-name: my-config.yml # disable-autolabeler: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} avogadrolibs-1.101.0/.github/workflows/update-i18n-templates.yml000066400000000000000000000015161506155467400245210ustar00rootroot00000000000000name: update-i18n-templates on: workflow_dispatch: schedule: # * is a special character in YAML so you have to quote this string # run weekly, every Sunday at 01:23 - cron: '23 1 * * 0' jobs: update-i18n: name: Update translation templates runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Install packages run: | sudo apt-get -qq update sudo apt-get -qq install gettext - name: Run extract-messages run: | sh scripts/extract-messages.sh - name: Create pull request uses: peter-evans/create-pull-request@v7 with: commit-message: "Automated translation updates" signoff: true title: "Automated translation updates" branch: update-i18n-templates delete-branch: true labels: i18n avogadrolibs-1.101.0/.gitignore000066400000000000000000000052501506155467400163350ustar00rootroot00000000000000docs/build .DS_Store .vscode # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ avogadrolibs-1.101.0/.misspell-fixer.ignore000066400000000000000000000000411506155467400205640ustar00rootroot00000000000000^avogadro/quantumio/gamessus.cpp avogadrolibs-1.101.0/.pre-commit-config.yaml000066400000000000000000000010511506155467400206210ustar00rootroot00000000000000# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - id: check-json - id: check-case-conflict - id: check-toml - repo: https://github.com/pre-commit/mirrors-clang-format rev: 'v17.0.6' # Use the sha / tag you want to point at hooks: - id: clang-format avogadrolibs-1.101.0/CITATION.cff000066400000000000000000000023531506155467400162400ustar00rootroot00000000000000# This CITATION.cff file was generated with cffinit. # Visit https://bit.ly/cffinit to generate yours today! cff-version: 1.2.0 title: Avogadro message: >- Please cite this software using the metadata from 'preferred-citation'. type: software authors: - given-names: Marcus family-names: Hanwell orcid: 'https://orcid.org/0000-0002-5851-5272' - given-names: Geoffrey family-names: Hutchison affiliation: University of Pittsburgh orcid: 'https://orcid.org/0000-0002-1757-1980' identifiers: - type: doi value: 10.1186/1758-2946-4-17 description: Avogadro Paper - type: doi value: 10.1186/1758-2946-5-25 description: Avogadro2 Chemical Markup Language repository-code: 'https://github.com/openchemistry/avogadrolibs' url: 'https://two.avogadro.cc/' abstract: >- Avogadro is an advanced molecule editor and visualizer designed for cross-platform use in computational chemistry, molecular modeling, bioinformatics, materials science, and related areas. It offers flexible high quality rendering and a powerful plugin architecture. keywords: - open source - cheminformatics - computational chemistry - scientific visualization - open chemistry - open science - OpenGL license: BSD-3-Clause avogadrolibs-1.101.0/CMakeLists.txt000066400000000000000000000115321506155467400171050ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.24 FATAL_ERROR) project(AvogadroLibs) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # Use C++17 when possible option(PYTHON_WHEEL_BUILD "Is this a Python wheel build?" OFF) mark_as_advanced(PYTHON_WHEEL_BUILD) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_EXTENSIONS False) # Set symbol visibility defaults for all targets. set(CMAKE_CXX_VISIBILITY_PRESET "hidden") set(CMAKE_VISIBILITY_INLINES_HIDDEN True) include(BuildType) include(BuildLocation) include(CompilerFlags) include(InstallLocation) include(DetermineVersion) # Set up our version. set(AvogadroLibs_VERSION_MAJOR "1") set(AvogadroLibs_VERSION_MINOR "101") set(AvogadroLibs_VERSION_PATCH "0") set(AvogadroLibs_VERSION "${AvogadroLibs_VERSION_MAJOR}.${AvogadroLibs_VERSION_MINOR}.${AvogadroLibs_VERSION_PATCH}") find_package(Git) determine_version(${AvogadroLibs_SOURCE_DIR} ${GIT_EXECUTABLE} "AvogadroLibs") # path for data / fragment files which could be overriden by package maintainers # by default we assume we're using the openchemistry superbuild layout # .. this is used by the fragment library, molecules, crystals, etc. set(AvogadroLibs_SOURCEDATA_DIR "${AvogadroLibs_SOURCE_DIR}/..") option(BUILD_SHARED_LIBS "Build with shared libraries" ON) # Before any plugins are defined, and before any add_subdirectory calls: set_property(GLOBAL PROPERTY AvogadroLibs_PLUGINS) set_property(GLOBAL PROPERTY AvogadroLibs_STATIC_PLUGINS) if(MSVC) add_definitions("-D_CRT_SECURE_NO_WARNINGS" "-DNOMINMAX -D_USE_MATH_DEFINES") # Ensure __cplusplus is correct, otherwise it defaults to 199711L which isn't true # https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-160 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus") message(STATUS "Setting MSVC debug information format to 'Embedded'") set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:Embedded>") set(CMAKE_VS_GLOBALS "UseMultiToolTask=true" "DebugInformationFormat=OldStyle" ) endif() option(ENABLE_TESTING "Enable testing and building the tests." OFF) option(TEST_QTGL "Build the Qt OpenGL test application" OFF) option(USE_OPENGL "Enable libraries that use OpenGL" ON) option(USE_HDF5 "Enable optional HDF5 features" OFF) option(USE_QT "Enable libraries that use Qt 5 or 6" ON) option(USE_VTK "Enable libraries that use VTK" OFF) option(USE_LIBARCHIVE "Enable optional Libarchive features" ON) option(USE_LIBMSYM "Enable optional features using libmsym" ON) option(USE_SPGLIB "Enable optional features using spglib" ON) option(USE_MMTF "Enable optional features using mmtf" OFF) # MMTF is no longer maintained option(USE_PYTHON "Use Python to wrap some of our API" OFF) option(USE_EXTERNAL_NLOHMANN "Use an externally-provided version of the nlohmann JSON library" OFF) option(USE_EXTERNAL_PUGIXML "Use an externally-provided version of pugixml" OFF) option(USE_EXTERNAL_STRUCT "Use an externally-provided version of the struct binary data formatting library" OFF) set(QT_VERSION "5" CACHE STRING "What major version of Qt") set(QT_VERSIONS_SUPPORTED 5 6) set_property(CACHE QT_VERSION PROPERTY STRINGS 5 6) if(NOT QT_VERSION IN_LIST QT_VERSIONS_SUPPORTED) message(FATAL_ERROR "Qt version must be one of ${QT_VERSIONS_SUPPORTED}") endif() add_subdirectory(thirdparty) add_subdirectory(utilities) add_subdirectory(avogadro) if(ENABLE_TESTING) include(CTest) enable_testing() add_subdirectory(tests) endif() option(BUILD_DOCUMENTATION "Build project documentation" OFF) if(BUILD_DOCUMENTATION) add_subdirectory(docs) endif() if(USE_PYTHON) add_subdirectory(python) endif() # SKBUILD is set for binary wheel if (NOT SKBUILD) install( FILES README.md CONTRIBUTING.md LICENSE DESTINATION "${INSTALL_DOC_DIR}/avogadrolibs") endif() # After all add_subdirectory calls, so the list of plugins is complete: get_property(AvogadroLibs_PLUGINS GLOBAL PROPERTY AvogadroLibs_PLUGINS) get_property(AvogadroLibs_STATIC_PLUGINS GLOBAL PROPERTY AvogadroLibs_STATIC_PLUGINS) configure_file(${AvogadroLibs_SOURCE_DIR}/cmake/CTestCustom.cmake.in ${AvogadroLibs_BINARY_DIR}/CTestCustom.cmake) configure_file("${AvogadroLibs_SOURCE_DIR}/cmake/AvogadroLibsConfig.cmake.in" "${AvogadroLibs_BINARY_DIR}/AvogadroLibsConfig.cmake" @ONLY) configure_file("${AvogadroLibs_SOURCE_DIR}/cmake/AvogadroLibsConfigVersion.cmake.in" "${AvogadroLibs_BINARY_DIR}/AvogadroLibsConfigVersion.cmake" @ONLY) # SKBUILD is set for binary wheel if (NOT SKBUILD) install(FILES "${AvogadroLibs_BINARY_DIR}/AvogadroLibsConfig.cmake" "${AvogadroLibs_BINARY_DIR}/AvogadroLibsConfigVersion.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findlibmsym.cmake" DESTINATION "${INSTALL_LIBRARY_DIR}/cmake/avogadrolibs") install(EXPORT "AvogadroLibsTargets" NAMESPACE Avogadro:: DESTINATION "${INSTALL_LIBRARY_DIR}/cmake/avogadrolibs") endif() avogadrolibs-1.101.0/CODE_OF_CONDUCT.md000066400000000000000000000062411506155467400171450ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at avogadro-devel@lists.sourceforge.net. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ avogadrolibs-1.101.0/CONTRIBUTING.md000066400000000000000000000172221506155467400166000ustar00rootroot00000000000000# Contributing Avogadro welcomes new development and ideas! We are an open community, made better by all of us. If you're an Avogadro user who wants to give back, a great place to start is supporting your fellow users on the [Avogadro Forum](https://discuss.avogadro.cc/). Questions of all levels are posted, and you may find that there are some for which your experience is very valuable! If you find a bug or have a feature suggestion, please take a moment to submit a description to the [GitHub Tracker](https://github.com/openchemistry/avogadrolibs/issues/), which helps as an open database of issues and suggestions as the code is developed. While there are several different GitHub repositories for the project, we will move issues around if they better fit a particular component. For development of Avogadro itself, the [Avogadro Forum](https://discuss.avogadro.cc/) has sections for discussing ideas, planned directions, work in progress, etc. This includes both the C++ libraries and Python scripts and utilities. ## Contributing Code Our project uses the standard GitHub pull request process for code review and integration. Please check our [development][Development] guide for more details on developing and contributing to the project. The [GitHub issue tracker](https://github.com/openchemistry/avogadrolibs/issues) can be used to report bugs, make feature requests, etc. The best way to coordinate development, get support, and offer feedback is through the [Avogadro Discussion forum](https://discuss.avogadro.cc/) If you are new to Git, the Software Carpentry's [Version Control with Git](https://swcarpentry.github.io/git-novice/) tutorial is a good place to start. More learning resources are listed in the [GitHub Learning Resources] (https://help.github.com/en/github/getting-started-with-github/git-and-github-learning-resources) 1. Make sure you have a free [GitHub](https://github.com/) account. To increase the security of your account, we strongly recommend that you configure [two-factor authentication](https://docs.github.com/en/github/authenticating-to-github/securing-your-account-with-two-factor-authentication-2fa). Additionally, you may want to [sign your commits](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification). We will not accept commits without your full name in the signature for proper attribution and citation. Please do not submit (semi-)anonymous contributions. 2. Fork the [OpenChemistry repository](https://github.com/openchemistry/openchemistry) on GitHub to make your changes. To keep your copy up to date with respect to the main repository, you need to frequently [sync your fork](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork): In general, most changes occur in the `avogadrolibs` repository, with some fewer changes in the `avogadroapp` repository too. ``` git remote add upstream https://github.com/openchemistry/avogadrolibs git fetch upstream git checkout dev git merge upstream/dev ``` 3. Build OpenChemistry and `avogadrolibs`. You will find full instructions on [building from source code](http://two.avogadro.cc/install/build.html) on the Avogadro2 website. 4. Avogadro contains dozens of tests of different types and complexity and running each is difficult and probably not reasonable on your workstation. Most will be run as part of our Continuous Integration as part of a pull request. If possible, also try to add new tests for the features added or bugs fixed by your pull request. Developers reviewing your pull request will be happy to help you add or run the relevant tests as part of the pull request review process. 5. Write a useful and properly formatted commit message. Follow [these guidelines and template](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines), in particular start your message with a short imperative sentence on a single line, possibly followed by a blank line and a more detailed explanation. In the detailed explanation it's good to include relevant external references (e.g. GitHub issue fixed) using full URLs, and errors or tracebacks the commit is supposed to fix. You can use the Markdown syntax for lists and code highlighting, wrapping the explanation text at 72 characters when possible. 6. Commit and push your changes to your [fork](https://help.github.com/en/github/using-git/pushing-commits-to-a-remote-repository). 7. Open a [pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request) with these changes. Your pull request message ideally should include: * Why you made the changes (e.g. references to GitHub issues being fixed). * A description of the implementation of the changes. * How to test the changes, if you haven't included specific tests already. 8. The pull request should pass all the continuous integration tests which are automatically started by GitHub. 9. Your pull request will be handled by code review and the continuous integration tests. ## Coding Conventions ### C++ Features * Don't use exceptions or asserts - Avogadro is production code and should never crash * Prefer solutions from the Qt library over Boost/others in Qt dependent code * In Avogadro use the C++11 or C++17 features where necessary, they fall back to Boost on older compilers * Be careful about using newer language features, since Avogadro runs on many different compilers and platforms. Visual C++ and Clang may not support every feature in GCC. * Minimize dependencies on third party libraries, think carefully before adding more. If in doubt, please ask. * Use templates where they make sense ### Casting * Avoid C-style casts, prefer C++ (static_cast, dynamic_cast, const_cast, reinterpret_cast) * For Qt classes, and Qt derived classes prefer qobject_cast over dynamic_cast ### Aesthetics * Prefer enums to define constants over static const int or defines * Prefer verbose argument names in headers * Most IDEs show the argument names in their autocompletion * It looks better in the generated documentation * Poor style making people guess what an argument is for * Avoid abbreviations, as they are often ambiguous and we can afford the extra bytes * Please use comments and docstrings frequently. You will appreciate it yourself when looking back over code. It may make sense now, but in a few weeks or months, you may not remember why you wrote that particular implementation. (Let alone reading other people's code in a large project.) For more on [Coding Style](http://two.avogadro.cc/contrib/style.html) see the [style guide](http://two.avogadro.cc/contrib/style.html) which is enforced through `clang-format` which runs as part of our continuous integration. ## A Quick Note About Plugins For the most part, Avogadro plugins should be published to your own repository. Examples: * Input Generators: * Example Commands: * cclib Import: * Nanocar Builder: * ASE: * RDKit: * GenIce: * SciKitNano: [Development]: http://two.avogadro.cc/contrib/code.html "Development guide" [Wiki]: http://wiki.openchemistry.org/ "Open Chemistry wiki" [Doxygen]: http://two.avogadro.cc/api/index.html "API documentation" avogadrolibs-1.101.0/CTestConfig.cmake000066400000000000000000000003761506155467400175230ustar00rootroot00000000000000set(CTEST_PROJECT_NAME "AvogadroLibs") set(CTEST_NIGHTLY_START_TIME "23:59:59 UTC") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "cdash.openchemistry.org") set(CTEST_DROP_LOCATION "/submit.php?project=AvogadroLibs") set(CTEST_DROP_SITE_CDASH TRUE) avogadrolibs-1.101.0/LICENSE000066400000000000000000000027451506155467400153600ustar00rootroot00000000000000Copyright (c) 2011-2020, Kitware, Inc. 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. avogadrolibs-1.101.0/PRIVACY.md000066400000000000000000000061711506155467400160070ustar00rootroot00000000000000 # Privacy Policy This privacy policy applies to the Avogadro application (hereby referred to as "Application") that was created by the Avogadro Project (hereby referred to as "Service Provider") as an Open Source service. This service is intended for use "AS IS". **What information does the Application obtain and how is it used?** The Application does not obtain any information when you download and use it. Registration is not required to use the Application. **Does the Application collect precise real time location information of the device?** This Application does not collect precise information about the location of your device. **Do third parties see and/or have access to information obtained by the Application?** Since the Application does not collect any information, no data is shared with third parties. **What are my opt-out rights?** You can stop all collection of information by the Application easily by uninstalling it. You may use the standard uninstall processes as may be available as part of your device. **Children** The Application is not used to knowingly solicit data from or market to children under the age of 13. The Service Provider does not knowingly collect personally identifiable information from children. The Service Provider encourages all children to never submit any personally identifiable information through the Application and/or Services. The Service Provider encourage parents and legal guardians to monitor their children's Internet usage and to help enforce this Policy by instructing their children never to provide personally identifiable information through the Application and/or Services without their permission. If you have reason to believe that a child has provided personally identifiable information to the Service Provider through the Application and/or Services, please contact the Service Provider (https://discuss.avogadro.cc/) so that they will be able to take the necessary actions. You must also be at least 16 years of age to consent to the processing of your personally identifiable information in your country (in some countries we may allow your parent or guardian to do so on your behalf). **Security** The Service Provider is concerned about safeguarding the confidentiality of your information. However, since the Application does not collect any information, there is no risk of your data being accessed by unauthorized individuals. **Changes** This Privacy Policy may be updated from time to time for any reason. The Service Provider will notify you of any changes to their Privacy Policy by updating this page with the new Privacy Policy. You are advised to consult this Privacy Policy regularly for any changes, as continued use is deemed approval of all changes. This privacy policy is effective as of 2024-12-12 **Your Consent** By using the Application, you are consenting to the processing of your information as set forth in this Privacy Policy now and as amended by the Service Provider. **Contact Us** If you have any questions regarding privacy while using the Application, or have questions about the practices, please contact the Service Provider at https://discuss.avogadro.cc/ avogadrolibs-1.101.0/README.md000066400000000000000000000135471506155467400156340ustar00rootroot00000000000000# ![Avogadro 2][Avogadro2Logo] Avogadro 2 [![Latest Release](https://img.shields.io/github/v/release/openchemistry/avogadrolibs)](https://github.com/OpenChemistry/avogadrolibs/releases) [![BSD License](https://img.shields.io/github/license/openchemistry/avogadrolibs)](https://github.com/OpenChemistry/avogadrolibs/blob/master/LICENSE) [![Build Status](https://img.shields.io/github/actions/workflow/status/openchemistry/avogadrolibs/build_cmake.yml?branch=master)](https://github.com/OpenChemistry/avogadrolibs/actions) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/44bb12662c564ed8a27ee8a7fd89ed50)](https://app.codacy.com/gh/OpenChemistry/avogadrolibs/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Download Count](https://avogadro.cc/downloads.svg?readme)](https://github.com/OpenChemistry/avogadrolibs/releases) [![Citation Count](https://avogadro.cc/citations.svg?readme)](http://doi.org/10.1186/1758-2946-4-17) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) [![GitHub contributors](https://img.shields.io/github/contributors/openchemistry/avogadrolibs.svg?style=flat&color=0bf)](https://github.com/OpenChemistry/avogadrolibs/graphs/contributors) [![OpenCollective Backers](https://img.shields.io/opencollective/all/open-chemistry)](https://opencollective.com/open-chemistry) ## Introduction Avogadro is an advanced molecular editor designed for cross-platform use in computational chemistry, molecular modeling, education, bioinformatics, materials science, and related areas. It offers flexible rendering and a powerful plugin architecture. Core features and goals of the Avogadro project include: * Open-source, distributed under the liberal 3-clause BSD license * Cross-platform, with builds on Linux, Mac OS X and Windows * An intuitive interface designed to be useful to the whole community * Fast and efficient, embracing the latest technologies * Extensible, making extensive use of a plugin architecture * Flexible, supporting a range of chemical data formats and packages Avogadro 2 began as a rewrite of the original [Avogadro 1.x][Avogadro1], which is now unsupported. The successor is faster, better, much more stable, and more featureful. A final couple of features yet to be ported will be implemented by the time of the 2.0 release, but in the meantime Avogadro 2 already has [much new functionality of its own](https://two.avogadro.cc/docs/whats-new-in-avogadro-2/). Avogadro's codebase is split across a [libraries repository](https://github.com/openchemistry/avogadrolibs) and an [application repository](https://github.com/openchemistry/avogadroapp). The new code architecture provides a high-performance rendering engine, modern code development, and significantly improved speed and stability. Avogadro is being developed as part of the [Open Chemistry][OpenChemistry] project by an open community, which was started at [Kitware][Kitware] as an open-source community project. ## Installing For the most up-to-date experience use the nightly builds prepared by GitHub actions for: * [Linux (AppImage)](https://nightly.link/OpenChemistry/avogadrolibs/workflows/build_linux/master/Avogadro2-x86_64.AppImage.zip) * [macOS (Apple Silicon)](https://nightly.link/OpenChemistry/avogadrolibs/workflows/build_m1/master/macOS-arm64.dmg.zip) * [macOS (Intel)](https://nightly.link/OpenChemistry/avogadrolibs/workflows/build_cmake/master/macOS.dmg.zip) * [Windows](https://nightly.link/OpenChemistry/avogadrolibs/workflows/build_cmake/master/Win64.exe.zip) We also maintain a [`beta` Flatpak](https://two.avogadro.cc/install/flatpak.html#install-flatpak-beta) for Linux that is updated with the lastest changes every week or two. For full releases and an overview of all available ways to obtain Avogadro see the [overview](Install) on the Avogadro website. Binaries and the source code for each release can be found on the [GitHub releases page](https://github.com/OpenChemistry/avogadrolibs/releases). If you would like to build from source we recommend that you follow our [build guide][Build]. ## User guide Our [user documentation](https://two.avogadro.cc/docs/) can be found on the Avogadro website, as well as a brief guide to [getting started](https://two.avogadro.cc/docs/getting-started/). ## Contributing We welcome *all* kinds of contributions as a community project, from bug reports, feature suggestions, language translations, Python plugins, and C++ code development. Our project uses the standard GitHub pull request process for code review and integration. Please check our [contribution][Contribution] guide for more details on developing and contributing to the project. The [GitHub issue tracker](https://github.com/openchemistry/avogadrolibs/issues/) can be used to report bugs, make feature requests, etc. Our API is [documented online][API] with updated documentation generated nightly. To introduce yourself, ask for help, or general discussion, we welcome everyone to our [forum](https://discuss.avogadro.cc/) Contributors Hall of Fame: [Avogadro2Logo]: https://raw.githubusercontent.com/OpenChemistry/avogadrolibs/master/docs/avogadro2_64.png "Avogadro2" [OpenChemistry]: https://openchemistry.org/ "Open Chemistry Project" [OpenChemistryLogo]: https://raw.githubusercontent.com/OpenChemistry/avogadrolibs/master/docs/OpenChemistry128.png "Open Chemistry" [Kitware]: https://kitware.com/ "Kitware, Inc." [Avogadro1]: https://avogadro.cc/ "Avogadro 1" [Build]: https://two.avogadro.cc/develop/build "Building Avogadro" [Install]: https://two.avogadro.cc/install/ "Installing Avogadro" [Contribution]: https://two.avogadro.cc/contrib/ "Contribution guide" [API]: https://two.avogadro.cc/develop/classlist/ "API documentation" avogadrolibs-1.101.0/SECURITY.md000066400000000000000000000015531506155467400161400ustar00rootroot00000000000000# Security Policy ## Supported Versions At the moment, Avogadro2 is in beta release. As such, we can most easily support security updates on the most recent release and current nightly snapshots. We will work with distributions (e.g., Ubuntu, FreeBSD, etc.) to provide security patches for widely used long-term-support OS. | Version | Supported | | ------- | ------------------ | | 1.101.x | :white_check_mark: | | 1.100.x | :white_check_mark: | | < 1.100 | :x: | ## Reporting a Vulnerability We currently support private security vulnerability reporting through our [GitHub](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability) pages. We will work to verify the security vulnerability within 14 days and a patch or new release within 30 days of reporting. avogadrolibs-1.101.0/STYLE.md000066400000000000000000000131251506155467400155670ustar00rootroot00000000000000# clang-format We use [clang-format][clang-format] to keep formatting in the code base consistent. Please run clang-format on your patches before submitting. clang-format ships with a Python script ```clang/tools/clang-format-diff.py``` that can be used to reformat patches. For example the following command will reformat all the lines in the latest commit ```shell git diff -U0 HEAD^ | clang-format-diff.py -i -p1 ``` clang-format also provides [git-clang-format][git-clang-format], a script that more closely integrates with git. If you add this script to your path you can using the following command to reformat all the lines in the latest commit. ```shell git clang-format HEAD~1 ``` ## clang-format-diff locations by platform The exact location of the Python script varies by platform/distro. The table below provides the location on some common platform/distro's | Platform/Distro. | Location | | ---------------- |:-------------------------------------:| | Arch Linux | /usr/share/clang/clang-format-diff.py | | Ubuntu | /usr/bin/clang-format-diff-3.8 | The script can also be downloaded [here][clang-format-diff]. ## Code style This project is developed primarily in C++ and Python. Please follow these code style guidelines when contributing code to our project. * Alphabetize includes * Use #include "xxx" for local includes, #include \ for external includes. * Do not add comment separators before function definitions. * Split long lines, when reasonable, to avoid going over 80 characters per line. * Add a space after the commas in parameter lists, e.g., function(a, b, c), not function(a,b,c) * Add spaces between operators, e.g. 5 - 2, not 5-2. * For class names, use CamelCase, starting their names with an upper-case letter. * For local variables and function names, use camelCase, starting names with a lower-case letter. * For member variables, prefix them with m\_, i.e. m\_camelCase, starting the name with a lower-case letter. * For comments, add a space between // and the beginning of the comment, e.g., * // A comment * \# Python comment * Use 2 spaces when indenting C++ code, 4 spaces for Python code. * Do not indent inside namespaces, e.g., ```c++ namespace Avogadro { namespace Core { void foo(); } } ``` * Curly braces marking the start and end of a code block should be on separate lines and aligned vertically with the statement preceding the block, e.g., ```c++ if (condition) { statement; } for (int i = 0; i < n; ++i) { statement; } ``` * Assume that C++11 features are available, and prefer them over legacy macros, defines, etc. A few examples follow, but are not exhaustive. * Use override to specify member overrides in derived classes. * Set default values of member variables directly in definitions. * Use nullptr instead of NULL. ### C++ Features * Don't use exceptions * Prefer solutions from the Qt library over others in Qt dependent code * Minimize dependencies on third party libraries, think carefully before adding more * Use templates where they make sense ### Including Headers * In public headers, always use this form to include project headers: #include * Prefer declaration of types in public headers over including headers for the type ```c++ namespace Avogadro { class MyClass; } ``` * In source files include specialized headers first, then dependency headers, then generic headers ```c++ #include "myapiheader.h" // Our header #include // Avogadro header from a different module #include // Qt header #include // STL ``` * If you need to include the export header for the module do it as the first include ```c++ #include "avogadrorenderingexport.h" ``` * Private headers are denoted by _p.h endings, and should not be included in public headers * Use the Qt module and camel-cased header * Never include Qt module headers such as QtGui, instead include the header for the class being used ```c++ #include // WRONG (module header)! #include // Correct ``` ### Namespaces * Avogadro uses nested namespaces * Everything is inside the Avogadro namespace * Code in the core module is in the Avogadro::Core namespace * Don't overspecify, i.e. code in the Avogadro namespace doesn't need to use Avogadro:: * Qt signals and slots are one exception where MOC often needs a little help * Never use using inside a public header * Only pull in specific symbols in source files, i.e. using Avogadro::Core::Molecule; ### Casting * Avoid C-style casts, prefer C++ (static_cast, dynamic_cast, const_cast, reinterpret_cast) * For Qt classes, and Qt derived classes prefer qobject_cast over dynamic_cast ### Aesthetics * Prefer enums to define constants over static const int or defines * Prefer verbose argument names in headers * Most IDEs show the argument names in their autocompletion * It looks better in the generated documentation * Poor style making people guess what an argument is for * Avoid abbreviations, as they are often ambiguous and we can afford the extra bytes [clang-format]: http://llvm.org/releases/3.8.0/tools/clang/docs/ClangFormatStyleOptions.html [git-clang-format]: https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/git-clang-format [flake8]: https://pypi.python.org/pypi/flake8 [clang-format-diff]: https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format-diff.py avogadrolibs-1.101.0/avogadro/000077500000000000000000000000001506155467400161455ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/CMakeLists.txt000066400000000000000000000051441506155467400207110ustar00rootroot00000000000000include(GenerateExportHeader) # May want to adjust the name, this more performs boilerplate library stuff now. function(avogadro_add_library name) # Add an alias target to expose to other targets. add_library(Avogadro::${name} ALIAS ${name}) # Use the new AUTOMOC support for Qt libraries (CMake 2.8.6). if(${name} MATCHES "^Qt") set_target_properties(${name} PROPERTIES AUTOMOC TRUE) endif() if(BUILD_SHARED_LIBS) set_target_properties(${name} PROPERTIES OUTPUT_NAME Avogadro${name} VERSION "${AvogadroLibs_VERSION_MAJOR}.${AvogadroLibs_VERSION_MINOR}.${AvogadroLibs_VERSION_PATCH}" SOVERSION ${AvogadroLibs_VERSION_MAJOR}) endif() string(TOLOWER ${name} _lower_name) string(TOUPPER ${name} _upper_name) # Generate the necessary export headers. generate_export_header(${name} BASE_NAME AVOGADRO${_upper_name} EXPORT_FILE_NAME avogadro${_lower_name}export.h) target_sources(${name} PUBLIC FILE_SET HEADERS FILES "${CMAKE_CURRENT_BINARY_DIR}/avogadro${_lower_name}export.h") # Set up the build and install include directories for the target. target_include_directories(${name} PUBLIC "$" "$" "$" "$") # Install the target and its headers. install(TARGETS ${name} EXPORT "AvogadroLibsTargets" FILE_SET HEADERS DESTINATION "${INSTALL_INCLUDE_DIR}/avogadro" RUNTIME DESTINATION "${INSTALL_RUNTIME_DIR}" LIBRARY DESTINATION "${INSTALL_LIBRARY_DIR}" ARCHIVE DESTINATION "${INSTALL_ARCHIVE_DIR}" INCLUDES DESTINATION "${INSTALL_INCLUDE_DIR}") endfunction() # Simple wrapper to collect boilerplate for adding headers to targets. function(avogadro_headers _name) target_sources(${_name} PUBLIC FILE_SET HEADERS BASE_DIRS ${AvogadroLibs_SOURCE_DIR}/avogadro ${AvogadroLibs_BINARY_DIR}/avogadro FILES ${ARGN}) endfunction() add_subdirectory(core) add_subdirectory(calc) add_subdirectory(io) add_subdirectory(quantumio) # SKBUILD is set for binary wheel if(NOT SKBUILD) add_subdirectory(command) endif() if(USE_OPENGL) add_subdirectory(rendering) endif() if(USE_QT) if (NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") endif() add_subdirectory(qtgui) if(USE_OPENGL) add_subdirectory(qtopengl) endif() # Add unconditionally as this talks to MoleQueue, but doesn't depend on it. add_subdirectory(molequeue) if(USE_VTK) add_subdirectory(vtk) endif() add_subdirectory(qtplugins) endif() avogadrolibs-1.101.0/avogadro/calc/000077500000000000000000000000001506155467400170475ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/calc/CMakeLists.txt000066400000000000000000000006631506155467400216140ustar00rootroot00000000000000add_library(Calc) avogadro_headers(Calc chargemodel.h chargemanager.h defaultmodel.h energycalculator.h energymanager.h gradients.h lennardjones.h uff.h uffdata.h ) target_sources(Calc PRIVATE chargemodel.cpp chargemanager.cpp defaultmodel.cpp energycalculator.cpp energymanager.cpp lennardjones.cpp uff.cpp ) avogadro_add_library(Calc) target_link_libraries(Calc PUBLIC Avogadro::Core cppoptlib) avogadrolibs-1.101.0/avogadro/calc/chargemanager.cpp000066400000000000000000000142351506155467400223440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "chargemanager.h" #include "chargemodel.h" #include "defaultmodel.h" namespace Avogadro::Calc { // Helper function to convert a string to lowercase // to register all lower-case identifiers std::string toLower(const std::string& str) { std::string result = str; std::transform(result.begin(), result.end(), result.begin(), ::tolower); return result; } ChargeManager& ChargeManager::instance() { static ChargeManager instance; return instance; } void ChargeManager::appendError(const std::string& errorMessage) { m_error += errorMessage + "\n"; } bool ChargeManager::registerModel(ChargeModel* model) { return instance().addModel(model); } bool ChargeManager::unregisterModel(const std::string& identifier) { return instance().removeModel(identifier); } bool ChargeManager::addModel(ChargeModel* model) { if (model == nullptr) { appendError("Supplied model was null."); return false; } if (m_identifiers.find(model->identifier()) != m_identifiers.end()) { appendError("Model " + model->identifier() + " already loaded."); return false; } // If we got here then the format is unique enough to be added. size_t index = m_models.size(); m_models.push_back(model); std::string lowerId = toLower(model->identifier()); m_identifiers[lowerId] = index; m_identifierToName[lowerId] = model->name(); return true; } bool ChargeManager::removeModel(const std::string& identifier) { std::string lowerId = toLower(identifier); auto ids = m_identifiers[lowerId]; m_identifiers.erase(lowerId); m_identifierToName.erase(lowerId); ChargeModel* model = m_models[ids]; if (model != nullptr) { m_models[ids] = nullptr; delete model; } return true; } std::string ChargeManager::nameForModel(const std::string& identifier) const { std::string lowerId = toLower(identifier); auto it = m_identifierToName.find(lowerId); if (it == m_identifierToName.end()) { return identifier; } return it->second; } ChargeManager::ChargeManager() { // add any default models here (EEM maybe?) } ChargeManager::~ChargeManager() { // Delete the models that were loaded. for (auto& m_model : m_models) { delete m_model; } m_models.clear(); } std::set ChargeManager::identifiersForMolecule( const Core::Molecule& molecule) const { // start with the types already in the molecule std::set identifiers = molecule.partialChargeTypes(); // check our models for compatibility for (auto* m_model : m_models) { // We check that every element in the molecule // is handled by the model auto mask = m_model->elements() & molecule.elements(); if (mask.count() == molecule.elements().count()) identifiers.insert(m_model->identifier()); // this one will work } return identifiers; } MatrixX ChargeManager::partialCharges(const std::string& identifier, Core::Molecule& molecule) const { // first check if the type is found in the molecule // (i.e., read from a file not computed dynamically) auto molIdentifiers = molecule.partialChargeTypes(); std::string lowerId = toLower(identifier); if (molIdentifiers.find(lowerId) != molIdentifiers.end()) { return molecule.partialCharges(lowerId); } // otherwise go through our list if (m_identifiers.find(lowerId) == m_identifiers.end()) { MatrixX charges(molecule.atomCount(), 1); // we have to return something, so zeros return charges; } const auto id = m_identifiers[lowerId]; const ChargeModel* model = m_models[id]; return model->partialCharges(molecule); } Vector3 ChargeManager::dipoleMoment(const std::string& identifier, const Core::Molecule& molecule) const { // If the type is found in the molecule // we'll use the DefaultModel to handle the dipole moment auto molIdentifiers = molecule.partialChargeTypes(); std::string lowerId = toLower(identifier); if (molIdentifiers.find(lowerId) != molIdentifiers.end()) { DefaultModel model(lowerId); // so it knows which charges to use return model.dipoleMoment(molecule); } // otherwise go through our list if (m_identifiers.find(lowerId) == m_identifiers.end()) { return Vector3(0.0, 0.0, 0.0); } if (molecule.atomCount() < 2) { return Vector3(0.0, 0.0, 0.0); } const auto id = m_identifiers[lowerId]; const ChargeModel* model = m_models[id]; return model->dipoleMoment(molecule); } double ChargeManager::potential(const std::string& identifier, Core::Molecule& molecule, const Vector3& point) const { // If the type is found in the molecule // we'll use the DefaultModel to handle the potential auto molIdentifiers = molecule.partialChargeTypes(); if (molIdentifiers.find(identifier) != molIdentifiers.end()) { DefaultModel model(identifier); // so it knows which charges to use return model.potential(molecule, point); } // otherwise go through our list if (m_identifiers.find(identifier) == m_identifiers.end()) { return 0.0; } const auto id = m_identifiers[identifier]; const ChargeModel* model = m_models[id]; return model->potential(molecule, point); } Core::Array ChargeManager::potentials( const std::string& identifier, Core::Molecule& molecule, const Core::Array& points) const { // As above auto molIdentifiers = molecule.partialChargeTypes(); if (molIdentifiers.find(identifier) != molIdentifiers.end()) { DefaultModel model(identifier); // so it knows which charges to use return model.potentials(molecule, points); } if (m_identifiers.find(identifier) == m_identifiers.end()) { Core::Array potentials(points.size(), 0.0); return potentials; } const auto id = m_identifiers[identifier]; const ChargeModel* model = m_models[id]; return model->potentials(molecule, points); } } // namespace Avogadro::Calc avogadrolibs-1.101.0/avogadro/calc/chargemanager.h000066400000000000000000000123041506155467400220040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_CHARGEMANAGER_H #define AVOGADRO_CALC_CHARGEMANAGER_H #include "avogadrocalcexport.h" #include #include #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace Calc { class ChargeModel; /** * @class ChargeManager chargemanager.h * * @brief Class to manage registration, searching and creation of partial charge * models. * @author Geoffrey R. Hutchison * * The charge manager is a singleton class that handles the runtime * registration, search, creation and eventual destruction of electrostatics * models. It can be used to gain a listing of available models, register new * models, etc. * * All electrostatics can take place independent of this code, but for automated * registration and look up, this is the preferred API. It is possible to use * the convenience API without ever dealing directly with a model class. */ class AVOGADROCALC_EXPORT ChargeManager { public: /** * Get the singleton instance of the charge manager. This instance should * not be deleted. */ static ChargeManager& instance(); /** * @brief Register a new charge model with the manager. * @param model An instance of the model to manage, the manager assumes * ownership of the object passed in. * @return True on success, false on failure. */ static bool registerModel(ChargeModel* model); /** * @brief Unregister a charge model from the manager. * @param identifier The identifier for the model to remove. * @return True on success, false on failure. */ static bool unregisterModel(const std::string& identifier); /** * Add the supplied @p model to the manager, registering its ID and other * relevant data for later lookup. The manager assumes ownership of the * supplied object. * @return True on success, false on failure. */ bool addModel(ChargeModel* model); /** * Remove the model with the identifier @a identifier from the manager. * @return True on success, false on failure. */ bool removeModel(const std::string& identifier); /** * Get a list of all loaded identifiers */ std::set identifiers() const; /** * @brief Get a list of models that work for this molecule. * * Includes partial charge types in the molecule itself (e.g., from a file) * This is probably the method you want to get a list for a user */ std::set identifiersForMolecule( const Core::Molecule& molecule) const; std::string nameForModel(const std::string& identifier) const; /** * This method will calculate atomic partial charges and save them, * if the model is available (i.e., the molecule will change) * Note that some models do not have well-defined atomic partial charges * @return atomic partial charges for the molecule, or 0.0 if undefined */ MatrixX partialCharges(const std::string& identifier, Core::Molecule& mol) const; /** * Calculate the atomic partial charges and leave the molecule unchanged. * Note that some models do not have well-defined atomic partial charges * @return the dipole moment for the molecule, or 0.0 if undefined */ MatrixX partialCharges(const std::string& identifier, const Core::Molecule& mol) const; /** * @return the dipole moment for the molecule, or 0.0 if the model is not * available */ Vector3 dipoleMoment(const std::string& identifier, const Core::Molecule& mol) const; /** * @return the potential at the point for the molecule, or 0.0 if the model is * not available */ double potential(const std::string& identifier, Core::Molecule& mol, const Vector3& point) const; /** * @return the potentials at the point for the molecule, or an array of 0.0 if * the model is not available */ Core::Array potentials(const std::string& identifier, Core::Molecule& mol, const Core::Array& points) const; /** * Get any errors that have been logged when loading models. */ std::string error() const; private: using ChargeIdMap = std::map; ChargeManager(); ~ChargeManager(); ChargeManager(const ChargeManager&); // Not implemented. ChargeManager& operator=(const ChargeManager&); // Not implemented. /** * @brief Append warnings/errors to the error message string. * @param errorMessage The error message to append. */ void appendError(const std::string& errorMessage); std::vector m_models; mutable ChargeIdMap m_identifiers; mutable std::map m_identifierToName; std::string m_error; }; } // namespace Calc } // namespace Avogadro #endif // AVOGADRO_CALC_CHARGEMANAGER_H avogadrolibs-1.101.0/avogadro/calc/chargemodel.cpp000066400000000000000000000054251506155467400220330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "chargemodel.h" #include #include #include #include #include namespace Avogadro { using Core::Array; using Core::Molecule; namespace Calc { #ifndef M_PI constexpr double M_PI = 3.14159265358979323846; #endif Vector3 ChargeModel::dipoleMoment(const Molecule& mol) const { if (mol.atomCount() < 2) return Vector3(0.0, 0.0, 0.0); // default is to get the set of partial atomic charges // (some models might do something more sophisticated) const MatrixX charges = partialCharges(mol); // also get the positions of the atoms const Array positions = mol.atomPositions3d(); Vector3 dipole(0.0, 0.0, 0.0); if (static_cast(charges.rows()) != positions.size()) std::cout << "Error: charges " << charges.rows() << " != positions " << positions.size() << std::endl; for (Eigen::Index i = 0; i < charges.size(); ++i) dipole += charges(i, 0) * positions[i]; dipole *= 4.80320471257; // convert to Debye from electron-Angstrom return dipole; } double ChargeModel::potential(Molecule& mol, const Vector3& point) const { // default is to get the set of partial atomic charges const MatrixX charges = partialCharges(mol); // also get the positions of the atoms const Array positions = mol.atomPositions3d(); // @todo: this is naive and inefficient // calculate the atoms within a cutoff distance // and sum the potentials // note this is usually multithreaded by the caller // but more efficient methods can be implemented double potential = 0.0; for (unsigned int i = 0; i < charges.size(); ++i) { double distance = (positions[i] - point).norm(); if (distance > 0.01) { // drop small distances to avoid overflow potential += charges(i, 0) / distance; } } return potential / m_dielectric; } Array ChargeModel::potentials(Core::Molecule& mol, const Array& points) const { // This is naive and slow, but can be re-implemented by methods // for batching Array potentials(points.size(), 0.0); for (unsigned int i = 0; i < points.size(); ++i) potentials[i] = potential(mol, points[i]); return potentials; } void ChargeModel::appendError(const std::string& errorString, bool newLine) const { m_error += errorString; if (newLine) m_error += "\n"; } } // namespace Calc } // namespace Avogadro avogadrolibs-1.101.0/avogadro/calc/chargemodel.h000066400000000000000000000101461506155467400214740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_CHARGEMODEL_H #define AVOGADRO_CALC_CHARGEMODEL_H #include "avogadrocalcexport.h" #include #include #include #include #include namespace Avogadro::Calc { /** * @class ChargeModel chargemodel.h * @brief General API for charge / electrostatics models * @author Geoffrey R. Hutchison * * This serves as the common base class for electrostatics models. * Many use atomic point charges, but we also allow for custom models * for Slater / Gaussian distributions, distributed electrostatics, * use of quantum mechanics, etc. * * Key methods are to determine either atomic partial charges or * electrostatic potentials at particular points in space. * * There is a default implementation for the electrostatic potential * at points in space, based on the atomic partial charges. If you * implement a different mechanism, you should override this method. */ class AVOGADROCALC_EXPORT ChargeModel { public: ChargeModel() = default; virtual ~ChargeModel() = default; /** * Create a new instance of the model. Ownership passes to the * caller. */ virtual ChargeModel* newInstance() const = 0; /** * @brief A unique identifier, used to retrieve models programmatically. * EEM2, NPA, etc. A runtime warning will be generated if the identifier * is not unique. */ virtual std::string identifier() const = 0; /** * @brief A user-visible name of the model (e.g., "Natural Population * Analysis") */ virtual std::string name() const = 0; /** * @brief Indicate if your method only treats a subset of elements * @return an element mask corresponding to the defined subset */ virtual Core::Molecule::ElementMask elements() const = 0; /** * Set the dielectric constant for the model. * @param dielectric constant. */ virtual void setDielectric(float dielectric) { m_dielectric = dielectric; }; /** * @return The dielectric constant. */ virtual float dielectric() const { return m_dielectric; } virtual MatrixX partialCharges(Core::Molecule& mol) const = 0; virtual MatrixX partialCharges(const Core::Molecule& mol) const = 0; /** * @brief Calculate the dipole moment of the molecule. * * Defaults to using the partial charges and atomic positions * to calculate the net dipole moment. * @return The dipole moment vector of the molecule */ virtual Vector3 dipoleMoment(const Core::Molecule& mol) const; /** * @brief Calculate the electrostatic potential at a particular point in * space. * @param mol The molecule to calculate the potential for. * @param point The point in space to calculate the potential at. * @return The electrostatic potential at the point. */ virtual double potential(Core::Molecule& mol, const Vector3& point) const; /** * @brief Calculate the electrostatic potential at multiple points * @param mol The molecule to calculate the potential for. * @param array The points in space to calculate the potential at. * @return The electrostatic potential at the points in an array. * * This method is used for batch calculation and defaults to simply * calculating each point at a time. Some methods work faster in batches. */ virtual Core::Array potentials( Core::Molecule& mol, const Core::Array& points) const; protected: /** * @brief Append an error to the error string for the model. * @param errorString The error to be added. * @param newLine Add a new line after the error string? */ void appendError(const std::string& errorString, bool newLine = true) const; private: mutable std::string m_error; float m_dielectric = 1.0f; }; } // namespace Avogadro::Calc #endif // AVOGADRO_CALC_CHARGEMODEL_H avogadrolibs-1.101.0/avogadro/calc/defaultmodel.cpp000066400000000000000000000017241506155467400222240ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "defaultmodel.h" #include #include namespace Avogadro::Calc { // Base class constructors are called automatically DefaultModel::DefaultModel(const std::string& id) : m_identifier(id) { // we don't know which elements are in the molecule // but we can just say all of them are okay // (because this method should work for any molecule) m_elements.set(); } MatrixX DefaultModel::partialCharges(Core::Molecule& mol) const { return mol.partialCharges(m_identifier); } MatrixX DefaultModel::partialCharges(const Core::Molecule& mol) const { return mol.partialCharges(m_identifier); } } // namespace Avogadro::Calc avogadrolibs-1.101.0/avogadro/calc/defaultmodel.h000066400000000000000000000043121506155467400216650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_DEFAULTMODEL_H #define AVOGADRO_CALC_DEFAULTMODEL_H #include "avogadrocalcexport.h" #include namespace Avogadro { namespace Core { class Molecule; } namespace Calc { /** * @class DefaultModel defaultmodel.h * @brief Default charge model for file-provided atomic charges * @author Geoffrey R. Hutchison * * This is a default model for using atomic partial charges from a * file (e.g., quantum chemistry packages often provide Mulliken charges) * * The class */ class AVOGADROCALC_EXPORT DefaultModel : public ChargeModel { public: DefaultModel(const std::string& identifier = ""); ~DefaultModel() override = default; /** * Create a new instance of the file format class. Ownership passes to the * caller. */ DefaultModel* newInstance() const override { return new DefaultModel; } /** * @brief A unique identifier defined by the file */ std::string identifier() const override { return m_identifier; } /** * @brief Set the identifier */ virtual void setIdentifier(const std::string& identifier) { m_identifier = identifier; } /** * @brief We don't have any other name beyond the identifier in the file */ std::string name() const override { return m_identifier; } /** * @brief This default method is defined for whatever is in a molecule * @return all elements - technically not true, but we don't have the mol */ Core::Molecule::ElementMask elements() const override { return (m_elements); } /** * @brief Retrieve the relevant charges from the molecule for our defined type */ MatrixX partialCharges(Core::Molecule& mol) const override; MatrixX partialCharges(const Core::Molecule& mol) const override; protected: std::string m_identifier; Core::Molecule::ElementMask m_elements; }; } // namespace Calc } // namespace Avogadro #endif // AVOGADRO_CALC_CHARGEMODEL_H avogadrolibs-1.101.0/avogadro/calc/energycalculator.cpp000066400000000000000000000246211506155467400231230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "energycalculator.h" #include "gradients.h" #include #include using Eigen::Vector3d; namespace Avogadro::Calc { void EnergyCalculator::gradient(const TVector& x, TVector& grad) { finiteGradient(x, grad); // clean and handle frozen atoms cleanGradients(grad); // add any constraints constraintGradients(x, grad); } void EnergyCalculator::cleanGradients(TVector& grad) { unsigned int size = grad.rows(); // check for overflows -- in case of divide by zero, etc. for (unsigned int i = 0; i < size; ++i) { if (!std::isfinite(grad[i]) || std::isnan(grad[i])) { grad[i] = 0.0; } } // freeze any masked atoms or coordinates if (m_mask.rows() == size) grad = grad.cwiseProduct(m_mask); else std::cerr << "Error: mask size " << m_mask.rows() << " " << grad.rows() << std::endl; } void EnergyCalculator::setConstraints( const std::vector& constraints) { m_distanceConstraints.clear(); m_angleConstraints.clear(); m_torsionConstraints.clear(); m_outOfPlaneConstraints.clear(); for (const auto& constraint : constraints) { switch (constraint.type()) { case Core::Constraint::DistanceConstraint: m_distanceConstraints.push_back(constraint); break; case Core::Constraint::AngleConstraint: m_angleConstraints.push_back(constraint); break; case Core::Constraint::TorsionConstraint: m_torsionConstraints.push_back(constraint); break; case Core::Constraint::OutOfPlaneConstraint: m_outOfPlaneConstraints.push_back(constraint); break; default: std::cerr << "Unknown constraint type: " << constraint.type() << std::endl; } } } std::vector EnergyCalculator::constraints() const { Index totalSize = m_distanceConstraints.size() + m_angleConstraints.size() + m_torsionConstraints.size() + m_outOfPlaneConstraints.size(); std::vector allConstraints; allConstraints.reserve(totalSize); for (const auto& constraint : m_distanceConstraints) allConstraints.push_back(constraint); for (const auto& constraint : m_angleConstraints) allConstraints.push_back(constraint); for (const auto& constraint : m_torsionConstraints) allConstraints.push_back(constraint); for (const auto& constraint : m_outOfPlaneConstraints) allConstraints.push_back(constraint); return allConstraints; } Real EnergyCalculator::constraintEnergies(const TVector& x) { Real totalEnergy = 0.0; for (const auto& constraint : m_distanceConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size())) // shouldn't happen - invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vAB = vA - vB; const Real distance = vAB.norm(); const Real delta = distance - constraint.value(); // harmonic restraint totalEnergy += constraint.k() * delta * delta; } for (const auto& constraint : m_angleConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); const Index c = constraint.cIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size()) || (3 * c + 2 >= x.size())) // shouldn't happen, invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vC(x[3 * c], x[3 * c + 1], x[3 * c + 2]); const Real angle = calculateAngle(vA, vB, vC); const Real delta = angle - constraint.value(); // harmonic restraint totalEnergy += constraint.k() * delta * delta; } for (const auto& constraint : m_torsionConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); const Index c = constraint.cIndex(); const Index d = constraint.dIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size()) || (3 * c + 2 >= x.size()) || (3 * d + 2 >= x.size())) // shouldn't happen, invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vC(x[3 * c], x[3 * c + 1], x[3 * c + 2]); const Vector3d vD(x[3 * d], x[3 * d + 1], x[3 * d + 2]); const Real angle = calculateDihedral(vA, vB, vC, vD); const Real delta = angle - constraint.value(); // harmonic restraint totalEnergy += constraint.k() * delta * delta; } for (const auto& constraint : m_outOfPlaneConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); const Index c = constraint.cIndex(); const Index d = constraint.dIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size()) || (3 * c + 2 >= x.size()) || (3 * d + 2 >= x.size())) // shouldn't happen, invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vC(x[3 * c], x[3 * c + 1], x[3 * c + 2]); const Vector3d vD(x[3 * d], x[3 * d + 1], x[3 * d + 2]); const Real angle = outOfPlaneAngle(vA, vB, vC, vD); const Real delta = angle - constraint.value(); // harmonic restraint totalEnergy += constraint.k() * delta * delta; } return totalEnergy; } void EnergyCalculator::constraintGradients(const TVector& x, TVector& grad) { for (const auto& constraint : m_distanceConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size())) // shouldn't happen - invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vAB = vA - vB; const Real distance = vAB.norm(); const Real delta = distance - constraint.value(); const Real dE = constraint.k() * 2 * delta; grad[3 * a] += dE * vAB[0]; grad[3 * a + 1] += dE * vAB[1]; grad[3 * a + 2] += dE * vAB[2]; grad[3 * b] -= dE * vAB[0]; grad[3 * b + 1] -= dE * vAB[1]; grad[3 * b + 2] -= dE * vAB[2]; } for (const auto& constraint : m_angleConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); const Index c = constraint.cIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size()) || (3 * c + 2 >= x.size())) // shouldn't happen, invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vC(x[3 * c], x[3 * c + 1], x[3 * c + 2]); Vector3d aGrad, bGrad, cGrad; const Real angle = angleGradient(vA, vB, vC, aGrad, bGrad, cGrad); const Real delta = angle - constraint.value(); const Real dE = constraint.k() * 2 * delta; // update the master gradients grad[3 * a] += dE * aGrad[0]; grad[3 * a + 1] += dE * aGrad[1]; grad[3 * a + 2] += dE * aGrad[2]; grad[3 * b] += dE * bGrad[0]; grad[3 * b + 1] += dE * bGrad[1]; grad[3 * b + 2] += dE * bGrad[2]; grad[3 * c] += dE * cGrad[0]; grad[3 * c + 1] += dE * cGrad[1]; grad[3 * c + 2] += dE * cGrad[2]; } for (const auto& constraint : m_torsionConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); const Index c = constraint.cIndex(); const Index d = constraint.dIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size()) || (3 * c + 2 >= x.size()) || (3 * d + 2 >= x.size())) // shouldn't happen, invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vC(x[3 * c], x[3 * c + 1], x[3 * c + 2]); const Vector3d vD(x[3 * d], x[3 * d + 1], x[3 * d + 2]); Vector3d aGrad, bGrad, cGrad, dGrad; const Real angle = dihedralGradient(vA, vB, vC, vD, aGrad, bGrad, cGrad, dGrad); const Real delta = angle - constraint.value(); const Real dE = constraint.k() * 2 * delta; // update the master gradients grad[3 * a] += dE * aGrad[0]; grad[3 * a + 1] += dE * aGrad[1]; grad[3 * a + 2] += dE * aGrad[2]; grad[3 * b] += dE * bGrad[0]; grad[3 * b + 1] += dE * bGrad[1]; grad[3 * b + 2] += dE * bGrad[2]; grad[3 * c] += dE * cGrad[0]; grad[3 * c + 1] += dE * cGrad[1]; grad[3 * c + 2] += dE * cGrad[2]; grad[3 * d] += dE * dGrad[0]; grad[3 * d + 1] += dE * dGrad[1]; grad[3 * d + 2] += dE * dGrad[2]; } for (const auto& constraint : m_outOfPlaneConstraints) { const Index a = constraint.aIndex(); const Index b = constraint.bIndex(); const Index c = constraint.cIndex(); const Index d = constraint.dIndex(); if ((3 * a + 2 >= x.size()) || (3 * b + 2 >= x.size()) || (3 * c + 2 >= x.size()) || (3 * d + 2 >= x.size())) // shouldn't happen, invalid constraint continue; const Vector3d vA(x[3 * a], x[3 * a + 1], x[3 * a + 2]); const Vector3d vB(x[3 * b], x[3 * b + 1], x[3 * b + 2]); const Vector3d vC(x[3 * c], x[3 * c + 1], x[3 * c + 2]); const Vector3d vD(x[3 * d], x[3 * d + 1], x[3 * d + 2]); Vector3d aGrad, bGrad, cGrad, dGrad; const Real angle = outOfPlaneGradient(vA, vB, vC, vD, aGrad, bGrad, cGrad, dGrad); const Real delta = angle - constraint.value(); const Real dE = constraint.k() * 2 * delta; // update the master gradients grad[3 * a] += dE * aGrad[0]; grad[3 * a + 1] += dE * aGrad[1]; grad[3 * a + 2] += dE * aGrad[2]; grad[3 * b] += dE * bGrad[0]; grad[3 * b + 1] += dE * bGrad[1]; grad[3 * b + 2] += dE * bGrad[2]; grad[3 * c] += dE * cGrad[0]; grad[3 * c + 1] += dE * cGrad[1]; grad[3 * c + 2] += dE * cGrad[2]; grad[3 * d] += dE * dGrad[0]; grad[3 * d + 1] += dE * dGrad[1]; grad[3 * d + 2] += dE * dGrad[2]; } } } // namespace Avogadro::Calc avogadrolibs-1.101.0/avogadro/calc/energycalculator.h000066400000000000000000000110211506155467400225560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_ENERGYCALCULATOR_H #define AVOGADRO_CALC_ENERGYCALCULATOR_H #include "avogadrocalcexport.h" #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace Calc { class AVOGADROCALC_EXPORT EnergyCalculator : public cppoptlib::Problem { public: EnergyCalculator() = default; ~EnergyCalculator() override = default; /** * Create a new instance of the model. Ownership passes to the * caller. */ virtual EnergyCalculator* newInstance() const = 0; /** * @return a unique identifier for this calculator. */ virtual std::string identifier() const = 0; /** * @return A short translatable name for this method (e.g., MMFF94, UFF, etc.) */ virtual std::string name() const = 0; /** * @return a description of the method */ virtual std::string description() const = 0; /** * Called to set the configuration (e.g., for a GUI options dialog) */ virtual bool setConfiguration([[maybe_unused]] Core::VariantMap& config) { return true; } /** * @brief Indicate if your method only treats a subset of elements * @return an element mask corresponding to the defined subset */ virtual Core::Molecule::ElementMask elements() const = 0; /** * @brief Indicate if your method can handle unit cells * @return true if unit cells are supported */ virtual bool acceptsUnitCell() const { return false; } /** * @brief Indicate if your method can handle ions * Many methods only treat neutral systems, either * a neutral molecule or a neutral unit cell. * * @return true if ions are supported */ virtual bool acceptsIons() const { return false; } /** * @brief Indicate if your method can handle radicals * Most methods only treat closed-shell molecules. * @return true if radicals are supported */ virtual bool acceptsRadicals() const { return false; } /** * Calculate the gradients for this method, defaulting to numerical * finite-difference methods */ void gradient(const TVector& x, TVector& grad) override; /** * Called to 'clean' gradients @param grad (e.g., for constraints) */ void cleanGradients(TVector& grad); /** * Called to get the energies for the current set of constraints. * which should be added to the value() method for real energies * in derived classes * @return the sum of the constraint energies */ Real constraintEnergies(const TVector& x); /** * Called to get the gradients for the current set of constraints. * which should be added to the gradient() method in derived classes. * @param x the current coordinates * @param grad the gradient vector to be updated with constraint gradients */ void constraintGradients(const TVector& x, TVector& grad); /** * Called to get the constraints for this method. * @return the constraints for this method */ std::vector constraints() const; // Set the constraints for this method void setConstraints(const std::vector& constraints); /** * Called to update the "frozen" mask (e.g., during editing) */ void setMask(TVector mask) { m_mask = mask; } /** * @return the frozen atoms mask */ TVector mask() const { return m_mask; } /** * Called when the current molecule changes. */ virtual void setMolecule(Core::Molecule* mol) = 0; protected: /** * @brief Append an error to the error string for the model. * @param errorString The error to be added. * @param newLine Add a new line after the error string? */ void appendError(const std::string& errorString, bool newLine = true) const; TVector m_mask; // optimize or frozen atom mask // Separate the constraints into different types // for speed and convenience. std::vector m_distanceConstraints; std::vector m_angleConstraints; std::vector m_torsionConstraints; std::vector m_outOfPlaneConstraints; private: mutable std::string m_error; }; } // end namespace Calc } // end namespace Avogadro #endif // AVOGADRO_CALC_ENERGYCALCULATOR_H avogadrolibs-1.101.0/avogadro/calc/energymanager.cpp000066400000000000000000000070751506155467400224100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "energymanager.h" #include "energycalculator.h" #include "lennardjones.h" #include "uff.h" namespace Avogadro::Calc { EnergyManager& EnergyManager::instance() { static EnergyManager instance; return instance; } void EnergyManager::appendError(const std::string& errorMessage) { m_error += errorMessage + "\n"; } bool EnergyManager::registerModel(EnergyCalculator* model) { return instance().addModel(model); } bool EnergyManager::unregisterModel(const std::string& identifier) { return instance().removeModel(identifier); } bool EnergyManager::addModel(EnergyCalculator* model) { if (model == nullptr) { appendError("Supplied model was null."); return false; } if (m_identifiers.find(model->identifier()) != m_identifiers.end()) { appendError("Model " + model->identifier() + " already loaded."); return false; } // If we got here then the model is unique enough to be added. size_t index = m_models.size(); m_models.push_back(model); m_identifiers[model->identifier()] = index; m_identifierToName[model->identifier()] = model->name(); return true; } EnergyCalculator* EnergyManager::model(const std::string& identifier) const { auto it = m_identifiers.find(identifier); if (it == m_identifiers.end()) { return nullptr; } return m_models[it->second]->newInstance(); } bool EnergyManager::removeModel(const std::string& identifier) { auto ids = m_identifiers[identifier]; m_identifiers.erase(identifier); m_identifierToName.erase(identifier); auto* model = m_models[ids]; if (model != nullptr) { m_models[ids] = nullptr; delete model; } return true; } std::string EnergyManager::nameForModel(const std::string& identifier) const { auto it = m_identifierToName.find(identifier); if (it == m_identifierToName.end()) { return identifier; } return it->second; } EnergyManager::EnergyManager() { // add any default models here // LJ is the fallback, since it can handle anything // (maybe not well, but it can handle it) addModel(new LennardJones); // UFF is good for a wide range of molecules addModel(new UFF); } EnergyManager::~EnergyManager() { // Delete the models that were loaded. for (auto& m_model : m_models) { delete m_model; } m_models.clear(); } std::set EnergyManager::identifiersForMolecule( const Core::Molecule& molecule) const { std::set identifiers; // check our models for compatibility for (auto* m_model : m_models) { if (m_model == nullptr) continue; // we can check easy things first // - is the molecule an ion based on total charge // - is the molecule a radical based on spin multiplicity // - does the molecule have a unit cell if (molecule.totalCharge() != 0 && !m_model->acceptsIons()) continue; if (molecule.totalSpinMultiplicity() != 1 && !m_model->acceptsRadicals()) continue; if (molecule.unitCell() != nullptr && !m_model->acceptsUnitCell()) continue; // Finally, we check that every element in the molecule // is handled by the model auto mask = m_model->elements() & molecule.elements(); if (mask.count() == molecule.elements().count()) identifiers.insert(m_model->identifier()); // this one will work } return identifiers; } } // namespace Avogadro::Calc avogadrolibs-1.101.0/avogadro/calc/energymanager.h000066400000000000000000000103221506155467400220420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_ENERGYMANAGER_H #define AVOGADRO_CALC_ENERGYMANAGER_H #include "avogadrocalcexport.h" #include #include #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace Calc { class EnergyCalculator; /** * @class EnergyManager chargemanager.h * * @brief Class to manage registration, searching and creation of force field * (energy) calculators. * @author Geoffrey R. Hutchison * * The energy manager is a singleton class that handles the runtime * registration, search, creation and eventual destruction of calculators * for geometry optimization and molecular dynamics. * It can be used to gain a listing of available models, register new * models, etc. * * All energy calculation can take place independent of this code, but for * automated registration and look up, this is the preferred API. */ class AVOGADROCALC_EXPORT EnergyManager { public: /** * Get the singleton instance of the energy manager. This instance should * not be deleted. */ static EnergyManager& instance(); /** * @brief Register a new model with the manager. * @param model An instance of the calculator to manage, the manager assumes * ownership of the object passed in. * @return True on success, false on failure. */ static bool registerModel(EnergyCalculator* model); /** * @brief Unregister a charge model from the manager. * @param identifier The identifier for the model to remove. * @return True on success, false on failure. */ static bool unregisterModel(const std::string& identifier); /** * Add the supplied @p model to the manager, registering its ID and other * relevant data for later lookup. The manager assumes ownership of the * supplied object. * @return True on success, false on failure. */ bool addModel(EnergyCalculator* model); /** * Remove the model with the identifier @a identifier from the manager. * @return True on success, false on failure. */ bool removeModel(const std::string& identifier); /** * New instance of the model for the specified @p identifier. Ownership * is passed to the caller. * @param identifier The unique identifier of the model. * @return Instance of the model, nullptr if not found. Ownership passes to * the caller. */ EnergyCalculator* model(const std::string& identifier) const; /** * Get a list of all loaded identifiers */ std::set identifiers() const; /** * @brief Get a list of models that work for this molecule. * * This is probably the method you want to get a list for a user */ std::set identifiersForMolecule( const Core::Molecule& molecule) const; /** * @brief Get the name of the model for the specified identifier. * * The name is a user-visible string, and may be translated. * @param identifier The unique identifier of the model. * @return The name of the model, or an empty string if not found. */ std::string nameForModel(const std::string& identifier) const; /** * Get any errors that have been logged when loading models. */ std::string error() const; private: using ModelIdMap = std::map; EnergyManager(); ~EnergyManager(); EnergyManager(const EnergyManager&); // Not implemented. EnergyManager& operator=(const EnergyManager&); // Not implemented. /** * @brief Append warnings/errors to the error message string. * @param errorMessage The error message to append. */ void appendError(const std::string& errorMessage); std::vector m_models; mutable ModelIdMap m_identifiers; mutable std::map m_identifierToName; std::string m_error; }; } // namespace Calc } // namespace Avogadro #endif // AVOGADRO_CALC_ENERGYMANAGER_H avogadrolibs-1.101.0/avogadro/calc/gradients.h000066400000000000000000000106621506155467400212050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_GRADIENTS_H #define AVOGADRO_CALC_GRADIENTS_H #include #include #include namespace Avogadro::Calc { /** * Calculate the components of the gradient for the angle a-b-c * @return the angle between a-b-c in radians */ inline Real angleGradient(const Vector3& a, const Vector3& b, const Vector3& c, Vector3& aGrad, Vector3& bGrad, Vector3& cGrad) { cGrad = bGrad = aGrad = { 0.0, 0.0, 0.0 }; const Vector3 ab = a - b; const Vector3 cb = c - b; const Real rab = ab.norm(); const Real rcb = cb.norm(); const Real dot = ab.dot(cb); const Real norms = rab * rcb; const Real angle = std::acos(-dot / norms); if (rab < 1.e-3 || rcb < 1.e-3) return angle; const Vector3 ab_cross_cb = ab.cross(cb); Real crossNorm = ab_cross_cb.norm(); if (crossNorm < 1.e-6) return angle; // Use the cross product to get the gradients const Vector3 n = ab_cross_cb / crossNorm; // Gradients of the cross products Vector3 grad_cross_a = (cb.cross(n)).stableNormalized(); Vector3 grad_cross_c = (n.cross(ab)).stableNormalized(); Vector3 grad_cross_b = -(grad_cross_a + grad_cross_c); // Gradients of the dot product Vector3 grad_dot_a = cb; Vector3 grad_dot_c = ab; Vector3 grad_dot_b = -(cb + ab); // Final gradient using atan2 derivative: d/dx(atan2(y,x)) = (x*dy/dx - // y*dx/dx)/(x^2 + y^2) const Real denom = crossNorm * crossNorm + dot * dot; aGrad = (grad_cross_a * dot - crossNorm * grad_dot_a) / denom; bGrad = (grad_cross_b * dot - crossNorm * grad_dot_b) / denom; cGrad = (grad_cross_c * dot - crossNorm * grad_dot_c) / denom; return angle; } /** * Calculate the components of the gradient for the dihedral i-j-k-; * @return the torsion angle around i-j-k-l in radians */ inline Real dihedralGradient(const Vector3& i, const Vector3& j, const Vector3& k, const Vector3& l, Vector3& iGrad, Vector3& jGrad, Vector3& kGrad, Vector3& lGrad) { lGrad = kGrad = jGrad = iGrad = { 0.0, 0.0, 0.0 }; // get the bond vectors Vector3 ij = j - i; Vector3 jk = k - j; Vector3 kl = l - k; Real rij = ij.norm(); Real rjk = jk.norm(); Real rkl = kl.norm(); Real phi = calculateDihedral(i, j, k, l) * DEG_TO_RAD; // check if the bond vectors are near zero if (rij < 1e-3 || rjk < 1e-3 || rkl < 1e-3) return phi; // skip this torsion Real sinPhi = sin(phi); Real cosPhi = cos(phi); // skip this torsion if (std::abs(sinPhi) < 1e-6) return phi; // Using the BallView / Open Babel formula // http://dx.doi.org/10.22028/D291-25896 (Appendix A) // Thanks to Andreas Moll // for the derivation of the gradients // get the unit vectors Vector3 n1 = ij / rij; Vector3 n2 = jk / rjk; Vector3 n3 = kl / rkl; // get the angles between ijk and jkl Vector3 n1_cross_n2 = n1.cross(n2); Vector3 n2_cross_n3 = n2.cross(n3); // check for near-zero cross products if (n1_cross_n2.norm() < 1e-6 || n2_cross_n3.norm() < 1e-6) return phi; // skip this torsion Real sinAngleIJK = n1_cross_n2.norm(); Real sinAngleJKL = n2_cross_n3.norm(); Real cosAngleIJK = n1.dot(n2); Real cosAngleJKL = n2.dot(n3); // get the gradient components iGrad = -n1_cross_n2 / (rij * sinAngleIJK * sinAngleIJK); lGrad = n2_cross_n3 / (rkl * sinAngleJKL * sinAngleJKL); // grad_j and grad_k are a bit more complicated // clamp the cosines to -1 to 1 cosAngleIJK = std::clamp(cosAngleIJK, -1.0, 1.0); cosAngleJKL = std::clamp(cosAngleJKL, -1.0, 1.0); Real fraction1 = (rij / rjk) * (-cosAngleIJK); Real fraction2 = (rkl / rjk) * (-cosAngleJKL); jGrad = iGrad * (fraction1 - 1) - lGrad * (fraction2); kGrad = -(iGrad + lGrad + jGrad); return phi; } inline Real outOfPlaneGradient(const Vector3& point, const Vector3& b, const Vector3& c, const Vector3& d, Vector3& aGrad, Vector3& bGrad, Vector3& cGrad, Vector3& dGrad) { dGrad = cGrad = bGrad = aGrad = { 0.0, 0.0, 0.0 }; return 0.0; } } // namespace Avogadro::Calc #endif // AVOGADRO_CALC_GRADIENTS_H avogadrolibs-1.101.0/avogadro/calc/lennardjones.cpp000066400000000000000000000124071506155467400222410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "lennardjones.h" #include "avogadro/core/avogadrocore.h" #include #include #include namespace Avogadro::Calc { LennardJones::LennardJones() : m_molecule(nullptr), m_cell(nullptr), m_radii(), m_vdw(true), m_depth(100.0), m_exponent(6), m_elements() { // defined for 1-118 for (unsigned int i = 1; i <= 118; ++i) { m_elements.set(i); } } void LennardJones::setMolecule(Core::Molecule* mol) { m_molecule = mol; if (mol == nullptr) { return; // nothing to do } m_mask = mol->frozenAtomMask(); m_cell = mol->unitCell(); // could be nullptr Index numAtoms = mol->atomCount(); // track atomic radii for this molecule m_radii.setZero(); Eigen::MatrixXd radii(numAtoms, numAtoms); Eigen::MatrixXd mask(numAtoms * 3, 1); mask.setOnes(); m_mask = mask; for (Index i = 0; i < numAtoms; ++i) { Core::Atom atom1 = mol->atom(i); unsigned char number1 = atom1.atomicNumber(); double r1; if (m_vdw) r1 = Core::Elements::radiusVDW(number1); else r1 = Core::Elements::radiusCovalent(number1); for (Index j = i + 1; j < numAtoms; ++j) { Core::Atom atom2 = mol->atom(j); unsigned char number2 = atom2.atomicNumber(); double r2; if (m_vdw) r2 = Core::Elements::radiusVDW(number2); else r2 = Core::Elements::radiusCovalent(number2); radii(i, j) = r1 + r2; // expected distance } } m_radii = radii; } Real LennardJones::value(const Eigen::VectorXd& x) { if (!m_molecule) return 0.0; // FYI https://en.wikipedia.org/wiki/Lennard-Jones_potential //@todo handle unit cells and minimum distances Index numAtoms = m_molecule->atomCount(); Real energy = 0.0; // we put the conditional here outside the double loop if (m_cell == nullptr) { // regular molecule for (Index i = 0; i < numAtoms; ++i) { Vector3 ipos(x[3 * i], x[3 * i + 1], x[3 * i + 2]); for (Index j = i + 1; j < numAtoms; ++j) { Vector3 jpos(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Real r = (ipos - jpos).norm(); if (r < 0.1) r = 0.1; // ensure we don't divide by zero Real ratio = pow((m_radii(i, j) / r), m_exponent); energy += m_depth * (ratio * ratio - 2.0 * (ratio)); } } } else { // use the unit cell to get minimum distances for (Index i = 0; i < numAtoms; ++i) { Vector3 ipos(x[3 * i], x[3 * i + 1], x[3 * i + 2]); for (Index j = i + 1; j < numAtoms; ++j) { Vector3 jpos(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Real r = m_cell->distance(ipos, jpos); if (r < 0.1) r = 0.1; // ensure we don't divide by zero Real ratio = pow((m_radii(i, j) / r), m_exponent); energy += m_depth * (ratio * ratio - 2.0 * (ratio)); } } } // qDebug() << " lj: " << energy; energy += constraintEnergies(x); return energy; } void LennardJones::gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (!m_molecule) return; // clear the gradients grad.setZero(); Index numAtoms = m_molecule->atomCount(); // we put the conditional here outside the double loop if (m_cell == nullptr) { // regular molecule for (Index i = 0; i < numAtoms; ++i) { Vector3 ipos(x[3 * i], x[3 * i + 1], x[3 * i + 2]); for (Index j = i + 1; j < numAtoms; ++j) { Vector3 jpos(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Vector3 force = ipos - jpos; Real r = force.norm(); if (r < 0.1) r = 0.1; // ensure we don't divide by zero Real rad = pow(m_radii(i, j), m_exponent); Real term1 = -2 * (m_exponent)*rad * rad * pow(r, -2 * m_exponent - 1); Real term2 = 2 * (m_exponent)*rad * pow(r, -1 * m_exponent - 1); Real dE = m_depth * (term1 + term2); force = (dE / r) * force; // update gradients for (unsigned int c = 0; c < 3; ++c) { grad[3 * i + c] += force[c]; grad[3 * j + c] -= force[c]; } } } } else { // unit cell for (Index i = 0; i < numAtoms; ++i) { Vector3 ipos(x[3 * i], x[3 * i + 1], x[3 * i + 2]); for (Index j = i + 1; j < numAtoms; ++j) { Vector3 jpos(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Vector3 force = m_cell->minimumImage(ipos - jpos); Real r = force.norm(); if (r < 0.1) r = 0.1; // ensure we don't divide by zero Real rad = pow(m_radii(i, j), m_exponent); Real term1 = -2 * (m_exponent)*rad * rad * pow(r, -2 * m_exponent - 1); Real term2 = 2 * (m_exponent)*rad * pow(r, -1 * m_exponent - 1); Real dE = m_depth * (term1 + term2); force = (dE / r) * force; // update gradients for (unsigned int c = 0; c < 3; ++c) { grad[3 * i + c] += force[c]; grad[3 * j + c] -= force[c]; } } } } // handle any constraints cleanGradients(grad); constraintGradients(x, grad); } } // namespace Avogadro::Calc avogadrolibs-1.101.0/avogadro/calc/lennardjones.h000066400000000000000000000033211506155467400217010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_LENNARDJONES_H #define AVOGADRO_CALC_LENNARDJONES_H #include "avogadrocalcexport.h" #include namespace Avogadro { namespace Core { class Molecule; class UnitCell; } // namespace Core namespace Calc { class AVOGADROCALC_EXPORT LennardJones : public EnergyCalculator { public: LennardJones(); ~LennardJones() override = default; LennardJones* newInstance() const override { return new LennardJones; } std::string identifier() const override { return "LJ"; } std::string name() const override { return "Lennard-Jones"; } std::string description() const override { return "Universal Lennard-Jones potential"; } bool acceptsIons() const override { return true; } bool acceptsRadicals() const override { return true; } bool acceptsUnitCell() const override { return true; } Core::Molecule::ElementMask elements() const override { return (m_elements); } Real value(const Eigen::VectorXd& x) override; void gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) override; /** * Called when the current molecule changes. */ void setMolecule(Core::Molecule* mol) override; protected: Core::Molecule* m_molecule; Core::UnitCell* m_cell; Eigen::MatrixXd m_radii; bool m_vdw; Real m_depth; int m_exponent; Core::Molecule::ElementMask m_elements; }; } // namespace Calc } // namespace Avogadro #endif // AVOGADRO_CALC_LENNARDJONES_H avogadrolibs-1.101.0/avogadro/calc/lennardjonesdata.h000066400000000000000000000250461506155467400225430ustar00rootroot00000000000000/***************************************************************************** # CDDL HEADER START # # The contents of this file are subject to the terms of the Common Development # and Distribution License Version 1.0 (the "License"). # # You can obtain a copy of the license at # http://www.opensource.org/licenses/CDDL-1.0. See the License for the # specific language governing permissions and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each file and # include the License file in a prominent location with the name LICENSE.CDDL. # If applicable, add the following below this CDDL HEADER, with the fields # enclosed by brackets "[]" replaced with your own identifying information: # # Portions Copyright (c) [yyyy] [name of copyright owner]. All rights reserved. # # CDDL HEADER END # # # Copyright (c) 2015, Regents of the University of Minnesota. # All rights reserved. # # Contributors: # Ryan S. Elliott # Andrew Akerson # # * Sigma parameters are set to (2^{-1/6})*r_0, where r_0 is the atomic # covalent radius. Covalent radii for elements 1--96 were taken from Wolfram # Mathematica's `ElementData["CovalentRadius"]' command. Covalent radii for # elements 97--118 were taken from Fig. 3 of the article Pyykko, M. Atsumi, # J. Chem. Eur. J. 15 (2009) 12770. # # * Epsilon parameters are set to the bond dissociation energy. Bond # dissociation energies for elements 1--55, 57--60, and 61--84 were taken # from the CRC Handbook of Chemistry and Physics, 91st Edition, # Ed. W.H. Haynes, 2010. (as posted here: # http://staff.ustc.edu.cn/~luo971/2010-91-CRC-BDEs-Tables.pdf) # # The value (cohesive energy, in this case) for element 56 was obtained from # p. 50 in Charles Kittel. Introduction to Solid State Physics, 8th # edition. Hoboken, NJ: John Wiley & Sons, Inc, 2005. # # The bond dissociation energy value for element 61 was obtained from # "Interpolation scheme for the cohesive energies for the lanthanides and # actinides" Borje Johansson and Anders Rosengren, Phys. Rev. B 11, 1367 # (1975). # # The bond dissociation energies for elements 85--118 were not found in the # literature. Thus, the values used here are approximated by subtracting 10% # from the value for the element in the same Group (column) and previous # Period (row) of the periodic table. *****************************************************************************/ // Formatted as a C / C++ header for Avogadro // from OpenKIM LennardJones612_UniversalShifted.txt // https://doi.org/10.25950/962b4967 // This is a 'universal' parameterization for the LennardJones612 model driver // for all species types supported by the KIM API. The parameterization uses a // shifted cutoff so that all interactions have a continuous energy function at // the cutoff radius. // This model was automatically fit using Lorentz-Berthelot mixing rules. It // reproduces the dimer equilibrium separation (covalent radii) and the bond // dissociation energies. It has not been fitted to other physical properties // and its ability to model structures other than dimers is unknown. #ifndef AVOGADRO_CALC_LENNARDJONES_DATA #define AVOGADRO_CALC_LENNARDJONES_DATA // values are # cutoff # epsilon # sigma // cutoff and sigmas are in units of Angstroms // epsilon are in units of Electron Volts float ljparams[][3] = { { 4.0, 1.0, 1.0 }, // dummy { 2.2094300, 4.4778900, 0.5523570 }, // hydrogen { 1.9956100, 0.0009421, 0.4989030 }, { 9.1228000, 1.0496900, 2.2807000 }, { 6.8421000, 0.5729420, 1.7105300 }, { 6.0581100, 2.9670300, 1.5145300 }, { 5.4166600, 6.3695300, 1.3541700 }, { 5.0603000, 9.7537900, 1.2650800 }, { 4.7039500, 5.1264700, 1.1759900 }, { 4.0625000, 1.6059200, 1.0156200 }, { 4.1337700, 0.0036471, 1.0334400 }, { 11.8311000, 0.7367450, 2.9577800 }, { 10.0493000, 0.0785788, 2.5123300 }, { 8.6239000, 2.7006700, 2.1559700 }, { 7.9111800, 3.1743100, 1.9778000 }, { 7.6260900, 5.0305000, 1.9065200 }, { 7.4835500, 4.3692700, 1.8708900 }, { 7.2697300, 4.4832800, 1.8174300 }, { 7.5548200, 0.0123529, 1.8887100 }, { 14.4682000, 0.5517990, 3.6170500 }, { 12.5439000, 0.1326790, 3.1359600 }, { 12.1162000, 1.6508000, 3.0290600 }, { 11.4035000, 1.1802700, 2.8508800 }, { 10.9046000, 2.7524900, 2.7261500 }, { 9.9067900, 1.5367900, 2.4767000 }, { 9.9067900, 0.5998880, 2.4767000 }, { 9.4078900, 1.1844200, 2.3519700 }, { 8.9802600, 1.2776900, 2.2450600 }, { 8.8377200, 2.0757200, 2.2094300 }, { 9.4078900, 2.0446300, 2.3519700 }, { 8.6951700, 0.1915460, 2.1737900 }, { 8.6951700, 1.0642000, 2.1737900 }, { 8.5526300, 2.7017100, 2.1381600 }, { 8.4813600, 3.9599000, 2.1203400 }, { 8.5526300, 3.3867700, 2.1381600 }, { 8.5526300, 1.9706300, 2.1381600 }, { 8.2675400, 0.0173276, 2.0668900 }, { 15.6798000, 0.4682650, 3.9199500 }, { 13.8980000, 0.1339230, 3.4745100 }, { 13.5417000, 2.7597500, 3.3854200 }, { 12.4726000, 3.0520100, 3.1181500 }, { 11.6886000, 5.2782000, 2.9221500 }, { 10.9759000, 4.4749900, 2.7439700 }, { 10.4770000, 3.3815900, 2.6192400 }, { 10.4057000, 1.9617200, 2.6014200 }, { 10.1206000, 2.4058200, 2.5301500 }, { 9.9067900, 1.3709700, 2.4767000 }, { 10.3344000, 1.6497600, 2.5836100 }, { 10.2632000, 0.0377447, 2.5657900 }, { 10.1206000, 0.8113140, 2.5301500 }, { 9.9067900, 1.9005700, 2.4767000 }, { 9.9067900, 3.0882800, 2.4767000 }, { 9.8355200, 2.6312300, 2.4588800 }, { 9.9067900, 1.5393800, 2.4767000 }, { 9.9780700, 0.0238880, 2.4945200 }, { 17.3903000, 0.4166420, 4.3475900 }, { 15.3235000, 1.9000000, 3.8308600 }, { 14.7533000, 2.4996100, 3.6883200 }, { 14.5395000, 2.5700800, 3.6348700 }, { 14.4682000, 1.2994600, 3.6170500 }, { 14.3257000, 0.8196050, 3.5814100 }, { 14.1831000, 3.2413400, 3.5457800 }, { 14.1118000, 0.5211220, 3.5279600 }, { 14.1118000, 0.4299180, 3.5279600 }, { 13.9693000, 2.0995600, 3.4923200 }, { 13.8267000, 1.3999900, 3.4566900 }, { 13.6842000, 0.6900550, 3.4210500 }, { 13.6842000, 0.6900550, 3.4210500 }, { 13.4704000, 0.7387660, 3.3676000 }, { 13.5417000, 0.5211220, 3.3854200 }, { 13.3278000, 0.1303990, 3.3319600 }, { 13.3278000, 1.4331500, 3.3319600 }, { 12.4726000, 3.3608600, 3.1181500 }, { 12.1162000, 4.0034300, 3.0290600 }, { 11.5460000, 6.8638900, 2.8865100 }, { 10.7621000, 4.4387100, 2.6905100 }, { 10.2632000, 4.2625300, 2.5657900 }, { 10.0493000, 3.7028700, 2.5123300 }, { 9.6929800, 3.1401000, 2.4232400 }, { 9.6929800, 2.3058000, 2.4232400 }, { 9.4078900, 0.0454140, 2.3519700 }, { 10.3344000, 0.5770870, 2.5836100 }, { 10.4057000, 0.8589880, 2.6014200 }, { 10.5482000, 2.0798700, 2.6370600 }, { 9.9780700, 1.8995300, 2.4945200 }, { 10.6908000, 1.3854420, 2.6727000 }, { 10.6908000, 0.0214992, 2.6727000 }, { 18.5307000, 0.3749778, 4.6326700 }, { 15.7511000, 1.7100000, 3.9377700 }, { 15.3235000, 2.2496490, 3.8308600 }, { 14.6820000, 2.3130720, 3.6705000 }, { 14.2544000, 1.1695140, 3.5635900 }, { 13.9693000, 0.7376445, 3.4923200 }, { 13.5417000, 2.9172060, 3.3854200 }, { 13.3278000, 0.4690098, 3.3319600 }, { 12.8289000, 0.3869262, 3.2072400 }, { 12.0450000, 1.8896040, 3.0112400 }, { 11.9737000, 1.2599910, 2.9934200 }, { 11.9737000, 0.6210495, 2.9934200 }, { 11.7599000, 0.6210495, 2.9399700 }, { 11.9024000, 0.6648894, 2.9756000 }, { 12.3300000, 0.4690098, 3.0825100 }, { 12.5439000, 0.1173591, 3.1359600 }, { 11.4748000, 1.2898350, 2.8686900 }, { 11.1897000, 3.0247740, 2.7974200 }, { 10.6195000, 3.6030870, 2.6548800 }, { 10.1919000, 6.1775010, 2.5479700 }, { 10.0493000, 3.9948390, 2.5123300 }, { 9.5504300, 3.8362770, 2.3876100 }, { 9.1940700, 3.3325830, 2.2985200 }, { 9.1228000, 2.8260900, 2.2807000 }, { 8.6239000, 2.0752200, 2.1559700 }, { 8.6951700, 0.0408726, 2.1737900 }, { 9.6929800, 0.5193783, 2.4232400 }, { 10.1919000, 0.7730892, 2.5479700 }, { 11.5460000, 1.8718830, 2.8865100 }, { 12.4726000, 1.7095770, 3.1181500 }, { 11.7599000, 1.2468978, 2.9399700 }, { 11.1897000, 0.0193493, 2.7974200 } }; #endif // AVOGADRO_CALC_LENNARDJONES_DATA_Havogadrolibs-1.101.0/avogadro/calc/uff.cpp000066400000000000000000001237041506155467400203420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "uff.h" #include "uffdata.h" #include #include #include #include #include #include #include namespace Avogadro::Calc { using namespace Core; using Eigen::Vector3d; enum Coordination { Resonant = 0, // conjugated / aromatic Linear = 1, // sp linear Trigonal = 2, // sp2 planar Tetrahedral = 3, // sp3 tetrahedral SquarePlanar = 4, // square planar TrigonalBipyramidal = 5, // trigonal bipyramidal Octahedral = 6, // octahedral TrigonalBipentagonal = 7, // trigonal bipentagonal Other = 8 // including higher coordination }; class UFFBond // track the bond parameters { public: Index _atom1; Index _atom2; Real _r0; Real _kb; }; class UFFAngle { public: Index _atom1; Index _atom2; Index _atom3; Real _theta0; Real _kijk; Real _c0; Real _c1; Real _c2; Coordination coordination; }; class UFFTorsion { public: Index _atom1; Index _atom2; Index _atom3; Index _atom4; Real _cos_phi0; Real _ijkl; short _n; // periodicity }; class UFFOOP { public: Index _atom1; // central atom Index _atom2; Index _atom3; Index _atom4; Real _c0; Real _c1; Real _c2; Real _koop; }; class UFFVdW { public: Index _atom1; Index _atom2; Real _depth; Real _x; }; class UFFPrivate // track the particular calculations for a molecule { public: std::vector m_atomTypes; // entry row in uffparams std::vector m_bonds; std::vector m_angles; std::vector m_oops; std::vector m_torsions; std::vector m_vdws; Core::Molecule* m_molecule; Core::UnitCell* m_cell; UFFPrivate(Core::Molecule* mol = nullptr) : m_molecule(mol) { if (mol == nullptr || mol->atomCount() < 2) { return; // nothing to do } setAtomTypes(); setBonds(); setAngles(); setOOPs(); setTorsions(); setVdWs(); m_cell = mol->unitCell(); // could be nullptr } void setAtomTypes() { // set up the calculations including atom types and parameters // loop through atoms to get the atom types m_atomTypes.reserve(m_molecule->atomCount()); for (Index i = 0; i < m_molecule->atomCount(); ++i) { const Core::Atom& atom = m_molecule->atom(i); int atomicNumber = atom.atomicNumber(); int atomType = -1; for (int j = 0; j < 126; ++j) { if (uffparams[j].element == atomicNumber) { atomType = j; // not guaranteed - only the first type for this element // we can peek ahead to see if there are any more for this element if (j < 125 && uffparams[j + 1].element == atomicNumber) { // there are multiple types for this element const Array bonds = m_molecule->bonds(i); if (atomicNumber == 1) { // hydrogen has two types, so count the bonds if (bonds.size() == 1) { atomType = j; // hydrogen with one bond break; } else { // hydrogen with more than one bond = bridging type atomType = j + 1; break; } } // remaining elements, we want the coordination number // and possibly formal charge [[maybe_unused]] int charge = m_molecule->formalCharge(i); // UFF fortunately only has square planar as special cases // so if it's more than 4 bonds, we just use that type if (bonds.size() > 4) { // check the atom type symbols char coord = '4' + (bonds.size() - 4); if (uffparams[j].label[2] == coord) { atomType = j; break; } else { // check the next one if (uffparams[j + 1].label[2] == coord) { atomType = j + 1; break; } } } else { // count double and triple bonds int doubleBonds = 0; int tripleBonds = 0; for (const Bond bond : bonds) { unsigned char order = bond.order(); if (order == 2) { ++doubleBonds; } else if (order == 3) { ++tripleBonds; } } char coord = '3'; if (tripleBonds > 0 || doubleBonds > 1) coord = '1'; // sp linear else if (doubleBonds == 1) coord = '2'; // sp2 trigonal else if (doubleBonds == 0 && tripleBonds == 0) coord = '3'; // sp3 tetrahedral // check the atom type symbols if (uffparams[j].label[2] == coord) { atomType = j; break; } else { // bump up until we find one while (j < 125 && uffparams[j].label[2] != coord) { ++j; } atomType = j; break; } } break; } // nope it's the only one for this element // we're done break; } } m_atomTypes.push_back(atomType); } // we need to do one more pass to set aromatic / resonance types // i.e., if an sp2 atom has neighbors, mark them as resonant for (Index i = 0; i < m_molecule->atomCount(); ++i) { const Core::Atom& atom = m_molecule->atom(i); int atomicNumber = atom.atomicNumber(); // carbon, nitrogen, oxygen and sulfur sp2 atoms if (atomicNumber == 6 || atomicNumber == 7 || atomicNumber == 8 || atomicNumber == 16) { const char symbolChar = uffparams[m_atomTypes[i]].label[2]; // we might have an aromatic / resonant N, O, or S // e.g., furan, thiophene .. we also mark amide N // if it's an sp3 carbon, skip it if (atomicNumber == 6 && symbolChar == '3') continue; // check the neighbors const std::vector& neighbors = m_molecule->graph().neighbors(i); // we can skip carbonyl oxygen (i.e., only one neighbor) if ((atomicNumber == 8 || atomicNumber == 16) && neighbors.size() == 1) continue; bool resonant = false; for (Index j : neighbors) { auto symbolLabel = uffparams[m_atomTypes[j]].label; if (symbolLabel.size() < 3) continue; // not a resonant type if (symbolLabel[2] == '2' || symbolLabel[2] == 'R') { resonant = true; break; } } if (resonant) { // set the resonant type if (atomicNumber == 7 && symbolChar == '3') m_atomTypes[i] = m_atomTypes[i] + 1; // N_R after N_3 else if (atomicNumber == 8 && symbolChar == '3') m_atomTypes[i] = m_atomTypes[i] + 2; // O_R after O_3 and O_3_z else if (atomicNumber == 16 && symbolChar == '3') { // loop until we find 'R' .. might be a few different S types while (uffparams[m_atomTypes[i]].label[2] != 'R') ++m_atomTypes[i]; } else m_atomTypes[i] = m_atomTypes[i] - 1; // C_R before C_2 } } } } // calculate the ideal bond distance between two atoms // used in a few places Real calculateRij(Index atom1, Index atom2) { Real ri = uffparams[m_atomTypes[atom1]].r1; Real rj = uffparams[m_atomTypes[atom2]].r1; Real r0 = ri + rj; // bond order correction Bond bond = m_molecule->bond(atom1, atom2); Real order = static_cast(bond.order()); // check if it's a resonant / aromatic bond auto symbol1 = uffparams[m_atomTypes[atom1]].label; auto symbol2 = uffparams[m_atomTypes[atom2]].label; if (symbol1.size() > 2 && symbol1[2] == 'R' && symbol2.size() > 2 && symbol2[2] == 'R') { order = 1.5; // tweak for amide if ((symbol1[0] == 'N' && symbol2[0] == 'C') || (symbol1[0] == 'C' && symbol2[0] == 'N')) order = 1.41; } Real rbo = -0.1332 * r0 * log(order); // electronegativity correction Real chi1 = uffparams[m_atomTypes[atom1]].Xi; Real chi2 = uffparams[m_atomTypes[atom2]].Xi; Real ren = ri * rj * pow((sqrt(chi1) - sqrt(chi2)), 2) / (chi1 * ri + chi2 * rj); // UFF publication has an error (electronegativity correction): // https://towhee.sourceforge.net/forcefields/uff.html return r0 + rbo - ren; } void setBonds() { // loop through the bonds for (Index i = 0; i < m_molecule->bondCount(); ++i) { const Core::Bond& bond = m_molecule->bond(i); Index atom1 = bond.atom1().index(); Index atom2 = bond.atom2().index(); UFFBond b; b._atom1 = atom1; b._atom2 = atom2; b._r0 = calculateRij(atom1, atom2); Real z1 = uffparams[m_atomTypes[atom1]].Z1; Real z2 = uffparams[m_atomTypes[atom2]].Z1; b._kb = 664.12 * z1 * z2 / pow((b._r0), 3); m_bonds.push_back(b); /* std::cout << " bond " << atom1 << " " << uffparams[m_atomTypes[atom1]].label << " " << atom2 << " " << uffparams[m_atomTypes[atom2]].label << " " << b._r0 << " " << b._kb << std::endl; */ } } void setAngles() { AngleIterator ai(m_molecule); auto angle = ai.begin(); while (angle != ai.end()) { Index i = std::get<0>(angle); Index j = std::get<1>(angle); Index k = std::get<2>(angle); UFFAngle a; a._atom1 = i; a._atom2 = j; a._atom3 = k; Real theta0 = uffparams[m_atomTypes[j]].theta0 * DEG_TO_RAD; a._theta0 = theta0 * RAD_TO_DEG; // store in degrees for consistency // calculate the kijk Real rij = calculateRij(i, j); Real rjk = calculateRij(j, k); /* std::cout << " Angle " << i << " " << j << " " << k << " " << rij << " " << rjk << " " << theta0 << std::endl; */ Real rik = sqrt(rij * rij + rjk * rjk - 2 * rij * rjk * cos(theta0)); Real Zi = uffparams[m_atomTypes[i]].Z1; Real Zk = uffparams[m_atomTypes[k]].Z1; Real cosTheta0 = cos(theta0); Real cosTheta0Sq = cosTheta0 * cosTheta0; // see https://towhee.sourceforge.net/forcefields/uff.html // e.g., original paper had some typos a._kijk = 664.12 / (rij * rjk) * (Zi * Zk) / (pow(rik, 5)) * rij * rjk; a._kijk *= (3 * rij * rjk * (1 - cosTheta0Sq) - rik * rik * cosTheta0); // calculate the c0, c1, c2 terms // based on coordination of the central atom std::string label = uffparams[m_atomTypes[j]].label; auto neighbors = m_molecule->graph().neighbors(j); a._c0 = 0.0; a._c1 = 0.0; a._c2 = 0.0; if (label.size() < 3 || neighbors.size() == 1) { // linear a.coordination = Linear; a._c0 = 1.0; } else if ((label[2] == '2' || label[2] == 'R') && neighbors.size() == 3) { a.coordination = Trigonal; a._kijk = a._kijk / 9.0; // divide by n**2 a._c0 = 3.0; } else if (label[2] == '4') { a.coordination = SquarePlanar; a._kijk = a._kijk / 16.0; a._c0 = 4.0; } else if (label[2] == '5') { // TODO a.coordination = TrigonalBipyramidal; } else if (label[2] == '6') { a.coordination = Octahedral; a._kijk = a._kijk / 16.0; a._c0 = 4.0; } else if (neighbors.size() > 6) { // TODO - trigonal bipentagonal and higher coordination // (e.g., as a repulsion between the other atoms) a.coordination = Other; } else { a.coordination = Tetrahedral; Real sinTheta0 = sin(theta0); a._c2 = 1.0 / (4.0 * sinTheta0 * sinTheta0); a._c1 = -4.0 * a._c2 * cosTheta0; a._c0 = a._c2 * (2.0 * cosTheta0 * cosTheta0 + 1.0); } m_angles.push_back(a); angle = ++ai; } } void setOOPs() { // loop through atoms checking for cases with 3 neighbors for (Index i = 0; i < m_molecule->atomCount(); ++i) { auto neighbors = m_molecule->graph().neighbors(i); if (neighbors.size() != 3) continue; // also only for certain elements const Core::Atom& atom = m_molecule->atom(i); int atomicNumber = atom.atomicNumber(); switch (atomicNumber) { case 6: // carbon case 7: // nitrogen case 8: // oxygen case 15: // phos. case 33: // as case 51: // sb case 83: // bi break; default: // no inversion term for this element continue; } UFFOOP oop; oop._atom1 = i; oop._atom2 = neighbors[0]; oop._atom3 = neighbors[1]; oop._atom4 = neighbors[2]; std::string symbol = uffparams[m_atomTypes[i]].label; if (symbol == "N_R" || symbol == "N_2" || symbol == "N_R" || symbol == "O_2" || symbol == "O_R") { oop._c0 = 1.0; oop._c1 = -1.0; oop._c2 = 0.0; oop._koop = 6.0; } else if (symbol == "P_3+3" || symbol == "As3+3" || symbol == "Sb3+3" || symbol == "Bi3+3") { Real phi; switch (atomicNumber) { case 15: // P phi = 84.4339 * DEG_TO_RAD; break; case 33: // As phi = 86.9735 * DEG_TO_RAD; break; case 51: // Sb phi = 87.7047 * DEG_TO_RAD; break; case 83: // Bi default: phi = 90.0; } oop._c1 = -4.0 * cos(phi); oop._c2 = 1.0; oop._c0 = -1.0 * oop._c1 * cos(phi) + oop._c2 * cos(2.0 * phi); oop._koop = 22.0; } else if (symbol == "C_2" || symbol == "C_R") { oop._c0 = 1.0; oop._c1 = -1.0; oop._c2 = 0.0; oop._koop = 6.0; // check if one of the other atoms is "O_2" if (uffparams[m_atomTypes[neighbors[0]]].label == "O_2" || uffparams[m_atomTypes[neighbors[1]]].label == "O_2" || uffparams[m_atomTypes[neighbors[2]]].label == "O_2") { oop._koop = 50.0; } } else { continue; } m_oops.push_back(oop); } } void setTorsions() { DihedralIterator di(m_molecule); auto dihedral = di.begin(); while (dihedral != di.end()) { Index i = std::get<0>(dihedral); Index j = std::get<1>(dihedral); Index k = std::get<2>(dihedral); Index l = std::get<3>(dihedral); // check the bond order of j-k // (if it's not rotatable, we can skip this one) Bond bond = m_molecule->bond(j, k); if (bond.order() != 1) { dihedral = ++di; continue; } UFFTorsion t; t._atom1 = i; t._atom2 = j; t._atom3 = k; t._atom4 = l; // default is for sp3-sp3 Real order = static_cast(bond.order()); auto symbol1 = uffparams[m_atomTypes[j]].label; auto symbol2 = uffparams[m_atomTypes[k]].label; // TODO: a bunch of special cases if (symbol1.size() < 3 || symbol2.size() < 3 || symbol1[2] == '3' || symbol2[2] == '3') { // default is sp3-sp3 t._n = 3; Real Vi_j = uffparams[m_atomTypes[j]].Vi; Real Vi_k = uffparams[m_atomTypes[k]].Vi; Real phi0 = 60.0; // handle some special cases // e.g. a pair of group 6 sp3 atoms auto atomicNumberJ = m_molecule->atom(j).atomicNumber(); auto atomicNumberK = m_molecule->atom(k).atomicNumber(); switch (atomicNumberJ) { case 8: // oxygen t._n = 2; phi0 = 90.0; // hydrogen peroxide H-O-O-H Vi_j = 2.0; break; case 16: // sulfur case 34: // selenium case 52: // tellurium case 84: // polonium Vi_j = 6.8; t._n = 2; phi0 = 90.0; default: break; } switch (atomicNumberK) { case 8: // oxygen t._n = 2; phi0 = 90.0; // hydrogen peroxide H-O-O-H Vi_k = 2.0; break; case 16: // sulfur case 34: // selenium case 52: // tellurium case 84: // polonium Vi_k = 6.8; t._n = 2; phi0 = 90.0; default: break; } t._cos_phi0 = cos(t._n * phi0 * DEG_TO_RAD); // geometric mean of the two V1 parameters t._ijkl = 0.5 * sqrt(Vi_j * Vi_k); } else if (symbol1[2] == 'R' && symbol2[2] == 'R') { order = 1.5; // tweak for amide if ((symbol1[0] == 'N' && symbol2[0] == 'C') || (symbol1[0] == 'C' && symbol2[0] == 'N')) order = 1.41; t._n = 2; t._cos_phi0 = cos(t._n * 180.0 * DEG_TO_RAD); t._ijkl = 5.0 * sqrt(uffparams[m_atomTypes[j]].Uj * uffparams[m_atomTypes[k]].Uj); t._ijkl *= 0.5 * (1.0 + 4.18 * log(order)); } else if ((symbol1[2] == '2' && symbol2[2] == '3') || (symbol1[2] == '3' && symbol2[2] == '2')) { // sp2-sp3 t._cos_phi0 = cos(0.0 * DEG_TO_RAD); t._n = 6; t._ijkl = 0.5; // exceptions for Oxygen, Sulfur, Selenium, Tellurium, Polonium auto atomicNumberJ = m_molecule->atom(j).atomicNumber(); auto atomicNumberK = m_molecule->atom(k).atomicNumber(); if (atomicNumberJ == 8 || atomicNumberK == 8 || atomicNumberJ == 16 || atomicNumberK == 16 || atomicNumberJ == 34 || atomicNumberK == 34 || atomicNumberJ == 52 || atomicNumberK == 52 || atomicNumberJ == 84 || atomicNumberK == 84) { t._n = 2; t._cos_phi0 = cos(90.0 * DEG_TO_RAD); } } else { dihedral = ++di; continue; } m_torsions.push_back(t); dihedral = ++di; } } // check if atoms i and j are 1-2 or 1-3 connected // fairly fast because we're only checking neighbors bool areConnected(Index i, Index j) { const std::vector& neighbors = m_molecule->graph().neighbors(i); const std::vector& neighbors2 = m_molecule->graph().neighbors(j); for (Index k : neighbors) { if (k == j) return true; for (Index l : neighbors2) { if (l == k) return true; } } return false; } void setVdWs() { // we do a double-loop through the atoms // and check for 1-2 or 1-3 with areConnected for (Index i = 0; i < m_molecule->atomCount(); ++i) { for (Index j = i + 1; j < m_molecule->atomCount(); ++j) { if (!areConnected(i, j)) { UFFVdW v; v._atom1 = i; v._atom2 = j; v._depth = sqrt(uffparams[m_atomTypes[i]].D1 * uffparams[m_atomTypes[j]].D1); v._x = sqrt(uffparams[m_atomTypes[i]].x1 * uffparams[m_atomTypes[j]].x1); m_vdws.push_back(v); } } } } Real bondEnergies(const Eigen::VectorXd& x) { Real energy = 0.0; for (const UFFBond& bond : m_bonds) { Index i = bond._atom1; Index j = bond._atom2; Real r0 = bond._r0; Real kb = bond._kb; Real dx = x[3 * i] - x[3 * j]; Real dy = x[3 * i + 1] - x[3 * j + 1]; Real dz = x[3 * i + 2] - x[3 * j + 2]; Real r = std::hypot(dx, dy, dz); Real dr = r - r0; /* std::cout << " Bond " << i << " " << j << " " << r0 << " " << r << " " << dr << std::endl; */ // the 0.5 * kb is already in the kb to save a multiplication energy += kb * dr * dr; } return energy; } Real angleEnergies(const Eigen::VectorXd& x) { Real energy = 0.0; for (const UFFAngle& angle : m_angles) { Index i = angle._atom1; Index j = angle._atom2; Index k = angle._atom3; Real theta0 = angle._theta0 * DEG_TO_RAD; Real kijk = angle._kijk; Real dx1 = x[3 * i] - x[3 * j]; Real dy1 = x[3 * i + 1] - x[3 * j + 1]; Real dz1 = x[3 * i + 2] - x[3 * j + 2]; Real dx2 = x[3 * k] - x[3 * j]; Real dy2 = x[3 * k + 1] - x[3 * j + 1]; Real dz2 = x[3 * k + 2] - x[3 * j + 2]; Real r1 = std::hypot(dx1, dy1, dz1); Real r2 = std::hypot(dx2, dy2, dz2); Real dot = dx1 * dx2 + dy1 * dy2 + dz1 * dz2; Real theta = acos(dot / (r1 * r2)); /* std::cout << " Angle " << i << " " << j << " " << k << " " << r1 << " " << r2 << " " << theta * RAD_TO_DEG << " " << theta0 * RAD_TO_DEG << std::endl; */ // TODO - migrate special cases from Open Babel Coordination coord = angle.coordination; Real c0 = angle._c0; Real c1 = angle._c1; Real c2 = angle._c2; switch (coord) { case Linear: // fixed typo in UFF paper (it's 1+ cos(theta) not 1 - cos(theta)) energy += kijk * (1 + cos(c0 * theta)); break; case Trigonal: case Resonant: case SquarePlanar: case Octahedral: // c0 contains n for these cases // and kijk is already divided by n**2 // i.e., if the angle is less than approx theta0, energy goes up // exponentially energy += kijk * (1 - cos(c0 * theta)) + exp(-20.0 * (theta - theta0 + 0.25)); break; case Tetrahedral: { Real cosTheta = cos(theta); // use cos 2t = (2cos^2 - 1) energy += kijk * (c0 + c1 * cosTheta + c2 * (2 * cosTheta * cosTheta - 1)); break; } case TrigonalBipyramidal: case TrigonalBipentagonal: case Other: default: // just use a harmonic potential // but these should actually be set up as VdW repulsions // so this shouldn't ever happen energy += kijk * (theta - theta0) * (theta - theta0); } } return energy; } Real oopEnergies(const Eigen::VectorXd& x) { Real energy = 0.0; for (const UFFOOP& oop : m_oops) { // for UFF - I is defined as the central atom Index i = oop._atom1; Index j = oop._atom2; Index k = oop._atom3; Index l = oop._atom4; Real koop = oop._koop; Real c0 = oop._c0; Real c1 = oop._c1; Real c2 = oop._c2; Vector3d vi(x[3 * i], x[3 * i + 1], x[3 * i + 2]); Vector3d vj(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Vector3d vk(x[3 * k], x[3 * k + 1], x[3 * k + 2]); Vector3d vl(x[3 * l], x[3 * l + 1], x[3 * l + 2]); // use outOfPlaneAngle() from angletools.h Real angle = outOfPlaneAngle(vi, vj, vk, vl) * DEG_TO_RAD; energy += koop * (c0 + c1 * cos(angle) + c2 * cos(2 * angle)); } return energy; } Real torsionEnergies(const Eigen::VectorXd& x) { Real energy = 0.0; for (const UFFTorsion& torsion : m_torsions) { Index i = torsion._atom1; Index j = torsion._atom2; Index k = torsion._atom3; Index l = torsion._atom4; Vector3d vi(x[3 * i], x[3 * i + 1], x[3 * i + 2]); Vector3d vj(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Vector3d vk(x[3 * k], x[3 * k + 1], x[3 * k + 2]); Vector3d vl(x[3 * l], x[3 * l + 1], x[3 * l + 2]); Real phi = calculateDihedral(vi, vj, vk, vl) * DEG_TO_RAD; Real cosPhi = cos(torsion._n * phi); Real cosPhi0 = torsion._cos_phi0; Real kijkl = torsion._ijkl; // 0.5 * kijkl is already in the kijkl to save a multiplication energy += kijkl * (1.0 - cosPhi0 * cosPhi); } return energy; } Real vdwEnergies(const Eigen::VectorXd& x) { Real energy = 0.0; for (const UFFVdW& vdw : m_vdws) { Index i = vdw._atom1; Index j = vdw._atom2; Real depth = vdw._depth; Real xij = vdw._x; Real x6 = xij * xij * xij * xij * xij * xij; Real x12 = x6 * x6; // TODO: use the unit cell if available Real dx = x[3 * i] - x[3 * j]; Real dy = x[3 * i + 1] - x[3 * j + 1]; Real dz = x[3 * i + 2] - x[3 * j + 2]; // we don't need a square root since 6 and 12 are even powers Real r2 = (dx * dx + dy * dy + dz * dz); Real r6 = r2 * r2 * r2; Real r12 = r6 * r6; energy += depth * (x12 / r12 - 2 * x6 / r6); } return energy; } void bondGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { for (const UFFBond& bond : m_bonds) { Index i = bond._atom1; Index j = bond._atom2; Real r0 = bond._r0; Real kb = bond._kb; Real dx = x[3 * i] - x[3 * j]; Real dy = x[3 * i + 1] - x[3 * j + 1]; Real dz = x[3 * i + 2] - x[3 * j + 2]; Real r = std::hypot(dx, dy, dz); Real dr = r - r0; Real f = 2.0 * kb * dr / r; grad[3 * i] += f * dx; grad[3 * i + 1] += f * dy; grad[3 * i + 2] += f * dz; grad[3 * j] -= f * dx; grad[3 * j + 1] -= f * dy; grad[3 * j + 2] -= f * dz; } } void angleGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { // j is the central atom (i-j-k) for (const UFFAngle& angle : m_angles) { Index i = angle._atom1; Index j = angle._atom2; Index k = angle._atom3; Real theta0 = angle._theta0 * DEG_TO_RAD; Real kijk = angle._kijk; const Vector3d vi(x[3 * i], x[3 * i + 1], x[3 * i + 2]); const Vector3d vj(x[3 * j], x[3 * j + 1], x[3 * j + 2]); const Vector3d vk(x[3 * k], x[3 * k + 1], x[3 * k + 2]); const Vector3d ij = vi - vj; const Vector3d kj = vk - vj; Real rij = ij.norm(); Real rkj = kj.norm(); // check if these are near-zero if (rij < 1e-3 || rkj < 1e-3) continue; // skip this angle Real dot = ij.dot(kj); Vector3d ij_cross_kj = ij.cross(kj); Real crossNorm = ij_cross_kj.norm(); // check for near-zero cross product if (crossNorm < 1e-6) continue; // skip this angle Real theta = atan2(crossNorm, dot); // clamp the angle to -pi to pi if (theta < -M_PI) theta += 2 * M_PI; else if (theta > M_PI) theta -= 2 * M_PI; /* std::cout << " AngleGrad " << i << " " << j << " " << k << " " << theta0 * RAD_TO_DEG << " " << theta * RAD_TO_DEG << " " << dtheta * RAD_TO_DEG << " " << kijk << std::endl; std::cout << " Norms " << rij << " " << rkj << " " << rki << std::endl; */ // dE / dtheta is a bit annoying with UFF // because there are a bunch of special cases Real f = 0.0; Real c0 = angle._c0; Real c1 = angle._c1; Real c2 = angle._c2; switch (angle.coordination) { case Linear: // fixed typo in UFF paper (it's 1+ cos(theta) not 1 - cos(theta)) // energy += kijk * (1 + cos(c0 * theta)); f = kijk * c0 * sin(c0 * theta); break; case Trigonal: case Resonant: case SquarePlanar: case Octahedral: // c0 contains n for these cases // and kijk is already divided by n**2 // i.e., if the angle is less than approx theta0, energy goes up // exponentially // energy += // kijk * (1 - cos(c0 * theta)) + exp(-20.0 * (theta - theta0 + // 0.25)); f = kijk * c0 * sin(c0 * theta) + 20.0 * exp(-20.0 * (theta - theta0 + 0.25)) * sin(theta); break; case Tetrahedral: { Real cosTheta = cos(theta); Real sinTheta = sin(theta); // use cos 2t = (2cos^2 - 1) // use sin 2t = 2sin(t)cos(t) // energy += // kijk * (c0 + c1 * cosTheta + c2 * (2 * cosTheta * cosTheta - 1)); f = -kijk * (c1 * sinTheta + c2 * 2 * (2 * cosTheta * sinTheta)); break; } case TrigonalBipyramidal: case TrigonalBipentagonal: case Other: default: // energy += kijk * (theta - theta0) * (theta - theta0); f = 2.0 * kijk * (theta - theta0) * sin(theta); break; } // check for nan if (std::isnan(f)) continue; // Use the cross product to get the gradients Vector3d n = ij_cross_kj / crossNorm; // Gradients of the cross products Vector3d grad_cross_i = (kj.cross(n)).stableNormalized(); Vector3d grad_cross_k = (n.cross(ij)).stableNormalized(); Vector3d grad_cross_j = -(grad_cross_i + grad_cross_k); // Gradients of the dot product Vector3d grad_dot_i = kj; Vector3d grad_dot_k = ij; Vector3d grad_dot_j = -(kj + ij); // Final gradient using atan2 derivative: d/dx(atan2(y,x)) = (x*dy/dx - // y*dx/dx)/(x^2 + y^2) Real denom = crossNorm * crossNorm + dot * dot; Vector3d grad_i = f * (grad_cross_i * dot - crossNorm * grad_dot_i) / denom; Vector3d grad_j = f * (grad_cross_j * dot - crossNorm * grad_dot_j) / denom; Vector3d grad_k = f * (grad_cross_k * dot - crossNorm * grad_dot_k) / denom; // Add the gradients to the total gradients for each atom grad[3 * i] += grad_i[0]; grad[3 * i + 1] += grad_i[1]; grad[3 * i + 2] += grad_i[2]; grad[3 * j] += grad_j[0]; grad[3 * j + 1] += grad_j[1]; grad[3 * j + 2] += grad_j[2]; grad[3 * k] += grad_k[0]; grad[3 * k + 1] += grad_k[1]; grad[3 * k + 2] += grad_k[2]; } } void oopGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { for (const UFFOOP& oop : m_oops) { // for UFF - I is defined as the central atom Index i = oop._atom1; Index j = oop._atom2; Index k = oop._atom3; Index l = oop._atom4; Real koop = oop._koop; [[maybe_unused]] Real c0 = oop._c0; Real c1 = oop._c1; Real c2 = oop._c2; Vector3d vi(x[3 * i], x[3 * i + 1], x[3 * i + 2]); Vector3d vj(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Vector3d vk(x[3 * k], x[3 * k + 1], x[3 * k + 2]); Vector3d vl(x[3 * l], x[3 * l + 1], x[3 * l + 2]); // use outOfPlaneAngle() from angletools.h Real angle = outOfPlaneAngle(vi, vj, vk, vl) * DEG_TO_RAD; Real sinAngle = sin(angle); // dE / dangle Real dE = koop * (-c1 * sinAngle - 2.0 * c2 * sin(2.0 * angle)); // check for nan if (std::isnan(dE)) continue; // Get the bond vectors Vector3d ij = vj - vi; Vector3d ik = vk - vi; Vector3d il = vl - vi; Real rij = ij.norm(); Real rik = ik.norm(); Real ril = il.norm(); // check if the bond vectors are near zero if (rij < 1e-3 || rik < 1e-3 || ril < 1e-3) continue; // skip this oop // normalize the bond vectors ij = ij / rij; ik = ik / rik; il = il / ril; // we also need the angle between the bonds (i.e., j-i-k) Real cosTheta = ij.dot(ik) / (rij * rik); // clamp the cosTheta to -1 to 1 cosTheta = std::clamp(cosTheta, -1.0, 1.0); Real theta = acos(cosTheta); Real sinTheta = sin(theta); // get the cross products [[maybe_unused]] Eigen::Vector3d ij_cross_ik = ij.cross(ik).stableNormalized(); Eigen::Vector3d ik_cross_il = ik.cross(il).stableNormalized(); Eigen::Vector3d ij_cross_il = ij.cross(il).stableNormalized(); // some common factors [[maybe_unused]] Real numerator = cosTheta * sinAngle / sinTheta; // get the forces on the atoms Real dj0 = -dE * (ik_cross_il[0] - ij[0] + (ik[0] * cosTheta * sinAngle / sinTheta)) / (rij * sinTheta); Real dj1 = -dE * (ik_cross_il[1] - ij[1] + (ik[1] * cosTheta * sinAngle / sinTheta)) / (rij * sinTheta); Real dj2 = -dE * (ik_cross_il[2] - ij[2] + (ik[2] * cosTheta * sinAngle / sinTheta)) / (rij * sinTheta); grad[3 * j] += dj0; grad[3 * j + 1] += dj1; grad[3 * j + 2] += dj2; Real dk0 = -dE * (ij_cross_il[0] - ik[0] + (ij[0] * cosTheta * sinAngle / sinTheta)) / (rik * sinTheta); Real dk1 = -dE * (ij_cross_il[1] - ik[1] + (ij[1] * cosTheta * sinAngle / sinTheta)) / (rik * sinTheta); Real dk2 = -dE * (ij_cross_il[2] - ik[2] + (ij[2] * cosTheta * sinAngle / sinTheta)) / (rik * sinTheta); grad[3 * k] += dk0; grad[3 * k + 1] += dk1; grad[3 * k + 2] += dk2; Real dl0 = -dE * (-ij_cross_il[0] / sinTheta - il[0] * sinAngle) / ril; Real dl1 = -dE * (-ij_cross_il[1] / sinTheta - il[1] * sinAngle) / ril; Real dl2 = -dE * (-ij_cross_il[2] / sinTheta - il[2] * sinAngle) / ril; grad[3 * l] += dl0; grad[3 * l + 1] += dl1; grad[3 * l + 2] += dl2; // i is the central atom, so add the other forces grad[3 * i] -= dj0 + dk0 + dl0; grad[3 * i + 1] -= dj1 + dk1 + dl1; grad[3 * i + 2] -= dj2 + dk2 + dl2; } } void torsionGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { for (const UFFTorsion& torsion : m_torsions) { Index i = torsion._atom1; Index j = torsion._atom2; Index k = torsion._atom3; Index l = torsion._atom4; Vector3d vi(x[3 * i], x[3 * i + 1], x[3 * i + 2]); Vector3d vj(x[3 * j], x[3 * j + 1], x[3 * j + 2]); Vector3d vk(x[3 * k], x[3 * k + 1], x[3 * k + 2]); Vector3d vl(x[3 * l], x[3 * l + 1], x[3 * l + 2]); // get the bond vectors Vector3d ij = vj - vi; Vector3d jk = vk - vj; Vector3d kl = vl - vk; Real rij = ij.norm(); Real rjk = jk.norm(); Real rkl = kl.norm(); // check if the bond vectors are near zero if (rij < 1e-3 || rjk < 1e-3 || rkl < 1e-3) { continue; // skip this torsion } Real phi = calculateDihedral(vi, vj, vk, vl) * DEG_TO_RAD; Real sinPhi = sin(phi); Real cosPhi = cos(phi); Real cosPhi0 = torsion._cos_phi0; Real kijkl = torsion._ijkl; // dE / dphi Real dE = kijkl * torsion._n * sin(torsion._n * phi) * cosPhi0; // skip this torsion if (std::abs(sinPhi) < 1e-6 || std::isnan(dE)) continue; // Using the BallView / Open Babel formula // http://dx.doi.org/10.22028/D291-25896 (Appendix A) // Thanks to Andreas Moll // for the derivation of the gradients // get the unit vectors Vector3d n1 = ij / rij; Vector3d n2 = jk / rjk; Vector3d n3 = kl / rkl; // get the angles between ijk and jkl Vector3d n1_cross_n2 = n1.cross(n2); Vector3d n2_cross_n3 = n2.cross(n3); // check for near-zero cross products if (n1_cross_n2.norm() < 1e-6 || n2_cross_n3.norm() < 1e-6) { continue; // skip this torsion } Real sinAngleIJK = n1_cross_n2.norm(); Real sinAngleJKL = n2_cross_n3.norm(); Real cosAngleIJK = n1.dot(n2); Real cosAngleJKL = n2.dot(n3); // get the gradient components Vector3d grad_i = -n1_cross_n2 / (rij * sinAngleIJK * sinAngleIJK); Vector3d grad_l = n2_cross_n3 / (rkl * sinAngleJKL * sinAngleJKL); // grad_j and grad_k are a bit more complicated // clamp the cosines to -1 to 1 cosAngleIJK = std::clamp(cosAngleIJK, -1.0, 1.0); cosAngleJKL = std::clamp(cosAngleJKL, -1.0, 1.0); Real fraction1 = (rij / rjk) * (-cosAngleIJK); Real fraction2 = (rkl / rjk) * (-cosAngleJKL); Vector3d grad_j = grad_i * (fraction1 - 1) - grad_l * (fraction2); Vector3d grad_k = -(grad_i + grad_l + grad_j); // add the gradients to the total gradients for each atom grad[3 * i] += dE * grad_i[0]; grad[3 * i + 1] += dE * grad_i[1]; grad[3 * i + 2] += dE * grad_i[2]; grad[3 * j] += dE * grad_j[0]; grad[3 * j + 1] += dE * grad_j[1]; grad[3 * j + 2] += dE * grad_j[2]; grad[3 * k] += dE * grad_k[0]; grad[3 * k + 1] += dE * grad_k[1]; grad[3 * k + 2] += dE * grad_k[2]; grad[3 * l] += dE * grad_l[0]; grad[3 * l + 1] += dE * grad_l[1]; grad[3 * l + 2] += dE * grad_l[2]; } } void vdwGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { for (const UFFVdW& vdw : m_vdws) { Index i = vdw._atom1; Index j = vdw._atom2; Real depth = vdw._depth; Real xij = vdw._x; // dE / dr for a Lennard-Jones potential // E = depth * (x^12 / r^12 - 2 * x^6 / r^6) // dE / dr = -12 * depth * x^12 / r^13 + 12 * depth * x^6 / r^7 // = -12 * depth * x^6 / r^7 * (x^6 / r^6 - 1) // TODO: handle unit cells and periodic boundary conditions Real dx = x[3 * i] - x[3 * j]; Real dy = x[3 * i + 1] - x[3 * j + 1]; Real dz = x[3 * i + 2] - x[3 * j + 2]; Real r2 = dx * dx + dy * dy + dz * dz; Real r6 = r2 * r2 * r2; Real r7 = r6 * sqrt(r2); Real x6 = xij * xij * xij * xij * xij * xij; Real dE = 12 * depth * x6 / r7 * (1 - x6 / r6); grad[3 * i] += dE * dx; grad[3 * i + 1] += dE * dy; grad[3 * i + 2] += dE * dz; grad[3 * j] -= dE * dx; grad[3 * j + 1] -= dE * dy; grad[3 * j + 2] -= dE * dz; } } }; UFF::UFF() : d(nullptr) { // defined for 1-102 for (unsigned int i = 0; i <= 102; ++i) { m_elements.set(i); } } UFF::~UFF() {} void UFF::setMolecule(Core::Molecule* mol) { m_molecule = mol; if (mol == nullptr) { return; // nothing to do } int numAtoms = mol->atomCount(); if (numAtoms < 2) return; // nothing to do for single atoms // start with assigning atom types if (d != nullptr) delete d; d = new UFFPrivate(mol); } Real UFF::value(const Eigen::VectorXd& x) { if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return 0.0; if (m_molecule->atomCount() < 2) return 0.0; // no bonds Real energy = 0.0; // bond component energy += d->bondEnergies(x); // angle component energy += d->angleEnergies(x); // torsion component energy += d->torsionEnergies(x); // TODO: out-of-plane component // energy += d->oopEnergies(x); // van der Waals component energy += d->vdwEnergies(x); // UFF doesn't have electrostatics // Add constraint energies energy += constraintEnergies(x); return energy; } Real UFF::bondEnergy(const Eigen::VectorXd& x) { Real energy = 0.0; if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return energy; // nothing to do if (m_molecule->atomCount() < 2) return energy; // no bonds energy = d->bondEnergies(x); return energy; } Real UFF::angleEnergy(const Eigen::VectorXd& x) { Real energy = 0.0; if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return energy; // nothing to do if (m_molecule->atomCount() < 3) return energy; // no angle energy = d->angleEnergies(x); return energy; } Real UFF::oopEnergy(const Eigen::VectorXd& x) { Real energy = 0.0; if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return energy; // nothing to do if (m_molecule->atomCount() < 4) return energy; // no oop energy = d->oopEnergies(x); return energy; } Real UFF::torsionEnergy(const Eigen::VectorXd& x) { Real energy = 0.0; if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return energy; // nothing to do if (m_molecule->atomCount() < 4) return energy; // no torsion energy = d->torsionEnergies(x); return energy; } Real UFF::vdwEnergy(const Eigen::VectorXd& x) { Real energy = 0.0; if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return energy; // nothing to do if (m_molecule->atomCount() < 2) return energy; // nothing to do energy = d->vdwEnergies(x); return energy; } void UFF::gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { // clear the gradients grad.setZero(); if (!m_molecule || !d || x.size() != 3 * m_molecule->atomCount()) return; // nothing to do if (m_molecule->atomCount() < 2) return; // no bonds // bond gradients d->bondGradient(x, grad); // angle gradients d->angleGradient(x, grad); // torsion gradients d->torsionGradient(x, grad); // TODO: out-of-plane gradients // d->oopGradient(x, grad); // van der Waals gradients d->vdwGradient(x, grad); // UFF doesn't have electrostatics so we're done // handle any constraints cleanGradients(grad); constraintGradients(x, grad); } void UFF::bondGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return; // nothing to do if (m_molecule->atomCount() < 2) return; // no bonds d->bondGradient(x, grad); } void UFF::angleGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return; // nothing to do if (m_molecule->atomCount() < 3) return; // no bonds d->angleGradient(x, grad); } void UFF::oopGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return; // nothing to do if (m_molecule->atomCount() < 4) return; // no bonds d->oopGradient(x, grad); } void UFF::torsionGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return; // nothing to do if (m_molecule->atomCount() < 4) return; // no bonds d->torsionGradient(x, grad); } void UFF::vdwGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (!m_molecule || !d || x.size() != static_cast(3 * m_molecule->atomCount())) return; // nothing to do if (m_molecule->atomCount() < 2) return; // no bonds d->vdwGradient(x, grad); } } // namespace Avogadro::Calc avogadrolibs-1.101.0/avogadro/calc/uff.h000066400000000000000000000042331506155467400200020ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CALC_UFF_H #define AVOGADRO_CALC_UFF_H #include "avogadrocalcexport.h" #include namespace Avogadro { namespace Core { class Molecule; } namespace Calc { class UFFPrivate; class AVOGADROCALC_EXPORT UFF : public EnergyCalculator { public: UFF(); ~UFF() override; UFF* newInstance() const override { return new UFF; } std::string identifier() const override { return "UFF"; } std::string name() const override { return "UFF"; } std::string description() const override { return "Universal Force Field"; } Core::Molecule::ElementMask elements() const override { return (m_elements); } Real value(const Eigen::VectorXd& x) override; void gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) override; // handle both ions and radicals bool acceptsIons() const override { return true; } bool acceptsRadicals() const override { return true; } // TODO: handle unit cells // components Real bondEnergy(const Eigen::VectorXd& x); Real angleEnergy(const Eigen::VectorXd& x); Real oopEnergy(const Eigen::VectorXd& x); Real torsionEnergy(const Eigen::VectorXd& x); Real vdwEnergy(const Eigen::VectorXd& x); void bondGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad); void angleGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad); void oopGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad); void torsionGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad); void vdwGradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad); /** * Called when the current molecule changes. */ void setMolecule(Core::Molecule* mol) override; protected: Core::Molecule* m_molecule; Core::Molecule::ElementMask m_elements; // track the particular calculations for a molecule UFFPrivate* d = nullptr; }; } // namespace Calc } // namespace Avogadro #endif // AVOGADRO_CALC_UFF_H avogadrolibs-1.101.0/avogadro/calc/uffdata.h000066400000000000000000000266671506155467400206530ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ // Formatted as a C / C++ header for Avogadro // from RDKit Params.cpp // https://github.com/rdkit/rdkit/blob/master/Code/ForceField/UFF/Params.cpp #ifndef AVOGADRO_CALC_UFF_DATA #define AVOGADRO_CALC_UFF_DATA #include struct parameter { int element; std::string label; float r1; float theta0; float x1; float D1; float zeta; float Z1; float Vi; float Uj; float Xi; float Hard; float Radius; }; parameter uffparams[] = { { 0, "Du", 0.01, 180, 0.4, 5000, 12, 10, 0, 0, 9.66, 14.92, 0.7 }, { 1, "H_", 0.354, 180, 2.886, 0.044, 12, 0.712, 0, 0, 4.528, 6.9452, 0.371 }, { 1, "H_b", 0.46, 83.5, 2.886, 0.044, 12, 0.712, 0, 0, 4.528, 6.9452, 0.371 }, { 2, "He4+4", 0.849, 90, 2.362, 0.056, 15.24, 0.098, 0, 0, 9.66, 14.92, 1.3 }, { 3, "Li", 1.336, 180, 2.451, 0.025, 12, 1.026, 0, 2, 3.006, 2.386, 1.557 }, { 4, "Be3+2", 1.074, 109.47, 2.745, 0.085, 12, 1.565, 0, 2, 4.877, 4.443, 1.24 }, { 5, "B_3", 0.838, 109.47, 4.083, 0.18, 12.052, 1.755, 0, 2, 5.11, 4.75, 0.822 }, { 5, "B_2", 0.828, 120, 4.083, 0.18, 12.052, 1.755, 0, 2, 5.11, 4.75, 0.822 }, { 6, "C_3", 0.757, 109.47, 3.851, 0.105, 12.73, 1.912, 2.119, 2, 5.343, 5.063, 0.759 }, { 6, "C_R", 0.729, 120, 3.851, 0.105, 12.73, 1.912, 0, 2, 5.343, 5.063, 0.759 }, { 6, "C_2", 0.732, 120, 3.851, 0.105, 12.73, 1.912, 0, 2, 5.343, 5.063, 0.759 }, { 6, "C_1", 0.706, 180, 3.851, 0.105, 12.73, 1.912, 0, 2, 5.343, 5.063, 0.759 }, { 7, "N_3", 0.7, 106.7, 3.66, 0.069, 13.407, 2.544, 0.45, 2, 6.899, 5.88, 0.715 }, { 7, "N_R", 0.699, 120, 3.66, 0.069, 13.407, 2.544, 0, 2, 6.899, 5.88, 0.715 }, { 7, "N_2", 0.685, 111.2, 3.66, 0.069, 13.407, 2.544, 0, 2, 6.899, 5.88, 0.715 }, { 7, "N_1", 0.656, 180, 3.66, 0.069, 13.407, 2.544, 0, 2, 6.899, 5.88, 0.715 }, { 8, "O_3", 0.658, 104.51, 3.5, 0.06, 14.085, 2.3, 0.018, 2, 8.741, 6.682, 0.669 }, { 8, "O_3_z", 0.528, 146, 3.5, 0.06, 14.085, 2.3, 0.018, 2, 8.741, 6.682, 0.669 }, { 8, "O_R", 0.68, 110, 3.5, 0.06, 14.085, 2.3, 0, 2, 8.741, 6.682, 0.669 }, { 8, "O_2", 0.634, 120, 3.5, 0.06, 14.085, 2.3, 0, 2, 8.741, 6.682, 0.669 }, { 8, "O_1", 0.639, 180, 3.5, 0.06, 14.085, 2.3, 0, 2, 8.741, 6.682, 0.669 }, { 9, "F_", 0.668, 180, 3.364, 0.05, 14.762, 1.735, 0, 2, 10.874, 7.474, 0.706 }, { 10, "Ne4+4", 0.92, 90, 3.243, 0.042, 15.44, 0.194, 0, 2, 11.04, 10.55, 1.768 }, { 11, "Na", 1.539, 180, 2.983, 0.03, 12, 1.081, 0, 1.25, 2.843, 2.296, 2.085 }, { 12, "Mg3+2", 1.421, 109.47, 3.021, 0.111, 12, 1.787, 0, 1.25, 3.951, 3.693, 1.5 }, { 13, "Al3", 1.244, 109.47, 4.499, 0.505, 11.278, 1.792, 0, 1.25, 4.06, 3.59, 1.201 }, { 14, "Si3", 1.117, 109.47, 4.295, 0.402, 12.175, 2.323, 1.225, 1.25, 4.168, 3.487, 1.176 }, { 15, "P_3+3", 1.101, 93.8, 4.147, 0.305, 13.072, 2.863, 2.4, 1.25, 5.463, 4, 1.102 }, { 15, "P_3+5", 1.056, 109.47, 4.147, 0.305, 13.072, 2.863, 2.4, 1.25, 5.463, 4, 1.102 }, { 15, "P_3+q", 1.056, 109.47, 4.147, 0.305, 13.072, 2.863, 2.4, 1.25, 5.463, 4, 1.102 }, { 16, "S_3+2", 1.064, 92.1, 4.035, 0.274, 13.969, 2.703, 0.484, 1.25, 6.928, 4.486, 1.047 }, { 16, "S_3+4", 1.049, 103.2, 4.035, 0.274, 13.969, 2.703, 0.484, 1.25, 6.928, 4.486, 1.047 }, { 16, "S_3+6", 1.027, 109.47, 4.035, 0.274, 13.969, 2.703, 0.484, 1.25, 6.928, 4.486, 1.047 }, { 16, "S_R", 1.077, 92.2, 4.035, 0.274, 13.969, 2.703, 0, 1.25, 6.928, 4.486, 1.047 }, { 16, "S_2", 0.854, 120, 4.035, 0.274, 13.969, 2.703, 0, 1.25, 6.928, 4.486, 1.047 }, { 17, "Cl", 1.044, 180, 3.947, 0.227, 14.866, 2.348, 0, 1.25, 8.564, 4.946, 0.994 }, { 18, "Ar4+4", 1.032, 90, 3.868, 0.185, 15.763, 0.3, 0, 1.25, 9.465, 6.355, 2.108 }, { 19, "K_", 1.953, 180, 3.812, 0.035, 12, 1.165, 0, 0.7, 2.421, 1.92, 2.586 }, { 20, "Ca6+2", 1.761, 90, 3.399, 0.238, 12, 2.141, 0, 0.7, 3.231, 2.88, 2 }, { 21, "Sc3+3", 1.513, 109.47, 3.295, 0.019, 12, 2.592, 0, 0.7, 3.395, 3.08, 1.75 }, { 22, "Ti3+4", 1.412, 109.47, 3.175, 0.017, 12, 2.659, 0, 0.7, 3.47, 3.38, 1.607 }, { 22, "Ti6+4", 1.412, 90, 3.175, 0.017, 12, 2.659, 0, 0.7, 3.47, 3.38, 1.607 }, { 23, "V_3+5", 1.402, 109.47, 3.144, 0.016, 12, 2.679, 0, 0.7, 3.65, 3.41, 1.47 }, { 24, "Cr6+3", 1.345, 90, 3.023, 0.015, 12, 2.463, 0, 0.7, 3.415, 3.865, 1.402 }, { 25, "Mn6+2", 1.382, 90, 2.961, 0.013, 12, 2.43, 0, 0.7, 3.325, 4.105, 1.533 }, { 26, "Fe3+2", 1.27, 109.47, 2.912, 0.013, 12, 2.43, 0, 0.7, 3.76, 4.14, 1.393 }, { 26, "Fe6+2", 1.335, 90, 2.912, 0.013, 12, 2.43, 0, 0.7, 3.76, 4.14, 1.393 }, { 27, "Co6+3", 1.241, 90, 2.872, 0.014, 12, 2.43, 0, 0.7, 4.105, 4.175, 1.406 }, { 28, "Ni4+2", 1.164, 90, 2.834, 0.015, 12, 2.43, 0, 0.7, 4.465, 4.205, 1.398 }, { 29, "Cu3+1", 1.302, 109.47, 3.495, 0.005, 12, 1.756, 0, 0.7, 4.2, 4.22, 1.434 }, { 30, "Zn3+2", 1.193, 109.47, 2.763, 0.124, 12, 1.308, 0, 0.7, 5.106, 4.285, 1.4 }, { 31, "Ga3+3", 1.26, 109.47, 4.383, 0.415, 11, 1.821, 0, 0.7, 3.641, 3.16, 1.211 }, { 32, "Ge3", 1.197, 109.47, 4.28, 0.379, 12, 2.789, 0.701, 0.7, 4.051, 3.438, 1.189 }, { 33, "As3+3", 1.211, 92.1, 4.23, 0.309, 13, 2.864, 1.5, 0.7, 5.188, 3.809, 1.204 }, { 34, "Se3+2", 1.19, 90.6, 4.205, 0.291, 14, 2.764, 0.335, 0.7, 6.428, 4.131, 1.224 }, { 35, "Br", 1.192, 180, 4.189, 0.251, 15, 2.519, 0, 0.7, 7.79, 4.425, 1.141 }, { 36, "Kr4+4", 1.147, 90, 4.141, 0.22, 16, 0.452, 0, 0.7, 8.505, 5.715, 2.27 }, { 37, "Rb", 2.26, 180, 4.114, 0.04, 12, 1.592, 0, 0.2, 2.331, 1.846, 2.77 }, { 38, "Sr6+2", 2.052, 90, 3.641, 0.235, 12, 2.449, 0, 0.2, 3.024, 2.44, 2.415 }, { 39, "Y_3+3", 1.698, 109.47, 3.345, 0.072, 12, 3.257, 0, 0.2, 3.83, 2.81, 1.998 }, { 40, "Zr3+4", 1.564, 109.47, 3.124, 0.069, 12, 3.667, 0, 0.2, 3.4, 3.55, 1.758 }, { 41, "Nb3+5", 1.473, 109.47, 3.165, 0.059, 12, 3.618, 0, 0.2, 3.55, 3.38, 1.603 }, { 42, "Mo6+6", 1.467, 90, 3.052, 0.056, 12, 3.4, 0, 0.2, 3.465, 3.755, 1.53 }, { 42, "Mo3+6", 1.484, 109.47, 3.052, 0.056, 12, 3.4, 0, 0.2, 3.465, 3.755, 1.53 }, { 43, "Tc6+5", 1.322, 90, 2.998, 0.048, 12, 3.4, 0, 0.2, 3.29, 3.99, 1.5 }, { 44, "Ru6+2", 1.478, 90, 2.963, 0.056, 12, 3.4, 0, 0.2, 3.575, 4.015, 1.5 }, { 45, "Rh6+3", 1.332, 90, 2.929, 0.053, 12, 3.5, 0, 0.2, 3.975, 4.005, 1.509 }, { 46, "Pd4+2", 1.338, 90, 2.899, 0.048, 12, 3.21, 0, 0.2, 4.32, 4, 1.544 }, { 47, "Ag1+1", 1.386, 180, 3.148, 0.036, 12, 1.956, 0, 0.2, 4.436, 3.134, 1.622 }, { 48, "Cd3+2", 1.403, 109.47, 2.848, 0.228, 12, 1.65, 0, 0.2, 5.034, 3.957, 1.6 }, { 49, "In3+3", 1.459, 109.47, 4.463, 0.599, 11, 2.07, 0, 0.2, 3.506, 2.896, 1.404 }, { 50, "Sn3", 1.398, 109.47, 4.392, 0.567, 12, 2.961, 0.199, 0.2, 3.987, 3.124, 1.354 }, { 50, "Sb3+3", 1.407, 91.6, 4.42, 0.449, 13, 2.704, 1.1, 0.2, 4.899, 3.342, 1.404 }, { 51, "Te3+2", 1.386, 90.25, 4.47, 0.398, 14, 2.882, 0.3, 0.2, 5.816, 3.526, 1.38 }, { 52, "I_", 1.382, 180, 4.5, 0.339, 15, 2.65, 0, 0.2, 6.822, 3.762, 1.333 }, { 53, "Xe4+4", 1.267, 90, 4.404, 0.332, 12, 0.556, 0, 0.2, 7.595, 4.975, 2.459 }, { 54, "Cs", 2.57, 180, 4.517, 0.045, 12, 1.573, 0, 0.1, 2.183, 1.711, 2.984 }, { 55, "Ba6+2", 2.277, 90, 3.703, 0.364, 12, 2.727, 0, 0.1, 2.814, 2.396, 2.442 }, { 56, "La3+3", 1.943, 109.47, 3.522, 0.017, 12, 3.3, 0, 0.1, 2.8355, 2.7415, 2.071 }, { 57, "Ce6+3", 1.841, 90, 3.556, 0.013, 12, 3.3, 0, 0.1, 2.774, 2.692, 1.925 }, { 58, "Pr6+3", 1.823, 90, 3.606, 0.01, 12, 3.3, 0, 0.1, 2.858, 2.564, 2.007 }, { 59, "Nd6+3", 1.816, 90, 3.575, 0.01, 12, 3.3, 0, 0.1, 2.8685, 2.6205, 2.007 }, { 60, "Pm6+3", 1.801, 90, 3.547, 0.009, 12, 3.3, 0, 0.1, 2.881, 2.673, 2 }, { 61, "Sm6+3", 1.78, 90, 3.52, 0.008, 12, 3.3, 0, 0.1, 2.9115, 2.7195, 1.978 }, { 62, "Eu6+3", 1.771, 90, 3.493, 0.008, 12, 3.3, 0, 0.1, 2.8785, 2.7875, 2.227 }, { 63, "Gd6+3", 1.735, 90, 3.368, 0.009, 12, 3.3, 0, 0.1, 3.1665, 2.9745, 1.968 }, { 64, "Tb6+3", 1.732, 90, 3.451, 0.007, 12, 3.3, 0, 0.1, 3.018, 2.834, 1.954 }, { 65, "Dy6+3", 1.71, 90, 3.428, 0.007, 12, 3.3, 0, 0.1, 3.0555, 2.8715, 1.934 }, { 66, "Ho6+3", 1.696, 90, 3.409, 0.007, 12, 3.416, 0, 0.1, 3.127, 2.891, 1.925 }, { 67, "Er6+3", 1.673, 90, 3.391, 0.007, 12, 3.3, 0, 0.1, 3.1865, 2.9145, 1.915 }, { 68, "Tm6+3", 1.66, 90, 3.374, 0.006, 12, 3.3, 0, 0.1, 3.2514, 2.9329, 2 }, { 69, "Yb6+3", 1.637, 90, 3.355, 0.228, 12, 2.618, 0, 0.1, 3.2889, 2.965, 2.158 }, { 70, "Lu6+3", 1.671, 90, 3.64, 0.041, 12, 3.271, 0, 0.1, 2.9629, 2.4629, 1.896 }, { 71, "Hf3+4", 1.611, 109.47, 3.141, 0.072, 12, 3.921, 0, 0.1, 3.7, 3.4, 1.759 }, { 72, "Ta3+5", 1.511, 109.47, 3.17, 0.081, 12, 4.075, 0, 0.1, 5.1, 2.85, 1.605 }, { 73, "W_6+6", 1.392, 90, 3.069, 0.067, 12, 3.7, 0, 0.1, 4.63, 3.31, 1.538 }, { 73, "W_3+4", 1.526, 109.47, 3.069, 0.067, 12, 3.7, 0, 0.1, 4.63, 3.31, 1.538 }, { 73, "W_3+6", 1.38, 109.47, 3.069, 0.067, 12, 3.7, 0, 0.1, 4.63, 3.31, 1.538 }, { 74, "Re6+5", 1.372, 90, 2.954, 0.066, 12, 3.7, 0, 0.1, 3.96, 3.92, 1.6 }, { 74, "Re3+7", 1.314, 109.47, 2.954, 0.066, 12, 3.7, 0, 0.1, 3.96, 3.92, 1.6 }, { 75, "Os6+6", 1.372, 90, 3.12, 0.037, 12, 3.7, 0, 0.1, 5.14, 3.63, 1.7 }, { 76, "Ir6+3", 1.371, 90, 2.84, 0.073, 12, 3.731, 0, 0.1, 5, 4, 1.866 }, { 77, "Pt4+2", 1.364, 90, 2.754, 0.08, 12, 3.382, 0, 0.1, 4.79, 4.43, 1.557 }, { 78, "Au4+3", 1.262, 90, 3.293, 0.039, 12, 2.625, 0, 0.1, 4.894, 2.586, 1.618 }, { 79, "Hg1+2", 1.34, 180, 2.705, 0.385, 12, 1.75, 0, 0.1, 6.27, 4.16, 1.6 }, { 80, "Tl3+3", 1.518, 120, 4.347, 0.68, 11, 2.068, 0, 0.1, 3.2, 2.9, 1.53 }, { 81, "Pb3", 1.459, 109.47, 4.297, 0.663, 12, 2.846, 0.1, 0.1, 3.9, 3.53, 1.444 }, { 82, "Bi3+3", 1.512, 90, 4.37, 0.518, 13, 2.47, 1, 0.1, 4.69, 3.74, 1.514 }, { 83, "Po3+2", 1.5, 90, 4.709, 0.325, 14, 2.33, 0.3, 0.1, 4.21, 4.21, 1.48 }, { 84, "At", 1.545, 180, 4.75, 0.284, 15, 2.24, 0, 0.1, 4.75, 4.75, 1.47 }, { 85, "Rn4+4", 1.42, 90, 4.765, 0.248, 16, 0.583, 0, 0.1, 5.37, 5.37, 2.2 }, { 86, "Fr", 2.88, 180, 4.9, 0.05, 12, 1.847, 0, 0, 2, 2, 2.3 }, { 87, "Ra6+2", 2.512, 90, 3.677, 0.404, 12, 2.92, 0, 0, 2.843, 2.434, 2.2 }, { 88, "Ac6+3", 1.983, 90, 3.478, 0.033, 12, 3.9, 0, 0, 2.835, 2.835, 2.108 }, { 89, "Th6+4", 1.721, 90, 3.396, 0.026, 12, 4.202, 0, 0, 3.175, 2.905, 2.018 }, { 90, "Pa6+4", 1.711, 90, 3.424, 0.022, 12, 3.9, 0, 0, 2.985, 2.905, 1.8 }, { 91, "U_6+4", 1.684, 90, 3.395, 0.022, 12, 3.9, 0, 0, 3.341, 2.853, 1.713 }, { 92, "Np6+4", 1.666, 90, 3.424, 0.019, 12, 3.9, 0, 0, 3.549, 2.717, 1.8 }, { 93, "Pu6+4", 1.657, 90, 3.424, 0.016, 12, 3.9, 0, 0, 3.243, 2.819, 1.84 }, { 94, "Am6+4", 1.66, 90, 3.381, 0.014, 12, 3.9, 0, 0, 2.9895, 3.0035, 1.942 }, { 95, "Cm6+3", 1.801, 90, 3.326, 0.013, 12, 3.9, 0, 0, 2.8315, 3.1895, 1.9 }, { 96, "Bk6+3", 1.761, 90, 3.339, 0.013, 12, 3.9, 0, 0, 3.1935, 3.0355, 1.9 }, { 97, "Cf6+3", 1.75, 90, 3.313, 0.013, 12, 3.9, 0, 0, 3.197, 3.101, 1.9 }, { 98, "Es6+3", 1.724, 90, 3.299, 0.012, 12, 3.9, 0, 0, 3.333, 3.089, 1.9 }, { 99, "Fm6+3", 1.712, 90, 3.286, 0.012, 12, 3.9, 0, 0, 3.4, 3.1, 1.9 }, { 100, "Md6+3", 1.689, 90, 3.274, 0.011, 12, 3.9, 0, 0, 3.47, 3.11, 1.9 }, { 101, "No6+3", 1.679, 90, 3.248, 0.011, 12, 3.9, 0, 0, 3.475, 3.175, 1.9 }, { 102, "Lw6+3", 1.698, 90, 3.236, 0.011, 12, 3.9, 0, 0, 3.5, 3.2, 1.9 } }; #endif // AVOGADRO_CALC_UFF_DATA_H avogadrolibs-1.101.0/avogadro/command/000077500000000000000000000000001506155467400175635ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/command/CMakeLists.txt000066400000000000000000000004021506155467400223170ustar00rootroot00000000000000add_executable(avocjsontocml cjsontocml.cpp) target_link_libraries(avocjsontocml Avogadro::IO) add_executable(avobabel avobabel.cpp) target_link_libraries(avobabel Avogadro::IO) add_executable(qube qube.cpp) target_link_libraries(qube Avogadro::QuantumIO) avogadrolibs-1.101.0/avogadro/command/avobabel.cpp000066400000000000000000000053101506155467400220410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include #include #include #include #include #include using Avogadro::Core::Molecule; using Avogadro::Io::FileFormatManager; using std::cin; using std::cout; using std::endl; using std::ostringstream; using std::string; void printHelp(); int main(int argc, char* argv[]) { // Process the command line arguments, see what has been requested. string inFormat; string outFormat; string inFile; string outFile; for (int i = 1; i < argc; ++i) { string current(argv[i]); if (current == "--help" || current == "-h") { printHelp(); return 0; } else if (current == "--version" || current == "-v") { cout << "Version: " << Avogadro::version() << endl; return 0; } else if (current == "-i" && i + 1 < argc) { inFormat = argv[++i]; cout << "input format " << inFormat << endl; } else if (current == "-o" && i + 1 < argc) { outFormat = argv[++i]; cout << "output format " << outFormat << endl; } else if (inFile.empty()) { inFile = argv[i]; } else if (outFile.empty()) { outFile = argv[i]; } } // Now read/write the molecule, if possible. Otherwise output errors. FileFormatManager& mgr = FileFormatManager::instance(); Molecule mol; if (!inFile.empty()) { if (!mgr.readFile(mol, inFile, inFormat)) { cout << "Failed to read " << inFile << " (" << inFormat << ")" << endl; return 1; } } else if (!inFormat.empty()) { ostringstream inFileString; string line; while (getline(cin, line)) inFileString << line; if (!inFileString.str().empty()) { if (!mgr.readString(mol, inFileString.str(), inFormat)) { cout << "Failed to read input stream: " << inFileString.str() << endl; return 1; } } } else { cout << "Error, no input file or stream supplied with format." << endl; } if (!outFile.empty()) { if (!mgr.writeFile(mol, outFile, outFormat)) { cout << "Failed to write " << outFile << " (" << outFormat << ")" << endl; return 1; } } else { if (outFormat.empty()) outFormat = "cjson"; string out; mgr.writeString(mol, out, outFormat); cout << out << endl; } return 0; } void printHelp() { cout << "Usage: avobabel [-i ] [-o ] " "\n" << endl; } avogadrolibs-1.101.0/avogadro/command/cjsontocml.cpp000066400000000000000000000015531506155467400224460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "avogadro/core/molecule.h" #include "avogadro/io/fileformatmanager.h" #include #include #include using Avogadro::Core::Molecule; using Avogadro::Io::FileFormatManager; using std::cin; using std::cout; using std::ostringstream; using std::string; int main() { FileFormatManager& mgr = FileFormatManager::instance(); Molecule mol; ostringstream cjson; string line; while (getline(cin, line)) { cjson << line; } mgr.readString(mol, cjson.str(), "cjson"); string cml; mgr.writeString(mol, cml, "cml"); cout << cml; } avogadrolibs-1.101.0/avogadro/command/qube.cpp000066400000000000000000000120751506155467400212300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Core::Cube; using Avogadro::Core::GaussianSetTools; using Avogadro::Core::Molecule; using Avogadro::Io::FileFormatManager; using std::cin; using std::cout; using std::endl; using std::ostringstream; using std::string; using Eigen::Vector3d; using Eigen::Vector3i; static const double BOHR_TO_ANGSTROM = 0.529177249; static const double ANGSTROM_TO_BOHR = 1.0 / BOHR_TO_ANGSTROM; void printHelp(); int main(int argc, char* argv[]) { // Register our quantum file format. FileFormatManager& mgr = FileFormatManager::instance(); mgr.registerFormat(new Avogadro::QuantumIO::GAMESSUSOutput); mgr.registerFormat(new Avogadro::QuantumIO::GaussianFchk); mgr.registerFormat(new Avogadro::QuantumIO::MoldenFile); mgr.registerFormat(new Avogadro::QuantumIO::MopacAux); // Process the command line arguments, see what has been requested. string inFormat; int orbitalNumber = 0; string inFile; bool density = false; for (int i = 1; i < argc; ++i) { string current(argv[i]); if (current == "--help" || current == "-h") { printHelp(); return 0; } else if (current == "--version" || current == "-v") { cout << "Version: " << Avogadro::version() << endl; return 0; } else if (current == "-i" && i + 1 < argc) { inFormat = argv[++i]; cout << "input format " << inFormat << endl; } else if (current == "-orb" && i + 1 < argc) { orbitalNumber = atoi(argv[++i]); // cout << "plot orbital " << orbitalNumber << endl; } else if (current == "-dens" && i < argc) { density = true; } else if (inFile.empty()) { inFile = argv[i]; } } // Now read/write the molecule, if possible. Otherwise output errors. Molecule mol; if (!inFile.empty()) { if (!mgr.readFile(mol, inFile, inFormat)) { cout << "Failed to read " << inFile << " (" << inFormat << ")" << endl; return 1; } } else if (!inFormat.empty()) { ostringstream inFileString; string line; while (getline(cin, line)) inFileString << line; if (!inFileString.str().empty()) { if (!mgr.readString(mol, inFileString.str(), inFormat)) { cout << "Failed to read input stream: " << inFileString.str() << endl; return 1; } } } else { cout << "Error, no input file or stream supplied with format." << endl; } if ((orbitalNumber > 0) && density) { cout << "Error, choose either density or a single orbital, not both." << endl; return 1; } // cube header cout << "Avogadro generated cube" << endl; if (orbitalNumber > 0) cout << "Orbital " << orbitalNumber << endl; else cout << "Electron Density" << endl; // set box dimensions in Bohr Vector3d min = Vector3d(-10.0, -10.0, -10.0); Vector3d max = Vector3d(10.0, 10.0, 10.0); Vector3i points = Vector3i(61, 61, 61); Cube* m_qube = new Cube; m_qube->setLimits(min * BOHR_TO_ANGSTROM, max * BOHR_TO_ANGSTROM, points); min = m_qube->position(0) * ANGSTROM_TO_BOHR; Vector3d spacing = m_qube->spacing() * ANGSTROM_TO_BOHR; int nat = mol.atomCount(); printf("%4d %11.6f %11.6f %11.6f\n", nat, min.x(), min.y(), min.z()); printf("%4d %11.6f %11.6f %11.6f\n", points.x(), spacing.x(), 0.0, 0.0); printf("%4d %11.6f %11.6f %11.6f\n", points.y(), 0.0, spacing.y(), .0); printf("%4d %11.6f %11.6f %11.6f\n", points.z(), 0.0, 0.0, spacing.z()); // atoms for (int iatom = 0; iatom < nat; iatom++) { printf("%4d %11.6f %11.6f %11.6f %11.6f\n", mol.atomicNumber(iatom), 0.0, mol.atomPosition3d(iatom).x() * ANGSTROM_TO_BOHR, mol.atomPosition3d(iatom).y() * ANGSTROM_TO_BOHR, mol.atomPosition3d(iatom).z() * ANGSTROM_TO_BOHR); } if (orbitalNumber > 0) cout << "1 " << orbitalNumber << endl; auto* m_tools = new GaussianSetTools(&mol); // print the qube values int linecount = 0; for (unsigned int i = 0; i < m_qube->data()->size(); i++) { if (i % points.z() == 0 && i > 0) { linecount = 0; printf("\n"); } double value = m_tools->calculateMolecularOrbital(m_qube->position(i), orbitalNumber); printf("%13.5E", value); // line wrapping linecount++; if (linecount % 6 == 0 && i > 0) printf("\n"); else printf(" "); } printf("\n"); return 0; } void printHelp() { cout << "Usage: qube [-i ] [-dens] [-orb ] [-v / --version] \n" << endl; } avogadrolibs-1.101.0/avogadro/core/000077500000000000000000000000001506155467400170755ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/core/CMakeLists.txt000066400000000000000000000052001506155467400216320ustar00rootroot00000000000000find_package(Eigen3 REQUIRED) # configure the version header configure_file("${PROJECT_SOURCE_DIR}/cmake/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h") # Separate out header only components into an interface library. add_library(Headers INTERFACE) add_library(Avogadro::Headers ALIAS Headers) target_include_directories(Headers INTERFACE "$") target_sources(Headers PUBLIC FILE_SET HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES angletools.h angleiterator.h array.h avogadrocore.h color3f.h constraint.h contrastcolor.h coordinateset.h matrix.h types.h utilities.h vector.h ) target_link_libraries(Headers INTERFACE Eigen3::Eigen) install(TARGETS Headers EXPORT "AvogadroLibsTargets" FILE_SET HEADERS DESTINATION "${INSTALL_INCLUDE_DIR}") # Now add the main core library for Avogadro. add_library(Core) avogadro_headers(Core atom.h atomtyper.h atomtyper-inline.h atomutilities.h basisset.h bond.h coordinateblockgenerator.h crystaltools.h cube.h dihedraliterator.h elements.h gaussianset.h gaussiansettools.h graph.h layer.h layermanager.h mesh.h molecule.h mutex.h nameatomtyper.h neighborperceiver.h residue.h ringperceiver.h secondarystructure.h slaterset.h slatersettools.h spacegroups.h symbolatomtyper.h unitcell.h variant.h variant-inline.h variantmap.h "${CMAKE_CURRENT_BINARY_DIR}/version.h" ) target_sources(Core PRIVATE angleiterator.cpp atomutilities.cpp coordinateblockgenerator.cpp crystaltools.cpp cube.cpp elements.cpp dihedraliterator.cpp gaussianset.cpp gaussiansettools.cpp graph.cpp layer.cpp layermanager.cpp mesh.cpp mdlvalence_p.h molecule.cpp mutex.cpp nameatomtyper.cpp neighborperceiver.cpp residue.cpp ringperceiver.cpp secondarystructure.cpp slaterset.cpp slatersettools.cpp spacegroups.cpp symbolatomtyper.cpp unitcell.cpp variantmap.cpp version.cpp ) # We currently build core without shared_mutex for Python wheels. if(NOT PYTHON_WHEEL_BUILD) avogadro_headers(Core sharedmutex.h) target_sources(Core PRIVATE sharedmutex.cpp) endif() if(USE_SPGLIB) find_package(Spglib REQUIRED) avogadro_headers(Core avospglib.h) target_sources(Core PRIVATE avospglib.cpp) target_link_libraries(Core PRIVATE Spglib::symspg) endif() # The std::shared_mutex class needs pthreads on Linux. if(UNIX AND NOT APPLE AND NOT PYTHON_WHEEL_BUILD) find_package(Threads) target_link_libraries(Core PRIVATE ${CMAKE_THREAD_LIBS_INIT}) endif() avogadro_add_library(Core) target_link_libraries(Core PUBLIC Avogadro::Headers) avogadrolibs-1.101.0/avogadro/core/angleiterator.cpp000066400000000000000000000051121506155467400224400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "angleiterator.h" #include #include namespace Avogadro::Core { AngleIterator::AngleIterator(const Molecule* mol) : m_a(MaxIndex), m_b(0), m_c(MaxIndex), m_mol(mol) { } Angle AngleIterator::begin() { return ++(*this); } Angle AngleIterator::operator++() { // never any angles if (m_mol == nullptr || m_mol->atomCount() < 3 || m_mol->bondCount() < 2) return std::make_tuple(MaxIndex, MaxIndex, MaxIndex); Graph graph = m_mol->graph(); Index count = m_mol->atomCount(); // true if we have a valid current state // (i.e. false at the start since a == b == c) bool valid = (m_a != MaxIndex && m_c != MaxIndex); Index aIndex = MaxIndex; Index cIndex = MaxIndex; // if we don't have a valid state, try to find an initial angle if (!valid) { for (Index i = 0; i < count; ++i) { const auto& neighbors = graph.neighbors(i); if (neighbors.size() < 2) continue; m_b = i; m_a = 0; m_c = 1; aIndex = neighbors[m_a]; cIndex = neighbors[m_c]; valid = true; break; } } else { // we have a valid state, try to find the next angle const auto& neighbors = graph.neighbors(m_b); // first check if we can increment m_c if (m_c + 1 < neighbors.size()) { ++m_c; aIndex = neighbors[m_a]; cIndex = neighbors[m_c]; } else { // we can't increment m_c, try to increment m_a if (m_a < neighbors.size() - 2) { ++m_a; m_c = m_a + 1; aIndex = neighbors[m_a]; cIndex = neighbors[m_c]; } else { // we can't increment m_a, try to increment m_b // and reset m_a and m_c valid = false; for (Index i = m_b + 1; i < count; ++i) { const auto& newNeighbors = graph.neighbors(i); if (newNeighbors.size() < 2) continue; m_b = i; m_a = 0; m_c = 1; aIndex = newNeighbors[m_a]; cIndex = newNeighbors[m_c]; valid = true; break; } } } } if (valid) { m_current = std::make_tuple(aIndex, m_b, cIndex); } else // no more valid angles m_current = std::make_tuple(MaxIndex, MaxIndex, MaxIndex); return m_current; } // end ++ operator } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/angleiterator.h000066400000000000000000000024021506155467400221040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_ANGLEITERATOR_H #define AVOGADRO_CORE_ANGLEITERATOR_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include namespace Avogadro::Core { class Molecule; using Angle = std::tuple; class AVOGADROCORE_EXPORT AngleIterator { public: /** * Constructor. */ AngleIterator(const Molecule* mol); ~AngleIterator() {} Angle* operator*() { return &m_current; } Angle begin(); Angle end() const { return std::make_tuple(Avogadro::MaxIndex, Avogadro::MaxIndex, Avogadro::MaxIndex); } Angle operator++(); bool operator!=(const AngleIterator& other) { return (m_a != other.m_a || m_b != other.m_b || m_c != other.m_c); } private: // m_b is the current vertex // m_a and m_c are instead index into the neighbors of m_b Index m_a, m_b, m_c; Angle m_current; const Molecule* m_mol; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_ANGLEITERATOR_H avogadrolibs-1.101.0/avogadro/core/angletools.h000066400000000000000000000073531506155467400214250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_ANGLETOOLS_H #define AVOGADRO_CORE_ANGLETOOLS_H #include #include namespace Avogadro { /** * Calculate the bond angle between two bond vectors. * @param b0 The first bond vector (a-b) * @param b1 The second bond vector (b-c) * @return The bond angle in degrees. */ inline Real bondAngle(const Vector3& b0, const Vector3& b1) { // standard formula, e.g. // https://scicomp.stackexchange.com/q/27689/14517 // Since we're using bonds, v. small angles are okay // only problem is if bond lengths are v. v. small // but that's unlikely in practice const Real dot = -1.0 * b0.dot(b1); const Real norms = b0.norm() * b1.norm(); return std::acos(dot / norms) * RAD_TO_DEG_D; } /** * Calculate the dihedral angle between three bond vectors. * @param b0 The first bond vector (a-b) * @param b1 The second bond vector (b-c) * @param b2 The third bond vector (c-d) * @return The dihedral angle in degrees. */ inline Real dihedralAngle(const Vector3& b0, const Vector3& b1, const Vector3& b2) { // See Praxeolitic https://stackoverflow.com/a/34245697/131896 const Vector3 n0 = -1.0 * b0; const Vector3 b1n = b1.normalized(); // v = projection of b0 onto plane perpendicular to b1 // = n0 minus component that aligns with b1 // w = projection of b2 onto plane perpendicular to b1 // = b2 minus component that aligns with b1 const Vector3 v = n0 - n0.dot(b1n) * b1n; const Vector3 w = b2 - b2.dot(b1n) * b1n; // angle between v and w in a plane is the torsion angle // v and w may not be normalized but that's fine since tan is y/x const Real x(v.dot(w)); const Real y(b1n.cross(v).dot(w)); return std::atan2(y, x) * RAD_TO_DEG_D; } /** * Calculate the angle between three points in space. * @param v1 The first point. * @param v2 The second point (vertex). * @param v3 The third point. * @return The angle in degrees. */ inline Real calculateAngle(const Vector3& v1, const Vector3& v2, const Vector3& v3) { Vector3 v12 = v1 - v2; Vector3 v23 = v2 - v3; return bondAngle(v12, v23); } /** * Calculate the dihedral angle between four points in space. * @param v1 The first point. * @param v2 The second point. * @param v3 The third point. * @param v4 The fourth point. * @return The dihedral angle in degrees. */ inline Real calculateDihedral(const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& v4) { Vector3 v12 = v2 - v1; Vector3 v23 = v3 - v2; Vector3 v34 = v4 - v3; return dihedralAngle(v12, v23, v34); } /** * Calculate the out-of-plane angle for a point in space. * @param point The point to calculate the angle for. * @param b The first point in the plane. * @param c The second point in the plane. * @param d The third point in the plane. * @return The out-of-plane angle in degrees. */ inline Real outOfPlaneAngle(const Vector3& point, const Vector3& b, const Vector3& c, const Vector3& d) { Real angle = 0.0; Vector3 bc = b - c; Vector3 cd = c - d; Vector3 normal = bc.cross(cd); Vector3 ac = point - c; // we can get the angle by taking the dot product of the normal // with the vector from the point to the center of the triangle Real theta = std::acos(ac.dot(normal) / (ac.norm() * normal.norm())); angle = 90.0 - (theta * RAD_TO_DEG_D); return angle; } } // namespace Avogadro #endif // AVOGADRO_CORE_ANGLETOOLS_H avogadrolibs-1.101.0/avogadro/core/array.h000066400000000000000000000231201506155467400203620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_ARRAY_H #define AVOGADRO_CORE_ARRAY_H #include "avogadrocore.h" #include #include namespace Avogadro::Core { using std::size_t; namespace internal { template class ArrayRefContainer { public: using ValueType = T; using Parent = std::vector; // The parent container for iterators etc // STL compatibility, forward typedefs from std::vector: using value_type = typename Parent::value_type; using allocator_type = typename Parent::allocator_type; using reference = typename Parent::reference; using const_reference = typename Parent::const_reference; using pointer = typename Parent::pointer; using const_pointer = typename Parent::const_pointer; using iterator = typename Parent::iterator; using const_iterator = typename Parent::const_iterator; using reverse_iterator = typename Parent::reverse_iterator; using const_reverse_iterator = typename Parent::const_reverse_iterator; using difference_type = typename Parent::difference_type; using size_type = typename Parent::size_type; ArrayRefContainer() : m_ref(1), data() {} explicit ArrayRefContainer(const size_t n, const ValueType& value = ValueType()) : m_ref(1), data(n, value) { } ArrayRefContainer(const ArrayRefContainer& other) : m_ref(1), data(other.data) { } template ArrayRefContainer(InputIterator first, InputIterator last) : m_ref(1), data(first, last) { } // Increment the reference count. void reref() { ++m_ref; } // Decrement the reference count, return true unless the reference count has // dropped to zero. When it returns false, this object should be deleted. bool deref() { if (m_ref) --m_ref; return m_ref > 0; } unsigned int ref() const { return m_ref; } // Reference count unsigned int m_ref; // Container for our data std::vector data; }; } // namespace internal /** * @class Array array.h * @brief Base class for array containers. * * This templated class gives us a container with copy-on-write semantics, * allowing for functions to effectively share data without exposing access or * copying large amounts of data until the container is changed. * * All const functions can be called without copying any data, but a call to a * non-const function will trigger a detach call. This is a no-op when the * reference count is 1, and will perform a deep copy when the reference count * is greater than 1. */ template class Array { public: using Container = internal::ArrayRefContainer; using ValueType = T; /** Typedefs for STL compatibility @{ */ using value_type = typename Container::value_type; using allocator_type = typename Container::allocator_type; using reference = typename Container::reference; using const_reference = typename Container::const_reference; using pointer = typename Container::pointer; using const_pointer = typename Container::const_pointer; using iterator = typename Container::iterator; using const_iterator = typename Container::const_iterator; using reverse_iterator = typename Container::reverse_iterator; using const_reverse_iterator = typename Container::const_reverse_iterator; using difference_type = typename Container::difference_type; using size_type = typename Container::size_type; /** @} */ /** Constructors for new containers. */ Array() : d(new Container()) {} explicit Array(const size_t n, const ValueType& value = ValueType()) : d(new Container(n, value)) { } template Array(InputIterator first, InputIterator last) : d(new Container(first, last)) { } /** Copy constructor, note the copy made of the internal data of other. */ Array(const Array& other) { other.d->reref(); d = other.d; } ~Array(); /** * Explicitly detach from any other implicitly shared containers. This is not * normally necessary, but can be useful when you want to ensure you have a * copy of all data. */ void detachWithCopy(); /** * Explicitly detach from any other implicitly shared containers. This * version does not copy the data. */ void detach(); /** Retrieve a pointer to the underlying data. */ T* data() { detachWithCopy(); return d->data.data(); } const T* data() const { return d->data.data(); } const T* constData() const { return d->data.data(); } size_t size() const { return d->data.size(); } size_t max_size() const { return d->data.max_size(); } bool empty() const { return d->data.empty(); } size_t capacity() const { return d->data.capacity(); } void reserve(const size_t& sz) { detachWithCopy(); d->data.reserve(sz); } void resize(const size_t& sz, const ValueType& t = ValueType()) { detachWithCopy(); d->data.resize(sz, t); } void clear() { detach(); d->data.clear(); } const_iterator begin() const { return d->data.begin(); } const_iterator end() const { return d->data.end(); } iterator begin() { detachWithCopy(); return d->data.begin(); } iterator end() { detachWithCopy(); return d->data.end(); } const_reverse_iterator rbegin() const { return d->data.rbegin(); } const_reverse_iterator rend() const { return d->data.rend(); } reverse_iterator rbegin() { detachWithCopy(); return d->data.rbegin(); } reverse_iterator rend() { detachWithCopy(); return d->data.rend(); } reference front() { detachWithCopy(); return d->data.front(); } const_reference front() const { return d->data.front(); } reference back() { detachWithCopy(); return d->data.back(); } const_reference back() const { return d->data.back(); } template void assign(InputIterator first, InputIterator last) { detachWithCopy(); d->data.assign(first, last); } void assign(size_type n, const value_type& val) { detachWithCopy(); d->data.assign(n, val); } void push_back(const ValueType& v) { detachWithCopy(); d->data.push_back(v); } void pop_back() { detachWithCopy(); d->data.pop_back(); } iterator insert(iterator position, const value_type& val) { detachWithCopy(); return d->data.insert(position, val); } void insert(iterator position, size_type n, const value_type& val) { detachWithCopy(); d->data.insert(position, n, val); } template void insert(iterator position, InputIterator first, InputIterator last) { detachWithCopy(); d->data.insert(position, first, last); } iterator erase(iterator position) { detachWithCopy(); return d->data.erase(position); } iterator erase(iterator first, iterator last) { detachWithCopy(); return d->data.erase(first, last); } const ValueType& operator[](const std::size_t& idx) const { return d->data[idx]; } ValueType& operator[](const std::size_t& idx) { detachWithCopy(); return d->data[idx]; } ValueType at(const std::size_t& idx) const { return d->data.at(idx); } template Array& operator=(const std::vector& v) { detach(); d->data = v; return *this; } template Array& operator=(const Array& v) { detach(); d->data = v.d->data; return *this; } Array& operator=(const Array& v) { if (this != &v) { detach(); d->data = v.d->data; } return *this; } void swap(Array& other) { using std::swap; swap(d, other.d); } /** * @param index array position to delete * if the index is valid swap it with the last position and pop back. * This function does not preserve the elements order. */ void swapAndPop(Index index) { if (index >= d->data.size()) { return; } if (index != d->data.size() - 1) { d->data[index] = d->data.back(); } d->data.pop_back(); } protected: Container* d; }; template inline Array::~Array() { if (d && !d->deref()) delete d; } template inline void Array::detachWithCopy() { if (d && d->ref() != 1) { auto* o = new Container(*d); d->deref(); d = o; } } template inline void Array::detach() { if (d && d->ref() != 1) { d->deref(); d = new Container; } } template inline bool operator==(const Array& lhs, const Array& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template inline bool operator!=(const Array& lhs, const Array& rhs) { return !(lhs == rhs); } template inline bool operator<(const Array& lhs, const Array& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template inline bool operator>(const Array& lhs, const Array& rhs) { return rhs < lhs; } template inline bool operator<=(const Array& lhs, const Array& rhs) { return !(rhs < lhs); } template inline bool operator>=(const Array& lhs, const Array& rhs) { return !(lhs < rhs); } template inline void swap(Array& lhs, Array& rhs) { lhs.swap(rhs); } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_ARRAY_H avogadrolibs-1.101.0/avogadro/core/atom.h000066400000000000000000000215711506155467400202140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_ATOM_H #define AVOGADRO_CORE_ATOM_H #include "avogadrocore.h" #include "vector.h" namespace Avogadro::Core { enum AtomHybridization { PerceivedOctaheadral = -6, PerceivedTrigonalBipyramidal = -5, PerceivedSquarePlanar = -4, PerceivedSP3 = -3, PerceivedSP2 = -2, PerceivedSP = -1, HybridizationUnknown = 0, SP = 1, SP2 = 2, SP3 = 3, SquarePlanar = 4, TrigonalBipyramidal = 5, Octahedral = 6 }; enum AtomicNumber : unsigned char { Hydrogen = 1, Carbon = 6, Nitrogen = 7, Oxygen = 8 }; /** * @class Atom atom.h * The atom class represents an atom in a molecule. * To use the appropriate atom implementation for a specific molecule * implementation, use the [MoleculeClass]::AtomType typedef. */ template class AtomTemplate { public: using MoleculeType = Molecule_T; /** Creates a new, invalid atom object. */ AtomTemplate(); /** * Creates a new atom object representing the atom at index @p i in molecule * @p m. */ AtomTemplate(MoleculeType* m, Index i); /** * @return True if @a this and @a other share the same index and molecule. */ bool operator==(const AtomTemplate& other) const; /** * @return True if @a this and @a other do not share the same index or * molecule. */ bool operator!=(const AtomTemplate& other) const; /** * Prefix increment operator. Increment this Atom's index by 1 and return a * self-reference. Check isValid() before calling any other methods. */ AtomTemplate& operator++(); /** * Postfix increment operator. Increment this Atom's index by 1 and return a * copy of the current Atom. Check isValid() before calling any other methods. */ AtomTemplate operator++(int); /** * Prefix decrement operator. Decrement this Atom's index by 1 and return a * self-reference. Check isValid() before calling any other methods. */ AtomTemplate& operator--(); /** * Postfix decrement operator. Decrement this Atom's index by 1 and return a * copy of the current Atom. Check isValid() before calling any other methods. */ AtomTemplate operator--(int); /** * @return True if the molecule is set and the index is less than the number * of atoms. */ bool isValid() const; /** * @return The molecule that contains this Atom. */ MoleculeType* molecule() const; /** * @return The index of this atom in molecule(). */ Index index() const; /** * The proton count of the atom. * @{ */ void setAtomicNumber(unsigned char num); unsigned char atomicNumber() const; /** @} */ /** * The 2D position of this atom. * @note Not all molecule types support 2D positions. If the current * MoleculeType class does not, calling this method will result in a * compilation error. * @{ */ void setPosition2d(const Vector2& pos); Vector2 position2d() const; /** @} */ /** * The 3D position of this atom. * @{ */ void setPosition3d(const Vector3& pos); Vector3 position3d() const; /** @} */ /** * The hybridization / coordination of this atom * @{ */ void setHybridization(AtomHybridization hyb); AtomHybridization hybridization() const; /** @} */ /** * The formal charge of this atom * @{ */ void setFormalCharge(signed char charge); signed char formalCharge() const; /** @} */ /** * The color of this atom * @{ */ void setColor(Vector3ub color); Vector3ub color() const; /** @} */ /** * The layer of this atom * @{ */ void setLayer(size_t layer); size_t layer() const; /** @} */ /** * Is the atom selected. * {@ */ void setSelected(bool selected); bool selected() const; /** @} */ /** * The force on this atom. * {@ */ void setForceVector(const Vector3& force); Vector3 forceVector() const; void setLabel(const std::string& label); std::string label() const; /** @} */ private: MoleculeType* m_molecule; Index m_index; }; template AtomTemplate::AtomTemplate() : m_molecule(nullptr), m_index(MaxIndex) { } template AtomTemplate::AtomTemplate(MoleculeType* m, Index i) : m_molecule(m), m_index(i) { } template bool AtomTemplate::operator==( const AtomTemplate& other) const { return m_molecule == other.m_molecule && m_index == other.m_index; } template bool AtomTemplate::operator!=( const AtomTemplate& other) const { return m_molecule != other.m_molecule || m_index != other.m_index; } template AtomTemplate& AtomTemplate::operator++() { ++m_index; return *this; } template AtomTemplate AtomTemplate::operator++(int) { AtomTemplate result(m_molecule, m_index++); return result; } template AtomTemplate& AtomTemplate::operator--() { --m_index; return *this; } template AtomTemplate AtomTemplate::operator--(int) { AtomTemplate result(m_molecule, m_index--); return result; } template bool AtomTemplate::isValid() const { return m_molecule && m_index < m_molecule->atomCount(); } template typename AtomTemplate::MoleculeType* AtomTemplate::molecule() const { return m_molecule; } template Index AtomTemplate::index() const { return m_index; } template void AtomTemplate::setAtomicNumber(unsigned char num) { m_molecule->setAtomicNumber(m_index, num); } template unsigned char AtomTemplate::atomicNumber() const { return m_molecule->atomicNumber(m_index); } template void AtomTemplate::setPosition2d(const Vector2& pos) { m_molecule->setAtomPosition2d(m_index, pos); } template Vector2 AtomTemplate::position2d() const { return m_molecule->atomPositions2d().size() > 0 ? m_molecule->atomPositions2d()[m_index] : Vector2::Zero(); } template void AtomTemplate::setPosition3d(const Vector3& pos) { m_molecule->setAtomPosition3d(m_index, pos); } template Vector3 AtomTemplate::position3d() const { return m_molecule->atomPositions3d().size() > 0 ? m_molecule->atomPositions3d()[m_index] : Vector3::Zero(); } template void AtomTemplate::setHybridization(AtomHybridization hyb) { m_molecule->setHybridization(m_index, hyb); } template AtomHybridization AtomTemplate::hybridization() const { return m_molecule->hybridization(m_index); } template void AtomTemplate::setFormalCharge(signed char charge) { m_molecule->setFormalCharge(m_index, charge); } template signed char AtomTemplate::formalCharge() const { return m_molecule->formalCharge(m_index); } template void AtomTemplate::setColor(Vector3ub color) { m_molecule->setColor(m_index, std::move(color)); } template Vector3ub AtomTemplate::color() const { return m_molecule->color(m_index); } template void AtomTemplate::setLayer(size_t layer) { m_molecule->setLayer(m_index, layer); } template size_t AtomTemplate::layer() const { return m_molecule->layer(m_index); } template void AtomTemplate::setSelected(bool selected) { m_molecule->setAtomSelected(m_index, selected); } template bool AtomTemplate::selected() const { return m_molecule->atomSelected(m_index); } template void AtomTemplate::setForceVector(const Vector3& force) { m_molecule->setForceVector(m_index, force); } template Vector3 AtomTemplate::forceVector() const { return m_molecule->forceVectors().size() > 0 ? m_molecule->forceVectors()[m_index] : Vector3::Zero(); } template void AtomTemplate::setLabel(const std::string& label) { m_molecule->setAtomLabel(m_index, label); } template std::string AtomTemplate::label() const { return m_molecule->atomLabel(m_index); } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_ATOM_H avogadrolibs-1.101.0/avogadro/core/atomtyper-inline.h000066400000000000000000000035751506155467400225600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_ATOMTYPER_INLINE_H #define AVOGADRO_CORE_ATOMTYPER_INLINE_H #include "atomtyper.h" #include "atom.h" #include "molecule.h" namespace Avogadro::Core { template AtomTyper::AtomTyper(const Molecule* mol) : m_molecule(mol) { } template AtomTyper::~AtomTyper() { } template void AtomTyper::setMolecule(const Molecule* mol) { if (m_molecule != mol) { m_molecule = mol; reset(); } } template void AtomTyper::run() { initialize(); Index numAtoms = m_molecule ? m_molecule->atomCount() : 0; for (Index atomId = 0; atomId < numAtoms; ++atomId) { Atom atom = m_molecule->atom(atomId); m_types.push_back(type(atom)); } } template OutputType AtomTyper::atomType(const Atom& atom) { OutputType result; if (atom.isValid() && atom.molecule() == m_molecule) { // Return the calculated value if we've already run the typer. if (atom.index() < m_types.size()) { result = m_types[atom.index()]; } else { initialize(); result = type(atom); } } return result; } template Array AtomTyper::types() const { return m_types; } template void AtomTyper::reset() { m_types.clear(); } template void AtomTyper::initialize() { m_types.reserve(m_molecule ? m_molecule->atomCount() : 0); } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_ATOMTYPER_INLINE_H avogadrolibs-1.101.0/avogadro/core/atomtyper.h000066400000000000000000000045301506155467400212740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_ATOMTYPER_H #define AVOGADRO_CORE_ATOMTYPER_H #include namespace Avogadro::Core { class Atom; class Molecule; /** * @class AtomTyper atomtyper.h * @brief The AtomTyper class provides a base interface for generating a list of * type identifiers describing the atoms in a molecule. */ template class AtomTyper { public: using ValueType = OutputType; explicit AtomTyper(const Molecule* mol = nullptr); virtual ~AtomTyper(); /** * @param mol The molecule with atoms to type. */ void setMolecule(const Molecule* mol); /** * Iterate through the molecule and generate type descriptions for each atom. * The results can be obtained by calling types(). */ virtual void run(); /** * Perform a type lookup on the specified atom. If run() has been called * previously, a cached result is returned. * @return The type of @a atom. */ virtual OutputType atomType(const Atom& atom); /** * @return An Array of OutputType objects. There will be one object for each * atom of the input Molecule, and they are ordered by the corresponding * atom's index. */ Array types() const; /** * Reset the typer's internal state. This is called when the molecule is * changed. The base implementation clears the m_types Array. */ virtual void reset(); protected: /** * Perform any setup needed that needs to be done prior to calling type(). The * base implementation of this function reserves enough room in the m_types * Array for the current Molecule. */ virtual void initialize(); /** * Determines the type of the atom. * @param atom The atom to type. * @return The type of @a atom. */ virtual OutputType type(const Atom& atom) = 0; /** The molecule on which to operate. */ const Molecule* m_molecule; /** The array of types to be populated. */ Array m_types; }; } // namespace Avogadro::Core #include #endif // AVOGADRO_CORE_ATOMTYPER_H avogadrolibs-1.101.0/avogadro/core/atomutilities.cpp000066400000000000000000000163271506155467400225060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "atomutilities.h" #include #include constexpr double M_TETRAHED = 109.47122063449069389; namespace Avogadro::Core { using NeighborListType = Array; inline unsigned int countExistingBonds(const NeighborListType& bonds) { unsigned int result(0); for (auto bond : bonds) { result += static_cast(bond.order()); } return result; } AtomHybridization AtomUtilities::perceiveHybridization(const Atom& atom) { const NeighborListType bonds(atom.molecule()->bonds(atom)); const unsigned int numberOfBonds(countExistingBonds(bonds)); // bond order sum AtomHybridization hybridization = SP3; // default to sp3 // TODO: Handle hypervalent species, SO3, SO4, lone pairs, etc. if (numberOfBonds > 4) { // hybridization = numberOfBonds; // e.g., octahedral, trig. bipyr., // etc. } else { // Count multiple bonds unsigned int numTripleBonds = 0; unsigned int numDoubleBonds = 0; for (auto bond : bonds) { if (bond.order() == 2) numDoubleBonds++; else if (bond.order() == 3) numTripleBonds++; } if (numTripleBonds > 0 || numDoubleBonds > 1) hybridization = SP; // sp else if (numDoubleBonds > 0) hybridization = SP2; // sp2 // special case for nitrogen in an amide if (atom.atomicNumber() == 7 && hybridization == SP3) { // look through the neighbors for a C=O for (auto bond : bonds) { Atom a1 = bond.getOtherAtom(atom); if (a1.atomicNumber() == 6 && bond.order() == 1) { const NeighborListType nbrBonds(atom.molecule()->bonds(a1)); for (auto nbrBond : nbrBonds) { Atom a2 = nbrBond.getOtherAtom(a1); if (a2.index() == atom.index()) continue; // we want a *new* atom, not the nitrogen if (a2.atomicNumber() == 8 && nbrBond.order() == 2) { hybridization = SP2; break; } } } } } } return hybridization; } // Generate bond geometries // First, the default fallback (random vectors) // Also applies when you have a linear geometry and just need one new vector // (it doesn't matter where it goes). Vector3 AtomUtilities::generateNewBondVector( const Atom& atom, const std::vector& allVectors, AtomHybridization hybridization) { Vector3 newPos; bool success = false; int currentValence = static_cast(allVectors.size()); // No bonded atoms, just pick a random vector if (currentValence == 0) { newPos = Vector3::Random().normalized(); return newPos; } else if (currentValence == 1) { // One bonded atom const Vector3& bond1 = allVectors[0]; // Check what's attached to our neighbor -- we want to set trans to the // neighbor Vector3 bond2(0.0, 0.0, 0.0); const NeighborListType bonds(atom.molecule()->bonds(atom)); for (auto bond : bonds) { Atom a1 = bond.getOtherAtom(atom); const NeighborListType nbrBonds(atom.molecule()->bonds(a1)); for (auto nbrBond : nbrBonds) { Atom a2 = nbrBond.getOtherAtom(a1); if (a2.index() == atom.index()) continue; // we want a *new* atom Vector3 delta = a2.position3d() - a1.position3d(); if (!delta.isZero(1e-5)) bond2 = delta.normalized(); // Check for carboxylate (CO2) if ((atom.atomicNumber() == 8) // atom for H is O && (a1.atomicNumber() == 6) // central atom is C && (nbrBond.order() == 2) && (a2.atomicNumber() == 8)) break; // make sure the H will be trans to the C=O } } Vector3 v1, v2; v1 = bond1.cross(bond2); if (bond2.norm() < 1.0e-5 || v1.norm() < 1.0e-5) { // std::cout << " creating a random paired atom " << std::endl; v2 = Vector3::Random().normalized(); double angle = fabs(acos(bond1.dot(v2))); while (angle < 45.0 * DEG_TO_RAD || angle > 135.0 * DEG_TO_RAD) { v2 = Vector3::Random().normalized(); angle = fabs(acos(bond1.dot(v2))); // std::cout << "angle = " << angle*RAD_TO_DEG << std::endl; } v1 = bond1.cross(v2); // so find a perpendicular, given the random vector v2 = bond1.cross(v1); } else { // std::cout << " found a neighbor for trans " << std::endl; v1 = bond1.cross(bond2); v2 = -1.0 * bond1.cross(v1); } v2.normalize(); switch (hybridization) { case SP: case SquarePlanar: case TrigonalBipyramidal: newPos = bond1; // 180 degrees away from the current neighbor break; case SP2: // sp2 newPos = bond1 - v2 * tan(DEG_TO_RAD * 120.0); break; case Octahedral: // octahedral newPos = bond1 - v2 * tan(DEG_TO_RAD * 90.0); break; case SP3: default: newPos = (bond1 - v2 * tan(DEG_TO_RAD * M_TETRAHED)); break; } // std::cout << " one bond " << newPos.normalized() << std::endl; return -1.0 * newPos.normalized(); } // end one bond else if (currentValence == 2) { const Vector3& bond1 = allVectors[0]; const Vector3& bond2 = allVectors[1]; Vector3 v1 = bond1 + bond2; v1.normalize(); switch (hybridization) { case SP: // shouldn't happen, but maybe with metal atoms? case SP2: newPos = v1; // point away from the two existing bonds break; case SP3: default: Vector3 v2 = bond1.cross(bond2); // find the perpendicular v2.normalize(); // newPos = bond1 - v2 * tan(DEG_TO_RAD * (M_TETRAHED)); newPos = v2 + v1 * (sqrt(2.0) / 2.0); } // std::cout << " two bonds " << newPos.normalized() << std::endl; return -1.0 * newPos.normalized(); } // end two bonds else if (currentValence == 3) { const Vector3& bond1 = allVectors[0]; const Vector3& bond2 = allVectors[1]; const Vector3& bond3 = allVectors[2]; // need to handle different hybridizations here // since the base of the tetrahedron should be symmetric // the sum of the three bond vectors should cancel the angular parts // and point in the new direction.. just need to normalize and rescale newPos = -1.0 * (bond1 + bond2 + bond3); // std::cout << " three bonds " << newPos.normalized() << std::endl; return newPos.normalized(); } // Fallback: // Try 10 times to generate a random vector that doesn't overlap with // an existing bond. If we can't, just give up and let the overlap occur. // Tolerance for two vectors being "too close" in radians (pi/8). const Avogadro::Real cosRadTol = cos(static_cast(M_PI) / static_cast(8.)); for (int attempt = 0; !success && attempt < 10; ++attempt) { newPos = Vector3::Random().normalized(); success = true; for (auto it = allVectors.begin(), itEnd = allVectors.end(); success && it != itEnd; ++it) { success = newPos.dot(*it) < cosRadTol; } } return newPos; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/atomutilities.h000066400000000000000000000022341506155467400221430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_ATOMUTILITIES_H #define AVOGADRO_QTGUI_ATOMUTILITIES_H #include "avogadrocoreexport.h" #include #include namespace Avogadro::Core { class Atom; class Molecule; class AVOGADROCORE_EXPORT AtomUtilities { public: /** * Perceive the geometry / hybridization bonded to @a atom. * Ideally, the client should cache the hybridization number * by calling setHybridization() later */ static AtomHybridization perceiveHybridization(const Atom& atom); /** * Generate a new bond vector (unit length) */ static Vector3 generateNewBondVector( const Atom& atom, const std::vector& currentVectors, AtomHybridization hybridization); private: AtomUtilities(); // Not implemented ~AtomUtilities(); // Not implemented }; } // namespace Avogadro::Core #endif // AVOGADRO_QTGUI_ATOMUTILITIES_H avogadrolibs-1.101.0/avogadro/core/avogadrocore.h000066400000000000000000000057661506155467400217370ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_H #define AVOGADRO_CORE_H #include #include /** Prevent compiler error when using std::numeric_limits::max() */ #if defined(_MSC_VER) && defined(max) #undef max #endif /** * This macro marks a parameter as unused. Its purpose is to disable the * compiler from emitting unused parameter warnings. */ #define AVO_UNUSED(variable) (void)variable /** * This macro marks a class as not copyable. It should be used in the private * section of a class's declaration. */ #define AVO_DISABLE_COPY(Class) \ Class(const Class&); \ Class& operator=(const Class&); namespace Avogadro { /** Typedef for a real number. */ using Real = double; /** Typedef for indices and sizes. */ using Index = size_t; inline constexpr Index MaxIndex = std::numeric_limits::max(); /** Used to represent an invalid atomic number. */ constexpr unsigned char InvalidElement = 255; /** * Minimum value for atomic numbers that represent custom, non-elemental * particles. */ constexpr unsigned char CustomElementMin = 128; /** * Maximum value for atomic numbers that represent custom, non-elemental * particles. */ constexpr unsigned char CustomElementMax = 254; /** * Count of atomic number values that are used to represent custom, * non-elemental particles. */ inline constexpr unsigned char CustomElementCount = CustomElementMax - CustomElementMin + 1; /** * @return True if @a atomicNumber denotes a custom element type. */ namespace Core { constexpr bool isCustomElement(unsigned char atomicNumber) { return atomicNumber >= CustomElementMin && atomicNumber <= CustomElementMax; } } // namespace Core /** Unit conversion factors. @{ */ constexpr double PI_D = 3.141592653589793238462643; constexpr float PI_F = static_cast(PI_D); constexpr Real PI = static_cast(PI_D); constexpr double DEG_TO_RAD_D = PI_D / 180.0; constexpr float DEG_TO_RAD_F = static_cast(DEG_TO_RAD_D); constexpr Real DEG_TO_RAD = static_cast(DEG_TO_RAD_D); constexpr double RAD_TO_DEG_D = 180.0 / PI_D; constexpr float RAD_TO_DEG_F = static_cast(RAD_TO_DEG_D); constexpr Real RAD_TO_DEG = static_cast(RAD_TO_DEG_D); constexpr double BOHR_TO_ANGSTROM_D = 0.52917721092; constexpr float BOHR_TO_ANGSTROM_F = static_cast(BOHR_TO_ANGSTROM_D); constexpr Real BOHR_TO_ANGSTROM = static_cast(BOHR_TO_ANGSTROM_D); constexpr double ANGSTROM_TO_BOHR_D = 1.0 / BOHR_TO_ANGSTROM_D; constexpr float ANGSTROM_TO_BOHR_F = static_cast(ANGSTROM_TO_BOHR_D); constexpr Real ANGSTROM_TO_BOHR = static_cast(ANGSTROM_TO_BOHR_D); /** @} */ } // namespace Avogadro #endif // AVOGADRO_CORE_H avogadrolibs-1.101.0/avogadro/core/avospglib.cpp000066400000000000000000000111621506155467400215700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "avospglib.h" #include "array.h" #include "matrix.h" #include "molecule.h" #include "unitcell.h" #include "vector.h" #include extern "C" { #include "spglib.h" } namespace Avogadro::Core { unsigned short AvoSpglib::getHallNumber(Molecule& mol, double cartTol) { if (!mol.unitCell()) return 0; const UnitCell* uc = mol.unitCell(); Matrix3 cellMat = uc->cellMatrix(); double lattice[3][3]; // Spglib expects column vectors for (Index i = 0; i < 3; ++i) { for (Index j = 0; j < 3; ++j) { lattice[i][j] = cellMat(i, j); } } Index numAtoms = mol.atomCount(); auto* positions = new double[numAtoms][3]; int* types = new int[numAtoms]; const Array& atomicNums = mol.atomicNumbers(); const Array& pos = mol.atomPositions3d(); // Positions need to be in fractional coordinates for (Index i = 0; i < numAtoms; ++i) { Vector3 fracCoords = uc->toFractional(pos[i]); positions[i][0] = fracCoords[0]; positions[i][1] = fracCoords[1]; positions[i][2] = fracCoords[2]; types[i] = atomicNums[i]; } SpglibDataset* data = spg_get_dataset(lattice, positions, types, numAtoms, cartTol); if (!data) { std::cerr << "Cannot determine spacegroup.\n"; delete[] positions; delete[] types; return 0; } unsigned short hallNumber = data->hall_number; // Cleanup time spg_free_dataset(data); delete[] positions; delete[] types; mol.setHallNumber(hallNumber); return hallNumber; } bool AvoSpglib::reduceToPrimitive(Molecule& mol, double cartTol) { return standardizeCell(mol, cartTol, true, false); } bool AvoSpglib::conventionalizeCell(Molecule& mol, double cartTol) { return standardizeCell(mol, cartTol, false, true); } bool AvoSpglib::symmetrize(Molecule& mol, double cartTol) { return standardizeCell(mol, cartTol, true, true); } bool AvoSpglib::standardizeCell(Molecule& mol, double cartTol, bool toPrimitive, bool idealize) { if (!mol.unitCell()) return false; const UnitCell* uc = mol.unitCell(); Matrix3 cellMat = uc->cellMatrix(); double lattice[3][3]; // Spglib expects column vectors for (Index i = 0; i < 3; ++i) { for (Index j = 0; j < 3; ++j) { lattice[i][j] = cellMat(i, j); } } Index numAtoms = mol.atomCount(); // spg_standardize_cell() can cause the number of atoms to increase by // as much as 4x if toPrimitive is false. // So, we must make these arrays at least 4x the number of atoms. // If toPrimitive is true, then we will just use the number of atoms. // See http://atztogo.github.io/spglib/api.html#spg-standardize-cell int numAtomsMultiplier = toPrimitive ? 1 : 4; auto* positions = new double[numAtoms * numAtomsMultiplier][3]; int* types = new int[numAtoms * numAtomsMultiplier]; const Array& atomicNums = mol.atomicNumbers(); const Array& pos = mol.atomPositions3d(); // Positions need to be in fractional coordinates for (Index i = 0; i < numAtoms; ++i) { Vector3 fracCoords = uc->toFractional(pos[i]); positions[i][0] = fracCoords[0]; positions[i][1] = fracCoords[1]; positions[i][2] = fracCoords[2]; types[i] = atomicNums[i]; } // Run the spglib algorithm Index newNumAtoms = spg_standardize_cell(lattice, positions, types, numAtoms, toPrimitive, !idealize, cartTol); // If 0 is returned, the algorithm failed. if (newNumAtoms == 0) { delete[] positions; delete[] types; return false; } // Let's create a new molecule with the information Molecule newMol; // First, we will make the unit cell Matrix3 newCellMat; for (Index i = 0; i < 3; ++i) { for (Index j = 0; j < 3; ++j) { newCellMat(i, j) = lattice[i][j]; } } auto* newCell = new UnitCell(newCellMat); newMol.setUnitCell(newCell); // Next, add in the atoms for (Index i = 0; i < newNumAtoms; ++i) { Atom newAtom = newMol.addAtom(types[i]); Vector3 newAtomPos(positions[i][0], positions[i][1], positions[i][2]); // We must convert it back to cartesian before adding it newAtom.setPosition3d(newCell->toCartesian(newAtomPos)); } delete[] positions; delete[] types; // Set the new molecule mol = newMol; // Set the space group in the molecule getHallNumber(mol, cartTol); return true; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/avospglib.h000066400000000000000000000062061506155467400212400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_AVO_SPGLIB_H #define AVOGADRO_CORE_AVO_SPGLIB_H #include "avogadrocoreexport.h" #include "molecule.h" namespace Avogadro::Core { /** * @class AvoSpglib avospglib.h * @brief The AvoSpglib class provides an interface between Avogadro and Spglib. */ class AVOGADROCORE_EXPORT AvoSpglib { public: AvoSpglib(); ~AvoSpglib(); /** * Use spglib to find the Hall number for a crystal. If the unit cell does not * exist or if the algorithm fails, 0 will be returned. * * If the algorithm succeeds, the hall number will be set in the molecule. * * @param mol The molecule to be handled. * @param cartTol The cartesian tolerance for spglib. * @return The Hall number for the crystal. */ static unsigned short getHallNumber(Molecule& mol, double cartTol = 1e-5); /** * Use spglib to reduce the crystal to a primitive cell. Unless the molecule * is missing its unit cell, it will be edited by spglib. Positions are * not idealized. * * If the algorithm succeeds, the hall number will be set in the molecule. * * @param mol The molecule to be reduced to its primitive cell. * @param cartTol The cartesian tolerance for spglib. * @return False if the molecule has no unit cell or if the spglib algorithm failed. True otherwise. */ static bool reduceToPrimitive(Molecule& mol, double cartTol = 1e-5); /** * Use spglib to refine the crystal to its conventional cell. Unless the * molecule is missing its unit cell, it will be edited by spglib. Positions * are idealized. * * If the algorithm succeeds, the hall number will be set in the molecule. * * @param mol The molecule to be conventionalized. * @param cartTol The cartesian tolerance for spglib. * @return False if the molecule has no unit cell or if the * spglib algorithm failed. True otherwise. */ static bool conventionalizeCell(Molecule& mol, double cartTol = 1e-5); /** * Use spglib to symmetrize the crystal. Unless the molecule is missing * its unit cell, it will be edited by spglib. It will be reduced * to its primitive form, and positions will be idealized. * * If the algorithm succeeds, the hall number will be set in the molecule. * * @param mol The molecule to be conventionalized. * @param cartTol The cartesian tolerance for spglib. * @return False if the molecule has no unit cell or if the * spglib algorithm failed. True otherwise. */ static bool symmetrize(Molecule& mol, double cartTol = 1e-5); private: // Called by reduceToPrimitive(), conventionalizeCell(), and symmetrize() // Calls spg_standardize_cell() static bool standardizeCell(Molecule& mol, double cartTol, bool toPrimitive, bool idealize); }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_AVO_SPGLIB_H avogadrolibs-1.101.0/avogadro/core/basisset.h000066400000000000000000000200031506155467400210560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_BASISSET_H #define AVOGADRO_CORE_BASISSET_H #include "avogadrocoreexport.h" #include "core/variant.h" #include #include namespace Avogadro::Core { class Molecule; /** * @class BasisSet basisset.h * @brief BasisSet contains basis set data. * @author Marcus D. Hanwell * * This is the base class for basis sets, and has two derived classes - * GaussianSet and SlaterSet. It must be populated with data, with other classes * capable of performing calculations on the data or writing it out. */ class AVOGADROCORE_EXPORT BasisSet { public: /** * Constructor. */ BasisSet() = default; /** * Destructor. */ virtual ~BasisSet() = default; /** * Clone. */ virtual BasisSet* clone() const = 0; /** * @brief The ElectronType enum describes the type of electrons being set or * retrieved. If Paired, then Alpha and Beta cannot be set, if Alpha or Beta * then both must be set. */ enum ElectronType { Paired, Alpha, Beta }; /** * Set the number of electrons in the BasisSet. * @param n The number of electrons in the BasisSet. * @param type The type of the electrons (Alpha, Beta, or Paired). */ virtual void setElectronCount(unsigned int n, ElectronType type = Paired); /** * @param type The type of the electrons (Alpha, Beta, or Paired). * @return The number of electrons in the molecule. */ unsigned int electronCount(ElectronType type = Paired) const; /** * Set the molecule for the basis set. */ void setMolecule(Molecule* molecule_) { m_molecule = molecule_; } /** * Get the molecule this basis set belongs to. */ Molecule* molecule() { return m_molecule; } const Molecule* molecule() const { return m_molecule; } /** * Set the name of the basis set. */ void setName(const std::string& name) { m_name = name; } /** * Get the name of the basis set. */ std::string name() const { return m_name; } /** * Set the name of the basis set. */ void setTheoryName(const std::string& name) { m_theoryName = name; } /** * Get the name of the basis set. */ std::string theoryName() const { return m_theoryName; } /** * @return The number of molecular orbitals in the BasisSet. */ virtual unsigned int molecularOrbitalCount( ElectronType type = Paired) const = 0; /** * @return The molecular orbital number corresponding to the HOMO orbital. */ unsigned int homo(ElectronType type = Paired) const { return lumo(type) - 1; } /** * @return The molecular orbital number corresponding to the LUMO orbital. */ unsigned int lumo(ElectronType type = Paired) const; /** * @return True of the basis set is valid, false otherwise. * Default is true, if false then the basis set is likely unusable. */ virtual bool isValid() = 0; /** * @return the orbital symmetry labels (if they exist) for the MOs */ std::vector symmetryLabels(ElectronType type = Paired) const { if (type == Paired || type == Alpha) return m_symmetryLabels[0]; else return m_symmetryLabels[1]; } /** * Set the orbital symmetry labels (a1, t2g, etc.) for the molecular * orbitals */ void setSymmetryLabels(const std::vector& labels, ElectronType type = Paired); /** * @brief Set the molecular orbital energies, expected in Hartrees. * @param energies The vector containing energies for the MOs of type * @param type The type of the electrons being set. */ void setMolecularOrbitalEnergy(const std::vector& energies, ElectronType type = Paired); /** * @brief Set the molecular orbital occupancies. * @param occ The occupancies for the MOs of type. * @param type The type of the electrons being set. */ void setMolecularOrbitalOccupancy(const std::vector& occ, ElectronType type = Paired); std::vector& moEnergy(ElectronType type = Paired) { if (type == Paired || type == Alpha) return m_moEnergy[0]; else return m_moEnergy[1]; } std::vector moEnergy(ElectronType type = Paired) const { if (type == Paired || type == Alpha) return m_moEnergy[0]; else return m_moEnergy[1]; } std::vector& moOccupancy(ElectronType type = Paired) { if (type == Paired || type == Alpha) return m_moOccupancy[0]; else return m_moOccupancy[1]; } std::vector moOccupancy(ElectronType type = Paired) const { if (type == Paired || type == Alpha) return m_moOccupancy[0]; else return m_moOccupancy[1]; } protected: /** * Total number of electrons, 0 is alpha electrons and 1 is beta electrons. * For closed shell calculations alpha is doubly occupied and there are no * beta electrons. */ std::array m_electrons = {}; /** * The Molecule holds the atoms (and possibly bonds) read in from the output * file. Most basis sets have orbitals around these atoms, but this is not * necessarily the case. */ Molecule* m_molecule = nullptr; /** * The name of the basis set, this is usually a string identifier referencing * a standard basis set when only one is used. */ std::string m_name; /** * The name of the theory used for the calculation. */ std::string m_theoryName; /** * The orbital symmetry labels (if they exist) for the MOs */ std::vector m_symmetryLabels[2]; /** * @brief This block stores energies for the molecular orbitals (same * convention as the molecular orbital coefficients). */ std::vector m_moEnergy[2]; /** * @brief The occupancy of the molecular orbitals. */ std::vector m_moOccupancy[2]; }; inline unsigned int BasisSet::lumo(ElectronType type) const { if (type == Beta) { // check if we have occupancy data if (m_moOccupancy[1].size() > 0) { for (unsigned int i = 0; i < m_moOccupancy[1].size(); ++i) { if (m_moOccupancy[1][i] == 0) return i; } } } else { // alpha or paired // check if we have occupancy data - more accurate if (m_moOccupancy[0].size() > 0) { for (unsigned int i = 0; i < m_moOccupancy[0].size(); ++i) { if (m_moOccupancy[0][i] == 0) return i; } } } // fall back to electron count return m_electrons[0] / 2 + 1; } inline void BasisSet::setElectronCount(unsigned int n, ElectronType type) { switch (type) { case Paired: m_electrons[0] = n; m_electrons[1] = 0; break; case Alpha: m_electrons[0] = n; break; case Beta: m_electrons[1] = n; break; default: // Shouldn't hit this condition. ; } } inline unsigned int BasisSet::electronCount(ElectronType type) const { switch (type) { case Paired: case Alpha: return m_electrons[0]; case Beta: return m_electrons[1]; default: // Shouldn't hit this condition. return 0; } } inline void BasisSet::setSymmetryLabels(const std::vector& labels, ElectronType type) { if (type == Paired || type == Alpha) m_symmetryLabels[0] = labels; else m_symmetryLabels[1] = labels; } inline void BasisSet::setMolecularOrbitalEnergy( const std::vector& energies, ElectronType type) { if (type == Beta) m_moEnergy[1] = energies; else m_moEnergy[0] = energies; } inline void BasisSet::setMolecularOrbitalOccupancy( const std::vector& occ, ElectronType type) { if (type == Beta) m_moOccupancy[1] = occ; else m_moOccupancy[0] = occ; } } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/bond.h000066400000000000000000000141541506155467400201750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_BOND_H #define AVOGADRO_CORE_BOND_H #include "avogadrocore.h" #include "atom.h" namespace Avogadro::Core { /** * @class Bond bond.h * The Bond class represents a bond in a molecule. * To use the appropriate bond implementation for a specific molecule * implementation, use the [MoleculeClass]::BondType typedef. */ template class BondTemplate { public: using MoleculeType = Molecule_T; using AtomType = typename Molecule_T::AtomType; /** Creates a new, invalid bond object. */ BondTemplate() = default; /** * Creates a bond object representing a bond at index @p i in molecule @p m. */ BondTemplate(MoleculeType* m, Index i); /** * @return True if @a this and @a other share the same index and molecule. */ bool operator==(const BondTemplate& other) const; /** * @return True if @a this and @a other do not share the same index or * molecule. */ bool operator!=(const BondTemplate& other) const; /** * Prefix increment operator. Increment this Bond's index by 1 and return a * self-reference. Check isValid() before calling any other methods. */ BondTemplate& operator++(); /** * Postfix increment operator. Increment this Bond's index by 1 and return a * copy of the current Atom. Check isValid() before calling any other methods. */ BondTemplate operator++(int); /** * Prefix decrement operator. Decrement this Bond's index by 1 and return a * self-reference. Check isValid() before calling any other methods. */ BondTemplate& operator--(); /** * Postfix decrement operator. Decrement this Bond's index by 1 and return a * copy of the current Atom. Check isValid() before calling any other methods. */ BondTemplate operator--(int); /** * @return True if the molecule is set and the index is less than the number * of bonds. */ bool isValid() const; /** * @return The molecule that contains this Bond. */ MoleculeType* molecule() const; /** * @return The index of this bond in molecule(). */ Index index() const; /** * An atom in the bond, such that atom1().index() < atom2.index(). * @{ */ AtomType atom1() const; AtomType atom2() const; /** @} */ /** * @return The atom in the bond such that returned.index() != index. */ AtomType getOtherAtom(Index index) const; /** * @return The atom in the bond such that returned.index() != atom.index(). */ AtomType getOtherAtom(AtomType atom) const; /** * The bond's order (single = 1, double = 2, etc.) * @{ */ void setOrder(unsigned char o); unsigned char order() const; /** @} */ /** * The length of the bond or 0.0 if the bond is invalid. */ Real length() const; /** * A label for the bond (if any) */ std::string label() const; private: MoleculeType* m_molecule = nullptr; Index m_index = MaxIndex; }; template BondTemplate::BondTemplate(MoleculeType* m, Index i) : m_molecule(m), m_index(i) { } template bool BondTemplate::operator==( const BondTemplate& other) const { return m_molecule == other.m_molecule && m_index == other.m_index; } template bool BondTemplate::operator!=( const BondTemplate& other) const { return m_molecule != other.m_molecule || m_index != other.m_index; } template BondTemplate& BondTemplate::operator++() { ++m_index; return *this; } template BondTemplate BondTemplate::operator++(int) { BondTemplate result(m_molecule, m_index++); return result; } template BondTemplate& BondTemplate::operator--() { --m_index; return *this; } template BondTemplate BondTemplate::operator--(int) { BondTemplate result(m_molecule, m_index--); return result; } template bool BondTemplate::isValid() const { return m_molecule && m_index < m_molecule->bondCount(); } template typename BondTemplate::MoleculeType* BondTemplate::molecule() const { return m_molecule; } template Index BondTemplate::index() const { return m_index; } template typename BondTemplate::AtomType BondTemplate::atom1() const { return AtomType(m_molecule, m_molecule->bondPairs()[m_index].first); } template typename BondTemplate::AtomType BondTemplate::atom2() const { return AtomType(m_molecule, m_molecule->bondPairs()[m_index].second); } template typename BondTemplate::AtomType BondTemplate::getOtherAtom(Index index) const { if (atom1().index() == index) return atom2(); else return atom1(); } template typename BondTemplate::AtomType BondTemplate::getOtherAtom( BondTemplate::AtomType atom) const { return getOtherAtom(atom.index()); } template void BondTemplate::setOrder(unsigned char o) { m_molecule->setBondOrder(m_index, o); } template unsigned char BondTemplate::order() const { return m_molecule->bondOrders()[m_index]; } template Real BondTemplate::length() const { if (!isValid()) return 0.0; return (atom1().position3d() - atom2().position3d()).norm(); } template std::string BondTemplate::label() const { return m_molecule->bondLabel(m_index); } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_BOND_H avogadrolibs-1.101.0/avogadro/core/color3f.h000066400000000000000000000071431506155467400206220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_COLOR3F_H #define AVOGADRO_CORE_COLOR3F_H #include namespace Avogadro::Core { namespace detail { /** Used to represent Maximum RGB value as a float. */ constexpr float k_MaxRGBColorValue = 255.0f; } // namespace detail /** * @class Color3f color3f.h * @brief Representation of an RGB color using three floats. * @author Marcus D. Hanwell * * This class represents a color as three floats ranging from 0.0 to 1.0 * specifying the intensity of the red, green and blue components of the * color. It is stored in memory as float[3], and so vectors containing this * type may be passed directly to OpenGL and other functions as C arrays. * * Several convenience functions are provided, the class is written with an * emphasis on efficiency and memory layout. */ class Color3f { public: /** * Constructor, results in a black Color3f object unless the RGB values are * set. * @param red Intensity (from 0.0 to 1.0) of the red component of the color. * @param green Intensity (from 0.0 to 1.0) of the green component of the * color. * @param blue Intensity (from 0.0 to 1.0) of the blue component of the color. */ Color3f(float red = 0.0, float green = 0.0, float blue = 0.0); /** * Constructor where the color is constructed from integer values. * @param red Intensity (from 0 to 255) of the red component of the color. * @param green Intensity (from 0 to 255) of the green component of the color. * @param blue Intensity (from 0 to 255) of the blue component of the color. */ Color3f(int red, int green, int blue); /** * Sets the color objects components. * @param red Intensity (from 0.0 to 1.0) of the red component of the color. * @param green Intensity (from 0.0 to 1.0) of the green component of the * color. * @param blue Intensity (from 0.0 to 1.0) of the blue component of the color. */ void set(float red, float green, float blue); /** * @return The intensity of the red component of the color (0.0 to 1.0). */ float red() const { return m_data[0]; } /** * @return The intensity of the green component of the color (0.0 to 1.0). */ float green() const { return m_data[1]; } /** * @return The intensity of the blue component of the color (0.0 to 1.0). */ float blue() const { return m_data[2]; } /** * @return Direct access to the underlying float array of size 3. */ float* data(); /** * This function is useful when calling OpenGL functions which expect a * float * array of size 3. * @return Direct access to the underlying float array of size 3. */ const float* data() const; protected: std::array m_data; }; inline Color3f::Color3f(float r, float g, float b) : m_data({ r, g, b }) {} // clang-format off inline Color3f::Color3f(int r, int g, int b) : m_data({static_cast(r) / detail::k_MaxRGBColorValue, static_cast(g) / detail::k_MaxRGBColorValue, static_cast(b) / detail::k_MaxRGBColorValue}) { } // clang-format on inline void Color3f::set(float r, float g, float b) { m_data[0] = r; m_data[1] = g; m_data[2] = b; } inline float* Color3f::data() { return m_data.data(); } inline const float* Color3f::data() const { return m_data.data(); } } // End namespace Avogadro::Core #endif // AVOGADRO_CORE_COLOR3F_H avogadrolibs-1.101.0/avogadro/core/constraint.h000066400000000000000000000100351506155467400214310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_CONSTRAINT_H #define AVOGADRO_CORE_CONSTRAINT_H #include "avogadrocoreexport.h" #include #include namespace Avogadro { namespace Core { /** * @class Constraint constraint.h * @brief Constraints for optimization / dynamics * @author Geoffrey R. Hutchison * * This class represents a distance, angle, or torsional constraint / restraint * during optimization or dynamics. More technically, these are implemented as * stiff harmonic oscillators restraining a particular atom set towards the * value. */ class AVOGADROCORE_EXPORT Constraint { public: enum Type { None = 0, DistanceConstraint, AngleConstraint, TorsionConstraint, OutOfPlaneConstraint, UnknownConstraint }; /** * Constructor, results in a zero distance constraint * @param a Atom index of the first atom of the constraint * @param b Atom index of the second atom of the constraint * @param c Atom index of the third atom (for angles or torsions) or MaxIndex * @param d Atom index of the fourth atom (for torsion constraints) or * MaxIndex * @param value The value of the constraint, either Angstrom for distance or * radians) */ Constraint(Index a, Index b, Index c = MaxIndex, Index d = MaxIndex, Real value = 0.0) : m_aIndex(a), m_bIndex(b), m_cIndex(c), m_dIndex(d), m_value(value) { } /** Set the constraint * @param a Atom index of the first atom of the constraint * @param b Atom index of the second atom of the constraint * @param c Atom index of the third atom (for angles or torsions) or MaxIndex * @param d Atom index of the fourth atom (for torsion constraints) or * MaxIndex * @param value The value of the constraint, either Angstrom for distance or * radians) */ void set(Index a, Index b, Index c = MaxIndex, Index d = MaxIndex, Real value = 0.0) { m_aIndex = a; m_bIndex = b; m_cIndex = c; m_dIndex = d; m_value = value; } /** * Set the constraint value (distance, angle, dihedral) * @param value The value of the constraint, either Angstrom for distance or * radians) */ void setValue(Real value) { m_value = value; } /** * @return the constraint value */ Real value() const { return m_value; } /** * @return the atoms in the constraint as a tuple */ std::tuple atoms() const { return std::make_tuple(m_aIndex, m_bIndex, m_cIndex, m_dIndex); } /** * @return the atom index from the constraint or MaxIndex */ Index aIndex() const { return m_aIndex; } Index bIndex() const { return m_bIndex; } Index cIndex() const { return m_cIndex; } Index dIndex() const { return m_dIndex; } Real k() const { return m_k; } void setK(Real k) { m_k = k; } /** * @return the type of constraint */ Constraint::Type type() const { if (m_type != None) return m_type; if (m_cIndex == MaxIndex && m_dIndex == MaxIndex) m_type = DistanceConstraint; else if (m_dIndex == MaxIndex) m_type = AngleConstraint; else if (m_dIndex != MaxIndex) m_type = TorsionConstraint; else m_type = UnknownConstraint; return m_type; } /** * Set the type of constraint * @param type The type of constraint */ void setType(Constraint::Type type) const { m_type = type; } protected: Index m_aIndex = MaxIndex; Index m_bIndex = MaxIndex; Index m_cIndex = MaxIndex; Index m_dIndex = MaxIndex; Real m_value = 0.0; Real m_k = 1000.0; // force constant, default to 1000 kcal/mol/Angstrom^2 mutable Constraint::Type m_type = None; // cached type, initialized to None }; } // End namespace Core } // End namespace Avogadro #endif // AVOGADRO_CORE_CONSTRAINT_H avogadrolibs-1.101.0/avogadro/core/contrastcolor.h000066400000000000000000000023361506155467400221460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_CONTRASTCOLOR_H #define AVOGADRO_CORE_CONTRASTCOLOR_H #include #include namespace Avogadro::Core { inline Vector3ub contrastColor(const Vector3ub& rgb) { // If we're far 'enough' (+/-32) away from 128, just invert the component. // If we're close to 128, inverting the color will end up too close to the // input -- adjust the component before inverting. const unsigned char minVal = 32; const unsigned char maxVal = 223; Vector3ub result; for (size_t i = 0; i < 3; ++i) { unsigned char input = rgb[i]; if (input > 160 || input < 96) result[i] = static_cast(255 - input); else result[i] = static_cast(255 - (input / 4)); // Clamp to 32-->223 to prevent pure black/white result[i] = std::clamp(result[i], minVal, maxVal); } return result; } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_CONTRASTCOLOR_H avogadrolibs-1.101.0/avogadro/core/coordinateblockgenerator.cpp000066400000000000000000000144231506155467400246560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "coordinateblockgenerator.h" #include #include #include #include #include #include namespace Avogadro::Core { std::string CoordinateBlockGenerator::generateCoordinateBlock() { if (!m_molecule) return {}; // Reset stream. m_stream.str(""); m_stream.clear(); // Create/cache some iterators for the specification string. std::string::const_iterator it; const std::string::const_iterator begin = m_specification.begin(); const std::string::const_iterator end = m_specification.end(); // Check the spec to see if certain items are needed. bool needElementSymbol(false); bool needElementName(false); bool needPosition(false); bool needFractionalPosition(false); for (it = begin; it != end; ++it) { switch (*it) { case 'S': case 'L': needElementSymbol = true; break; case 'N': needElementName = true; break; case 'x': case 'y': case 'z': needPosition = true; break; case 'a': case 'b': case 'c': needFractionalPosition = true; break; } } // Variables for loops below const Index numAtoms = m_molecule->atomCount(); Atom atom; unsigned char atomicNumber; const char* symbol = "\0"; const char* name = "\0"; Vector3 pos3d; Vector3 fpos3d; const UnitCell* cell = needFractionalPosition ? molecule()->unitCell() : nullptr; // widths/precisions enum { atomicNumberPrecision = 0, atomicNumberWidth = 3, atomicLabelWidth = 8, // 3 element symbol + 5 index coordinatePrecision = 6, coordinateWidth = 11, elementNameWidth = 13, // Currently the longest element name elementSymbolWidth = 3, gamessAtomicNumberPrecision = 1, gamessAtomicNumberWidth = 5 }; const int indexWidth( static_cast(std::log10(static_cast(numAtoms))) + 1); // Use fixed number format. m_stream << std::fixed; // Count the number for each element std::vector elementCounts(Elements::elementCount(), 0); // Iterate through the atoms for (Index atomI = 0; atomI < numAtoms; ++atomI) { atom = m_molecule->atom(atomI); atomicNumber = atom.atomicNumber(); elementCounts[atomicNumber]++; if (needElementSymbol) symbol = Core::Elements::symbol(atomicNumber); if (needElementName) name = Core::Elements::name(atomicNumber); if (needPosition) pos3d = atom.position3d(); if (needFractionalPosition) fpos3d = cell ? cell->toFractional(atom.position3d()) : Vector3::Zero(); switch (m_distanceUnit) { case Bohr: pos3d *= ANGSTROM_TO_BOHR_F; break; default: case Angstrom: break; } for (it = begin; it != end; ++it) { switch (*it) { case '_': // Space character. If we are not at the end of the spec, a space will // be added by default after the switch clause. If we are at the end, // add a space before the newline that will be added. if (it + 1 == end) m_stream << std::setw(1) << " "; break; case '#': m_stream << std::left << std::setw(indexWidth) << static_cast(atomI + 1); break; case 'Z': m_stream << std::left << std::setw(atomicNumberWidth) << std::setprecision(atomicNumberPrecision) << static_cast(atomicNumber); break; case 'G': m_stream << std::right << std::setw(gamessAtomicNumberWidth) << std::setprecision(gamessAtomicNumberPrecision) << static_cast(atomicNumber); break; case 'S': m_stream << std::left << std::setw(elementSymbolWidth) << symbol; break; case 'L': m_stream << std::left << symbol << elementCounts[atomicNumber] << " "; break; case 'N': m_stream << std::left << std::setw(elementNameWidth) << name; break; case 'x': m_stream << std::right << std::setw(coordinateWidth) << std::setprecision(coordinatePrecision) << pos3d.x(); break; case 'y': m_stream << std::right << std::setw(coordinateWidth) << std::setprecision(coordinatePrecision) << pos3d.y(); break; case 'z': m_stream << std::right << std::setw(coordinateWidth) << std::setprecision(coordinatePrecision) << pos3d.z(); break; case '0': m_stream << std::left << std::setw(1) << 0; break; case '1': m_stream << std::left << std::setw(1) << 1; break; case 'a': if (cell) { m_stream << std::right << std::setw(coordinateWidth) << std::setprecision(coordinatePrecision) << fpos3d.x(); } else { m_stream << std::right << std::setw(coordinateWidth) << "N/A"; } break; case 'b': if (cell) { m_stream << std::right << std::setw(coordinateWidth) << std::setprecision(coordinatePrecision) << fpos3d.y(); } else { m_stream << std::right << std::setw(coordinateWidth) << "N/A"; } break; case 'c': if (cell) { m_stream << std::right << std::setw(coordinateWidth) << std::setprecision(coordinatePrecision) << fpos3d.z(); } else { m_stream << std::right << std::setw(coordinateWidth) << "N/A"; } break; } // end switch // Prepare for next value. Push a space into the output stream if we are // not at the end of the line, or a newline if we are. m_stream << std::setw(1) << (it + 1 != end ? ' ' : '\n'); } // end spec char } // end for atom return m_stream.str(); } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/coordinateblockgenerator.h000066400000000000000000000066371506155467400243330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_COORDINATEBLOCKGENERATOR_H #define AVOGADRO_CORE_COORDINATEBLOCKGENERATOR_H #include "avogadrocoreexport.h" #include #include namespace Avogadro::Core { class Molecule; /** * @class CoordinateBlockGenerator coordinateblockgenerator.h * * @brief The CoordinateBlockGenerator class creates an aligned, formatted block * of atomic coordinates. * * See the setSpecification() documentation for details on specifying the block * format. */ class AVOGADROCORE_EXPORT CoordinateBlockGenerator { public: /** * Construct the default CoordinateBlockGenerator with an empty specification * and Angstrom distance units. */ CoordinateBlockGenerator() = default; /** * The molecule used as input. * @} */ void setMolecule(const Molecule* mol) { m_molecule = mol; } const Molecule* molecule() const { return m_molecule; } /** @} */ /** * The specification of the block format. * The characters in the specification string indicate the information needed * about each atom in the coordinate block. * - @c #: Atom index (one-based index) * - @c Z: Atomic number (e.g. "6" for carbon) * - @c L: Atomic label (e.g., "C1" for first carbon)") * - @c G: GAMESS-styled Atomic number (e.g. "6.0" for carbon) * - @c S: Element symbol (e.g. "C" for carbon) * - @c N: Element name (e.g. "Carbon") * - @c x: X cartesian coordinate * - @c y: Y cartesian coordinate * - @c z: Z cartesian coordinate * - @c a: 'a' lattice coordinate (unit cell required) * - @c b: 'b' lattice coordinate (unit cell required) * - @c c: 'c' lattice coordinate (unit cell required) * - @c 0: A literal "0". Useful for optimization flags. * - @c 1: A literal "1". Useful for optimization flags. * - @c _: A space character. Useful for alignment. * * For example, the specification string ~~~ __SZxyz110 ~~~ * will be replaced by a molecule-specific block of text similar to the * following: ~~~ C 6 1.126214 0.765886 0.000000 1 1 0 C 6 0.819345 -0.564955 0.000000 1 1 0 C 6 -0.598383 -0.795127 0.000000 1 1 0 C 6 -1.310706 0.370165 0.000000 1 1 0 S 16 -0.285330 1.757144 0.000000 1 1 0 H 1 2.130424 1.185837 0.000000 1 1 0 H 1 1.548377 -1.375303 0.000000 1 1 0 H 1 -1.033768 -1.794407 0.000000 1 1 0 H 1 -2.396173 0.450760 0.000000 1 1 0 ~~~ */ void setSpecification(const std::string& spec) { m_specification = spec; } std::string specification() const { return m_specification; } /** @} */ /** Distance unit used in the output. @{ */ enum DistanceUnit { Angstrom = 0, Bohr }; void setDistanceUnit(DistanceUnit unit) { m_distanceUnit = unit; } DistanceUnit distanceUnit() const { return m_distanceUnit; } /** @} */ /** * Generate and return the coordinate block. */ std::string generateCoordinateBlock(); private: const Molecule* m_molecule = nullptr; std::string m_specification; DistanceUnit m_distanceUnit = Angstrom; std::stringstream m_stream; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_COORDINATEBLOCKGENERATOR_H avogadrolibs-1.101.0/avogadro/core/coordinateset.h000066400000000000000000000057141506155467400221200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_COORDINATESET_H #define AVOGADRO_CORE_COORDINATESET_H #include "avogadrocore.h" #include #include namespace Avogadro::Core { /** * @class ArraySet coordinateset.h * @brief Base class for array type containers. * * This base class gives us the non-templated base class that stores all arrays * of data. You should use the base class as the container, and figure out its * type when using it. */ class ArraySet { public: ArraySet() : m_content(nullptr), m_data(nullptr) {} virtual ~ArraySet() { delete m_content; } /** @return true if the type of the array matches the input type. */ template bool isType(const T&) const { return m_content ? typeid(T) == m_content->type() : false; } /* template std::vector data() { if (m_data && isType(T)) return static_cast &>(*m_data); else return std::vector(); } */ protected: class PlaceHolder { public: virtual ~PlaceHolder() {} virtual const std::type_info& type() const = 0; virtual PlaceHolder* clone() const = 0; }; template class Holder : public PlaceHolder { public: Holder(const ValueType& value) : m_content(value) {} const std::type_info& type() const override { return typeid(ValueType); } PlaceHolder* clone() const override { return new Holder(m_content); } ValueType m_content; }; PlaceHolder* m_content; void* m_data; }; /** * @class CoordinateSet coordinateset.h * @brief Templated class for array type containers. * * This class gives us the derived templated class that stores arrays of data. * of data. This class should be used to store concrete arrays, and can be * cast to ArraySet when stored in generic containers. */ template class CoordinateSet : public ArraySet { public: CoordinateSet() { m_content = new Holder(T()); } ~CoordinateSet() override = default; // Properties void resize(Index _size) { m_coordinates.resize(_size); } Index size() const { return m_coordinates.size(); } std::vector& coordinates() { return m_coordinates; } const std::vector& coordinates() const { return m_coordinates; } // Inline operator methods. /** Returns the element at \index _index. */ T operator()(Index _index) const { return m_coordinates.at(_index); } T& operator[](Index _index) { return m_coordinates[_index]; } const T& operator[](Index _index) const { return m_coordinates[_index]; } private: std::vector m_coordinates; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_COORDINATESET_H avogadrolibs-1.101.0/avogadro/core/crystaltools.cpp000066400000000000000000000473461506155467400223610ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "crystaltools.h" #include "molecule.h" #include "unitcell.h" #include #include namespace Avogadro::Core { namespace { struct WrapAtomsToCellFunctor { const UnitCell& unitCell; WrapAtomsToCellFunctor(Molecule& molecule) : unitCell(*molecule.unitCell()) {} void operator()(Vector3& pos) { unitCell.wrapCartesian(pos, pos); } }; } // namespace bool CrystalTools::wrapAtomsToUnitCell(Molecule& molecule) { if (!molecule.unitCell()) return false; // remove any bonds first - otherwise they may wrap // across the unit cell strangely molecule.clearBonds(); std::for_each(molecule.atomPositions3d().begin(), molecule.atomPositions3d().end(), WrapAtomsToCellFunctor(molecule)); molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); return true; } bool CrystalTools::rotateToStandardOrientation(Molecule& molecule, Options opts) { if (!molecule.unitCell()) return false; const UnitCell& cell = *molecule.unitCell(); const Matrix3& before = cell.cellMatrix(); // Extract vector components: const Real& x1 = before(0, 0); const Real& y1 = before(1, 0); const Real& z1 = before(2, 0); const Real& x2 = before(0, 1); const Real& y2 = before(1, 1); const Real& z2 = before(2, 1); const Real& x3 = before(0, 2); const Real& y3 = before(1, 2); const Real& z3 = before(2, 2); // Cache some frequently used values: // Length of v1 const Real L1 = std::hypot(x1, y1, z1); // Squared norm of v1's yz projection const Real sqrdnorm1yz = y1 * y1 + z1 * z1; // Squared norm of v2's yz projection const Real sqrdnorm2yz = y2 * y2 + z2 * z2; // Determinant of v1 and v2's projections in yz plane const Real detv1v2yz = y2 * z1 - y1 * z2; // Scalar product of v1 and v2's projections in yz plane const Real dotv1v2yz = y1 * y2 + z1 * z2; // Used for denominators, since we want to check that they are // sufficiently far from 0 to keep things reasonable: Real denom; const Real DENOM_TOL = 1e-5; // Create target matrix, fill with zeros Matrix3 newMat(Matrix3::Zero()); // Set components of new v1: newMat(0, 0) = L1; // Set components of new v2: denom = L1; if (fabs(denom) < DENOM_TOL) return false; newMat(0, 1) = (x1 * x2 + y1 * y2 + z1 * z2) / denom; newMat(1, 1) = sqrt(x2 * x2 * sqrdnorm1yz + detv1v2yz * detv1v2yz - 2 * x1 * x2 * dotv1v2yz + x1 * x1 * sqrdnorm2yz) / denom; // Set components of new v3 newMat(0, 2) = (x1 * x3 + y1 * y3 + z1 * z3) / denom; denom = L1 * L1 * newMat(1, 1); if (fabs(denom) < DENOM_TOL) return false; newMat(1, 2) = (x1 * x1 * (y2 * y3 + z2 * z3) + x2 * (x3 * sqrdnorm1yz - x1 * (y1 * y3 + z1 * z3)) + detv1v2yz * (y3 * z1 - y1 * z3) - x1 * x3 * dotv1v2yz) / denom; denom = L1 * newMat(1, 1); if (fabs(denom) < DENOM_TOL) return false; // Numerator is determinant of original cell: newMat(2, 2) = before.determinant() / denom; return setCellMatrix(molecule, newMat, opts & TransformAtoms); } bool CrystalTools::setVolume(Molecule& molecule, Real newVolume, Options opts) { if (!molecule.unitCell()) return false; const UnitCell& cell = *molecule.unitCell(); const Real scaleFactor = std::pow(newVolume / cell.volume(), static_cast(1.0 / 3.0)); const Matrix3 newMatrix(cell.cellMatrix() * scaleFactor); return setCellMatrix(molecule, newMatrix, opts & TransformAtoms); } // A collection of fuzzy comparison operators used in the niggli reduction // algorithm: namespace { const double FUZZY_TOL(1e-5); template bool fuzzyLessThan(T v1, T v2, T prec = static_cast(FUZZY_TOL)) { return (v1 < (v2 - prec)); } template bool fuzzyGreaterThan(T v1, T v2, T prec = static_cast(FUZZY_TOL)) { return (v2 < (v1 - prec)); } template bool fuzzyEqual(T v1, T v2, T prec = static_cast(FUZZY_TOL)) { return (!(fuzzyLessThan(v1, v2, prec) || fuzzyGreaterThan(v1, v2, prec))); } template bool fuzzyNotEqual(T v1, T v2, T prec = static_cast(FUZZY_TOL)) { return (!(fuzzyEqual(v1, v2, prec))); } template bool fuzzyLessThanEq(T v1, T v2, T prec = static_cast(FUZZY_TOL)) { return (!fuzzyGreaterThan(v1, v2, prec)); } template bool fuzzyGreaterThanEq(T v1, T v2, T prec = static_cast(FUZZY_TOL)) { return (!lt(v1, v2, prec)); } template T niggliSign(T v) { // consider 0 to be positive return (v >= static_cast(0.)) ? static_cast(1.0) : static_cast(-1.0); } template T niggliRound(T v, T dec) { const T shift = std::pow(10.0, dec); const T shifted = v * shift; return std::floor(shifted + 0.5) / shift; } } // namespace bool CrystalTools::niggliReduce(Molecule& molecule, Options opts) { if (!molecule.unitCell()) return false; UnitCell& cell = *molecule.unitCell(); // Maximum number of iterations const unsigned int maxIterations = 1000; // Get cell parameters in storage units, convert deg->rad Real a = cell.a(); Real b = cell.b(); Real c = cell.c(); Real alpha = cell.alpha(); Real beta = cell.beta(); Real gamma = cell.gamma(); // Compute characteristic (step 0) Real A = a * a; Real B = b * b; Real C = c * c; Real xi = 2 * b * c * std::cos(alpha); Real eta = 2 * a * c * std::cos(beta); Real zeta = 2 * a * b * std::cos(gamma); // Return value. bool ret = false; // Comparison tolerance. Real tol = FUZZY_TOL * std::pow(a * b * c, static_cast(1.0 / 3.0)); // Initialize change of basis matrices: // // Although the reduction algorithm produces quantities directly // relatable to a,b,c,alpha,beta,gamma, we will calculate a change // of basis matrix to use instead, and discard A, B, C, xi, eta, // zeta. By multiplying the change of basis matrix against the // current cell matrix, we avoid the problem of handling the // orientation matrix already present in the cell. The inverse of // this matrix can also be used later to convert the atomic // positions. // tmpMat is used to build other matrices Matrix3 tmpMat; // Cache static matrices: // Swap x, y (Used in Step 1). Negatives ensure proper sign of final // determinant. tmpMat << 0, -1, 0, -1, 0, 0, 0, 0, -1; const Matrix3 C1(tmpMat); // Swap y, z (Used in Step 2). Negatives ensure proper sign of final // determinant tmpMat << -1, 0, 0, 0, 0, -1, 0, -1, 0; const Matrix3 C2(tmpMat); // For step 8: tmpMat << 1, 0, 1, 0, 1, 1, 0, 0, 1; const Matrix3 C8(tmpMat); // initial change of basis matrix tmpMat << 1, 0, 0, 0, 1, 0, 0, 0, 1; Matrix3 cob(tmpMat); // Enable debugging output here: /* #define NIGGLI_DEBUG(step) \ std::cout << iter << " " << step << " " << A << " " << B << " " << C \ << " " << xi << " " << eta << " " << zeta << std::endl; */ #define NIGGLI_DEBUG(step) // Allow Argument Dependent Lookup for swap using std::swap; // Perform iterative reduction: unsigned int iter; for (iter = 0; iter < maxIterations; ++iter) { // Step 1: if (fuzzyGreaterThan(A, B, tol) || (fuzzyEqual(A, B, tol) && fuzzyGreaterThan(std::fabs(xi), std::fabs(eta), tol))) { cob *= C1; swap(A, B); swap(xi, eta); NIGGLI_DEBUG(1); } // Step 2: if (fuzzyGreaterThan(B, C, tol) || (fuzzyEqual(B, C, tol) && fuzzyGreaterThan(std::fabs(eta), std::fabs(zeta), tol))) { cob *= C2; swap(B, C); swap(eta, zeta); NIGGLI_DEBUG(2); continue; } // Step 3: // Use exact comparisons in steps 3 and 4. if (xi * eta * zeta > 0) { // Update change of basis matrix: tmpMat << niggliSign(xi), 0, 0, 0, niggliSign(eta), 0, 0, 0, niggliSign(zeta); cob *= tmpMat; // Update characteristic xi = std::fabs(xi); eta = std::fabs(eta); zeta = std::fabs(zeta); NIGGLI_DEBUG(3); ++iter; } // Step 4: // Use exact comparisons for steps 3 and 4 else { // either step 3 or 4 should run // Update change of basis matrix: Real* p = nullptr; Real i = 1; Real j = 1; Real k = 1; if (xi > 0) { i = -1; } else if (!(xi < 0)) { p = &i; } if (eta > 0) { j = -1; } else if (!(eta < 0)) { p = &j; } if (zeta > 0) { k = -1; } else if (!(zeta < 0)) { p = &k; } if (i * j * k < 0) { if (!p) { // This was originally an error message displayed in a dialog: // Niggli-reduction failed. The input structure's lattice is confusing // the Niggli-reduction algorithm. Try making a small perturbation // (approx. 2 orders of magnitude smaller than the tolerance) to the // input lattices and try again. return false; } *p = -1; } tmpMat << i, 0, 0, 0, j, 0, 0, 0, k; cob *= tmpMat; // Update characteristic xi = -std::fabs(xi); eta = -std::fabs(eta); zeta = -std::fabs(zeta); NIGGLI_DEBUG(4); ++iter; } // Step 5: if (fuzzyGreaterThan(std::fabs(xi), B, tol) || (fuzzyEqual(xi, B, tol) && fuzzyLessThan(2 * eta, zeta, tol)) || (fuzzyEqual(xi, -B, tol) && fuzzyLessThan(zeta, Real(0), tol))) { Real signXi = niggliSign(xi); // Update change of basis matrix: tmpMat << 1, 0, 0, 0, 1, -signXi, 0, 0, 1; cob *= tmpMat; // Update characteristic C = B + C - xi * signXi; eta = eta - zeta * signXi; xi = xi - 2 * B * signXi; NIGGLI_DEBUG(5); continue; } // Step 6: if (fuzzyGreaterThan(std::fabs(eta), A, tol) || (fuzzyEqual(eta, A, tol) && fuzzyLessThan(2 * xi, zeta, tol)) || (fuzzyEqual(eta, -A, tol) && fuzzyLessThan(zeta, Real(0), tol))) { Real signEta = niggliSign(eta); // Update change of basis matrix: tmpMat << 1, 0, -signEta, 0, 1, 0, 0, 0, 1; cob *= tmpMat; // Update characteristic C = A + C - eta * signEta; xi = xi - zeta * signEta; eta = eta - 2 * A * signEta; NIGGLI_DEBUG(6); continue; } // Step 7: if (fuzzyGreaterThan(std::fabs(zeta), A, tol) || (fuzzyEqual(zeta, A, tol) && fuzzyLessThan(2 * xi, eta, tol)) || (fuzzyEqual(zeta, -A, tol) && fuzzyLessThan(eta, Real(0), tol))) { Real signZeta = niggliSign(zeta); // Update change of basis matrix: tmpMat << 1, -signZeta, 0, 0, 1, 0, 0, 0, 1; cob *= tmpMat; // Update characteristic B = A + B - zeta * signZeta; xi = xi - eta * signZeta; zeta = zeta - 2 * A * signZeta; NIGGLI_DEBUG(7); continue; } // Step 8: Real sumAllButC = A + B + xi + eta + zeta; if (fuzzyLessThan(sumAllButC, Real(0), tol) || (fuzzyEqual(sumAllButC, Real(0), tol) && fuzzyGreaterThan(2 * (A + eta) + zeta, Real(0), tol))) { // Update change of basis matrix: cob *= C8; // Update characteristic C = sumAllButC + C; xi = 2 * B + xi + zeta; eta = 2 * A + eta + zeta; NIGGLI_DEBUG(8); continue; } // Done! ret = true; break; } // No change if (iter == 0) return true; // Iteration limit exceeded: if (!ret) return false; // Update atoms if needed if (opts & TransformAtoms) { // Get fractional coordinates Array fcoords; if (!fractionalCoordinates(molecule, fcoords)) return false; // fix coordinates with COB matrix: const Matrix3 invCob(cob.inverse()); for (auto& fcoord : fcoords) { fcoord = invCob * fcoord; } // Update cell cell.setCellMatrix(cell.cellMatrix() * cob); // Reapply the fractional coordinates setFractionalCoordinates(molecule, fcoords); } else { // just update the matrix: cell.setCellMatrix(cell.cellMatrix() * cob); } return true; } bool CrystalTools::isNiggliReduced(const Molecule& molecule) { if (!molecule.unitCell()) return false; const UnitCell& cell = *molecule.unitCell(); const Real a = cell.a(); const Real b = cell.b(); const Real c = cell.c(); const Real alpha = cell.alpha(); const Real beta = cell.beta(); const Real gamma = cell.gamma(); const Real A = a * a; const Real B = b * b; const Real C = c * c; const Real xi = static_cast(2) * b * c * std::cos(alpha); const Real eta = static_cast(2) * a * c * std::cos(beta); const Real zeta = static_cast(2) * a * b * std::cos(gamma); const Real tol = FUZZY_TOL * ((a + b + c) * static_cast(1. / 3.)); // First check the Buerger conditions. Taken from: Gruber B.. Acta // Cryst. A. 1973;29(4):433-440. Available at: // http://scripts.iucr.org/cgi-bin/paper?S0567739473001063 // [Accessed December 15, 2010]. if (fuzzyGreaterThan(A, B, tol) || fuzzyGreaterThan(B, C, tol)) return false; if (fuzzyEqual(A, B, tol) && fuzzyGreaterThan(std::fabs(xi), std::fabs(eta), tol)) { return false; } if (fuzzyEqual(B, C, tol) && fuzzyGreaterThan(std::fabs(eta), std::fabs(zeta), tol)) { return false; } if (!(fuzzyGreaterThan(xi, static_cast(0.0), tol) && fuzzyGreaterThan(eta, static_cast(0.0), tol) && fuzzyGreaterThan(zeta, static_cast(0.0), tol)) && !(fuzzyLessThanEq(zeta, static_cast(0.0), tol) && fuzzyLessThanEq(zeta, static_cast(0.0), tol) && fuzzyLessThanEq(zeta, static_cast(0.0), tol))) { return false; } // Check against Niggli conditions (taken from Gruber 1973). The // logic of the second comparison is reversed from the paper to // simplify the algorithm. if (fuzzyEqual(xi, B, tol) && fuzzyGreaterThan(zeta, static_cast(2) * eta, tol)) { return false; } if (fuzzyEqual(eta, A, tol) && fuzzyGreaterThan(zeta, static_cast(2) * xi, tol)) { return false; } if (fuzzyEqual(zeta, A, tol) && fuzzyGreaterThan(eta, static_cast(2) * xi, tol)) { return false; } if (fuzzyEqual(xi, -B, tol) && fuzzyNotEqual(zeta, static_cast(0), tol)) { return false; } if (fuzzyEqual(eta, -A, tol) && fuzzyNotEqual(zeta, static_cast(0), tol)) { return false; } if (fuzzyEqual(zeta, -A, tol) && fuzzyNotEqual(eta, static_cast(0), tol)) { return false; } if (fuzzyEqual(xi + eta + zeta + A + B, static_cast(0), tol) && fuzzyGreaterThan(static_cast(2) * (A + eta) + zeta, static_cast(0), tol)) { return false; } // all good! return true; } bool CrystalTools::buildSupercell(Molecule& molecule, unsigned int a, unsigned int b, unsigned int c) { if (!molecule.unitCell()) return false; // Just a check. Hopefully this won't happen if (a == 0 || b == 0 || c == 0) { std::cerr << "Warning: in buildSupercell(), a, b, or c were set to zero." << "This function will not proceed. Returning false."; return false; } // Get the old vectors Vector3 oldA = molecule.unitCell()->aVector(); Vector3 oldB = molecule.unitCell()->bVector(); Vector3 oldC = molecule.unitCell()->cVector(); // Calculate new vectors Vector3 newA = oldA * a; Vector3 newB = oldB * b; Vector3 newC = oldC * c; // archive the old bond pairs and orders Array> bondPairs = molecule.bondPairs(); Array bondOrders = molecule.bondOrders(); // Add in the atoms to the new subcells of the supercell Index numAtoms = molecule.atomCount(); Array atoms = molecule.atomPositions3d(); Array atomicNums = molecule.atomicNumbers(); for (Index ind_a = 0; ind_a < a; ++ind_a) { for (Index ind_b = 0; ind_b < b; ++ind_b) { for (Index ind_c = 0; ind_c < c; ++ind_c) { // Skip over the subcell that already exists if (ind_a == 0 && ind_b == 0 && ind_c == 0) continue; // The positions of the new atoms are displacements of the old atoms Vector3 displacement = ind_a * oldA + ind_b * oldB + ind_c * oldC; for (Index i = 0; i < numAtoms; ++i) { Atom newAtom = molecule.addAtom(atomicNums.at(i)); newAtom.setPosition3d(atoms.at(i) + displacement); } } } } // now we need to add the bonds unsigned copies = molecule.atomCount() / numAtoms; // we loop through the original bonds to add copies for (Index i = 0; i < bondPairs.size(); ++i) { std::pair bond = bondPairs.at(i); for (unsigned j = 0; j < copies; ++j) { molecule.addBond(bond.first + j * numAtoms, bond.second + j * numAtoms, bondOrders.at(i)); } } // Now set the unit cell molecule.unitCell()->setAVector(newA); molecule.unitCell()->setBVector(newB); molecule.unitCell()->setCVector(newC); // We're done! return true; } namespace { struct TransformAtomsFunctor { TransformAtomsFunctor(const Matrix3& t) : transform(t) {} const Matrix3& transform; void operator()(Vector3& pos) { pos = transform * pos; } }; } // namespace bool CrystalTools::setCellMatrix(Molecule& molecule, const Matrix3& newCellColMatrix, Options opt) { if (opt & TransformAtoms && molecule.unitCell()) { const Matrix3 xform(newCellColMatrix * molecule.unitCell()->cellMatrix().inverse()); std::for_each(molecule.atomPositions3d().begin(), molecule.atomPositions3d().end(), TransformAtomsFunctor(xform)); } if (!UnitCell::isRegular(newCellColMatrix)) { std::cerr << __FUNCTION__ << " cell matrix is singular\n"; return false; } // only create a new unit cell if it doesn't exist if (!molecule.unitCell()) molecule.setUnitCell(new UnitCell); molecule.unitCell()->setCellMatrix(newCellColMatrix); return true; } namespace { struct FractionalCoordinatesFunctor { const UnitCell& unitCell; FractionalCoordinatesFunctor(const UnitCell& uc) : unitCell(uc) {} void operator()(Vector3& pos) { unitCell.toFractional(pos, pos); } }; } // namespace bool CrystalTools::fractionalCoordinates(const UnitCell& unitCell, const Array& cart, Array& frac) { if (&frac != &cart) // avoid self-copy... frac = cart; std::for_each(frac.begin(), frac.end(), FractionalCoordinatesFunctor(unitCell)); return true; } bool CrystalTools::fractionalCoordinates(const Molecule& molecule, Array& coords) { if (!molecule.unitCell()) return false; coords = molecule.atomPositions3d(); coords.resize(molecule.atomCount()); return fractionalCoordinates(*molecule.unitCell(), coords, coords); } namespace { struct SetFractionalCoordinatesFunctor { const UnitCell& unitCell; SetFractionalCoordinatesFunctor(const Molecule& molecule) : unitCell(*molecule.unitCell()) { } Vector3 operator()(const Vector3& pos) { return unitCell.toCartesian(pos); } }; } // namespace bool CrystalTools::setFractionalCoordinates(Molecule& molecule, const Array& coords) { if (!molecule.unitCell()) return false; if (coords.size() != molecule.atomCount()) return false; Array& output = molecule.atomPositions3d(); output.resize(coords.size()); std::transform(coords.begin(), coords.end(), output.begin(), SetFractionalCoordinatesFunctor(molecule)); return true; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/crystaltools.h000066400000000000000000000136441506155467400220200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_CRYSTALTOOLS_H #define AVOGADRO_CORE_CRYSTALTOOLS_H #include "avogadrocoreexport.h" #include "array.h" #include "avogadrocore.h" #include "matrix.h" #include "vector.h" namespace Avogadro::Core { class Molecule; class UnitCell; /** * @class CrystalTools crystaltools.h * @brief The CrystalTools class contains a collection of static functions * that perform common crystallographic operations on a Core::Molecule. */ class AVOGADROCORE_EXPORT CrystalTools { public: /** * @brief The Option enum provides bitwise option flags for the various * algorithms. */ enum Option { /** No options specified. */ None = 0x0, /** Transform atoms along with the unit cell. */ TransformAtoms = 0x1 }; using Options = int; /** * Adjust the atomic positions in @a molecule so that the fractional (lattice) * coordinates of all atoms are in the range [0, 1]. * @return True on success, false otherwise. */ static bool wrapAtomsToUnitCell(Molecule& molecule); /** * This function will rotate the input molecule so that the unit cell's 'a' * vector is aligned to the x-axis, and the 'b' axis is in the xy-plane. It * does not use trig functions or the cell parameters, since such * implementations are fragile and cannot handle negative cell angles. The * derivation of this algorithm can be found at * http://xtalopt.openmolecules.net/misc/rotateToStdOrientation.pdf. * * @param opts If TransformAtoms is specified, the atoms in @a molecule are * rotated along with the unit cell. * @return True on success, false otherwise. */ static bool rotateToStandardOrientation(Molecule& molecule, Options opts = None); /** * Isotropically scale the volume of the unit cell in @a molecule to @a * newVolume (in cubic angstrom). * @param opts If TransformAtoms is specified, the atoms in @a molecule are * adjusted so that their fractional (lattice) coordinates are preserved. * @return True on success, false on failure. */ static bool setVolume(Molecule& molecule, Real newVolume, Options opts = None); /** * Perform a Niggli reduction on @a molecule's unit cell. This produces a * canonical unit cell representation that strives to be as cubic as possible. * @note Implements the niggli reduction algorithm detailed in: * Grosse-Kunstleve RW, Sauter NK, Adams PD. Numerically stable * algorithms for the computation of reduced unit cells. Acta * Crystallographica Section A Foundations of * Crystallography. 2003;60(1):1-6. * @param opts If TransformAtoms is specified, the atom positions are modified * to ensure that the same extended atomic structure from the input is * represented by the output. * @return True on success, false on failure. */ static bool niggliReduce(Molecule& molecule, Options opts = None); /** * Return true if the unit cell in @a molecule is already Niggli-reduced. This * method checks the conditions listed in the paper Gruber B.. Acta Cryst. A. * 1973;29(4):433-440. */ static bool isNiggliReduced(const Molecule& mol); /** * Build a supercell by expanding upon the unit cell of @a molecule. It will * only return false if the molecule does not have a unit cell or if a, b, or * c is set to zero. * @param a The number of units along lattice vector a for the supercell * @param b The number of units along lattice vector b for the supercell * @param c The number of units along lattice vector c for the supercell * @return True on success, false on failure. */ static bool buildSupercell(Molecule& molecule, unsigned int a, unsigned int b, unsigned int c); /** * Set the unit cell in @a molecule to represent the real-space column-vector * unit cell description in @a newCellColMatrix. A unit cell is created if * needed. * @param opt If TransformAtoms is specified, the atoms in @a molecule are * adjusted so that their fractional (lattice) coordinates are preserved. This * option is ignored if the input molecule has no unit cell. * @return True on success, false otherwise. */ static bool setCellMatrix(Molecule& molecule, const Matrix3& newCellColMatrix, Options opt = None); /** * Populate the @a frac vector with the fractional representation of the * cartesian coordinates in @a cart, using @a unitCell to perform the * coordinate transformation. * @return True on success, false otherwise. */ static bool fractionalCoordinates(const UnitCell& unitCell, const Array& cart, Array& frac); /** * Populate the @a coords vector with the fractional coordinates of the atoms * in @a molecule, using the unit cell of @a molecule to perform the * coordinate transformation. Coordinates are ordered the same as the * Molecule::atomPositions3d() result. * @return True on success, false otherwise. */ static bool fractionalCoordinates(const Molecule& molecule, Array& coords); /** * Set the atomic positions of @a molecule to the fractional coordinates in * @a coords, using the unit cell of @a molecule to perform the coordinate * transformation. * @return */ static bool setFractionalCoordinates(Molecule& molecule, const Array& coords); private: CrystalTools(); // not implemented ~CrystalTools(); // not implemented }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_CRYSTALTOOLS_H avogadrolibs-1.101.0/avogadro/core/cube.cpp000066400000000000000000000316771506155467400205350ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cube.h" #include "molecule.h" #include "mutex.h" namespace Avogadro::Core { Cube::Cube() : m_data(0), m_min(0.0, 0.0, 0.0), m_max(0.0, 0.0, 0.0), m_spacing(0.0, 0.0, 0.0), m_points(0, 0, 0), m_minValue(0.0), m_maxValue(0.0), m_cubeType(None), m_lock(new Mutex) { } Cube::~Cube() { delete m_lock; m_lock = nullptr; } bool Cube::setLimits(const Vector3& min_, const Vector3& max_, const Vector3i& points) { // We can calculate all necessary properties and initialise our data Vector3 delta = max_ - min_; m_spacing = Vector3(delta.x() / (points.x() - 1), delta.y() / (points.y() - 1), delta.z() / (points.z() - 1)); m_min = min_; m_max = max_; m_points = points; m_data.resize(m_points.x() * m_points.y() * m_points.z()); return true; } bool Cube::setLimits(const Vector3& min_, const Vector3& max_, float spacing_) { Vector3 delta = max_ - min_; delta = delta / spacing_; return setLimits(min_, max_, delta.cast()); } bool Cube::setLimits(const Vector3& min_, const Vector3i& dim, float spacing_) { return setLimits(min_, dim, Vector3(spacing_, spacing_, spacing_)); } bool Cube::setLimits(const Vector3& min_, const Vector3i& dim, const Vector3& spacing_) { Vector3 max_ = Vector3(min_.x() + (dim.x() - 1) * spacing_[0], min_.y() + (dim.y() - 1) * spacing_[1], min_.z() + (dim.z() - 1) * spacing_[2]); m_min = min_; m_max = max_; m_points = dim; m_spacing = spacing_; m_data.resize(m_points.x() * m_points.y() * m_points.z()); return true; } bool Cube::setLimits(const Cube& cube) { m_min = cube.m_min; m_max = cube.m_max; m_points = cube.m_points; m_spacing = cube.m_spacing; m_data.resize(m_points.x() * m_points.y() * m_points.z()); return true; } bool Cube::setLimits(const Molecule& mol, float spacing_, float padding) { Index numAtoms = mol.atomCount(); Vector3 min_, max_; if (numAtoms) { Vector3 curPos = min_ = max_ = mol.atomPositions3d()[0]; for (Index i = 1; i < numAtoms; ++i) { curPos = mol.atomPositions3d()[i]; if (curPos.x() < min_.x()) min_.x() = curPos.x(); if (curPos.x() > max_.x()) max_.x() = curPos.x(); if (curPos.y() < min_.y()) min_.y() = curPos.y(); if (curPos.y() > max_.y()) max_.y() = curPos.y(); if (curPos.z() < min_.z()) min_.z() = curPos.z(); if (curPos.z() > max_.z()) max_.z() = curPos.z(); } } else { min_ = max_ = Vector3::Zero(); } // Now to take care of the padding term min_ += Vector3(-padding, -padding, -padding); max_ += Vector3(padding, padding, padding); return setLimits(min_, max_, spacing_); } std::vector* Cube::data() { return &m_data; } const std::vector* Cube::data() const { return &m_data; } bool Cube::setData(const std::vector& values) { if (!values.size()) return false; if (static_cast(values.size()) == m_points.x() * m_points.y() * m_points.z()) { m_data = values; // Now to update the minimum and maximum values m_minValue = m_maxValue = m_data[0]; for (float value : values) { if (value < m_minValue) m_minValue = value; else if (value > m_maxValue) m_maxValue = value; } return true; } else { return false; } } bool Cube::addData(const std::vector& values) { // Initialise the cube to zero if necessary if (!m_data.size()) m_data.resize(m_points.x() * m_points.y() * m_points.z()); if (values.size() != m_data.size() || !values.size()) return false; for (unsigned int i = 0; i < m_data.size(); i++) { m_data[i] += values[i]; if (m_data[i] < m_minValue) m_minValue = m_data[i]; else if (m_data[i] > m_maxValue) m_maxValue = m_data[i]; } return true; } unsigned int Cube::closestIndex(const Vector3& pos) const { int i, j, k; // Calculate how many steps each coordinate is along its axis i = int((pos.x() - m_min.x()) / m_spacing.x()); j = int((pos.y() - m_min.y()) / m_spacing.y()); k = int((pos.z() - m_min.z()) / m_spacing.z()); return i * m_points.y() * m_points.z() + j * m_points.z() + k; } Vector3i Cube::indexVector(const Vector3& pos) const { // Calculate how many steps each coordinate is along its axis int i, j, k; i = int((pos.x() - m_min.x()) / m_spacing.x()); j = int((pos.y() - m_min.y()) / m_spacing.y()); k = int((pos.z() - m_min.z()) / m_spacing.z()); return Vector3i(i, j, k); } Vector3 Cube::position(unsigned int index) const { int x, y, z; x = int(index / (m_points.y() * m_points.z())); y = int((index - (x * m_points.y() * m_points.z())) / m_points.z()); z = index % m_points.z(); return Vector3(x * m_spacing.x() + m_min.x(), y * m_spacing.y() + m_min.y(), z * m_spacing.z() + m_min.z()); } float Cube::value(int i, int j, int k) const { unsigned int index = i * m_points.y() * m_points.z() + j * m_points.z() + k; if (index < m_data.size()) return m_data[index]; else return 0.0; } std::array Cube::computeGradient(int i, int j, int k) const { int nx = m_points.x(); int ny = m_points.y(); int nz = m_points.z(); int dataIdx = (i * ny * nz) + (j * nz) + k; std::array, 3> x; std::array run; // X-direction if (i == 0) { x[0][0] = m_data[dataIdx + ny * nz]; x[0][1] = m_data[dataIdx]; run[0] = m_spacing.x(); } else if (i == (nx - 1)) { x[0][0] = m_data[dataIdx]; x[0][1] = m_data[dataIdx - ny * nz]; run[0] = m_spacing.x(); } else { x[0][0] = m_data[dataIdx + ny * nz]; x[0][1] = m_data[dataIdx - ny * nz]; run[0] = 2 * m_spacing.x(); } // Y-direction if (j == 0) { x[1][0] = m_data[dataIdx + nz]; x[1][1] = m_data[dataIdx]; run[1] = m_spacing.y(); } else if (j == (ny - 1)) { x[1][0] = m_data[dataIdx]; x[1][1] = m_data[dataIdx - nz]; run[1] = m_spacing.y(); } else { x[1][0] = m_data[dataIdx + nz]; x[1][1] = m_data[dataIdx - nz]; run[1] = 2 * m_spacing.y(); } // Z-direction if (k == 0) { x[2][0] = m_data[dataIdx + 1]; x[2][1] = m_data[dataIdx]; run[2] = m_spacing.z(); } else if (k == (nz - 1)) { x[2][0] = m_data[dataIdx]; x[2][1] = m_data[dataIdx - 1]; run[2] = m_spacing.z(); } else { x[2][0] = m_data[dataIdx + 1]; x[2][1] = m_data[dataIdx - 1]; run[2] = 2 * m_spacing.z(); } std::array ret; ret[0] = (x[0][1] - x[0][0]) / run[0]; ret[1] = (x[1][1] - x[1][0]) / run[1]; ret[2] = (x[2][1] - x[2][0]) / run[2]; return ret; } std::array, 8> Cube::getGradCube(int i, int j, int k) const { std::array, 8> grad; grad[0] = computeGradient(i, j, k); grad[1] = computeGradient(i + 1, j, k); grad[2] = computeGradient(i + 1, j + 1, k); grad[3] = computeGradient(i, j + 1, k); grad[4] = computeGradient(i, j, k + 1); grad[5] = computeGradient(i + 1, j, k + 1); grad[6] = computeGradient(i + 1, j + 1, k + 1); grad[7] = computeGradient(i, j + 1, k + 1); return grad; } std::array Cube::getValsCube(int i, int j, int k) const { std::array vals; vals[0] = getData(i, j, k); vals[1] = getData(i + 1, j, k); vals[2] = getData(i + 1, j + 1, k); vals[3] = getData(i, j + 1, k); vals[4] = getData(i, j, k + 1); vals[5] = getData(i + 1, j, k + 1); vals[6] = getData(i + 1, j + 1, k + 1); vals[7] = getData(i, j + 1, k + 1); return vals; } float Cube::getData(int i, int j, int k) const { int ny = m_points.y(); int nz = m_points.z(); return m_data[(i * ny * nz) + (j * nz) + k]; } std::array, 8> Cube::getPosCube(int i, int j, int k) const { std::array, 8> pos; float xpos = m_min.x() + (i * m_spacing.x()); float ypos = m_min.y() + (j * m_spacing.y()); float zpos = m_min.z() + (k * m_spacing.z()); pos[0][0] = xpos; pos[0][1] = ypos; pos[0][2] = zpos; pos[1][0] = xpos + m_spacing.x(); pos[1][1] = ypos; pos[1][2] = zpos; pos[2][0] = xpos + m_spacing.x(); pos[2][1] = ypos + m_spacing.y(); pos[2][2] = zpos; pos[3][0] = xpos; pos[3][1] = ypos + m_spacing.y(); pos[3][2] = zpos; pos[4][0] = xpos; pos[4][1] = ypos; pos[4][2] = zpos + m_spacing.z(); pos[5][0] = xpos + m_spacing.x(); pos[5][1] = ypos; pos[5][2] = zpos + m_spacing.z(); pos[6][0] = xpos + m_spacing.x(); pos[6][1] = ypos + m_spacing.y(); pos[6][2] = zpos + m_spacing.z(); pos[7][0] = xpos; pos[7][1] = ypos + m_spacing.y(); pos[7][2] = zpos + m_spacing.z(); return pos; } float Cube::value(const Vector3i& pos) const { unsigned int index = pos.x() * m_points.y() * m_points.z() + pos.y() * m_points.z() + pos.z(); if (index < m_data.size()) return m_data[index]; else return 6969.0; } float Cube::valuef(const Vector3f& pos) const { // This is a really expensive operation and so should be avoided // Interpolate the value at the supplied vector - trilinear interpolation... Vector3f delta = pos - m_min.cast(); // Find the integer low and high corners Vector3i lC(static_cast(delta.x() / m_spacing.x()), static_cast(delta.y() / m_spacing.y()), static_cast(delta.z() / m_spacing.z())); Vector3i hC(lC.x() + 1, lC.y() + 1, lC.z() + 1); // So there are six corners in total - work out the delta of the position // and the low corner const Vector3f lCf(lC.cast()); const Vector3f spacingf(m_spacing.cast()); Vector3f P((delta.x() - lCf.x() * spacingf.x()) / spacingf.x(), (delta.y() - lCf.y() * spacingf.y()) / spacingf.y(), (delta.z() - lCf.z() * spacingf.z()) / spacingf.z()); Vector3f dP = Vector3f(1.0f, 1.0f, 1.0f) - P; // Now calculate and return the interpolated value return static_cast( value(lC.x(), lC.y(), lC.z()) * dP.x() * dP.y() * dP.z() + value(hC.x(), lC.y(), lC.z()) * P.x() * dP.y() * dP.z() + value(lC.x(), hC.y(), lC.z()) * dP.x() * P.y() * dP.z() + value(lC.x(), lC.y(), hC.z()) * dP.x() * dP.y() * P.z() + value(hC.x(), lC.y(), hC.z()) * P.x() * dP.y() * P.z() + value(lC.x(), hC.y(), hC.z()) * dP.x() * P.y() * P.z() + value(hC.x(), hC.y(), lC.z()) * P.x() * P.y() * dP.z() + value(hC.x(), hC.y(), hC.z()) * P.x() * P.y() * P.z()); } float Cube::value(const Vector3& pos) const { // This is a really expensive operation and so should be avoided // Interpolate the value at the supplied vector - trilinear interpolation... Vector3 delta = pos - m_min; // Find the integer low and high corners Vector3i lC(static_cast(delta.x() / m_spacing.x()), static_cast(delta.y() / m_spacing.y()), static_cast(delta.z() / m_spacing.z())); Vector3i hC(lC.x() + 1, lC.y() + 1, lC.z() + 1); // So there are six corners in total - work out the delta of the position // and the low corner Vector3 P((delta.x() - lC.x() * m_spacing.x()) / m_spacing.x(), (delta.y() - lC.y() * m_spacing.y()) / m_spacing.y(), (delta.z() - lC.z() * m_spacing.z()) / m_spacing.z()); Vector3 dP = Vector3(1.0, 1.0, 1.0) - P; // Now calculate and return the interpolated value return value(lC.x(), lC.y(), lC.z()) * dP.x() * dP.y() * dP.z() + value(hC.x(), lC.y(), lC.z()) * P.x() * dP.y() * dP.z() + value(lC.x(), hC.y(), lC.z()) * dP.x() * P.y() * dP.z() + value(lC.x(), lC.y(), hC.z()) * dP.x() * dP.y() * P.z() + value(hC.x(), lC.y(), hC.z()) * P.x() * dP.y() * P.z() + value(lC.x(), hC.y(), hC.z()) * dP.x() * P.y() * P.z() + value(hC.x(), hC.y(), lC.z()) * P.x() * P.y() * dP.z() + value(hC.x(), hC.y(), hC.z()) * P.x() * P.y() * P.z(); } bool Cube::setValue(unsigned int i, unsigned int j, unsigned int k, float value_) { unsigned int index = i * m_points.y() * m_points.z() + j * m_points.z() + k; if (index >= m_data.size()) return false; m_data[index] = value_; if (value_ < m_minValue) m_minValue = value_; else if (value_ > m_maxValue) m_maxValue = value_; return true; } void Cube::fill(float value_) { std::fill(m_data.begin(), m_data.end(), value_); m_minValue = m_maxValue = value_; } bool Cube::fillStripe(unsigned int i, unsigned int j, unsigned int kfirst, unsigned int klast, float value_) { unsigned int stripeStartIndex = i * m_points.y() * m_points.z() + j * m_points.z(); unsigned int firstIndex = stripeStartIndex + kfirst; if (firstIndex >= m_data.size()) return false; unsigned int lastIndex = stripeStartIndex + klast; if (lastIndex >= m_data.size()) return false; std::fill(&m_data[firstIndex], &m_data[lastIndex + 1], value_); return true; } } // namespace Avogadro::Coreavogadrolibs-1.101.0/avogadro/core/cube.h000066400000000000000000000214511506155467400201670ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_CUBE_H #define AVOGADRO_CORE_CUBE_H #include "avogadrocoreexport.h" #include "vector.h" #include #include namespace Avogadro::Core { class Molecule; class Mutex; /** * @class Cube cube.h * @brief Provide a data structure for regularly spaced 3D grids. * @author Marcus D. Hanwell * @author Perminder Singh */ class AVOGADROCORE_EXPORT Cube { public: Cube(); ~Cube(); /** * \enum Type * Different Cube types relating to the data */ enum Type { VdW, SolventAccessible, SolventExcluded, ESP, ElectronDensity, SpinDensity, MO, FromFile, None }; /** * @return The minimum point in the cube. */ Vector3 min() const { return m_min; } /** * @return The maximum point in the cube. */ Vector3 max() const { return m_max; } /** * @return The spacing of the grid. */ Vector3 spacing() const { return m_spacing; } /** * @return The x, y and z dimensions of the cube. */ Vector3i dimensions() const { return m_points; } /** * Set the limits of the cube. * @param min The minimum point in the cube. * @param max The maximum point in the cube. * @param points The number of (integer) points in the cube. */ bool setLimits(const Vector3& min, const Vector3& max, const Vector3i& points); /** * Set the limits of the cube. * @param min The minimum point in the cube. * @param max The maximum point in the cube. * @param spacing The interval between points in the cube. */ bool setLimits(const Vector3& min, const Vector3& max, float spacing); /** * Set the limits of the cube. * @param min The minimum point in the cube. * @param dim The integer dimensions of the cube in x, y and z. * @param spacing The interval between points in the cube. */ bool setLimits(const Vector3& min, const Vector3i& dim, float spacing); /** * Set the limits of the cube. * @param min The minimum point in the cube. * @param dim The integer dimensions of the cube in x, y and z. * @param spacing The interval between points in the cube. */ bool setLimits(const Vector3& min, const Vector3i& dim, const Vector3& spacing); /** * Set the limits of the cube - copy the limits of an existing Cube. * @param cube Existing Cube to copy the limits from. */ bool setLimits(const Cube& cube); /** * Set the limits of the cube. * @param mol Molecule to take limits from * @param spacing The spacing of the regular grid * @param padding Padding around the molecule */ bool setLimits(const Molecule& mol, float spacing, float padding); /** * @return Pointer to the vector containing all the data in a one-dimensional * array. */ std::vector* data(); const std::vector* data() const; /** * Set the values in the cube to those passed in the vector. */ bool setData(const std::vector& values); /** * Adds the values in the cube to those passed in the vector. */ bool addData(const std::vector& values); /** * @return Const iterator to the beginning of a specific row in the cube data. */ std::vector::const_iterator getRowIter(int j, int k) const; /** * @return Index of the point closest to the position supplied. * @param pos Position to get closest index for. */ unsigned int closestIndex(const Vector3& pos) const; /** * @param pos Position to get closest index for. * @return The i, j, k index closest to the position supplied. */ Vector3i indexVector(const Vector3& pos) const; /** * @param index Index to be translated to a position. * @return Position of the given index. */ Vector3 position(unsigned int index) const; /** * This function is very quick as it just returns the value at the point. * @return Cube value at the integer point i, j, k. */ float value(int i, int j, int k) const; /** * This function is very quick as it just returns the value at the point. * @return Cube value at the integer point pos. */ float value(const Vector3i& pos) const; /** * This function uses trilinear interpolation to find the value at points * between those specified in the cube. * @return Cube value at the specified position. * @warning This function is quite computationally expensive and should be * avoided where possible. */ float valuef(const Vector3f& pos) const; /** * This function uses trilinear interpolation to find the value at points * between those specified in the cube. * @return Cube value at the specified position. * @warning This function is quite computationally expensive and should be * avoided where possible. */ float value(const Vector3& pos) const; /** * Sets the value at the specified point in the cube. * @param i x component of the position. * @param j y component of the position. * @param k z component of the position. * @param value Value at the specified position. */ bool setValue(unsigned int i, unsigned int j, unsigned int k, float value); /** * Sets the value at the specified index in the cube. * @param i 1-dimensional index of the point to set in the cube. */ bool setValue(unsigned int i, float value); /** * Sets all indices in the cube to the specified value. * @param value Value to fill the cube with. */ void fill(float value); /** * Sets all indices in a Z stripe of the cube to the specified value. * @param i x component of the position. * @param j y component of the position. * @param kfirst first z position to fill. * @param klast last z position to fill. * @param value Value to fill the stripe with. */ bool fillStripe(unsigned int i, unsigned int j, unsigned int kfirst, unsigned int klast, float value); /** * @return The minimum value at any point in the Cube. */ float minValue() const { return m_minValue; } /** * @return The maximum value at any point in the Cube. */ float maxValue() const { return m_maxValue; } void setName(const std::string& name_) { m_name = name_; } std::string name() const { return m_name; } void setCubeType(Type type) { m_cubeType = type; } Type cubeType() const { return m_cubeType; } /** * Provides locking. */ Mutex* lock() const { return m_lock; } /** * Compute the gradient at a specific point in the cube. * @param i x index * @param j y index * @param k z index * @return Gradient vector at the specified point. */ std::array computeGradient(int i, int j, int k) const; /** * Get the values of the eight corners of a cube defined by the indices * (i, j, k). * @param i x index * @param j y index * @param k z index * @return Array of values at the eight corners. */ std::array getValsCube(int i, int j, int k) const; /** * Get the gradients at the eight corners of the cube defined by the indices * (i, j, k). * @param i x index * @param j y index * @param k z index * @return Array of gradients at the eight corners. Each gradient is a 3D * vector. */ std::array, 8> getGradCube(int i, int j, int k) const; /** * Get the data value at the specified indices. * @param i x index * @param j y index * @param k z index * @return Value at the specified indices. */ float getData(int i, int j, int k) const; /** * Retrieves the positions of the eight corners of a cube at grid indices * (i, j, k). * * The indices (i, j, k) are converted to real-space positions (xpos, ypos, * zpos), mapping grid indices to physical coordinates. The method returns a * cube that spans one step in each of the x, y, and z directions, with step * sizes defined by `m_spacing`. * * @param i X-index. * @param j Y-index. * @param k Z-index. * @return A `std::array` of eight `(x, y, z)` coordinates representing the * cube's corners. */ std::array, 8> getPosCube(int i, int j, int k) const; protected: std::vector m_data; Vector3 m_min, m_max, m_spacing; Vector3i m_points; float m_minValue, m_maxValue; std::string m_name; Type m_cubeType; Mutex* m_lock; }; inline bool Cube::setValue(unsigned int i, float value_) { if (i < m_data.size()) { m_data[i] = value_; if (value_ > m_maxValue) m_maxValue = value_; if (value_ < m_minValue) m_minValue = value_; return true; } else return false; } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_CUBE_H avogadrolibs-1.101.0/avogadro/core/dihedraliterator.cpp000066400000000000000000000130421506155467400231270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "dihedraliterator.h" #include #include namespace Avogadro::Core { DihedralIterator::DihedralIterator(const Molecule* mol) : m_i(MaxIndex), m_jk(0), m_l(MaxIndex), m_mol(mol) { m_current = std::make_tuple(MaxIndex, MaxIndex, MaxIndex, MaxIndex); } Dihedral DihedralIterator::begin() { // all the logic is in the operator++ return ++(*this); } Dihedral DihedralIterator::operator++() { // impossible to have a dihedral, so quit if (m_mol == nullptr || m_mol->atomCount() < 4 || m_mol->bondCount() < 3) return std::make_tuple(MaxIndex, MaxIndex, MaxIndex, MaxIndex); Graph graph = m_mol->graph(); Index count = m_mol->bondCount(); // true if we have a valid current state // (i.e. false at the start) bool valid = (m_i != MaxIndex && m_l != MaxIndex); // m_jk is the current bond (i.e. index into the bonds) // m_i and m_l are instead index into the neighbors of m_jk // (i.e., m_i is the index into the neighbors of the first atom of the bond) // and m_l is the index into the neighbors of the second atom of the bond Index iIndex = MaxIndex; Index lIndex = MaxIndex; // if we don't have a valid state, try to find an initial dihedral if (!valid) { for (Index i = 0; i < count; ++i) { const Bond bond = m_mol->bond(i); const auto& neighbors1 = graph.neighbors(bond.atom1().index()); const auto& neighbors2 = graph.neighbors(bond.atom2().index()); if (neighbors1.size() < 1 || neighbors2.size() < 1) continue; // need to have at least one neighbor m_jk = i; // make sure that m_i doesn't point to atom2 m_i = 0; if (neighbors1[m_i] == bond.atom2().index()) { // try to increment m_i if (m_i + 1 < neighbors1.size()) { ++m_i; } else { continue; // this central bond doesn't work } } m_l = 0; // make sure that m_l doesn't point to atom1 if (neighbors2[m_l] == bond.atom1().index()) { // try to increment m_l if (m_l + 1 < neighbors2.size()) { ++m_l; } else { continue; // this central bond doesn't work } } iIndex = neighbors1[m_i]; lIndex = neighbors2[m_l]; valid = true; break; } } else { // we have a valid state, try to find the next dihedral const Bond bond = m_mol->bond(m_jk); const auto& neighbors1 = graph.neighbors(bond.atom1().index()); const auto& neighbors2 = graph.neighbors(bond.atom2().index()); // first check if we can increment m_l while (m_l + 1 < neighbors2.size()) { ++m_l; // make sure that m_l doesn't point to atom1 if (neighbors2[m_l] == bond.atom1().index()) { continue; // increment it again } iIndex = neighbors1[m_i]; lIndex = neighbors2[m_l]; m_current = std::make_tuple(iIndex, bond.atom1().index(), bond.atom2().index(), lIndex); return m_current; } // we can try to increment m_i while (m_i + 1 < neighbors1.size()) { ++m_i; // make sure that m_i doesn't point to atom2 if (neighbors1[m_i] == bond.atom2().index()) { // try to increment m_i again continue; } // reset m_l and make sure it doesn't point to atom1 m_l = 0; if (neighbors2[m_l] == bond.atom1().index()) { // try to increment m_l if (m_l + 1 < neighbors2.size()) { ++m_l; } else { continue; // this combination doesn't work } } iIndex = neighbors1[m_i]; lIndex = neighbors2[m_l]; m_current = std::make_tuple(iIndex, bond.atom1().index(), bond.atom2().index(), lIndex); return m_current; } // okay, try to increment m_jk and reset m_i and m_l valid = false; for (Index i = m_jk + 1; i < count; ++i) { const Bond bondI = m_mol->bond(i); const auto& newNeighbors1 = graph.neighbors(bondI.atom1().index()); const auto& newNeighbors2 = graph.neighbors(bondI.atom2().index()); if (newNeighbors1.size() < 1 || newNeighbors2.size() < 1) continue; // need to have at least one neighbor m_jk = i; // make sure that m_i doesn't point to atom2 m_i = 0; if (newNeighbors1[m_i] == bondI.atom2().index()) { // try to increment m_i if (m_i + 1 < newNeighbors1.size()) { ++m_i; } else { continue; // this central bond doesn't work } } m_l = 0; // make sure that m_l doesn't point to atom1 if (newNeighbors2[m_l] == bondI.atom1().index()) { // try to increment m_l if (m_l + 1 < newNeighbors2.size()) { ++m_l; } else { continue; // this central bond doesn't work } } iIndex = newNeighbors1[m_i]; lIndex = newNeighbors2[m_l]; valid = true; break; } } // did we find anything? if (valid) { const Bond bond = m_mol->bond(m_jk); m_current = std::make_tuple(iIndex, bond.atom1().index(), bond.atom2().index(), lIndex); return m_current; } else return std::make_tuple(MaxIndex, MaxIndex, MaxIndex, MaxIndex); } // end ++ operator } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/dihedraliterator.h000066400000000000000000000022201506155467400225700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_DIHEDRALITERATOR_H #define AVOGADRO_CORE_DIHEDRALITERATOR_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include namespace Avogadro::Core { class Molecule; using Dihedral = std::tuple; class AVOGADROCORE_EXPORT DihedralIterator { public: /** * Constructor. */ DihedralIterator(const Molecule* mol); ~DihedralIterator() = default; Dihedral* operator*() { return &m_current; } Dihedral begin(); Dihedral end() const { return std::make_tuple(MaxIndex, MaxIndex, MaxIndex, MaxIndex); } Dihedral operator++(); bool operator!=(const DihedralIterator& other) { return m_current != other.m_current; } private: Dihedral m_current; Index m_i, m_jk, m_l; const Molecule* m_mol; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_DIHEDRALITERATOR_H avogadrolibs-1.101.0/avogadro/core/elementdata.h000066400000000000000000000250601506155467400215340ustar00rootroot00000000000000#ifndef AVOGADRO_CORE_ELEMENTS_DATA #define AVOGADRO_CORE_ELEMENTS_DATA #include "elements.h" namespace Avogadro::Core { const char* const element_symbols[element_count] = { "Xx", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og" }; const char* const element_names[element_count] = { "Dummy", "Hydrogen", "Helium", "Lithium", "Beryllium", "Boron", "Carbon", "Nitrogen", "Oxygen", "Fluorine", "Neon", "Sodium", "Magnesium", "Aluminium", "Silicon", "Phosphorus", "Sulfur", "Chlorine", "Argon", "Potassium", "Calcium", "Scandium", "Titanium", "Vanadium", "Chromium", "Manganese", "Iron", "Cobalt", "Nickel", "Copper", "Zinc", "Gallium", "Germanium", "Arsenic", "Selenium", "Bromine", "Krypton", "Rubidium", "Strontium", "Yttrium", "Zirconium", "Niobium", "Molybdenum", "Technetium", "Ruthenium", "Rhodium", "Palladium", "Silver", "Cadmium", "Indium", "Tin", "Antimony", "Tellurium", "Iodine", "Xenon", "Caesium", "Barium", "Lanthanum", "Cerium", "Praseodymium", "Neodymium", "Promethium", "Samarium", "Europium", "Gadolinium", "Terbium", "Dysprosium", "Holmium", "Erbium", "Thulium", "Ytterbium", "Lutetium", "Hafnium", "Tantalum", "Tungsten", "Rhenium", "Osmium", "Iridium", "Platinum", "Gold", "Mercury", "Thallium", "Lead", "Bismuth", "Polonium", "Astatine", "Radon", "Francium", "Radium", "Actinium", "Thorium", "Protactinium", "Uranium", "Neptunium", "Plutonium", "Americium", "Curium", "Berkelium", "Californium", "Einsteinium", "Fermium", "Mendelevium", "Nobelium", "Lawrencium", "Rutherfordium", "Dubnium", "Seaborgium", "Bohrium", "Hassium", "Meitnerium", "Darmstadtium", "Roentgenium", "Copernicium", "Nihonium", "Flerovium", "Moscovium", "Livermorium", "Tennessine", "Oganesson" }; const double element_masses[element_count] = { // from IUPAC (2021 set) https://doi.org/10.1515/pac-2019-0603 // https://iupac.org/what-we-do/periodic-table-of-elements/ // https://iupac.qmul.ac.uk/AtWt/ // For Ar and Pb which are now ranges, we use the "abridged value" 0, 1.00784, 4.002602, 6.94, 9.012183, 10.81, 12.011, 14.007, 15.999, 18.998403, 20.1797, 22.989769, 24.305, 26.981538, 28.085, 30.973762, 32.06, 35.45, 39.94, 39.0983, 40.078, 44.955908, 47.867, 50.9415, 51.9961, 54.938043, 55.845, 58.933194, 58.6934, 63.546, 65.38, 69.723, 72.630, 74.921595, 78.971, 79.904, 83.798, 85.4678, 87.62, 88.90584, 91.224, 92.90637, 95.95, 97, 101.07, 102.90549, 106.42, 107.8682, 112.414, 114.818, 118.710, 121.760, 127.60, 126.90447, 131.293, 132.90447, 137.327, 138.90547, 140.116, 140.90766, 144.242, 145, 150.36, 151.964, 157.25, 158.92535, 162.500, 164.93033, 167.259, 168.93422, 173.045, 174.9668, 178.486, 180.94788, 183.84, 186.207, 190.23, 192.217, 195.084, 196.966570, 200.592, 204.38, 207.2, 208.9804, 209, 210, 222, 223, 226, 227, 232.0377, 231.0358, 238.02891, 237, 244, 243, 247, 247, 251, 252, 257, 258, 259, 262, 267, 270, 269, 270, 270, 278, 281, 281, 285, 286, 289, 289, 293, 293, 294 }; const unsigned char valence_electrons[element_count] = { // some of these are debatable, but are intended as a reasonable start 0, 1, 0, // He 1, 2, 3, 4, 5, 6, 7, 8, // Ne 1, 2, 3, 4, 5, 6, 7, 8, // Ar 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, // Kr 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, // Xe 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; const double element_VDW[element_count] = { // From Alvarez doi: 10.1039/C3DT50599E // Dalton Trans., 2013,42, 8617-8636 // Dummy, 1st row 0.69, 1.2, 1.43, // 2nd row (Li..Ne) 2.12, 1.98, 1.91, 1.77, 1.66, 1.50, 1.46, 1.58, // 3rd row (Na .. Ar) 2.50, 2.51, 2.25, 2.19, 1.90, 1.89, 1.82, 1.83, // 4th row (K, Ca) 2.73, 2.62, // 1st row TM (Sc.. Zn) 2.58, 2.46, 2.42, 2.45, 2.45, 2.44, 2.40, 2.40, 2.38, 2.39, // 4th row p-block (Ga .. Kr) 2.32, 2.29, 1.88, 1.82, 1.86, 2.25, // 5th row Rb, Sr 3.21, 2.84, // 2nd row TM (Y .. Cd) 2.75, 2.52, 2.56, 2.45, 2.44, 2.46, 2.44, 2.15, 2.53, 2.49, // 5th row p-block (Sn .. Xe) 2.43, 2.42, 2.47, 1.99, 2.04, 2.06, // 6th row Cs, Ba 3.48, 3.03, // Lanthanides (La..Gd) 2.98, 2.88, 2.92, 2.95, 2.90, 2.87, 2.83, // Lanthanides (Tb..Yb) 2.79, 2.87, 2.81, 2.83, 2.79, 2.80, // 3rd row TM (Lu..Hg) 2.74, 2.63, 2.53, 2.57, 2.49, 2.48, 2.41, 2.29, 2.32, 2.45, // 6th row p-block (Tl.. Bi) // 2.5 is a default here 2.47, 2.60, 2.54, 2.5, 2.5, 2.5, // 7th row // 2.5 is a default here 2.5, 2.5, // Actinides 2.8, 2.93, 2.88, 2.71, 2.82, 2.81, 2.83, 3.05, 3.38, 3.05, 3., 3., 3., 3., // Trans-actinides 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., // 7th row p-block 3., 3., 3., 3., 3., 3., 3., }; const double element_covalent[element_count] = { // From Pyykko doi: 10.1002/chem.200800987 // Dummy, 1st row 0.18, 0.32, 0.46, // 2nd row 1.33, 1.02, 0.85, 0.75, 0.71, 0.63, 0.64, 0.67, // 3rd row 1.55, 1.39, 1.26, 1.16, 1.11, 1.03, 0.99, 0.96, // 4th row K, Ca 1.96, 1.71, // 1st row TM (Sc.. Zn) 1.48, 1.36, 1.34, 1.22, 1.19, 1.16, 1.11, 1.10, 1.12, 1.18, // 4th row p-block (Ga..Kr) 1.24, 1.21, 1.21, 1.16, 1.14, 1.17, // 5th row Rb, Sr 2.10, 1.85, // 2nd row TM (Y..Cd) 1.63, 1.54, 1.47, 1.38, 1.28, 1.25, 1.25, 1.20, 1.28, 1.36, // 5th row p-block (In..Xe) 1.42, 1.40, 1.40, 1.36, 1.33, 1.31, // 6th row Cs, Ba 2.32, 1.96, // Lanthanides La..Gd 1.80, 1.63, 1.76, 1.74, 1.73, 1.72, 1.68, // Lanthanides Tb..Yb 1.69, 1.68, 1.67, 1.66, 1.65, 1.64, 1.70, // 3rd row TM (Lu..Hg) 1.62, 1.52, 1.46, 1.37, 1.31, 1.29, 1.22, 1.23, 1.24, 1.33, // 6th row p-block (Tl..Rn) 1.44, 1.44, 1.51, 1.45, 1.47, 1.42, // 7th row Fr, Ra 2.23, 2.01, // Actinides (Ac.. Am) 1.86, 1.75, 1.69, 1.70, 1.71, 1.72, 1.66, // Actinides (Cm..No) 1.66, 1.68, 1.68, 1.65, 1.67, 1.73, 1.76, // Trans-actinides 1.61, 1.57, 1.49, 1.43, 1.41, 1.34, 1.29, 1.28, 1.21, 1.22, 1.36, 1.43, 1.62, 1.75, 1.65, 1.57 }; const unsigned char element_color[element_count][3] = { // See, for example http://jmol.sourceforge.net/jscolors/index.en.html // Changes - H is not completely white to add contrast on light backgrounds // - C is slightly darker (i.e. 50% gray - consistent with Avo1) // - F is bluer to add contrast with Cl (e.g. CFC compounds) // # Du # H # He { 17, 127, 178 }, { 240, 240, 240 }, { 217, 255, 255 }, { 204, 128, 255 }, // # Be, B, C, N { 194, 255, 0 }, { 255, 181, 181 }, { 127, 127, 127 }, { 48, 80, 255 }, // # O, F, Ne, Na { 255, 13, 13 }, { 178, 255, 255 }, { 178, 227, 245 }, { 171, 91, 242 }, // # Mg { 138, 255, 0 }, { 191, 166, 166 }, { 240, 200, 160 }, { 255, 128, 0 }, // # S { 255, 255, 48 }, { 31, 240, 31 }, { 128, 209, 227 }, { 143, 64, 212 }, // # Ca { 61, 255, 0 }, { 230, 230, 230 }, { 191, 194, 199 }, { 166, 166, 171 }, // # Cr { 138, 153, 199 }, { 156, 122, 199 }, { 224, 102, 51 }, { 240, 144, 160 }, // # Ni { 80, 208, 80 }, { 200, 128, 51 }, { 125, 128, 176 }, { 194, 143, 143 }, // # Ge { 102, 143, 143 }, { 189, 128, 227 }, { 255, 161, 0 }, { 166, 41, 41 }, // # Kr { 92, 184, 209 }, { 112, 46, 176 }, { 0, 255, 0 }, { 148, 255, 255 }, // # Zr { 148, 224, 224 }, { 115, 194, 201 }, { 84, 181, 181 }, { 59, 158, 158 }, // # Ru { 36, 143, 143 }, { 10, 125, 140 }, { 0, 105, 133 }, { 192, 192, 192 }, // # Cd { 255, 217, 143 }, { 166, 117, 115 }, { 102, 128, 128 }, { 158, 99, 181 }, // # Te { 211, 122, 0 }, { 148, 0, 148 }, { 66, 158, 176 }, { 87, 23, 143 }, // # Ba { 0, 201, 0 }, { 112, 212, 255 }, { 255, 255, 199 }, { 217, 255, 199 }, // # Nd { 199, 255, 199 }, { 163, 255, 199 }, { 143, 255, 199 }, { 97, 255, 199 }, // # Gd { 69, 255, 199 }, { 48, 255, 199 }, { 31, 255, 199 }, { 0, 255, 156 }, // # Er { 0, 230, 117 }, { 0, 212, 82 }, { 0, 191, 56 }, { 0, 171, 36 }, // # Hf { 77, 194, 255 }, { 77, 166, 255 }, { 33, 148, 214 }, { 38, 102, 150 }, // # Os { 38, 102, 150 }, { 23, 84, 135 }, { 208, 208, 224 }, { 255, 209, 35 }, // # Hg { 184, 194, 208 }, { 166, 84, 77 }, { 87, 89, 97 }, { 158, 79, 181 }, // # Po { 171, 92, 0 }, { 117, 79, 69 }, { 66, 130, 150 }, { 66, 0, 102 }, { 0, 124, 0 }, { 112, 170, 249 }, { 0, 186, 255 }, { 0, 160, 255 }, { 0, 142, 255 }, { 0, 127, 255 }, { 0, 107, 255 }, { 84, 91, 242 }, { 119, 91, 226 }, { 137, 79, 226 }, { 160, 53, 211 }, { 178, 30, 211 }, { 178, 30, 186 }, { 178, 12, 165 }, { 188, 12, 135 }, { 198, 0, 102 }, { 204, 0, 89 }, { 209, 0, 79 }, { 216, 0, 68 }, { 224, 0, 56 }, { 229, 0, 45 }, { 232, 0, 38 }, { 234, 0, 35 }, { 237, 0, 33 }, { 239, 0, 30 }, { 242, 0, 28 }, { 244, 0, 25 }, { 247, 0, 22 }, { 249, 0, 20 }, { 252, 0, 17 }, { 255, 0, 15 } }; } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/elements.cpp000066400000000000000000000156621506155467400214270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "elements.h" #include "avogadrocore.h" #include "elementdata.h" #include "utilities.h" #include #include #include using Avogadro::Core::isCustomElement; namespace Avogadro::Core { // Handle custom element identifiers: namespace { const static std::string CustomElementSymbolPrefix = "X"; const static std::string CustomElementNamePrefix = "CustomElement_"; static std::vector CustomElementSymbols; static std::vector CustomElementNames; // Match carbon's radii static double CustomElementCovalentRadius = element_covalent[6]; static double CustomElementVDWRadius = element_VDW[6]; inline std::string encodeCustomElement(unsigned char atomicNumber) { std::string result; if (isCustomElement(atomicNumber)) { result.resize(2); unsigned char index = atomicNumber - Avogadro::CustomElementMin; result[0] = 'a' + static_cast(index / 26); result[1] = 'a' + static_cast(index % 26); } return result; } inline unsigned char decodeCustomElement(const std::string& str) { if (str.size() == 2) { if (str[0] >= 'a' && str[0] <= 'z' && str[1] >= 'a' && str[1] <= 'z') { return CustomElementMin + static_cast(str[0] - 'a') * 26 + static_cast(str[1] - 'a'); } } return Avogadro::InvalidElement; } inline unsigned char interpretCustomElementName(const std::string& name) { if (startsWith(name, CustomElementNamePrefix)) { const std::string number(name.substr(CustomElementNamePrefix.size())); return decodeCustomElement(number); } return InvalidElement; } inline std::string createCustomElementName(unsigned char atomicNumber) { return CustomElementNamePrefix + encodeCustomElement(atomicNumber); } inline const char* customElementName(unsigned char atomicNumber) { return CustomElementNames[atomicNumber - CustomElementMin].c_str(); } inline unsigned char interpretCustomElementSymbol(const std::string& symbol) { if (symbol.size() == 3) return decodeCustomElement(symbol.substr(1)); return InvalidElement; } inline std::string createCustomElementSymbol(unsigned char atomicNumber) { return CustomElementSymbolPrefix + encodeCustomElement(atomicNumber); } inline const char* customElementSymbol(unsigned char atomicNumber) { return CustomElementSymbols[atomicNumber - CustomElementMin].c_str(); } inline const unsigned char* customElementColor(unsigned char atomicNumber) { return Core::element_color[atomicNumber % element_count]; } // Initialize the static lookup tables. class InitializeCustomElementTables { public: InitializeCustomElementTables() { CustomElementSymbols.resize(CustomElementCount); CustomElementNames.resize(CustomElementCount); std::string suffix; for (unsigned char i = CustomElementMin; i <= CustomElementMax; ++i) { suffix = encodeCustomElement(i); CustomElementSymbols[i - CustomElementMin] = CustomElementSymbolPrefix + suffix; CustomElementNames[i - CustomElementMin] = CustomElementNamePrefix + suffix; } } } CustomElementTableInitializer; } // namespace unsigned char Elements::elementCount() { return element_count; } unsigned char Elements::atomicNumberFromName(const std::string& name) { for (unsigned char i = 0; i < element_count; ++i) if (name == element_names[i]) return i; return interpretCustomElementName(name); } unsigned char Elements::atomicNumberFromSymbol(const std::string& symbol) { if (symbol.length() == 1) { switch (symbol[0]) { case 'H': return 1; case 'B': return 5; case 'C': return 6; case 'N': return 7; case 'O': return 8; case 'F': return 9; case 'P': return 15; case 'S': return 16; case 'K': return 19; case 'V': return 23; case 'Y': return 39; case 'I': return 53; case 'W': return 74; case 'U': return 92; default: return InvalidElement; } } else { for (unsigned char i = 0; i < element_count; ++i) if (symbol == element_symbols[i]) return i; return interpretCustomElementSymbol(symbol); } } unsigned char Elements::guessAtomicNumber(const std::string& inputStr) { std::string str(trimmed(inputStr)); if (str.empty()) return InvalidElement; // atomic number? bool ok; int atomicNumberInt = lexicalCast(str, ok); if (ok) return static_cast(atomicNumberInt); // Format string as text std::transform(str.begin(), str.end(), str.begin(), tolower); str[0] = static_cast(toupper(static_cast(str[0]))); int length = str.size(); unsigned char atomicNumber = InvalidElement; while (length > 0) { if (length > 3) atomicNumber = atomicNumberFromName(str.substr(0, length)); else atomicNumber = atomicNumberFromSymbol(str.substr(0, length)); if (atomicNumber != InvalidElement) break; length--; } return atomicNumber; } const char* Elements::name(unsigned char atomicNumber) { if (atomicNumber < element_count) return element_names[atomicNumber]; else if (isCustomElement(atomicNumber)) return customElementName(atomicNumber); else return element_names[0]; } const char* Elements::symbol(unsigned char atomicNumber) { if (atomicNumber < element_count) return element_symbols[atomicNumber]; else if (isCustomElement(atomicNumber)) return customElementSymbol(atomicNumber); else return element_symbols[0]; } double Elements::mass(unsigned char atomicNumber) { if (atomicNumber < element_count) return element_masses[atomicNumber]; else return element_masses[0]; } double Elements::radiusVDW(unsigned char atomicNumber) { if (atomicNumber < element_count) return element_VDW[atomicNumber]; else if (isCustomElement(atomicNumber)) return CustomElementVDWRadius; else return element_VDW[0]; } double Elements::radiusCovalent(unsigned char atomicNumber) { if (atomicNumber < element_count) return element_covalent[atomicNumber]; else if (isCustomElement(atomicNumber)) return CustomElementCovalentRadius; else return element_covalent[0]; } const unsigned char* Elements::color(unsigned char atomicNumber) { if (atomicNumber < element_count) return element_color[atomicNumber]; else if (isCustomElement(atomicNumber)) return customElementColor(atomicNumber); else return element_color[0]; } unsigned char Elements::valenceElectrons(unsigned char atomicNumber) { if (atomicNumber < element_count) return valence_electrons[atomicNumber]; else return valence_electrons[0]; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/elements.h000066400000000000000000000074571506155467400210770ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_ELEMENTS_H #define AVOGADRO_CORE_ELEMENTS_H #include "avogadrocoreexport.h" #include namespace Avogadro::Core { constexpr unsigned char element_count = 119; //!< from 0 to 118 /** * @class Elements elements.h * @brief The Elements class stores basic data about chemical elements. * * The elements class gives a simple interface to basic data about chemical * elements. The data is automatically generated from the Blue Obelisk data * repository. * * The atomic numbers between the symbolic constants CustomElementMin and * CustomElementMax are used to represent non-elemental entities, such as * particles or structures units from MD simulations. Custom elements names and * symbols are returned as name="CustomElement_aa" and symbol="Xaa", where 'aa' * is some combination of lowercase letters that is unique to the particular * custom element atomic number. For all custom elements, the radii will match * Carbon, the color is random (but consistent), and the mass is zero. */ class AVOGADROCORE_EXPORT Elements { public: Elements() = default; ~Elements() = default; /** @return the number of elements in the database. */ static unsigned char elementCount(); /** * @return the atomic number from the supplied element @p name. If the name is * not recognised then Avogadro::InvalidElement will be returned. 0 represents * the dummy atom ("Dummy"). * @note The input string is expected to be lowercase with the first letter * capitalized. */ static unsigned char atomicNumberFromName(const std::string& name); /** * @return the atomic number from the supplied @p symbol. If the symbol is not * recognised then Avogadro::InvalidElement will be returned. 0 represents the * dummy atom ("Xx"). * @note The input string is expected to be lowercase with the first letter * capitalized. */ static unsigned char atomicNumberFromSymbol(const std::string& symbol); /** * Given a string @p str, attempt to identify an element symbol, name, or * atomic number. This method is slower and less reliable than the * atomicNumberFrom*() methods, and is only intended for making an initial * guess of user input. * @return the atomic number that best matches the string, or InvalidElement * if no match can be made. */ static unsigned char guessAtomicNumber(const std::string& str); /** @return the name of the element with the supplied @p atomicNumber. */ static const char* name(unsigned char atomicNumber); /** @return the symbol of the element with the supplied @p atomicNumber. */ static const char* symbol(unsigned char atomicNumber); /** @return the mass of the element with the supplied @p atomicNumber. */ static double mass(unsigned char atomicNumber); /** * @return the Van der Waals radius of the element with the supplied * @p atomicNumber. */ static double radiusVDW(unsigned char atomicNumber); /** @return the covalent radius of the element with the supplied * @p atomicNumber. */ static double radiusCovalent(unsigned char atomicNumber); /** * @return the default color of the element with the supplied @p atomicNumber. * This is a pointer to a static three component unsigned char color. */ static const unsigned char* color(unsigned char atomicNumber); /** @return the number of valence electrons for the supplied @p atomicNumber */ static unsigned char valenceElectrons(unsigned char atomicNumber); }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_ELEMENTS_H avogadrolibs-1.101.0/avogadro/core/gaussianset.cpp000066400000000000000000000511061506155467400221320ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gaussianset.h" #include "molecule.h" #include #include using std::cout; using std::endl; using std::vector; namespace Avogadro::Core { GaussianSet::GaussianSet() : m_numMOs(0), m_init(false), m_scfType(Rhf) {} unsigned int GaussianSet::addBasis(unsigned int atom, orbital type) { // Count the number of independent basis functions switch (type) { case S: ++m_numMOs; break; case P: m_numMOs += 3; break; case SP: m_numMOs += 4; break; case D: m_numMOs += 6; break; case D5: m_numMOs += 5; break; case F: m_numMOs += 10; break; case F7: m_numMOs += 7; break; case G: m_numMOs += 15; break; case G9: m_numMOs += 9; break; case H: m_numMOs += 21; break; case H11: m_numMOs += 11; break; case I: m_numMOs += 28; break; case I13: m_numMOs += 13; break; default: // Should never hit here ; } m_init = false; // Add to the new data structure, delete the old soon m_symmetry.push_back(type); m_atomIndices.push_back(atom); return static_cast(m_symmetry.size() - 1); } unsigned int GaussianSet::addGto(unsigned int basis, double c, double a) { if (m_gtoIndices.size() == basis) { m_gtoIndices.push_back(static_cast(m_gtoA.size())); } else if (m_gtoIndices.size() < basis) { cout << "Error, attempted to add a GTO for a basis too early. GTOs must be " << "added in order to ensure correct indexing.\n"; return 69696969; } m_gtoA.push_back(a); m_gtoC.push_back(c); return static_cast(m_gtoA.size() - 1); } void GaussianSet::setMolecularOrbitals(const vector& MOs, ElectronType type) { if (!m_numMOs) return; m_init = false; size_t index(0); if (type == Beta) index = 1; // Some programs don't output all MOs, so we take the amount of data // and divide by the number of atomic orbital functions. unsigned int columns = static_cast(MOs.size()) / m_numMOs; // cout << " Add MOs: " << m_numMOs << columns << endl; m_moMatrix[index].resize(m_numMOs, columns); for (unsigned int j = 0; j < columns; ++j) for (unsigned int i = 0; i < m_numMOs; ++i) m_moMatrix[index].coeffRef(i, j) = MOs[i + j * m_numMOs]; } void GaussianSet::setMolecularOrbitals(const vector& MOs, ElectronType type, Index idx) { if (!m_numMOs) return; size_t index = 0; if (type == Beta) index = 1; unsigned int columns = static_cast(MOs.size()) / m_numMOs; MatrixX moMatrix; moMatrix.resize(m_numMOs, columns); for (unsigned int j = 0; j < columns; ++j) for (unsigned int i = 0; i < m_numMOs; ++i) moMatrix.coeffRef(i, j) = MOs[i + j * m_numMOs]; if (idx <= m_moMatrixSet[index].size()) m_moMatrixSet[index].resize(idx + 1); m_moMatrixSet[index][idx] = moMatrix; } bool GaussianSet::setActiveSetStep(int index) { if (index >= static_cast(m_moMatrixSet[0].size()) || index >= static_cast(m_moMatrixSet[1].size())) { return false; } if (index >= m_molecule->coordinate3dCount()) return false; m_moMatrix[0] = m_moMatrixSet[0][index]; m_moMatrix[1] = m_moMatrixSet[1][index]; m_molecule->setCoordinate3d(index); return true; } void GaussianSet::setMolecularOrbitalNumber(const vector& nums, ElectronType type) { if (type == Beta) m_moNumber[1] = nums; else m_moNumber[0] = nums; } bool GaussianSet::setDensityMatrix(const MatrixX& m) { m_density.resize(m.rows(), m.cols()); m_density = m; return true; } bool GaussianSet::setSpinDensityMatrix(const MatrixX& m) { m_spinDensity.resize(m.rows(), m.cols()); m_spinDensity = m; return true; } unsigned int GaussianSet::molecularOrbitalCount(ElectronType type) const { size_t index(0); if (type == Beta) index = 1; return static_cast(m_moMatrix[index].rows()); } void GaussianSet::outputAll(ElectronType type) { size_t index(0); if (type == Beta) index = 1; // Can be called to print out a summary of the basis set as read in auto numAtoms = static_cast(m_molecule->atomCount()); cout << "\nGaussian Basis Set\nNumber of atoms:" << numAtoms << endl; switch (m_scfType) { case Rhf: cout << "RHF orbitals" << endl; break; case Uhf: cout << "UHF orbitals" << endl; break; case Rohf: cout << "ROHF orbitals" << endl; break; default: cout << "Unknown orbitals" << endl; } initCalculation(); cout << "Number of electrons = " << m_electrons[index] << endl; if (!isValid()) { cout << "Basis set is marked as invalid." << endl; return; } for (size_t i = 0; i < m_symmetry.size(); ++i) { cout << i << "\tAtom Index: " << m_atomIndices[i] << "\tSymmetry: " << m_symmetry[i] << "\tMO Index: " << m_moIndices[i] << "\tGTO Index: " << m_gtoIndices[i] << endl; } cout << "Symmetry: " << m_symmetry.size() << "\tgtoIndices: " << m_gtoIndices.size() << "\tLast gtoIndex: " << m_gtoIndices[m_symmetry.size()] << "\ngto size: " << m_gtoA.size() << " " << m_gtoC.size() << " " << m_gtoCN.size() << endl; for (size_t i = 0; i < m_symmetry.size(); ++i) { switch (m_symmetry[i]) { case S: cout << "Shell " << i << "\tS\n MO 1\t" << m_moMatrix[index](0, m_moIndices[i]) << "\t" << m_moMatrix[index](m_moIndices[i], 0) << endl; break; case P: cout << "Shell " << i << "\tP\n MO 1\t" << m_moMatrix[index](0, m_moIndices[i]) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 1) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 2) << endl; break; case D: cout << "Shell " << i << "\tD\n MO 1\t" << m_moMatrix[index](0, m_moIndices[i]) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 1) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 2) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 3) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 4) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 5) << endl; break; case D5: cout << "Shell " << i << "\tD5\n MO 1\t" << m_moMatrix[index](0, m_moIndices[i]) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 1) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 2) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 3) << "\t" << m_moMatrix[index](0, m_moIndices[i] + 4) << endl; break; case F: cout << "Shell " << i << "\tF\n MO 1"; for (short j = 0; j < 10; ++j) cout << "\t" << m_moMatrix[index](0, m_moIndices[i] + j); cout << endl; break; case F7: cout << "Shell " << i << "\tF7\n MO 1"; for (short j = 0; j < 7; ++j) cout << "\t" << m_moMatrix[index](0, m_moIndices[i] + j); cout << endl; break; default: cout << "Error: unhandled type...\n"; } unsigned int cIndex = m_gtoIndices[i]; for (size_t j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { if (j >= m_gtoA.size()) { cout << "Error, j is too large!" << j << m_gtoA.size() << endl; continue; } cout << cIndex << "\tc: " << m_gtoC[cIndex] << "\ta: " << m_gtoA[cIndex] << endl; ++cIndex; } } cout << "\nEnd of orbital data...\n"; } bool GaussianSet::isValid() { // TODO: Something useful here again - check the basis set makes sense... return true; } void GaussianSet::initCalculation() { if (m_init) return; // This currently just involves normalising all contraction coefficients m_gtoCN.clear(); // Initialise the new data structures that are hopefully more efficient unsigned int indexMO = 0; unsigned int skip = 0; // for unimplemented shells m_moIndices.resize(m_symmetry.size()); // Add a final entry to the gtoIndices m_gtoIndices.push_back(static_cast(m_gtoA.size())); for (unsigned int i = 0; i < m_symmetry.size(); ++i) { switch (m_symmetry[i]) { case S: m_moIndices[i] = indexMO++; m_cIndices.push_back(static_cast(m_gtoCN.size())); // Normalization of the S-type orbitals (normalization used in JMol) // (8 * alpha^3 / pi^3)^0.25 * exp(-alpha * r^2) for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 0.75) * 0.71270547); } break; case P: m_moIndices[i] = indexMO; indexMO += 3; m_cIndices.push_back(static_cast(m_gtoCN.size())); // Normalization of the P-type orbitals (normalization used in JMol) // (128 alpha^5 / pi^3)^0.25 * [x|y|z]exp(-alpha * r^2) for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 1.25) * 1.425410941); m_gtoCN.push_back(m_gtoCN.back()); m_gtoCN.push_back(m_gtoCN.back()); } break; case D: // Cartesian - 6 d components // Order in xx, yy, zz, xy, xz, yz m_moIndices[i] = indexMO; indexMO += 6; m_cIndices.push_back(static_cast(m_gtoCN.size())); // Normalization of the P-type orbitals (normalization used in JMol) // xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2) // xy|xz|yz: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2) for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 1.75) * 1.645922781); m_gtoCN.push_back(m_gtoCN.back()); m_gtoCN.push_back(m_gtoCN.back()); m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 1.75) * 2.850821881); m_gtoCN.push_back(m_gtoCN.back()); m_gtoCN.push_back(m_gtoCN.back()); } break; case D5: // Spherical - 5 d components // Order in d0, d+1, d-1, d+2, d-2 // Form d(z^2-r^2), dxz, dyz, d(x^2-y^2), dxy m_moIndices[i] = indexMO; indexMO += 5; m_cIndices.push_back(static_cast(m_gtoCN.size())); for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { m_gtoCN.push_back( m_gtoC[j] * pow(2048 * pow(m_gtoA[j], 7.0) / (9.0 * M_PI * M_PI * M_PI), 0.25)); m_gtoCN.push_back( m_gtoC[j] * pow(2048 * pow(m_gtoA[j], 7.0) / (M_PI * M_PI * M_PI), 0.25)); m_gtoCN.push_back(m_gtoCN.back()); // I think this is correct but really need to check... m_gtoCN.push_back( m_gtoC[j] * pow(128 * pow(m_gtoA[j], 7.0) / (M_PI * M_PI * M_PI), 0.25)); m_gtoCN.push_back( m_gtoC[j] * pow(2048 * pow(m_gtoA[j], 7.0) / (M_PI * M_PI * M_PI), 0.25)); } break; case F: /* Thanks, Jmol Cartesian forms for f (l = 3) basis functions: Type Normalization xxx [(32768 * alpha^9) / (225 * pi^3))]^(1/4) xxy [(32768 * alpha^9) / (9 * pi^3))]^(1/4) xxz [(32768 * alpha^9) / (9 * pi^3))]^(1/4) xyy [(32768 * alpha^9) / (9 * pi^3))]^(1/4) xyz [(32768 * alpha^9) / (1 * pi^3))]^(1/4) xzz [(32768 * alpha^9) / (9 * pi^3))]^(1/4) yyy [(32768 * alpha^9) / (225 * pi^3))]^(1/4) yyz [(32768 * alpha^9) / (9 * pi^3))]^(1/4) yzz [(32768 * alpha^9) / (9 * pi^3))]^(1/4) zzz [(32768 * alpha^9) / (225 * pi^3))]^(1/4) Thank you, Python pi = 3.141592653589793 (32768./225./(pi**3.))**(0.25) = 1.4721580892990938 (32768./9./(pi**3.))**(0.25) = 3.291845561298979 (32768./(pi**3.))**(0.25) = 5.701643762839922 */ { double norm1 = 1.4721580892990938; double norm2 = 3.291845561298979; double norm3 = 5.701643762839922; m_moIndices[i] = indexMO; indexMO += 10; m_cIndices.push_back(static_cast(m_gtoCN.size())); for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm1); // xxx m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm2); // xxy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm2); // xxz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm2); // xyy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm3); // xyz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm2); // xzz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm1); // yyy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm2); // yyz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm2); // yzz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm1); // zzz } } break; case F7: { // m-independent normalization factor // math.sqrt(2.**(3.+3./2.))/(math.pi**(3./4.))*math.sqrt(2.**3. / 15.) // same as norm1 above. double norm = 1.4721580892990935; m_moIndices[i] = indexMO; indexMO += 7; m_cIndices.push_back(static_cast(m_gtoCN.size())); for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm); // 0 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm); //+1 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm); //-1 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm); //+2 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm); //-2 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm); //+3 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.25) * norm); //-3 } } break; case G: { // 16 * (2.0/pi)^0.75 double norm = 11.403287525679843; double norm1 = norm / sqrt(7.0); double norm2 = norm / sqrt(35.0 / 3.0); double norm3 = norm / sqrt(35.0); m_moIndices[i] = indexMO; indexMO += 15; m_cIndices.push_back(static_cast(m_gtoCN.size())); for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { // molden order // xxxx yyyy zzzz xxxy xxxz yyyx yyyz zzzx zzzy, // xxyy xxzz yyzz xxyz yyxz zzxy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); // xxxx m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); // yyyy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); // zzzz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm1); // xxxy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm1); // xxxz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm1); // yyyx m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm1); // yyyz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm1); // zzzx m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm1); // zzzy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm2); // xxyy m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm2); // xxzz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm2); // yyzz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm3); // xxyz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm3); // yyxz m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm3); // zzxy } } break; case G9: { // 16 * (2.0/pi)^0.75 double norm = 11.403287525679843; m_moIndices[i] = indexMO; indexMO += 9; m_cIndices.push_back(static_cast(m_gtoCN.size())); for (unsigned j = m_gtoIndices[i]; j < m_gtoIndices[i + 1]; ++j) { m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); // 0 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //+1 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //-1 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //+2 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //-2 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //+3 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //-3 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //+4 m_gtoCN.push_back(m_gtoC[j] * pow(m_gtoA[j], 2.75) * norm); //-4 } } break; case H: skip = 21; break; case H11: skip = 11; break; case I: skip = 28; break; case I13: skip = 13; break; default: cout << "Basis set not handled - results may be incorrect.\n"; } if (skip) { cout << "Basis set not handled - results may be incorrect.\n"; m_moIndices[i] = indexMO; indexMO += skip; m_cIndices.push_back(static_cast(m_gtoCN.size())); skip = 0; } } m_init = true; } bool GaussianSet::generateDensityMatrix() { if (m_scfType == Unknown) return false; m_density.resize(m_numMOs, m_numMOs); m_density = MatrixX::Zero(m_numMOs, m_numMOs); for (unsigned int iBasis = 0; iBasis < m_numMOs; ++iBasis) { for (unsigned int jBasis = 0; jBasis <= iBasis; ++jBasis) { switch (m_scfType) { case Rhf: for (unsigned int iMO = 0; iMO < m_electrons[0] / 2; ++iMO) { double icoeff = m_moMatrix[0](iBasis, iMO); double jcoeff = m_moMatrix[0](jBasis, iMO); m_density(jBasis, iBasis) += 2.0 * icoeff * jcoeff; m_density(iBasis, jBasis) = m_density(jBasis, iBasis); } // cout << iBasis << ", " << jBasis << ": " << // m_density(iBasis, jBasis) // << endl; break; case Rohf: // ROHF is handled similarly to UHF case Uhf: for (unsigned int iaMO = 0; iaMO < m_electrons[0]; ++iaMO) { double icoeff = m_moMatrix[0](iBasis, iaMO); double jcoeff = m_moMatrix[0](jBasis, iaMO); m_density(jBasis, iBasis) += icoeff * jcoeff; m_density(iBasis, jBasis) = m_density(jBasis, iBasis); } for (unsigned int ibMO = 0; ibMO < m_electrons[1]; ibMO++) { double icoeff = m_moMatrix[1](iBasis, ibMO); double jcoeff = m_moMatrix[1](jBasis, ibMO); m_density(jBasis, iBasis) += icoeff * jcoeff; m_density(iBasis, jBasis) = m_density(jBasis, iBasis); } // cout << iBasis << ", " << jBasis << ": " << // m_density(iBasis, jBasis) // << endl; break; default: cout << "Unhandled scf type:" << m_scfType << endl; } } } return true; } bool GaussianSet::generateSpinDensityMatrix() { if (m_scfType != Uhf) return false; m_spinDensity.resize(m_numMOs, m_numMOs); m_spinDensity = MatrixX::Zero(m_numMOs, m_numMOs); for (unsigned int iBasis = 0; iBasis < m_numMOs; ++iBasis) { for (unsigned int jBasis = 0; jBasis <= iBasis; ++jBasis) { for (unsigned int iaMO = 0; iaMO < m_electrons[0]; ++iaMO) { double icoeff = m_moMatrix[0](iBasis, iaMO); double jcoeff = m_moMatrix[0](jBasis, iaMO); m_spinDensity(jBasis, iBasis) += icoeff * jcoeff; m_spinDensity(iBasis, jBasis) = m_spinDensity(jBasis, iBasis); } for (unsigned int ibMO = 0; ibMO < m_electrons[1]; ++ibMO) { double icoeff = m_moMatrix[1](iBasis, ibMO); double jcoeff = m_moMatrix[1](jBasis, ibMO); m_spinDensity(jBasis, iBasis) -= icoeff * jcoeff; m_spinDensity(iBasis, jBasis) = m_spinDensity(jBasis, iBasis); } cout << iBasis << ", " << jBasis << ": " << m_spinDensity(iBasis, jBasis) << endl; } } return true; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/gaussianset.h000066400000000000000000000220711506155467400215760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_GAUSSIANSET_H #define AVOGADRO_CORE_GAUSSIANSET_H #include "avogadrocoreexport.h" #include "basisset.h" #include #include #include namespace Avogadro::Core { /** * Enumeration of the SCF type. */ enum ScfType { Rhf, Uhf, Rohf, Unknown }; /** * @class GaussianSet gaussianset.h * @brief A container for Gaussian type outputs from QM codes. * @author Marcus D. Hanwell * * The GaussianSet class has a transparent data structure for storing the basis * sets output by many quantum mechanical codes. It has a certain hierarchy * where shells are built up from n primitives, in this case Gaussian Type * Orbitals (GTOs). Each shell has a type (S, P, D, F, etc) and is composed of * one or more GTOs. Each GTO has a contraction coefficient, c, and an exponent, * a. * * When calculating Molecular Orbitals (MOs) each orthogonal shell has an * independent coefficient. That is the S type orbitals have one coefficient, * the P type orbitals have three coefficients (Px, Py and Pz), the D type * orbitals have five (or six if cartesian types) coefficients, and so on. */ class AVOGADROCORE_EXPORT GaussianSet : public BasisSet { public: /** * Constructor. */ GaussianSet(); /** * Destructor. */ ~GaussianSet() override = default; /** * Clone. */ GaussianSet* clone() const override { return new GaussianSet(*this); } /** * Enumeration of the Gaussian type orbitals. */ enum orbital { S, SP, P, D, D5, F, F7, G, G9, H, H11, I, I13, UU }; /** * Add a basis to the basis set. * @param atom Index of the atom to add the Basis to. * @param type The type of the Basis being added. * @return The index of the added Basis. */ unsigned int addBasis(unsigned int atom, orbital type); /** * Add a GTO to the supplied basis. * @param basis The index of the Basis to add the GTO to. * @param c The contraction coefficient of the GTO. * @param a The exponent of the GTO. * @return The index of the added GTO. */ unsigned int addGto(unsigned int basis, double c, double a); /** * Set the molecular orbital (MO) coefficients to the GaussianSet. * @param MOs Vector containing the MO coefficients for the GaussianSet. * @param type The type of the MOs (Paired, Alpha, Beta). */ void setMolecularOrbitals(const std::vector& MOs, ElectronType type = Paired); /** * Set the molecular orbital (MO) coefficients for a given index. Note * that this must be used with coordinate sets to work correctly. * @param MOs Vector containing the MO coefficients for the GaussianSet. * @param type The type of the MOs (Paired, Alpha, Beta). * @param index The index of the MO in the sequence. */ void setMolecularOrbitals(const std::vector& MOs, ElectronType type, Index index); /** * Get the number of elements in the set. */ int setCount() { return static_cast(m_moMatrixSet[0].size()); } /** * Set the active element in the set, this expects a corresponding * coordinate set element, and will change the active MO matrix. */ bool setActiveSetStep(int index); /** * @brief This enables support of sparse orbital sets, and provides a mapping * from the index in memory to the actual molecular orbital number. * @param nums The MO numbers (starting with an index of 1 for the first one). * @param type The MO type (Paired, Alpha, Beta). */ void setMolecularOrbitalNumber(const std::vector& nums, ElectronType type = Paired); /** * Set the SCF density matrix for the GaussianSet. */ bool setDensityMatrix(const MatrixX& m); /** * Set the spin density matrix for the GaussianSet. */ bool setSpinDensityMatrix(const MatrixX& m); /** * @brief Generate the density matrix if we have the required information. * @return True on success, false on failure. */ bool generateDensityMatrix(); /** * @brief Generate the spin density matrix if we have the required * information. * @return True on success, false on failure. */ bool generateSpinDensityMatrix(); /** * @return The number of molecular orbitals in the GaussianSet. */ unsigned int molecularOrbitalCount(ElectronType type = Paired) const override; /** * Debug routine, outputs all of the data in the GaussianSet. * @param type The electrons to output the information for. */ void outputAll(ElectronType type = Paired); /** * @return True of the basis set is valid, false otherwise. * Default is true, if false then the basis set is likely unusable. */ bool isValid() override; /** * Set the SCF type for the object. */ void setScfType(ScfType type) { m_scfType = type; } /** * Get the SCF type for the object. */ ScfType scfType() const { return m_scfType; } /** * Set the functional name (if applicable). */ void setFunctionalName(const std::string& name) { m_functionalName = name; } /** * Get the functional name (empty if none used). */ std::string functionalName() const { return m_functionalName; } /** * Initialize the calculation, this must normally be done before anything. */ void initCalculation(); /** * Accessors for the various properties of the GaussianSet. */ std::vector& symmetry() { return m_symmetry; } std::vector symmetry() const { return m_symmetry; } std::vector& atomIndices() { return m_atomIndices; } std::vector atomIndices() const { return m_atomIndices; } std::vector& moIndices() { return m_moIndices; } std::vector moIndices() const { return m_moIndices; } std::vector& gtoIndices() { return m_gtoIndices; } std::vector gtoIndices() const { return m_gtoIndices; } std::vector& cIndices() { return m_cIndices; } std::vector cIndices() const { return m_cIndices; } std::vector& gtoA() { return m_gtoA; } std::vector gtoA() const { return m_gtoA; } std::vector& gtoC() { return m_gtoC; } std::vector gtoC() const { return m_gtoC; } std::vector& gtoCN() { initCalculation(); return m_gtoCN; } MatrixX& moMatrix(ElectronType type = Paired) { if (type == Paired || type == Alpha) return m_moMatrix[0]; else return m_moMatrix[1]; } MatrixX moMatrix(ElectronType type = Paired) const { if (type == Paired || type == Alpha) return m_moMatrix[0]; else return m_moMatrix[1]; } std::vector& moNumber(ElectronType type = Paired) { if (type == Paired || type == Alpha) return m_moNumber[0]; else return m_moNumber[1]; } std::vector moNumber(ElectronType type = Paired) const { if (type == Paired || type == Alpha) return m_moNumber[0]; else return m_moNumber[1]; } MatrixX& densityMatrix() { return m_density; } MatrixX& spinDensityMatrix() { return m_spinDensity; } private: /** * @brief This group is used once, and refers to the entire molecule. */ std::vector m_symmetry; //! Symmetry of the basis, S, P... std::vector m_atomIndices; //! Indices into the atomPos vector std::vector m_moIndices; //! Indices into the MO/density matrix std::vector m_gtoIndices; //! Indices into the GTO vector std::vector m_cIndices; //! Indices into m_gtoCN std::vector m_gtoA; //! The GTO exponent std::vector m_gtoC; //! The GTO contraction coefficient std::vector m_gtoCN; //! The GTO contraction coefficient (normalized) /** * @brief This block can be once (doubly) or in two parts (alpha and beta) for * open shell calculations. */ MatrixX m_moMatrix[2]; //! MO coefficient matrix /** * @brief If there are a sequence of related MOs, they are stored here, and * set as the active MOs upon demand. Alpha will store Paired or the Alpha, * Beta will store Beta coefficients for the appropriate calculation types. */ std::vector m_moMatrixSet[2]; /** * @brief This stores the molecular orbital number (when they are sparse). It * is used to lookup the actual index of the molecular orbital data. */ std::vector m_moNumber[2]; MatrixX m_density; //! Density matrix MatrixX m_spinDensity; //! Spin Density matrix unsigned int m_numMOs; //! The number of GTOs (not always!) bool m_init; //! Has the calculation been initialised? ScfType m_scfType; std::string m_functionalName; }; } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/gaussiansettools.cpp000066400000000000000000000523351506155467400232200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gaussiansettools.h" #include "cube.h" #include "gaussianset.h" #include "molecule.h" #include using std::vector; namespace Avogadro::Core { GaussianSetTools::GaussianSetTools(Molecule* mol) : m_molecule(mol) { if (m_molecule) { m_basis = dynamic_cast(m_molecule->basisSet()); m_cutoffDistances.resize(7, 0.0); // s, p, d, f, g, h, i (for now) calculateCutoffs(); } } bool GaussianSetTools::calculateMolecularOrbital(Cube& cube, int moNumber) const { for (size_t i = 0; i < cube.data()->size(); ++i) { Vector3 pos = cube.position(i); cube.setValue(i, calculateMolecularOrbital(pos, moNumber)); } return true; } double GaussianSetTools::calculateMolecularOrbital(const Vector3& position, int mo) const { if (mo > static_cast(m_basis->molecularOrbitalCount())) return 0.0; std::vector values(calculateValues(position)); const MatrixX& matrix = m_basis->moMatrix(m_type); int matrixSize(static_cast(matrix.rows())); // Now calculate the value of the density at this point in space double result(0.0); for (int i = 0; i < matrixSize; ++i) result += matrix(i, mo) * values[i]; return result; } bool GaussianSetTools::calculateElectronDensity(Cube& cube) const { const MatrixX& matrix = m_basis->densityMatrix(); if (matrix.rows() == 0 || matrix.cols() == 0) { // we don't have a density matrix, so generate one m_basis->generateDensityMatrix(); } for (size_t i = 0; i < cube.data()->size(); ++i) { Vector3 pos = cube.position(i); cube.setValue(i, calculateElectronDensity(pos)); } return true; } double GaussianSetTools::calculateElectronDensity(const Vector3& position) const { const MatrixX& matrix = m_basis->densityMatrix(); int matrixSize(static_cast(m_basis->moMatrix().rows())); if (matrix.rows() != matrixSize || matrix.cols() != matrixSize) { return 0.0; } std::vector values(calculateValues(position)); // Now calculate the value of the density at this point in space double rho(0.0); for (int i = 0; i < matrixSize; ++i) { // Calculate the off-diagonal parts of the matrix for (int j = 0; j < i; ++j) rho += 2.0 * matrix(i, j) * (values[i] * values[j]); // Now calculate the matrix diagonal rho += matrix(i, i) * (values[i] * values[i]); } return rho; } bool GaussianSetTools::calculateSpinDensity(Cube& cube) const { for (size_t i = 0; i < cube.data()->size(); ++i) { Vector3 pos = cube.position(i); cube.setValue(i, calculateSpinDensity(pos)); } return true; } double GaussianSetTools::calculateSpinDensity(const Vector3& position) const { const MatrixX& matrix = m_basis->spinDensityMatrix(); int matrixSize(static_cast(m_basis->moMatrix().rows())); if (matrix.rows() != matrixSize || matrix.cols() != matrixSize) { return 0.0; } std::vector values(calculateValues(position)); // Now calculate the value of the density at this point in space double rho(0.0); for (int i = 0; i < matrixSize; ++i) { // Calculate the off-diagonal parts of the matrix for (int j = 0; j < i; ++j) rho += 2.0 * matrix(i, j) * (values[i] * values[j]); // Now calculate the matrix diagonal rho += matrix(i, i) * (values[i] * values[i]); } return rho; } bool GaussianSetTools::isValid() const { return (m_molecule != nullptr) && (dynamic_cast(m_molecule->basisSet()) != nullptr); } inline bool GaussianSetTools::isSmall(double val) const { return std::abs(val) < 1e-12; } inline void GaussianSetTools::calculateCutoffs() { // Guesstimate a distance we can ignore the exp(-alpha * r^2) term // .. because it's negligible // This will depend on the angular momentum of the basis function // .. so we calculate it for whatever L values in this basis set const double threshold = 0.03 * 0.001; // 0.1% of a typical isovalue const double maxDistance = 100.0; // just in case (diffuse functions) // get the exponents and normalized coefficients const std::vector& exponents = m_basis->gtoA(); const std::vector& coefficients = m_basis->gtoCN(); const std::vector& sym = m_basis->symmetry(); // we loop through the "symmetry" (i.e., L values in this basis set) for (size_t i = 0; i < sym.size(); ++i) { int L = symToL[sym[i]]; // this is a hack, since not all coefficients will be the same // .. but it's a good approximation since they'll be similar unsigned int cIndex = m_basis->cIndices()[i]; const double coeff = std::abs(coefficients[cIndex]); // now loop through all exponents for this L value // (e.g., multiple terms - we don't know which is the most diffuse) for (unsigned int j = m_basis->gtoIndices()[i]; j < m_basis->gtoIndices()[i + 1]; ++j) { double alpha = exponents[j]; // except for S, we don't want to start at the origin double r = std::min(maxDistance, std::sqrt(L / (2 * alpha))); double value = coeff * std::pow(r, L) * std::exp(-alpha * r * r); while (value > threshold && r < maxDistance) { r += 0.25; value = coeff * std::pow(r, L) * std::exp(-alpha * r * r); } m_cutoffDistances[L] = std::max(m_cutoffDistances[L], r * r); } } } inline std::vector GaussianSetTools::calculateValues( const Vector3& position) const { m_basis->initCalculation(); Index atomsSize = m_molecule->atomCount(); size_t basisSize = m_basis->symmetry().size(); const std::vector& basis = m_basis->symmetry(); const std::vector& atomIndices = m_basis->atomIndices(); std::vector deltas; std::vector dr2; deltas.reserve(atomsSize); dr2.reserve(atomsSize); // Calculate our position Vector3 pos(position * ANGSTROM_TO_BOHR); // Calculate the deltas for the position for (Index i = 0; i < atomsSize; ++i) { deltas.emplace_back(pos - (m_molecule->atom(i).position3d() * ANGSTROM_TO_BOHR)); dr2.push_back(deltas[i].squaredNorm()); } // Allocate space for the values to be calculated. size_t matrixSize = m_basis->moMatrix().rows(); std::vector values; values.resize(matrixSize, 0.0); // Now calculate the values at this point in space for (unsigned int i = 0; i < basisSize; ++i) { // bail early if the distance is too big double cutoff = m_cutoffDistances[symToL[basis[i]]]; if (dr2[atomIndices[i]] > cutoff) continue; switch (basis[i]) { case GaussianSet::S: pointS(i, dr2[atomIndices[i]], values); break; case GaussianSet::P: pointP(i, deltas[atomIndices[i]], dr2[atomIndices[i]], values); break; case GaussianSet::D: pointD(i, deltas[atomIndices[i]], dr2[atomIndices[i]], values); break; case GaussianSet::D5: pointD5(i, deltas[atomIndices[i]], dr2[atomIndices[i]], values); break; case GaussianSet::F: pointF(i, deltas[atomIndices[i]], dr2[atomIndices[i]], values); break; case GaussianSet::F7: pointF7(i, deltas[atomIndices[i]], dr2[atomIndices[i]], values); break; case GaussianSet::G: pointG(i, deltas[atomIndices[i]], dr2[atomIndices[i]], values); break; case GaussianSet::G9: pointG9(i, deltas[atomIndices[i]], dr2[atomIndices[i]], values); break; default: // Not handled - return a zero contribution ; } } return values; } inline void GaussianSetTools::pointS(unsigned int moIndex, double dr2, std::vector& values) const { // S type orbitals - the simplest of the calculations with one component double tmp = 0.0; unsigned int cIndex = m_basis->cIndices()[moIndex]; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { tmp += m_basis->gtoCN()[cIndex++] * exp(-m_basis->gtoA()[i] * dr2); } // There is one MO coefficient per S shell basis. values[m_basis->moIndices()[moIndex]] = tmp; } inline void GaussianSetTools::pointP(unsigned int moIndex, const Vector3& delta, double dr2, std::vector& values) const { // P type orbitals have three components and each component has a different // independent MO weighting. Many things can be cached to save time though. unsigned int baseIndex = m_basis->moIndices()[moIndex]; Vector3 components(Vector3::Zero()); // Now iterate through the GTOs and sum their contributions unsigned int cIndex = m_basis->cIndices()[moIndex]; double tmpGTO = 0.0; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { tmpGTO = exp(-m_basis->gtoA()[i] * dr2); for (unsigned int j = 0; j < 3; ++j) { components[j] += m_basis->gtoCN()[cIndex++] * tmpGTO; } } for (unsigned int i = 0; i < 3; ++i) values[baseIndex + i] = components[i] * delta[i]; } inline void GaussianSetTools::pointD(unsigned int moIndex, const Vector3& delta, double dr2, std::vector& values) const { // D type orbitals have six components and each component has a different // independent MO weighting. Many things can be cached to save time though. unsigned int baseIndex = m_basis->moIndices()[moIndex]; double components[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; const vector& gtoA = m_basis->gtoA(); const vector& gtoCN = m_basis->gtoCN(); // Now iterate through the GTOs and sum their contributions unsigned int cIndex = m_basis->cIndices()[moIndex]; double tmpGTO = 0.0; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { // Calculate the common factor tmpGTO = exp(-gtoA[i] * dr2); for (double& component : components) component += gtoCN[cIndex++] * tmpGTO; } double componentsD[6] = { delta.x() * delta.x(), // xx delta.y() * delta.y(), // yy delta.z() * delta.z(), // zz delta.x() * delta.y(), // xy delta.x() * delta.z(), // xz delta.y() * delta.z() }; // yz for (int i = 0; i < 6; ++i) values[baseIndex + i] += components[i] * componentsD[i]; } inline void GaussianSetTools::pointD5(unsigned int moIndex, const Vector3& delta, double dr2, std::vector& values) const { // D type orbitals have five components and each component has a different // MO weighting. Many things can be cached to save time. unsigned int baseIndex = m_basis->moIndices()[moIndex]; double components[5] = { 0.0, 0.0, 0.0, 0.0, 0.0 }; const vector& gtoA = m_basis->gtoA(); const vector& gtoCN = m_basis->gtoCN(); // Now iterate through the D type GTOs and sum their contributions unsigned int cIndex = m_basis->cIndices()[moIndex]; double tmpGTO = 0.0; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { // Calculate the common factor tmpGTO = exp(-gtoA[i] * dr2); for (double& component : components) component += gtoCN[cIndex++] * tmpGTO; } // Calculate the prefactors double xx = delta.x() * delta.x(); double yy = delta.y() * delta.y(); double zz = delta.z() * delta.z(); double xy = delta.x() * delta.y(); double xz = delta.x() * delta.z(); double yz = delta.y() * delta.z(); double componentsD[5] = { zz - dr2, // 0 xz, // 1p yz, // 1n xx - yy, // 2p xy }; // 2n for (int i = 0; i < 5; ++i) values[baseIndex + i] += componentsD[i] * components[i]; } inline void GaussianSetTools::pointF(unsigned int moIndex, const Vector3& delta, double dr2, std::vector& values) const { // F type orbitals have 10 components and each component has a different // independent MO weighting. Many things can be cached to save time though. unsigned int baseIndex = m_basis->moIndices()[moIndex]; double components[10] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; const vector& gtoA = m_basis->gtoA(); const vector& gtoCN = m_basis->gtoCN(); // Now iterate through the GTOs and sum their contributions unsigned int cIndex = m_basis->cIndices()[moIndex]; double tmpGTO = 0.0; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { // Calculate the common factor tmpGTO = exp(-gtoA[i] * dr2); for (double& component : components) component += gtoCN[cIndex++] * tmpGTO; } double xxx = delta.x() * delta.x() * delta.x(); // xxx double xxy = delta.x() * delta.x() * delta.y(); // xxy double xxz = delta.x() * delta.x() * delta.z(); // xxz double xyy = delta.x() * delta.y() * delta.y(); // xyy double xyz = delta.x() * delta.y() * delta.z(); // xyz double xzz = delta.x() * delta.z() * delta.z(); // xzz double yyy = delta.y() * delta.y() * delta.y(); // yyy double yyz = delta.y() * delta.y() * delta.z(); // yyz double yzz = delta.y() * delta.z() * delta.z(); // yzz double zzz = delta.z() * delta.z() * delta.z(); // zzz double componentsF[10] = { // molden order // e.g https://gau2grid.readthedocs.io/en/latest/order.html xxx, yyy, zzz, xyy, xxy, xxz, xzz, yzz, yyz, xyz }; for (int i = 0; i < 10; ++i) values[baseIndex + i] += components[i] * componentsF[i]; } inline void GaussianSetTools::pointF7(unsigned int moIndex, const Vector3& delta, double dr2, std::vector& values) const { // F type orbitals have 7 components and each component has a different // independent MO weighting. Many things can be cached to save time though. unsigned int baseIndex = m_basis->moIndices()[moIndex]; double components[7] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; const vector& gtoA = m_basis->gtoA(); const vector& gtoCN = m_basis->gtoCN(); // Now iterate through the F type GTOs and sum their contributions unsigned int cIndex = m_basis->cIndices()[moIndex]; double tmpGTO = 0.0; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { // Calculate the common factor tmpGTO = exp(-gtoA[i] * dr2); for (double& component : components) component += gtoCN[cIndex++] * tmpGTO; } double xxx = delta.x() * delta.x() * delta.x(); // xxx double xxy = delta.x() * delta.x() * delta.y(); // xxy double xxz = delta.x() * delta.x() * delta.z(); // xxz double xyy = delta.x() * delta.y() * delta.y(); // xyy double xyz = delta.x() * delta.y() * delta.z(); // xyz double xzz = delta.x() * delta.z() * delta.z(); // xzz double yyy = delta.y() * delta.y() * delta.y(); // yyy double yyz = delta.y() * delta.y() * delta.z(); // yyz double yzz = delta.y() * delta.z() * delta.z(); // yzz double zzz = delta.z() * delta.z() * delta.z(); // zzz /* spherical combinations borrowed from CASINO/Crystal documentation linear combination 3,0 z^3 - 3/2 * (x^2z + y^2z) 2z^3 - 3 * (x^2z + y^2z) * 2 3,1 6 * xz^2 - 3/2 * (x^3 + xy^2) 4xz^2 - x^3 - xy^2 * 2/3 3,-1 6 * yz^2 - 3/2 * (x^2y + y^3) 4yz^2 - x^2y - y^3 * 2/3 3,2 15 * (x^2z - y^2z) x^2z - y^2z * 1/15 3,-2 30 * xyz xyz * 1/30 3,3 15 * x^3 - 45 * xy^2 x^3 - 3xy^2 * 1/15 3,-3 45 * x^2y - 15 * y^3 3x^2y - y^3 * 1/15 final normalization (2 - delta_m,0) * (l - |m|)! * root ------------------------------ (m-dependent) (l + m)! */ double root6 = 2.449489742783178; double root60 = 7.745966692414834; double root360 = 18.973665961010276; double componentsF[7] = { zzz - 3.0 / 2.0 * (xxz + yyz), (6.0 * xzz - 3.0 / 2.0 * (xxx + xyy)) / root6, (6.0 * yzz - 3.0 / 2.0 * (xxy + yyy)) / root6, (15.0 * (xxz - yyz)) / root60, (30.0 * xyz) / root60, (15.0 * xxx - 45.0 * xyy) / root360, (45.0 * xxy - 15.0 * yyy) / root360 }; for (int i = 0; i < 7; ++i) values[baseIndex + i] += components[i] * componentsF[i]; } inline void GaussianSetTools::pointG(unsigned int moIndex, const Vector3& delta, double dr2, vector& values) const { // G type orbitals have 15 components and each component has a different // independent MO weighting. Many things can be cached to save time though. unsigned int baseIndex = m_basis->moIndices()[moIndex]; double components[15] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; const vector& gtoA = m_basis->gtoA(); const vector& gtoCN = m_basis->gtoCN(); // Now iterate through the G type GTOs and sum their contributions unsigned int cIndex = m_basis->cIndices()[moIndex]; double tmpGTO = 0.0; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { // Calculate the common factor tmpGTO = exp(-gtoA[i] * dr2); for (double& component : components) component += gtoCN[cIndex++] * tmpGTO; } // e.g. XXXX YYYY ZZZZ XXXY XXXZ XYYY YYYZ ZZZX ZZZY XXYY XXZZ YYZZ XXYZ XYYZ // XYZZ const double xxxx = delta.x() * delta.x() * delta.x() * delta.x(); const double yyyy = delta.y() * delta.y() * delta.y() * delta.y(); const double zzzz = delta.z() * delta.z() * delta.z() * delta.z(); const double xxxy = delta.x() * delta.x() * delta.x() * delta.y(); const double xxxz = delta.x() * delta.x() * delta.x() * delta.z(); const double yyyx = delta.y() * delta.y() * delta.y() * delta.x(); const double yyyz = delta.y() * delta.y() * delta.y() * delta.z(); const double zzzx = delta.z() * delta.z() * delta.z() * delta.x(); const double zzzy = delta.z() * delta.z() * delta.z() * delta.y(); const double xxyy = delta.x() * delta.x() * delta.y() * delta.y(); const double xxzz = delta.x() * delta.x() * delta.z() * delta.z(); const double yyzz = delta.y() * delta.y() * delta.z() * delta.z(); const double xxyz = delta.x() * delta.x() * delta.y() * delta.z(); const double yyxz = delta.y() * delta.y() * delta.x() * delta.z(); const double zzxy = delta.z() * delta.z() * delta.x() * delta.y(); // molden order // https://www.theochem.ru.nl/molden/molden_format.html // https://gau2grid.readthedocs.io/en/latest/order.html // xxxx yyyy zzzz xxxy xxxz yyyx yyyz zzzx zzzy, // xxyy xxzz yyzz xxyz yyxz zzxy double componentsG[15] = { xxxx, yyyy, zzzz, xxxy, xxxz, yyyx, yyyz, zzzx, zzzy, xxyy, xxzz, yyzz, xxyz, yyxz, zzxy }; for (int i = 0; i < 15; ++i) values[baseIndex + i] += components[i] * componentsG[i]; } inline void GaussianSetTools::pointG9(unsigned int moIndex, const Vector3& delta, double dr2, vector& values) const { // G type orbitals have 9 spherical components and each component // has a different independent MO weighting. // Many things can be cached to save time though. unsigned int baseIndex = m_basis->moIndices()[moIndex]; double components[9] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; const vector& gtoA = m_basis->gtoA(); const vector& gtoCN = m_basis->gtoCN(); // Now iterate through the GTOs and sum their contributions unsigned int cIndex = m_basis->cIndices()[moIndex]; double tmpGTO = 0.0; for (unsigned int i = m_basis->gtoIndices()[moIndex]; i < m_basis->gtoIndices()[moIndex + 1]; ++i) { // Calculate the common factor tmpGTO = exp(-gtoA[i] * dr2); for (double& component : components) component += gtoCN[cIndex++] * tmpGTO; } double x2(delta.x() * delta.x()), y2(delta.y() * delta.y()), z2(delta.z() * delta.z()); double componentsG[9] = { 3.0 * dr2 * dr2 - 30.0 * dr2 * z2 + 35.0 * z2 * z2 * (1.0 / 8.0), delta.x() * delta.z() * (7.0 * z2 - 3.0 * dr2) * (sqrt(5.0) / 8.0), delta.y() * delta.z() * (7.0 * z2 - 3.0 * dr2) * (sqrt(5.0) / 8.0), (x2 - y2) * (7.0 * z2 - dr2) * (sqrt(5.0) / 4.0), delta.x() * delta.y() * (7.0 * z2 - dr2) * (sqrt(5.0) / 2.0), delta.x() * delta.z() * (x2 - 3.0 * y2) * (sqrt(7.0) / 4.0), delta.y() * delta.z() * (3.0 * x2 - y2) * (sqrt(7.0) / 4.0), (x2 * x2 - 6.0 * x2 * y2 + y2 * y2) * (sqrt(35.0) / 8.0), delta.x() * delta.y() * (x2 - y2) * (sqrt(35.0) / 2.0) }; for (int i = 0; i < 9; ++i) values[baseIndex + i] += components[i] * componentsG[i]; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/gaussiansettools.h000066400000000000000000000116211506155467400226560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_GAUSSIANSETTOOLS_H #define AVOGADRO_CORE_GAUSSIANSETTOOLS_H #include "avogadrocoreexport.h" #include "basisset.h" #include "vector.h" #include namespace Avogadro::Core { class Cube; class GaussianSet; class Molecule; /** * @class GaussianSetTools gaussiansettools.h * @brief Provide tools to calculate molecular orbitals, electron densities and * other derived data stored in a GaussianSet result. * @author Marcus D. Hanwell */ class AVOGADROCORE_EXPORT GaussianSetTools { public: explicit GaussianSetTools(Molecule* mol = nullptr); ~GaussianSetTools() = default; /** * @brief Set the electron type, must be called once MOs are available * @param type The electron type - Alpha, Beta, or Paired (default). */ void setElectronType(BasisSet::ElectronType type) { m_type = type; } /** * @brief Populate the cube with values for the molecular orbital. * @param cube The cube to be populated with values. * @param molecularOrbitalNumber The molecular orbital number. * @return True on success, false on failure. */ bool calculateMolecularOrbital(Cube& cube, int molecularOrbitalNumber) const; /** * @brief Calculate the value of the specified molecular orbital at the * position specified. * @param position The position in space to calculate the value. * @param molecularOrbitalNumber The molecular orbital number. * @return The value of the molecular orbital at the position specified. */ double calculateMolecularOrbital(const Vector3& position, int molecularOrbitalNumber) const; /** * @brief Populate the cube with values for the electron density. * @param cube The cube to be populated with values. * @return True on success, false on failure. */ bool calculateElectronDensity(Cube& cube) const; /** * @brief Calculate the value of the electron density at the position * specified. * @param position The position in space to calculate the value. * @return The value of the electron density at the position specified. */ double calculateElectronDensity(const Vector3& position) const; /** * @brief Populate the cube with values for the spin density. * @param cube The cube to be populated with values. * @return True on success, false on failure. */ bool calculateSpinDensity(Cube& cube) const; /** * @brief Calculate the value of the electron spin density at the position * specified. * @param position The position in space to calculate the value. * @return The value of the spin density at the position specified. */ double calculateSpinDensity(const Vector3& position) const; /** * @brief Check that the basis set is valid and can be used. * @return True if valid, false otherwise. */ bool isValid() const; private: Molecule* m_molecule; GaussianSet* m_basis; BasisSet::ElectronType m_type = BasisSet::Paired; std::vector m_cutoffDistances; bool isSmall(double value) const; // get the cutoff distance for the given angular momentum // .. and the current basis set (exponents) void calculateCutoffs(); /** * @brief Calculate the values at this position in space. The public calculate * functions call this function to prepare values before multiplying by the * molecular orbital or density matrix elements. * @param position The position in space to calculate the value. */ std::vector calculateValues(const Vector3& position) const; void pointS(unsigned int index, double dr2, std::vector& values) const; void pointP(unsigned int index, const Vector3& delta, double dr2, std::vector& values) const; void pointD(unsigned int index, const Vector3& delta, double dr2, std::vector& values) const; void pointD5(unsigned int index, const Vector3& delta, double dr2, std::vector& values) const; void pointF(unsigned int index, const Vector3& delta, double dr2, std::vector& values) const; void pointF7(unsigned int index, const Vector3& delta, double dr2, std::vector& values) const; void pointG(unsigned int index, const Vector3& delta, double dr2, std::vector& values) const; void pointG9(unsigned int index, const Vector3& delta, double dr2, std::vector& values) const; // map from symmetry to angular momentum // S, SP, P, D, D5, F, F7, G, G9, etc. const int symToL[13] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6 }; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_GAUSSIANSETTOOLS_H avogadrolibs-1.101.0/avogadro/core/graph.cpp000066400000000000000000000413511506155467400207060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "graph.h" #include #include #include #include #include #include namespace Avogadro::Core { Graph::Graph(size_t n) : m_adjacencyList(n), m_edgeMap(n), m_vertexToSubgraph(n) { for (size_t i = 0; i < n; i++) { m_vertexToSubgraph[i] = -1; m_loneVertices.insert(i); } } void Graph::setSize(size_t n) { // If the graph is being made smaller we first need to remove all of the edges // from the soon to be removed vertices. for (size_t i = n; i < m_adjacencyList.size(); ++i) { removeEdges(i); // Mark subgraph as dirty if vertex has one assigned if (m_vertexToSubgraph[i] >= 0) m_subgraphDirty[m_vertexToSubgraph[i]] = true; } m_vertexToSubgraph.resize(n); // Mark the new nodes as isolated, with no explicit subgraph for (size_t i = m_adjacencyList.size(); i < n; ++i) { m_vertexToSubgraph[i] = -1; m_loneVertices.insert(i); } m_adjacencyList.resize(n); m_edgeMap.resize(n); } size_t Graph::size() const { return vertexCount(); } bool Graph::isEmpty() const { return m_adjacencyList.empty(); } void Graph::clear() { m_adjacencyList.clear(); m_edgeMap.clear(); m_edgePairs.clear(); m_vertexToSubgraph.clear(); m_subgraphToVertices.clear(); m_subgraphDirty.clear(); } size_t Graph::addVertex() { setSize(size() + 1); return size() - 1; } void Graph::removeVertex(size_t index) { assert(index < size()); // Mark the subgraph as dirty, leave the work for later if (m_vertexToSubgraph[index] >= 0) m_subgraphDirty[m_vertexToSubgraph[index]] = true; // Remove the edges to the vertex. removeEdges(index); // Swap with last vertex. if (index < size() - 1) { std::swap(m_adjacencyList[index], m_adjacencyList.back()); size_t affectedIndex = m_adjacencyList.size() - 1; // NOLINTBEGIN(*) for (size_t i = 0; i < m_adjacencyList[index].size(); i++) { size_t otherIndex = m_adjacencyList[index][i]; for (size_t j = 0; j < m_adjacencyList[otherIndex].size(); j++) { if (m_adjacencyList[otherIndex][j] == affectedIndex) m_adjacencyList[otherIndex][j] = index; } } // NOLINTEND(*) std::swap(m_edgeMap[index], m_edgeMap.back()); for (size_t i = 0; i < m_edgeMap[index].size(); i++) { size_t edgeIndex = m_edgeMap[index][i]; if (m_edgePairs[edgeIndex].first == affectedIndex) m_edgePairs[edgeIndex].first = index; if (m_edgePairs[edgeIndex].second == affectedIndex) m_edgePairs[edgeIndex].second = index; } if (m_vertexToSubgraph[index] >= 0) m_subgraphToVertices[m_vertexToSubgraph[index]].erase(index); std::swap(m_vertexToSubgraph[index], m_vertexToSubgraph.back()); if (m_vertexToSubgraph[index] >= 0) { m_subgraphToVertices[m_vertexToSubgraph[index]].erase(affectedIndex); m_subgraphToVertices[m_vertexToSubgraph[index]].insert(index); } } m_adjacencyList.pop_back(); m_edgeMap.pop_back(); m_vertexToSubgraph.pop_back(); } void Graph::swapVertexIndices(size_t a, size_t b) { // Swap all references to a and b in m_adjacencyList for (size_t i = 0; i < m_adjacencyList[a].size(); i++) { size_t otherIndex = m_adjacencyList[a][i]; if (otherIndex == b) continue; // NOLINTBEGIN(*) for (size_t j = 0; j < m_adjacencyList[otherIndex].size(); j++) { if (m_adjacencyList[otherIndex][j] == a) { m_adjacencyList[otherIndex][j] = b; break; } } // NOLINTEND(*) } for (size_t i = 0; i < m_adjacencyList[b].size(); i++) { size_t otherIndex = m_adjacencyList[b][i]; if (otherIndex == a) continue; // NOLINTBEGIN(*) for (size_t j = 0; j < m_adjacencyList[otherIndex].size(); j++) { if (m_adjacencyList[otherIndex][j] == b) { m_adjacencyList[otherIndex][j] = a; break; } } // NOLINTEND(*) } std::swap(m_adjacencyList[a], m_adjacencyList[b]); // Update m_edgePairs using info from m_edgeMap for (size_t i = 0; i < m_edgeMap[a].size(); i++) { size_t edgeIndex = m_edgeMap[a][i]; if (m_edgePairs[edgeIndex].first == a) { m_edgePairs[edgeIndex].first = b; if (m_edgePairs[edgeIndex].second == b) m_edgePairs[edgeIndex].second = a; } if (m_edgePairs[edgeIndex].second == a) { m_edgePairs[edgeIndex].second = b; if (m_edgePairs[edgeIndex].first == b) m_edgePairs[edgeIndex].first = a; } } for (size_t i = 0; i < m_edgeMap[b].size(); i++) { size_t edgeIndex = m_edgeMap[b][i]; if (m_edgePairs[edgeIndex].first == b && m_edgePairs[edgeIndex].second != a) m_edgePairs[edgeIndex].first = a; if (m_edgePairs[edgeIndex].second == b && m_edgePairs[edgeIndex].first != a) m_edgePairs[edgeIndex].second = a; } std::swap(m_edgeMap[a], m_edgeMap[b]); } size_t Graph::vertexCount() const { return m_adjacencyList.size(); } size_t Graph::addEdge(size_t a, size_t b) { assert(a < size()); assert(b < size()); if (b < a) std::swap(a, b); std::vector& neighborsA = m_adjacencyList[a]; std::vector& neighborsB = m_adjacencyList[b]; // Ensure edge does not exist already. if (std::find(neighborsA.begin(), neighborsA.end(), b) != neighborsA.end()) { // NOLINTBEGIN(*) for (size_t i = 0; i < m_edgeMap[a].size(); i++) { size_t edgeIndex = m_edgeMap[a][i]; if (m_edgePairs[edgeIndex].first == b || m_edgePairs[edgeIndex].second == b) return edgeIndex; } // NOLINTEND(*) } // Merge subgraphs int subgraphA = m_vertexToSubgraph[a]; int subgraphB = m_vertexToSubgraph[b]; if (subgraphA < 0 && subgraphB < 0) { int newSubgraph = createNewSubgraph(); m_vertexToSubgraph[a] = newSubgraph; m_vertexToSubgraph[b] = newSubgraph; m_subgraphToVertices[newSubgraph].insert(a); m_subgraphToVertices[newSubgraph].insert(b); m_loneVertices.erase(a); m_loneVertices.erase(b); } else if (subgraphA < 0) { m_vertexToSubgraph[a] = subgraphB; m_subgraphToVertices[subgraphB].insert(a); m_loneVertices.erase(a); } else if (subgraphB < 0) { m_vertexToSubgraph[b] = subgraphA; m_subgraphToVertices[subgraphA].insert(b); m_loneVertices.erase(b); } else if (subgraphA != subgraphB) { m_subgraphDirty[subgraphA] = m_subgraphDirty[subgraphA] || m_subgraphDirty[subgraphB]; for (size_t i : m_subgraphToVertices[subgraphB]) { m_subgraphToVertices[subgraphA].insert(i); if (i < m_vertexToSubgraph.size()) m_vertexToSubgraph[i] = subgraphA; else m_vertexToSubgraph.push_back(subgraphA); } // Just leave it empty, it could be reused m_subgraphToVertices[subgraphB].clear(); } // Add the edge to each vertex' adjacency list. neighborsA.push_back(b); neighborsB.push_back(a); // Add the edge to each vertex' incident edge list. size_t newEdgeIndex = edgeCount(); m_edgeMap[a].push_back(newEdgeIndex); m_edgeMap[b].push_back(newEdgeIndex); m_edgePairs.push_back(std::pair(a, b)); return newEdgeIndex; } std::set Graph::checkConectivity(size_t a, size_t b) const { if (a == b) { return std::set(); } std::set visited; bool connected = false; std::stack nextNeighbors; visited.insert(a); nextNeighbors.push(a); while (!nextNeighbors.empty()) { size_t visiting = nextNeighbors.top(); visited.insert(visiting); nextNeighbors.pop(); const std::vector& neighbors = m_adjacencyList[visiting]; for (const auto& n : neighbors) { if (visiting == b) { connected = true; } if (visited.find(n) == visited.end()) { visited.insert(n); nextNeighbors.push(n); } } } if (connected) { return std::set(); } return visited; } void Graph::removeEdge(size_t a, size_t b) { assert(a < size()); assert(b < size()); std::vector& neighborsA = m_adjacencyList[a]; std::vector& neighborsB = m_adjacencyList[b]; auto iter = std::find(neighborsA.begin(), neighborsA.end(), b); if (iter == neighborsA.end()) return; std::swap(*iter, neighborsA.back()); neighborsA.pop_back(); std::swap(*std::find(neighborsB.begin(), neighborsB.end(), a), neighborsB.back()); neighborsB.pop_back(); size_t edgeIndex; for (size_t i = 0; i < m_edgeMap[a].size(); i++) { edgeIndex = m_edgeMap[a][i]; const std::pair& pair = m_edgePairs[edgeIndex]; if (pair.first == b || pair.second == b) { std::swap(m_edgeMap[a][i], m_edgeMap[a].back()); m_edgeMap[a].pop_back(); break; } } for (size_t i = 0; i < m_edgeMap[b].size(); i++) { if (m_edgeMap[b][i] == edgeIndex) { std::swap(m_edgeMap[b][i], m_edgeMap[b].back()); m_edgeMap[b].pop_back(); break; } } std::swap(m_edgePairs[edgeIndex], m_edgePairs.back()); m_edgePairs.pop_back(); size_t affectedIndex = m_edgePairs.size(); if (affectedIndex != edgeIndex) { std::vector& edgeList1 = m_edgeMap[m_edgePairs[edgeIndex].first]; *std::find(edgeList1.begin(), edgeList1.end(), affectedIndex) = edgeIndex; std::vector& edgeList2 = m_edgeMap[m_edgePairs[edgeIndex].second]; *std::find(edgeList2.begin(), edgeList2.end(), affectedIndex) = edgeIndex; } // Mark the subgraph as dirty, leave the work for later if (m_vertexToSubgraph[a] >= 0) m_subgraphDirty[m_vertexToSubgraph[a]] = true; } void Graph::removeEdge(size_t edgeIndex) { assert(edgeIndex < edgeCount()); const std::pair& pair = m_edgePairs[edgeIndex]; removeEdge(pair.first, pair.second); } void Graph::removeEdges() { for (size_t i = 0; i < m_adjacencyList.size(); ++i) { m_adjacencyList[i].clear(); m_edgeMap[i].clear(); m_vertexToSubgraph[i] = -1; m_loneVertices.insert(i); } m_edgePairs.clear(); m_subgraphToVertices.clear(); m_subgraphDirty.clear(); } void Graph::removeEdges(size_t index) { m_vertexToSubgraph[index] = -1; m_loneVertices.insert(index); // Mark the subgraph as dirty, leave the work for later if (m_vertexToSubgraph[index] >= 0) m_subgraphDirty[m_vertexToSubgraph[index]] = true; const std::vector& edges = m_edgeMap[index]; for (size_t i = 0; i < edges.size(); ++i) removeEdge(edges[i]); } void Graph::editEdgeInPlace(size_t edgeIndex, size_t a, size_t b) { auto& pair = m_edgePairs[edgeIndex]; // Remove references to the deleted edge from both endpoints. for (size_t i = 0; i < m_edgeMap[pair.first].size(); i++) { std::swap(m_edgeMap[pair.first][i], m_edgeMap[pair.first].back()); m_edgeMap[pair.first].pop_back(); } for (size_t i = 0; i < m_edgeMap[pair.second].size(); i++) { std::swap(m_edgeMap[pair.second][i], m_edgeMap[pair.second].back()); m_edgeMap[pair.second].pop_back(); } m_edgeMap[a].push_back(edgeIndex); m_edgeMap[b].push_back(edgeIndex); pair.first = a; pair.second = b; } void Graph::swapEdgeIndices(size_t edgeIndex1, size_t edgeIndex2) { // Find the 4 endpoints of both edges. const std::pair& pair1 = m_edgePairs[edgeIndex1]; std::array changeTo2; // NOLINTBEGIN(*) for (size_t i = 0; i < m_edgeMap[pair1.first].size(); i++) { if (m_edgeMap[pair1.first][i] == edgeIndex1) { changeTo2[0] = &m_edgeMap[pair1.first][i]; } } for (size_t i = 0; i < m_edgeMap[pair1.second].size(); i++) { if (m_edgeMap[pair1.second][i] == edgeIndex1) { changeTo2[1] = &m_edgeMap[pair1.second][i]; } } const std::pair& pair2 = m_edgePairs[edgeIndex2]; std::array changeTo1; for (size_t i = 0; i < m_edgeMap[pair2.first].size(); i++) { if (m_edgeMap[pair2.first][i] == edgeIndex2) { changeTo1[0] = &m_edgeMap[pair2.first][i]; } } for (size_t i = 0; i < m_edgeMap[pair2.second].size(); i++) { if (m_edgeMap[pair2.second][i] == edgeIndex2) { changeTo1[1] = &m_edgeMap[pair2.second][i]; } } // NOLINTEND(*) /* Swap m_edgeMap values only after reading everything, to avoid race condition. */ *changeTo2[0] = edgeIndex2; *changeTo2[1] = edgeIndex2; *changeTo1[0] = edgeIndex1; *changeTo1[1] = edgeIndex1; std::swap(m_edgePairs[edgeIndex1], m_edgePairs[edgeIndex2]); } size_t Graph::edgeCount() const { return m_edgePairs.size(); } std::vector Graph::neighbors(size_t index) const { if (index == size()) { std::vector emptyVector; return std::vector(emptyVector); } assert(index < size()); return std::vector(m_adjacencyList[index]); } std::vector Graph::edges(size_t index) const { assert(index < size()); return std::vector(m_edgeMap[index]); } std::pair Graph::endpoints(size_t index) const { assert(index < edgeCount()); return std::pair(m_edgePairs[index]); } size_t Graph::degree(size_t index) const { return neighbors(index).size(); } bool Graph::containsEdge(size_t a, size_t b) const { assert(a < size()); assert(b < size()); const std::vector& neighborsA = neighbors(a); return std::find(neighborsA.begin(), neighborsA.end(), b) != neighborsA.end(); } const Array>& Graph::edgePairs() const { return m_edgePairs; } int Graph::createNewSubgraph() const { // Try to find an empty subgraph to reuse for (size_t i = 0; i < m_subgraphToVertices.size(); i++) { if (!m_subgraphToVertices[i].size()) { m_subgraphDirty[i] = false; return int(i); } } // Otherwise, extend the list int r = m_subgraphDirty.size(); m_subgraphToVertices.emplace_back(); m_subgraphDirty.push_back(false); return r; } void Graph::checkSplitSubgraph(int subgraph) const { if (!m_subgraphDirty[subgraph]) return; m_subgraphDirty[subgraph] = false; // First, set all vertices to subgraph -1 (meaning not visited) for (size_t i : m_subgraphToVertices[subgraph]) m_vertexToSubgraph[i] = -1; // Then, try to classify all vertices in some subgraph int currentSubgraph = subgraph; std::set inputVertices = m_subgraphToVertices[subgraph]; m_subgraphToVertices[subgraph] = std::set(); for (size_t i : inputVertices) { if (m_vertexToSubgraph[i] < 0) { if (currentSubgraph < 0) currentSubgraph = createNewSubgraph(); // Walk through all connected vertices std::vector verticesToVisit; verticesToVisit.push_back(i); do { size_t currentVertex = verticesToVisit.back(); verticesToVisit.pop_back(); // Assign new vertices to the current subgraph if (m_vertexToSubgraph[currentVertex] < 0) { m_vertexToSubgraph[currentVertex] = currentSubgraph; m_subgraphToVertices[currentSubgraph].insert(currentVertex); const std::vector neighborList = neighbors(currentVertex); verticesToVisit.insert(verticesToVisit.end(), neighborList.begin(), neighborList.end()); } } while (verticesToVisit.size()); currentSubgraph = -1; } } } void Graph::updateSubgraphs() const { for (size_t v : m_loneVertices) { int newSubgraph = createNewSubgraph(); m_vertexToSubgraph[v] = newSubgraph; m_subgraphToVertices[newSubgraph].insert(v); } m_loneVertices.clear(); for (size_t i = 0; i < m_subgraphToVertices.size(); i++) { checkSplitSubgraph(i); } } std::vector> Graph::connectedComponents() const { updateSubgraphs(); std::vector> r; for (const std::set& s : m_subgraphToVertices) { if (!s.empty()) r.push_back(s); } return r; } std::set Graph::connectedComponent(size_t index) const { size_t subgraphIndex = subgraph(index); return m_subgraphToVertices[subgraphIndex]; } size_t Graph::subgraphsCount() const { updateSubgraphs(); size_t r = 0; for (const std::set& s : m_subgraphToVertices) { if (!s.empty()) r++; } return r; } size_t Graph::subgraph(size_t element) const { int r = m_vertexToSubgraph[element]; // Index -1 means disconnected (its own subgraph) if (r < 0) { r = m_subgraphToVertices.size(); m_subgraphToVertices.push_back(std::set()); m_subgraphToVertices[r].insert(element); m_subgraphDirty[r] = false; return r; } if (m_subgraphDirty[r]) { checkSplitSubgraph(r); r = m_vertexToSubgraph[element]; } return r; } size_t Graph::subgraphCount(size_t element) const { int subgraphIndex = subgraph(element); return m_subgraphToVertices[subgraphIndex].size(); } size_t Graph::getConnectedID(size_t index) const { return subgraph(index); } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/graph.h000066400000000000000000000162531506155467400203560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_GRAPH_H #define AVOGADRO_CORE_GRAPH_H #include "avogadrocoreexport.h" #include "array.h" #include #include namespace Avogadro::Core { /** * @class Graph graph.h * @brief The Graph class represents a graph data structure. * * A graph consists of vertices and edges, wherein every edge connects two * vertices. Each vertex is assigned an index, starting from 0 up to size() - 1. * Each edge is also assigned an index, from 0 to edgeCount() - 1. */ class AVOGADROCORE_EXPORT Graph { public: /** Creates a new, empty graph. */ Graph() = default; /** Creates a new graph containing size @p n vertices. */ explicit Graph(size_t n); /** Destroys the graph. */ ~Graph() = default; /** * Sets the number of vertices in the graph to size @p n. * * If @p n is smaller than \c size(), this removes all vertices with index * @p n or higher, as well as any edges connected to them, while preserving * all other vertices and edges. These vertices keep their existing indices, * while no guarantee is made regarding preserved edge indices. * * If @p n is larger than \c size(), a number of unconnected vertices are * added, up to the requested size. All existing vertices and edges are * preserved in their current form. */ void setSize(size_t n); /** @return the number of vertices in the graph. */ size_t size() const; /** @return \c true if the graph is empty (i.e. size() == \c 0). */ bool isEmpty() const; /** Removes all vertices and edges from the graph. */ void clear(); /** * Adds a vertex to the graph and returns its index. * The new vertex is initially not connected. All existing vertices and edges * are preserved and their indices unchanged. */ size_t addVertex(); /** * Removes the vertex at @p index from the graph, as well as all edges to it. * If @p index is not the highest vertex index in the graph, the vertex with * highest index will be assigned the index of the removed vertex. All other * vertices will keep their indices. No guarantees are made regarding edge * indices. */ void removeVertex(size_t index); /** * Swaps two vertices' indices, without affecting connectivity. * All other vertices and all edges keep their existing indices. */ void swapVertexIndices(size_t a, size_t b); /** @return the number of vertices in the graph. */ size_t vertexCount() const; /** * Adds an edge between vertices @p a and @p b and returns its index. * All existing vertices and edges are preserved unchanged. */ size_t addEdge(size_t a, size_t b); /** * Removes the edge between vertices @p a and @p b. * All vertices keep their indices. If the removed edge has an index lower * than the highest edge index in the graph, the edge with the highest index * is given the index of the removed edge. All other edges remain unchanged. */ void removeEdge(size_t a, size_t b); /** * Removes edge with index @p edgeIndex. * All vertices keep their indices. If @p edgeIndex is lower than the highest * edge index in the graph, the edge with the highest index is given the index * of the removed edge. All other edges remain unchanged. */ void removeEdge(size_t edgeIndex); /** Removes all of the edges from the graph, without affecting vertices. */ void removeEdges(); /** * Removes all of the edges that contain the vertex at @p index from the * graph. */ void removeEdges(size_t index); /** * Removes the edge at @p edgeIndex, and creates a new one between vertices * @p a and @p b, with the same index as the removed edge. All other edges and * vertices keep their current indices. */ void editEdgeInPlace(size_t edgeIndex, size_t a, size_t b); /** * Swaps two edges' indices, without affecting connectivity. * All other edges and all vertices keep their current indices. */ void swapEdgeIndices(size_t edgeIndex1, size_t edgeIndex2); /** @return the number of edges in the graph. */ size_t edgeCount() const; /** * @return a vector containing the indices of each vertex that the vertex at * index shares an edge with. */ std::vector neighbors(size_t index) const; /** * @return a vector containing the indices of each edge that the vertex at * @p index is an endpoint of; that is, the edges incident at it. */ std::vector edges(size_t index) const; /** * @return the indices of the two vertices that the edge at @p index connects; * that is, its endpoints. */ std::pair endpoints(size_t edgeIndex) const; /** @return the degree of the vertex at @p index. */ size_t degree(size_t index) const; /** * @return \c true if the graph contains an edge between vertices @p a and * @p b. */ bool containsEdge(size_t a, size_t b) const; /** * @return an array with all edges, where every element contains the indices * of both endpoints of the edge with index equal to the element's array * index. */ const Array>& edgePairs() const; /** * @return a vector of vector containing the indices of each vertex in each * connected component in the graph. */ std::vector> connectedComponents() const; /** * @return a set containing the indices of each vertex connected with @p * index. */ std::set connectedComponent(size_t index) const; /** @return the number of connected subgraphs. */ size_t subgraphsCount() const; /** @return the subgraph ID of the connected subgraph @p index lies in. */ size_t subgraph(size_t index) const; /** * @return the size of the connected subgraph that includes @p index, * that is, the number of connected vertices in it. */ size_t subgraphCount(size_t index) const; /** * @return the subgraph ID of the connected subgraph @p index lies in. */ size_t getConnectedID(size_t index) const; private: std::set checkConectivity(size_t a, size_t b) const; std::vector> m_adjacencyList; std::vector> m_edgeMap; Array> m_edgePairs; /** @return the (new or reused) index of a newly created empty subgraph. */ int createNewSubgraph() const; /** * If @p subgraph is marked as dirty, traverse it * to check if it has split into many new subgraphs, * and mark the resulting subgraph(s) as clean. */ void checkSplitSubgraph(int subgraph) const; /** * Traverse and mark clean all dirty subgraphs, * and create new subgraphs for all lone vertices. * All subgraph data becomes synchronized as a result. */ void updateSubgraphs() const; mutable std::vector m_vertexToSubgraph; mutable std::vector> m_subgraphToVertices; mutable std::vector m_subgraphDirty; mutable std::set m_loneVertices; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_GRAPH_H avogadrolibs-1.101.0/avogadro/core/layer.cpp000066400000000000000000000044331506155467400207210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "layer.h" #include namespace Avogadro::Core { void Layer::addAtom(size_t layer) { addAtom(layer, m_atomAndLayers.size()); } void Layer::addAtom(size_t layer, Index atom) { assert(layer <= m_maxLayer); if (atom == m_atomAndLayers.size()) { m_atomAndLayers.push_back(layer); } else if (atom > m_atomAndLayers.size()) { m_atomAndLayers.resize(layer + 1, MaxIndex); m_atomAndLayers[atom] = layer; } else { m_atomAndLayers[atom] = layer; } } void Layer::addAtomToActiveLayer(Index atom) { addAtom(m_activeLayer, atom); } void Layer::setActiveLayer(size_t layer) { assert(layer <= m_maxLayer + 1); m_activeLayer = layer; } void Layer::removeAtom(Index atom) { m_atomAndLayers.swapAndPop(atom); } void Layer::addLayer() { ++m_maxLayer; } void Layer::addLayer(size_t layer) { assert(layer <= m_maxLayer + 1); for (auto& atomLayer : m_atomAndLayers) { if (atomLayer >= layer) { ++atomLayer; } } ++m_maxLayer; } size_t Layer::getLayerID(Index atom) const { if (atom >= m_atomAndLayers.size()) { return MaxIndex; } else { return m_atomAndLayers[atom]; } } void Layer::clear() { m_atomAndLayers.clear(); m_activeLayer = m_maxLayer = 0; } size_t Layer::activeLayer() const { return m_activeLayer; } size_t Layer::maxLayer() const { return m_maxLayer; } size_t Layer::atomCount() const { return m_atomAndLayers.size(); } void Layer::removeLayer(size_t layer) { assert(layer <= m_maxLayer); if (m_maxLayer >= 1) { for (auto it = m_atomAndLayers.begin(); it != m_atomAndLayers.end();) { if (*it == layer) { it = m_atomAndLayers.erase(it); } else { if (*it > layer) { --(*it); } ++it; } } --m_maxLayer; } } void Layer::swapLayer(Index a, Index b) { // Allow Argument Dependent Lookup for swap using std::swap; swap(m_atomAndLayers[a], m_atomAndLayers[b]); } size_t Layer::layerCount() const { return m_maxLayer + 1; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/layer.h000066400000000000000000000036221506155467400203650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_LAYER_H #define AVOGADRO_CORE_LAYER_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include namespace Avogadro::Core { /** * @class Layer layer.h * @brief The Layer class represents a relation one to one between atoms ID * and layer ID, and stores the unique active layer. * Layer's ID are consecutively and there can't be a ID bigger than @p * m_maxLayer. */ class AVOGADROCORE_EXPORT Layer { public: Layer() = default; ~Layer() = default; // att atom to param layer void addAtom(size_t layer); void addAtom(size_t layer, Index atom); void addAtomToActiveLayer(Index atom); void removeAtom(Index atom); void removeLayer(size_t layer); /** @return the layer ID from the @p atom. */ size_t getLayerID(Index atom) const; /** @return the active Layer. */ size_t activeLayer() const; /** @return the maximum layer allowed. */ size_t maxLayer() const; /** @return the number of layers. */ size_t layerCount() const; /** @return The number of atoms. */ size_t atomCount() const; /** remove all IDs. */ void clear(); /** increase the maximum layer allowed .*/ void addLayer(); /** insert a layer at @p layer, equal or bigger previous layers will be * shifted. */ void addLayer(size_t layer); /** change @p m_activeLayer. */ void setActiveLayer(size_t layer); /** swap the layer ID from @p a and @p b. */ void swapLayer(Index a, Index b); private: Core::Array m_atomAndLayers; size_t m_activeLayer = 0; size_t m_maxLayer = 0; }; } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/layermanager.cpp000066400000000000000000000051321506155467400222510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "layermanager.h" #include namespace Avogadro::Core { using std::make_shared; using std::map; using std::shared_ptr; const Molecule* LayerManager::m_activeMolecule = nullptr; map> LayerManager::m_molToInfo; Layer& LayerManager::getMoleculeLayer() { assert(m_activeMolecule != nullptr); auto it = m_molToInfo.find(m_activeMolecule); assert(it != m_molToInfo.end()); return it->second->layer; } Layer& LayerManager::getMoleculeLayer(const Molecule* mol) { assert(mol != nullptr); auto it = m_molToInfo.find(mol); if (it == m_molToInfo.end()) { m_molToInfo[mol] = make_shared(mol); } return m_molToInfo[mol]->layer; } shared_ptr LayerManager::getMoleculeInfo() { assert(m_activeMolecule != nullptr); return m_molToInfo[m_activeMolecule]; } shared_ptr LayerManager::getMoleculeInfo(const Molecule* mol) { assert(mol != nullptr); auto it = m_molToInfo.find(mol); if (it == m_molToInfo.end()) { m_molToInfo[mol] = make_shared(mol); } return m_molToInfo[mol]; } Layer& LayerManager::getMoleculeLayer(const Molecule* original, const Molecule* copy) { assert(original != nullptr); assert(copy != nullptr); auto it = m_molToInfo.find(original); if (it == m_molToInfo.end()) { auto molecule = make_shared(original); m_molToInfo[original] = molecule; m_molToInfo[copy] = molecule; return m_molToInfo[original]->layer; } else { m_molToInfo[copy] = it->second; return it->second->layer; } } void LayerManager::deleteMolecule(const Molecule* mol) { assert(mol != nullptr); auto aux = m_molToInfo.find(mol); if (aux != m_molToInfo.end()) { auto id = aux->second->mol; if (id == mol) { auto it = m_molToInfo.begin(); while (it != m_molToInfo.end()) { if (id == it->second->mol) { it = m_molToInfo.erase(it); } else { ++it; } } } else { if (m_activeMolecule == mol) { m_activeMolecule = aux->second->mol; } m_molToInfo.erase(aux); } } } size_t LayerManager::layerCount() { assert(m_activeMolecule != nullptr); return m_molToInfo[m_activeMolecule]->layer.maxLayer() + 1; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/layermanager.h000066400000000000000000000063231506155467400217210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_LAYERMANAGER_H #define AVOGADRO_CORE_LAYERMANAGER_H #include "avogadrocoreexport.h" #include "array.h" #include "layer.h" #include #include #include #include #include #include namespace Avogadro::Core { class Molecule; /** * @class LayerData layermanager.h * @brief Interface to store layer data structure. */ struct LayerData { LayerData(std::string save = "") { deserialize(save); } /** save custom data, base save should never be called */ virtual std::string serialize() { return ""; } /** load the saved @p save data and wait to know the class type to recreate it */ virtual void deserialize(std::string save) { m_save = save; } virtual ~LayerData() = default; virtual LayerData* clone() { return new LayerData(serialize()); }; /** get the saved data */ std::string getSave() const { return m_save; } protected: std::string boolToString(bool b) { return b ? "true" : "false"; } bool stringToBool(std::string b) { return b == "true"; } std::string m_save; }; /** * @class MoleculeInfo layermanager.h * @brief All layer dependent data. Original molecule @p mol, is layer hidden * @p visible, accepts edits @p locked, and key-value data like @p enable, * and custom data @p settings. */ struct MoleculeInfo { const Molecule* mol; std::vector visible; std::vector locked; std::map> enable; std::map> settings; Layer layer; std::set loaded; MoleculeInfo(const Molecule* m) : mol(m) { locked.push_back(false); visible.push_back(true); } void clear() { visible.clear(); locked.clear(); enable.clear(); layer.clear(); } }; /** * @class LayerManager layermanager.h * @brief */ class AVOGADROCORE_EXPORT LayerManager { public: /** @return active molecule Layer */ static Layer& getMoleculeLayer(); /** @return Layer from @p mol and creates MoleculeInfo if not exists */ static Layer& getMoleculeLayer(const Molecule* mol); /** @return Layer from @p original and links @p original MoleculeInfo to @p * copy */ static Layer& getMoleculeLayer(const Molecule* original, const Molecule* copy); /** @return the MoleculeInfo from active molecule */ static std::shared_ptr getMoleculeInfo(); /** @return the MoleculeInfo from @p mol */ static std::shared_ptr getMoleculeInfo(const Molecule* mol); /** remove all data related to @p mol */ static void deleteMolecule(const Molecule* mol); /** @return the layer quantity from activeMolecule */ static size_t layerCount(); protected: static const Molecule* m_activeMolecule; static std::map> m_molToInfo; }; } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/matrix.h000066400000000000000000000016361506155467400205600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_MATRIX_H #define AVOGADRO_CORE_MATRIX_H #include "avogadrocore.h" #include namespace Avogadro { /** Typedefs for vector types. */ using Matrix2 = Eigen::Matrix; using Matrix3 = Eigen::Matrix; using Matrix4 = Eigen::Matrix; using MatrixX = Eigen::Matrix; using Matrix2f = Eigen::Matrix; using Matrix3f = Eigen::Matrix; using Matrix4f = Eigen::Matrix; using MatrixXf = Eigen::Matrix; } // namespace Avogadro #endif // AVOGADRO_CORE_MATRIX_H avogadrolibs-1.101.0/avogadro/core/mdlvalence_p.h000066400000000000000000000321071506155467400217020ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_MDLVALENCE_P_H #define AVOGADRO_CORE_MDLVALENCE_P_H namespace Avogadro::Core { /** * Calculate the full valency (e.g. number of expected bonds) for a given atom. * This function is adapted from the MDL valence model to indicate when an atom * is overbonded. * @param atomicNumber Atomic number of atom. * @param charge Formal charge of atom. * @param numBonds Number of existing bonds to atom. * @return The total number of expected bonds to the atom to satisfy valency. * May be less than @a numBonds if atom is overbonded. */ static unsigned int atomValence(const unsigned char atomicNumber, const int charge, const unsigned int numBonds) { switch (atomicNumber) { case 1: // H case 3: // Li case 11: // Na case 19: // K case 37: // Rb case 55: // Cs case 87: // Fr if (charge == 0) return 1; break; case 4: // Be case 12: // Mg case 20: // Ca case 38: // Sr case 56: // Ba case 88: // Ra switch (charge) { case 0: return 2; case 1: return 1; } break; case 5: // B switch (charge) { case -4: return 1; case -3: return 2; case -2: if (numBonds <= 3) return 3; return 5; case -1: return 4; case 0: return 3; case 1: return 2; case 2: return 1; } break; case 6: // C switch (charge) { case -3: return 1; case -2: return 2; case -1: if (numBonds <= 3) return 3; return 5; case 0: return 4; case 1: return 3; case 2: return 2; case 3: return 1; } break; case 7: // N switch (charge) { case -2: return 1; case -1: return 2; case 0: if (numBonds != 5) return 3; return 5; case 1: return 4; case 2: return 3; case 3: return 2; case 4: return 1; } break; case 8: // O switch (charge) { case -1: return 1; case 0: return 2; case 1: if (numBonds <= 3) return 3; return 5; case 2: return 4; case 3: return 3; case 4: return 2; case 5: return 1; } break; case 9: // F switch (charge) { case 0: return 1; case 1: return 2; case 2: if (numBonds <= 3) return 3; return 5; case 3: return 4; case 4: return 3; case 5: return 2; case 6: return 1; } break; case 13: // Al switch (charge) { case -4: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -3: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case -2: if (numBonds <= 3) return 3; return 5; case -1: return 4; case 0: return 3; case 1: return 2; case 2: return 1; } break; case 14: // Si switch (charge) { case -3: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -2: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case -1: if (numBonds <= 3) return 3; return 5; case 0: return 4; case 1: return 3; case 2: return 2; case 3: return 1; } break; case 15: // P switch (charge) { case -2: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -1: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 0: if (numBonds <= 3) return 3; return 5; case 1: return 4; case 2: return 3; case 3: return 2; case 4: return 1; } break; case 16: // S switch (charge) { case -1: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case 0: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 1: if (numBonds <= 3) return 3; return 5; case 2: return 4; case 3: return 3; case 4: return 2; case 5: return 1; } break; case 17: // Cl switch (charge) { case 0: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case 1: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 2: if (numBonds <= 3) return 3; return 5; case 3: return 4; case 4: return 3; case 5: return 2; case 6: return 1; } break; case 31: // Ga switch (charge) { case -4: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -3: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case -2: if (numBonds <= 3) return 3; return 5; case -1: return 4; case 0: return 3; case 2: return 1; } break; case 32: // Ge switch (charge) { case -3: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -2: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case -1: if (numBonds <= 3) return 3; return 5; case 0: return 4; case 1: return 3; case 3: return 1; } break; case 33: // As switch (charge) { case -2: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -1: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 0: if (numBonds <= 3) return 3; return 5; case 1: return 4; case 2: return 3; case 4: return 1; } break; case 34: // Se switch (charge) { case -1: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case 0: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 1: if (numBonds <= 3) return 3; return 5; case 2: return 4; case 3: return 3; case 5: return 1; } break; case 35: // Br switch (charge) { case 0: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case 1: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 2: if (numBonds <= 3) return 3; return 5; case 3: return 4; case 4: return 3; case 6: return 1; } break; case 49: // In switch (charge) { case -4: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -3: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case -2: if (numBonds <= 3) return 3; return 5; case -1: if (numBonds <= 2) return 2; return 4; case 0: return 3; case 2: return 1; } break; case 50: // Sn case 82: // Pb switch (charge) { case -3: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -2: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case -1: if (numBonds <= 3) return 3; return 5; case 0: if (numBonds <= 2) return 2; return 4; case 1: return 3; case 3: return 1; } break; case 51: // Sb case 83: // Bi switch (charge) { case -2: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -1: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 0: if (numBonds <= 3) return 3; return 5; case 1: if (numBonds <= 2) return 2; return 4; case 2: return 3; case 4: return 1; } break; case 52: // Te case 84: // Po switch (charge) { case -1: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case 0: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 1: if (numBonds <= 3) return 3; return 5; case 2: if (numBonds <= 2) return 2; return 4; case 3: return 3; case 5: return 1; } break; case 53: // I case 85: // At switch (charge) { case 0: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case 1: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case 2: if (numBonds <= 3) return 3; return 5; case 3: if (numBonds <= 2) return 2; return 4; case 4: return 3; case 6: return 1; } break; case 81: // Tl switch (charge) { case -4: if (numBonds <= 1) return 1; if (numBonds <= 3) return 3; if (numBonds <= 5) return 5; return 7; case -3: if (numBonds <= 2) return 2; if (numBonds <= 4) return 4; return 6; case -2: if (numBonds <= 3) return 3; return 5; case -1: if (numBonds <= 2) return 2; return 4; case 0: if (numBonds <= 1) return 1; return 3; } break; } return numBonds; } } // end namespace Avogadro::Core #endif // AVOGADRO_CORE_MDLVALENCE_P_Havogadrolibs-1.101.0/avogadro/core/mesh.cpp000066400000000000000000000137431506155467400205450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "mesh.h" #include "mutex.h" #include "neighborperceiver.h" namespace Avogadro::Core { Mesh::Mesh() : m_stable(true), m_other(0), m_cube(0), m_lock(new Mutex) { m_vertices.reserve(100); m_normals.reserve(100); m_colors.reserve(1); } Mesh::Mesh(const Mesh& other) : m_vertices(other.m_vertices), m_normals(other.m_normals), m_colors(other.m_colors), m_triangles(other.m_triangles), m_name(other.m_name), m_stable(true), m_isoValue(other.m_isoValue), m_other(other.m_other), m_cube(other.m_cube), m_lock(new Mutex) { } Mesh::~Mesh() { delete m_lock; m_lock = nullptr; } bool Mesh::reserve(unsigned int size, bool useColors) { m_vertices.reserve(size); m_normals.reserve(size); if (useColors) m_colors.reserve(size); return true; } void Mesh::setStable(bool isStable) { m_stable = isStable; } bool Mesh::stable() { return m_stable; } const Core::Array& Mesh::vertices() const { return m_vertices; } const Vector3f* Mesh::vertex(int n) const { return &(m_vertices[n * 3]); } bool Mesh::setTriangles(const Core::Array& values) { m_triangles.clear(); m_triangles = values; return true; } const Core::Array& Mesh::triangles() const { return m_triangles; } bool Mesh::setVertices(const Core::Array& values) { m_vertices.clear(); m_vertices = values; return true; } bool Mesh::addVertices(const Core::Array& values) { if (m_vertices.capacity() < m_vertices.size() + values.size()) m_vertices.reserve(m_vertices.capacity() * 2); if (values.size() % 3 == 0) { for (const auto& value : values) m_vertices.push_back(value); return true; } else { return false; } } const Core::Array& Mesh::normals() const { return m_normals; } const Vector3f* Mesh::normal(int n) const { return &(m_normals[n * 3]); } bool Mesh::setNormals(const Core::Array& values) { m_normals.clear(); m_normals = values; return true; } bool Mesh::addNormals(const Core::Array& values) { if (m_normals.capacity() < m_normals.size() + values.size()) m_normals.reserve(m_normals.capacity() * 2); if (values.size() % 3 == 0) { for (const auto& value : values) m_normals.push_back(value); return true; } else { return false; } } const Core::Array& Mesh::colors() const { return m_colors; } const Color3f* Mesh::color(int n) const { // If there is only one color return that, otherwise colored by vertex. if (m_colors.size() == 1) return m_colors.data(); else return &(m_colors[n * 3]); } bool Mesh::setColors(const Core::Array& values) { m_colors.clear(); m_colors = values; return true; } bool Mesh::addColors(const Core::Array& values) { if (m_colors.capacity() < m_colors.size() + values.size()) m_colors.reserve(m_colors.capacity() * 2); if (values.size() % 3 == 0) { for (auto value : values) m_colors.push_back(value); return true; } else { return false; } } bool Mesh::valid() const { return (m_vertices.size() == m_normals.size()) && (m_colors.size() == 1 || m_colors.size() == m_vertices.size()); } bool Mesh::clear() { m_vertices.clear(); m_normals.clear(); m_colors.clear(); return true; } Mesh& Mesh::operator=(const Mesh& other) { m_vertices = other.m_vertices; m_normals = other.m_normals; m_colors = other.m_colors; m_name = other.m_name; m_isoValue = other.m_isoValue; m_triangles = other.m_triangles; return *this; } void Mesh::smooth(int iterationCount) { if (m_vertices.empty() || iterationCount <= 0) return; // Build vertex adjacency information from triangles (1-ring) std::vector> adjacencyList(m_vertices.size()); for (const auto& tri : m_triangles) { size_t i = static_cast(tri.x()); size_t j = static_cast(tri.y()); size_t k = static_cast(tri.z()); adjacencyList[i].push_back(j); adjacencyList[i].push_back(k); adjacencyList[j].push_back(i); adjacencyList[j].push_back(k); adjacencyList[k].push_back(i); adjacencyList[k].push_back(j); } // Remove duplicate neighbors and sort for faster lookups later (if needed) for (auto& neighbors : adjacencyList) { std::sort(neighbors.begin(), neighbors.end()); neighbors.erase(std::unique(neighbors.begin(), neighbors.end()), neighbors.end()); } float weight = 1.0f; for (int iteration = 0; iteration < iterationCount; ++iteration) { Array newVertices = m_vertices; // Store smoothed vertices for (size_t i = 0; i < m_vertices.size(); ++i) { Vector3f sum(0.0f, 0.0f, 0.0f); size_t neighborCount = adjacencyList[i].size(); if (neighborCount > 0) { // Prevent division by zero for isolated vertices for (size_t neighbor : adjacencyList[i]) { sum += m_vertices[neighbor]; } sum += weight * m_vertices[i]; newVertices[i] = sum / (weight + neighborCount); } // Else keep the original vertex position if isolated } m_vertices = newVertices; // Update vertices after processing all } m_normals.clear(); m_normals.resize(m_vertices.size(), Vector3f(0.0f, 0.0f, 0.0f)); for (const auto& tri : m_triangles) { size_t i = static_cast(tri.x()); size_t j = static_cast(tri.y()); size_t k = static_cast(tri.z()); Vector3f a = m_vertices[i]; Vector3f b = m_vertices[j]; Vector3f c = m_vertices[k]; Vector3f triangleNormal = (b - a).cross(c - a).normalized(); m_normals[i] += triangleNormal; m_normals[j] += triangleNormal; m_normals[k] += triangleNormal; } for (auto& normal : m_normals) { normal.normalize(); } } } // End namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/mesh.h000066400000000000000000000136661506155467400202160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_MESH_H #define AVOGADRO_CORE_MESH_H #include "avogadrocoreexport.h" #include "array.h" #include "color3f.h" #include "vector.h" namespace Avogadro::Core { class Molecule; class Mutex; /** * @class Mesh mesh.h * @brief Encapsulation of a triangular mesh that makes up a surface. * @author Marcus D. Hanwell * * The Mesh class is a data container that provides a Mesh object. All * meshes should be owned by a Molecule. It should also be removed by the * Molecule that owns it. Meshes encapsulate triangular meshes that can also * have colors associated with each vertex. */ class MeshPrivate; class AVOGADROCORE_EXPORT Mesh { public: /** * Constructor. */ Mesh(); /** * Copy constructor */ Mesh(const Mesh& other); /** * Destructor. */ ~Mesh(); /** * Reserve the expected space for the mesh. This causes all member array * storage to call the reserve function with the number specified. * @param size Expected size of the mesh. * @param colors Should the colors array reserve this space too? Defaults * to false. * @return True on success. */ bool reserve(unsigned int size, bool colors = false); /** * This function allows long running calculations to mark the mesh as in * progress. * @param stable Indicate that the Mesh is currently being modified. */ void setStable(bool stable); /** * Indicate whether the Mesh is complete or currently being modified. In * general using Mesh values from an unstable Mesh is not advisable. * @return True if the Mesh is complete, false if it is being modified. */ bool stable(); /** * Set the iso value that was used to generate the Mesh. */ void setIsoValue(float value) { m_isoValue = value; } /** * @return The iso value used to generate the Mesh. */ float isoValue() const { return m_isoValue; } /** * Set the unique id of the other Mesh if this Mesh is part of a pair. */ void setOtherMesh(unsigned int other) { m_other = other; } /** * @return The unique id of the other Mesh if this is part of a pair. */ unsigned int otherMesh() const { return m_other; } /** * Set the unique id of the Cube the Mesh was generated from. */ void setCube(unsigned int cube_) { m_cube = cube_; } /** * @return The unique id of the Cube the Mesh was generated from. */ unsigned int cube() const { return m_cube; } /** * @return Array containing all of the vertices in a one dimensional array. */ const Core::Array& vertices() const; /** * @return The number of vertices. */ unsigned int numVertices() const { return static_cast(m_vertices.size()); } /** * @return Pointer to the first vertex of the specified triangle. */ const Vector3f* vertex(int n) const; bool setTriangles(const Core::Array& values); const Core::Array& triangles() const; /** * Clear the vertices vector and assign new values. */ bool setVertices(const Core::Array& values); /** * Add one or more vertices, i.e., the array is expected to be of length * 3 x n where n is an integer. */ bool addVertices(const Core::Array& values); /** * @return Array containing all of the normals in a one-dimensional array. */ const Core::Array& normals() const; /** * @return The number of normals. */ unsigned int numNormals() const { return static_cast(m_normals.size()); } /** * @return Pointer to the first normal of the specified triangle. */ const Vector3f* normal(int n) const; /** * Clear the normals array and assign new values. */ bool setNormals(const Core::Array& values); /** * Add one or more normals, i.e., the array is expected to be of length * 3 x n where n is an integer. */ bool addNormals(const Core::Array& values); /** * @return Array containing all of the colors in a one-dimensional array. */ const Core::Array& colors() const; /** * @return Pointer to the first color of the specified triangle. */ const Color3f* color(int n) const; /** * Clear the colors array and assign new values. */ bool setColors(const Core::Array& values); /** * Add one or more normals, i.e., the array is expected to be of length * 3 x n where n is an integer. */ bool addColors(const Core::Array& values); /** * Sanity checking function - is the mesh sane? * @return True if the Mesh object is sane and composed of the right number * of elements. */ bool valid() const; /** * Clear all mesh data. * @return True on success. */ bool clear(); /** * Overloaded operator. */ Mesh& operator=(const Mesh& other); /** * Set the name of the Mesh. */ void setName(const std::string& name_) { m_name = name_; } /** * @return The name of the Mesh. */ std::string name() const { return m_name; } /** * Provides locking. */ Mutex* lock() const { return m_lock; } /** * Applies Laplacian smoothing. * @param iterationCount number of smoothing passes to make. */ void smooth(int iterationCount = 6); friend class Molecule; private: Core::Array m_vertices; Core::Array m_normals; Core::Array m_colors; Core::Array m_triangles; std::string m_name; bool m_stable; float m_isoValue; unsigned int m_other; // Unique id of the other mesh if this is part of a pair unsigned int m_cube; // Unique id of the cube this mesh was generated from Mutex* m_lock; }; } // End namespace Avogadro::Core #endif // AVOGADRO_CORE_MESH_H avogadrolibs-1.101.0/avogadro/core/molecule.cpp000066400000000000000000001204151506155467400214110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molecule.h" #include "basisset.h" #include "cube.h" #include "elements.h" #include "gaussianset.h" #include "layermanager.h" #include "mdlvalence_p.h" #include "mesh.h" #include "neighborperceiver.h" #include "residue.h" #include "slaterset.h" #include "unitcell.h" #include #include #include #include #include namespace Avogadro::Core { Molecule::Molecule() : m_basisSet(nullptr), m_unitCell(nullptr), m_layers(LayerManager::getMoleculeLayer(this)) { m_elements.reset(); } Molecule::Molecule(const Molecule& other) : m_data(other.m_data), m_partialCharges(other.m_partialCharges), m_spectra(other.m_spectra), m_customElementMap(other.m_customElementMap), m_elements(other.m_elements), m_positions2d(other.m_positions2d), m_positions3d(other.m_positions3d), m_atomLabels(other.m_atomLabels), m_bondLabels(other.m_bondLabels), m_coordinates3d(other.m_coordinates3d), m_timesteps(other.m_timesteps), m_hybridizations(other.m_hybridizations), m_formalCharges(other.m_formalCharges), m_forceVectors(other.m_forceVectors), m_colors(other.m_colors), m_vibrationFrequencies(other.m_vibrationFrequencies), m_vibrationIRIntensities(other.m_vibrationIRIntensities), m_vibrationRamanIntensities(other.m_vibrationRamanIntensities), m_vibrationLx(other.m_vibrationLx), m_selectedAtoms(other.m_selectedAtoms), m_meshes(), m_cubes(), m_basisSet(other.m_basisSet ? other.m_basisSet->clone() : nullptr), m_unitCell(other.m_unitCell ? new UnitCell(*other.m_unitCell) : nullptr), m_residues(other.m_residues), m_hallNumber(other.m_hallNumber), m_constraints(other.m_constraints), m_frozenAtomMask(other.m_frozenAtomMask), m_graph(other.m_graph), m_bondOrders(other.m_bondOrders), m_atomicNumbers(other.m_atomicNumbers), m_layers(LayerManager::getMoleculeLayer(&other, this)) { // Copy over any meshes for (Index i = 0; i < other.meshCount(); ++i) { Mesh* m = addMesh(); *m = *other.mesh(i); } // Copy over any cubes for (Index i = 0; i < other.cubeCount(); ++i) { Cube* c = addCube(); *c = *other.cube(i); } // Make sure all the atoms are in the active layer if (other.m_layers.maxLayer() == 0) { for (Index i = 0; i < atomCount(); ++i) m_layers.addAtomToActiveLayer(i); } } void Molecule::readProperties(const Molecule& other) { m_atomLabels = other.m_atomLabels; m_bondLabels = other.m_bondLabels; m_colors = other.m_colors; // merge data maps by iterating through other's map for (auto it = other.m_data.constBegin(); it != other.m_data.constEnd(); ++it) { // even if we have the same key, we want to overwrite m_data.setValue(it->first, it->second); } // merge partial charge maps for (auto it = other.m_partialCharges.cbegin(); it != other.m_partialCharges.cend(); ++it) { m_partialCharges[it->first] = it->second; } // copy spectra m_spectra = other.m_spectra; // copy orbital information SlaterSet* slaterSet = dynamic_cast(other.m_basisSet); if (slaterSet != nullptr) { m_basisSet = slaterSet->clone(); m_basisSet->setMolecule(this); } GaussianSet* gaussianSet = dynamic_cast(other.m_basisSet); if (gaussianSet != nullptr) { m_basisSet = gaussianSet->clone(); m_basisSet->setMolecule(this); } // copy over spectra information if (other.m_vibrationFrequencies.size() > 0) { m_vibrationFrequencies = other.m_vibrationFrequencies; m_vibrationIRIntensities = other.m_vibrationIRIntensities; m_vibrationRamanIntensities = other.m_vibrationRamanIntensities; m_vibrationLx = other.m_vibrationLx; } // Copy over any meshes for (Index i = 0; i < other.meshCount(); ++i) { Mesh* m = addMesh(); *m = *other.mesh(i); } // Copy over any cubes for (Index i = 0; i < other.cubeCount(); ++i) { Cube* c = addCube(); *c = *other.cube(i); } } Molecule::Molecule(Molecule&& other) noexcept : m_data(other.m_data), m_partialCharges(std::move(other.m_partialCharges)), m_spectra(other.m_spectra), m_customElementMap(std::move(other.m_customElementMap)), m_elements(other.m_elements), m_positions2d(other.m_positions2d), m_positions3d(other.m_positions3d), m_atomLabels(other.m_atomLabels), m_bondLabels(other.m_bondLabels), m_coordinates3d(other.m_coordinates3d), m_timesteps(other.m_timesteps), m_hybridizations(other.m_hybridizations), m_formalCharges(other.m_formalCharges), m_colors(other.m_colors), m_vibrationFrequencies(other.m_vibrationFrequencies), m_vibrationIRIntensities(other.m_vibrationIRIntensities), m_vibrationRamanIntensities(other.m_vibrationRamanIntensities), m_vibrationLx(other.m_vibrationLx), m_selectedAtoms(std::move(other.m_selectedAtoms)), m_meshes(std::move(other.m_meshes)), m_cubes(std::move(other.m_cubes)), m_basisSet(std::exchange(other.m_basisSet, nullptr)), m_unitCell(std::exchange(other.m_unitCell, nullptr)), m_residues(other.m_residues), m_hallNumber(other.m_hallNumber), m_constraints(other.m_constraints), m_frozenAtomMask(other.m_frozenAtomMask), m_graph(other.m_graph), m_bondOrders(other.m_bondOrders), m_atomicNumbers(other.m_atomicNumbers), m_layers(LayerManager::getMoleculeLayer(this)) { // Copy the layers, only if they exist if (other.m_layers.maxLayer() > 0) m_layers = LayerManager::getMoleculeLayer(&other, this); else { // make sure all the atoms are in the active layer for (Index i = 0; i < atomCount(); ++i) m_layers.addAtomToActiveLayer(i); } } Molecule& Molecule::operator=(const Molecule& other) { if (this != &other) { m_data = other.m_data; m_partialCharges = other.m_partialCharges; m_spectra = other.m_spectra; m_customElementMap = other.m_customElementMap; m_elements = other.m_elements; m_positions2d = other.m_positions2d; m_positions3d = other.m_positions3d; m_atomLabels = other.m_atomLabels; m_bondLabels = other.m_bondLabels; m_coordinates3d = other.m_coordinates3d; m_timesteps = other.m_timesteps; m_hybridizations = other.m_hybridizations; m_formalCharges = other.m_formalCharges; m_colors = other.m_colors, m_vibrationFrequencies = other.m_vibrationFrequencies; m_vibrationIRIntensities = other.m_vibrationIRIntensities; m_vibrationRamanIntensities = other.m_vibrationRamanIntensities; m_vibrationLx = other.m_vibrationLx; m_selectedAtoms = other.m_selectedAtoms; m_residues = other.m_residues; m_graph = other.m_graph; m_bondOrders = other.m_bondOrders; m_atomicNumbers = other.m_atomicNumbers; m_hallNumber = other.m_hallNumber; m_constraints = other.m_constraints; m_frozenAtomMask = other.m_frozenAtomMask; clearMeshes(); // Copy over any meshes for (Index i = 0; i < other.meshCount(); ++i) { Mesh* m = addMesh(); *m = *other.mesh(i); } clearCubes(); // Copy over any cubes for (Index i = 0; i < other.cubeCount(); ++i) { Cube* c = addCube(); *c = *other.cube(i); } delete m_basisSet; m_basisSet = other.m_basisSet ? other.m_basisSet->clone() : nullptr; delete m_unitCell; m_unitCell = other.m_unitCell ? new UnitCell(*other.m_unitCell) : nullptr; // Copy the layers, only if they exist if (other.m_layers.maxLayer() > 0) m_layers = LayerManager::getMoleculeLayer(&other, this); else { // make sure all the atoms are in the active layer for (Index i = 0; i < atomCount(); ++i) m_layers.addAtomToActiveLayer(i); } } return *this; } Molecule& Molecule::operator=(Molecule&& other) noexcept { if (this != &other) { m_data = other.m_data; m_partialCharges = std::move(other.m_partialCharges); m_spectra = other.m_spectra; m_customElementMap = std::move(other.m_customElementMap); m_elements = other.m_elements; m_positions2d = other.m_positions2d; m_positions3d = other.m_positions3d; m_atomLabels = other.m_atomLabels; m_bondLabels = other.m_bondLabels; m_coordinates3d = other.m_coordinates3d; m_timesteps = other.m_timesteps; m_hybridizations = other.m_hybridizations; m_formalCharges = other.m_formalCharges; m_colors = other.m_colors; m_vibrationFrequencies = other.m_vibrationFrequencies; m_vibrationIRIntensities = other.m_vibrationIRIntensities; m_vibrationRamanIntensities = other.m_vibrationRamanIntensities; m_vibrationLx = other.m_vibrationLx; m_selectedAtoms = std::move(other.m_selectedAtoms); m_residues = other.m_residues; m_graph = other.m_graph; m_bondOrders = other.m_bondOrders; m_atomicNumbers = other.m_atomicNumbers; m_hallNumber = other.m_hallNumber; m_constraints = other.m_constraints; m_frozenAtomMask = other.m_frozenAtomMask; clearMeshes(); m_meshes = std::move(other.m_meshes); clearCubes(); m_cubes = std::move(other.m_cubes); delete m_basisSet; m_basisSet = std::exchange(other.m_basisSet, nullptr); delete m_unitCell; m_unitCell = std::exchange(other.m_unitCell, nullptr); // Copy the layers, if they exist if (other.m_layers.maxLayer() > 0) m_layers = LayerManager::getMoleculeLayer(&other, this); else { // make sure all the atoms are in the active layer for (Index i = 0; i < atomCount(); ++i) m_layers.addAtomToActiveLayer(i); } } return *this; } Molecule::~Molecule() { // LayerManager::deleteMolecule(this); delete m_basisSet; delete m_unitCell; clearMeshes(); clearCubes(); } Layer& Molecule::layer() { return m_layers; } const Layer& Molecule::layer() const { return m_layers; } void Molecule::setPartialCharges(const std::string& type, const MatrixX& value) { if (static_cast(value.size()) != atomCount()) return; m_partialCharges[type] = value; } MatrixX Molecule::partialCharges(const std::string& type) const { auto search = m_partialCharges.find(type); if (search != m_partialCharges.end()) { return search->second; // value from the map } else { MatrixX charges(atomCount(), 1); charges.fill(0.0); return charges; } } std::set Molecule::partialChargeTypes() const { std::set types; for (auto& it : m_partialCharges) types.insert(it.first); return types; } std::set Molecule::spectraTypes() const { std::set types; for (auto& it : m_spectra) types.insert(it.first); return types; } void Molecule::setSpectra(const std::string& type, const MatrixX& value) { m_spectra[type] = value; } MatrixX Molecule::spectra(const std::string& type) const { MatrixX value; auto search = m_spectra.find(type); if (search != m_spectra.end()) { value = search->second; // value from the map } return value; } void Molecule::addConstraint(Real value, Index a, Index b, Index c, Index d) { Constraint newConstraint(a, b, c, d, value); m_constraints.push_back(newConstraint); } void Molecule::removeConstraint(Index a, Index b, Index c, Index d) { // loop through and remove if the constraint matches all atom indexes for (auto it = m_constraints.begin(); it != m_constraints.end();) { if (it->aIndex() == a && it->bIndex() == b && it->cIndex() == c && it->dIndex() == d) { it = m_constraints.erase(it); return; } else ++it; } } void Molecule::setFrozenAtom(Index atomId, bool frozen) { if (atomId >= m_atomicNumbers.size()) return; Eigen::Index size = m_frozenAtomMask.rows(); auto newSize = static_cast(3 * m_atomicNumbers.size()); if (m_frozenAtomMask.rows() != newSize) m_frozenAtomMask.conservativeResize(newSize); if (m_frozenAtomMask.rows() > size) for (Eigen::Index i = size; i < m_frozenAtomMask.rows(); ++i) m_frozenAtomMask[i] = 1.0f; float value = frozen ? 0.0f : 1.0f; auto base = static_cast(atomId * 3); if (base <= m_frozenAtomMask.rows() - 3) { m_frozenAtomMask[base] = value; m_frozenAtomMask[base + 1] = value; m_frozenAtomMask[base + 2] = value; } } bool Molecule::frozenAtom(Index atomId) const { auto base = static_cast(atomId * 3); if (base <= m_frozenAtomMask.rows() - 3) { return (m_frozenAtomMask[base] == 0.0 && m_frozenAtomMask[base + 1] == 0.0 && m_frozenAtomMask[base + 2] == 0.0); } return false; } void Molecule::setFrozenAtomAxis(Index atomId, int axis, bool frozen) { Eigen::Index size = m_frozenAtomMask.rows(); auto newSize = static_cast(3 * m_atomicNumbers.size()); if (m_frozenAtomMask.rows() != newSize) m_frozenAtomMask.conservativeResize(newSize); if (m_frozenAtomMask.rows() > size) for (Eigen::Index i = size; i < m_frozenAtomMask.rows(); ++i) m_frozenAtomMask[i] = 1.0f; float value = frozen ? 0.0f : 1.0f; auto base = static_cast(atomId * 3); if (axis >= 0 && axis < 3 && base <= m_frozenAtomMask.rows() - 3) { m_frozenAtomMask[base + axis] = value; } } void Molecule::setData(const std::string& name, const Variant& value) { m_data.setValue(name, value); } Variant Molecule::data(const std::string& name) const { return m_data.value(name); } bool Molecule::hasData(const std::string& name) const { return m_data.hasValue(name); } void Molecule::setDataMap(const VariantMap& map) { m_data = map; } const VariantMap& Molecule::dataMap() const { return m_data; } VariantMap& Molecule::dataMap() { return m_data; } Array& Molecule::hybridizations() { return m_hybridizations; } const Array& Molecule::hybridizations() const { return m_hybridizations; } Array& Molecule::formalCharges() { return m_formalCharges; } const Array& Molecule::formalCharges() const { return m_formalCharges; } signed char Molecule::totalCharge() const { signed char charge = 0; // check the data map first if (m_data.hasValue("totalCharge")) { charge = m_data.value("totalCharge").toInt(); } else if (m_formalCharges.size() > 0) { for (Index i = 0; i < m_formalCharges.size(); ++i) charge += m_formalCharges[i]; return charge; } return charge; // should be zero } char Molecule::totalSpinMultiplicity() const { char spin = 1; // check the data map first if (m_data.hasValue("totalSpinMultiplicity")) { spin = m_data.value("totalSpinMultiplicity").toInt(); } else { // add up the electrons unsigned long electrons = 0; for (Index i = 0; i < m_atomicNumbers.size(); ++i) electrons += m_atomicNumbers[i]; // adjust by the total charge electrons -= totalCharge(); // if there are an even number of electrons, the spin is 1 // if there are an odd number of electrons, the spin is 2 // (might not be true, but a good default for many molecules) // %todo - adjust for inorganic / organometallics if (electrons % 2 == 0) spin = 1; else spin = 2; } return spin; // should be zero } Array& Molecule::colors() { return m_colors; } const Array& Molecule::colors() const { return m_colors; } Array& Molecule::atomPositions2d() { return m_positions2d; } const Array& Molecule::atomPositions2d() const { return m_positions2d; } Array& Molecule::atomPositions3d() { return m_positions3d; } const Array& Molecule::atomPositions3d() const { return m_positions3d; } const Molecule::CustomElementMap& Molecule::customElementMap() const { return m_customElementMap; } void Molecule::setCustomElementMap(const Molecule::CustomElementMap& map) { m_customElementMap = map; } Molecule::AtomType Molecule::addAtom(unsigned char number) { m_graph.addVertex(); m_atomicNumbers.push_back(number); // we're not going to easily handle custom elements if (number < element_count) m_elements.set(number); else m_elements.set(element_count - 1); // custom element m_layers.addAtomToActiveLayer(atomCount() - 1); m_partialCharges.clear(); return AtomType(this, static_cast(atomCount() - 1)); } Molecule::AtomType Molecule::addAtom(unsigned char number, Vector3 position3d) { if (m_positions3d.size() == atomCount()) { m_positions3d.push_back(position3d); } return Molecule::addAtom(number); } void Molecule::swapBond(Index a, Index b) { // Allow Argument Dependent Lookup for swap using std::swap; m_graph.swapEdgeIndices(a, b); swap(m_bondOrders[a], m_bondOrders[b]); } void Molecule::swapAtom(Index a, Index b) { // Allow Argument Dependent Lookup for swap using std::swap; Index max = a > b ? a : b; if (m_positions2d.size() >= max) swap(m_positions2d[a], m_positions2d[b]); if (m_positions3d.size() >= max) swap(m_positions3d[a], m_positions3d[b]); if (m_hybridizations.size() >= max) swap(m_hybridizations[a], m_hybridizations[b]); if (m_formalCharges.size() >= max) swap(m_formalCharges[a], m_formalCharges[b]); if (m_colors.size() >= max) swap(m_colors[a], m_colors[b]); swap(m_atomicNumbers[a], m_atomicNumbers[b]); m_graph.swapVertexIndices(a, b); m_layers.swapLayer(a, b); } bool Molecule::removeAtom(Index index) { if (index >= atomCount()) return false; if (m_positions2d.size() == atomCount()) m_positions2d.swapAndPop(index); if (m_positions3d.size() == atomCount()) m_positions3d.swapAndPop(index); if (m_hybridizations.size() == atomCount()) m_hybridizations.swapAndPop(index); if (m_formalCharges.size() == atomCount()) m_formalCharges.swapAndPop(index); if (m_colors.size() == atomCount()) m_colors.swapAndPop(index); if (m_selectedAtoms.size() == atomCount()) { // swap and pop on std::vector if (index != m_selectedAtoms.size() - 1) { m_selectedAtoms[index] = m_selectedAtoms.back(); } m_selectedAtoms.pop_back(); } m_partialCharges.clear(); removeBonds(index); // before we remove, check if there's any other atom of this element // (e.g., we removed the last oxygen) auto elementToRemove = m_atomicNumbers[index]; bool foundAnother = false; for (Index i = 0; i < atomCount(); ++i) { if (i == index) continue; if (m_atomicNumbers[index] == elementToRemove) { foundAnother = true; break; // we're done } } if (!foundAnother) m_elements.reset(elementToRemove); m_atomicNumbers.swapAndPop(index); m_graph.removeVertex(index); m_layers.removeAtom(index); return true; } bool Molecule::removeAtom(const AtomType& atom_) { return removeAtom(atom_.index()); } void Molecule::clearAtoms() { m_positions2d.clear(); m_positions3d.clear(); m_atomLabels.clear(); m_hybridizations.clear(); m_formalCharges.clear(); m_colors.clear(); m_atomicNumbers.clear(); m_bondOrders.clear(); m_bondLabels.clear(); m_graph.clear(); m_partialCharges.clear(); m_elements.reset(); } Molecule::AtomType Molecule::atom(Index index) const { assert(index < atomCount()); return AtomType(const_cast(this), index); } Molecule::BondType Molecule::addBond(Index atom1, Index atom2, unsigned char order) { assert(atom1 < m_atomicNumbers.size()); assert(atom2 < m_atomicNumbers.size()); Index index = bond(atom1, atom2).index(); if (index >= bondCount()) { m_graph.addEdge(atom1, atom2); m_bondOrders.push_back(order); index = static_cast(m_graph.edgeCount() - 1); } else { m_bondOrders[index] = order; } // any existing charges are invalidated m_partialCharges.clear(); return BondType(this, index); } Molecule::BondType Molecule::addBond(const AtomType& a, const AtomType& b, unsigned char order) { assert(a.isValid() && a.molecule() == this); assert(b.isValid() && b.molecule() == this); return addBond(a.index(), b.index(), order); } size_t calcNlogN(size_t n) { size_t aproxLog = 1; auto aux = static_cast(n); while (aux > 2.0f) { aux /= 2.0f; ++aproxLog; } return n * aproxLog; } bool Molecule::removeBond(Index index) { if (index >= bondCount()) return false; m_graph.removeEdge(index); m_bondOrders.swapAndPop(index); m_partialCharges.clear(); return true; } bool Molecule::removeBond(const BondType& bond_) { return removeBond(bond_.index()); } bool Molecule::removeBond(Index a, Index b) { return removeBond(bond(a, b).index()); } bool Molecule::removeBond(const AtomType& a, const AtomType& b) { return removeBond(bond(a, b).index()); } void Molecule::clearBonds() { m_bondOrders.clear(); m_graph.removeEdges(); m_graph.setSize(atomCount()); m_partialCharges.clear(); } Molecule::BondType Molecule::bond(Index index) const { assert(index < bondCount()); return BondType(const_cast(this), index); } Molecule::BondType Molecule::bond(const AtomType& a, const AtomType& b) const { assert(a.isValid() && a.molecule() == this); assert(b.isValid() && b.molecule() == this); return bond(a.index(), b.index()); } Molecule::BondType Molecule::bond(Index atomId1, Index atomId2) const { assert(atomId1 < atomCount()); assert(atomId2 < atomCount()); const std::vector& edgeIndices = m_graph.edges(atomId1); for (unsigned long index : edgeIndices) { const std::pair& pair = m_graph.endpoints(index); if (pair.first == atomId2 || pair.second == atomId2) return BondType(const_cast(this), index); } return BondType(); } Array Molecule::bonds(const AtomType& a) { if (!a.isValid()) return Array(); return bonds(a.index()); } Array Molecule::bonds(Index a) const { Array atomBonds; if (a < atomCount()) { const std::vector& edgeIndices = m_graph.edges(a); for (unsigned long index : edgeIndices) { if (m_graph.endpoints(index).first == a || m_graph.endpoints(index).second == a) { // work around to consult bonds without breaking constantness atomBonds.push_back(new BondType(const_cast(this), index)); } } } std::sort(atomBonds.begin(), atomBonds.end(), [](const BondType*& ba, const BondType*& bb) { return ba->index() < bb->index(); }); return atomBonds; } Array Molecule::bonds(Index a) { Array atomBonds; if (a < atomCount()) { const std::vector& edgeIndices = m_graph.edges(a); for (unsigned long index : edgeIndices) { auto bond = bondPair(index); if (bond.first == a || bond.second == a) atomBonds.push_back(BondType(this, index)); } } std::sort(atomBonds.begin(), atomBonds.end(), [](BondType& ba, BondType& bb) { return ba.index() < bb.index(); }); return atomBonds; } Mesh* Molecule::addMesh() { m_meshes.push_back(new Mesh); return m_meshes.back(); } Mesh* Molecule::mesh(Index index) { if (index < static_cast(m_meshes.size())) return m_meshes[index]; else return nullptr; } const Mesh* Molecule::mesh(Index index) const { if (index < static_cast(m_meshes.size())) return m_meshes[index]; else return nullptr; } void Molecule::clearMeshes() { while (!m_meshes.empty()) { delete m_meshes.back(); m_meshes.pop_back(); } } Cube* Molecule::addCube() { m_cubes.push_back(new Cube); return m_cubes.back(); } Cube* Molecule::cube(Index index) { if (index < static_cast(m_cubes.size())) return m_cubes[index]; else return nullptr; } const Cube* Molecule::cube(Index index) const { if (index < static_cast(m_cubes.size())) return m_cubes[index]; else return nullptr; } void Molecule::clearCubes() { while (!m_cubes.empty()) { delete m_cubes.back(); m_cubes.pop_back(); } } std::string Molecule::formula(const std::string& delimiter, int over) const { // Adapted from chemkit: // A map of atomic symbols to their quantity. std::map componentsCount = composition(); std::stringstream result; std::map::iterator iter; // Carbons first iter = componentsCount.find(6); if (iter != componentsCount.end()) { result << "C"; if (iter->second > static_cast(over)) result << delimiter << iter->second; componentsCount.erase(iter); // If carbon is present, hydrogens are next. iter = componentsCount.find(1); if (iter != componentsCount.end()) { result << delimiter << "H"; if (iter->second > static_cast(over)) result << delimiter << iter->second; componentsCount.erase(iter); } } // The rest: iter = componentsCount.begin(); while (iter != componentsCount.end()) { result << delimiter << Elements::symbol(iter->first); if (iter->second > static_cast(over)) result << delimiter << iter->second; ++iter; } return result.str(); } void Molecule::setUnitCell(UnitCell* uc) { if (uc != m_unitCell) { delete m_unitCell; m_unitCell = uc; } } double Molecule::mass() const { double m(0.0); for (Index i = 0; i < atomCount(); ++i) m += Elements::mass(atom(i).atomicNumber()); return m; } Vector3 Molecule::centerOfGeometry() const { Vector3 center(0.0, 0.0, 0.0); for (Index i = 0; i < atomCount(); ++i) center += atom(i).position3d(); return center / atomCount(); } Vector3 Molecule::centerOfMass() const { Vector3 center(0.0, 0.0, 0.0); for (Index i = 0; i < atomCount(); ++i) { AtomType curr_atom = atom(i); center += (curr_atom.position3d() * Elements::mass(curr_atom.atomicNumber())); } center /= mass(); center /= atomCount(); return center; } double Molecule::radius() const { double radius = 0.0; if (atomCount() > 0) { radius = (centerOfGeometry() - atom(0).position3d()).norm(); } return radius; } std::pair Molecule::bestFitPlane() const { return bestFitPlane(atomPositions3d()); } std::pair Molecule::bestFitPlane(const Array& pos) { // copy coordinates to matrix in Eigen format size_t num_atoms = pos.size(); assert(num_atoms >= 3); Eigen::Matrix coord( 3, num_atoms); for (size_t i = 0; i < num_atoms; ++i) { coord.col(i) = pos[i]; } // calculate centroid Vector3 centroid = coord.rowwise().mean(); // subtract centroid coord.colwise() -= centroid; // we only need the left-singular matrix auto svd = coord.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV); Vector3 plane_normal = svd.matrixU().rightCols<1>(); return std::make_pair(centroid, plane_normal); } Array Molecule::vibrationFrequencies() const { return m_vibrationFrequencies; } void Molecule::setVibrationFrequencies(const Array& freq) { m_vibrationFrequencies = freq; } Array Molecule::vibrationIRIntensities() const { return m_vibrationIRIntensities; } void Molecule::setVibrationIRIntensities(const Array& intensities) { m_vibrationIRIntensities = intensities; } Array Molecule::vibrationRamanIntensities() const { return m_vibrationRamanIntensities; } void Molecule::setVibrationRamanIntensities(const Array& intensities) { m_vibrationRamanIntensities = intensities; } Array Molecule::vibrationLx(int mode) const { if (mode >= 0 && mode < static_cast(m_vibrationLx.size())) return m_vibrationLx[mode]; return Array(); } void Molecule::setVibrationLx(const Array>& lx) { m_vibrationLx = lx; } void Molecule::perceiveBondOrders() { // check for coordinates and that there are some bonds if (m_positions3d.size() != atomCount() || m_positions3d.size() < 2 || m_graph.edgeCount() == 0) return; // save the existing bonds and bond orders // first calculate the unsaturated valence for every atom Array originalBonds = m_bondOrders; Array unsaturatedValence(atomCount(), 0); bool anyUnsaturated = false; for (Index i = 0; i < atomCount(); ++i) { unsigned char boSum = 0; for (auto bond : bonds(i)) { boSum += bond.order(); } unsaturatedValence[i] = atomValence(atomicNumber(i), formalCharge(i), bonds(i).size()) - boSum; if (unsaturatedValence[i] > 0) anyUnsaturated = true; } Index startIndex = 0; Index initialAtom = 0; while (anyUnsaturated) { // okay, we're first going to try placing *one* bond from our start atom // .. then we can try placing bonds anywhere // find the first atom with unsaturated valence of ONE bool foundStart = false; for (Index i = startIndex; i < atomCount(); ++i) { if (unsaturatedValence[i] == 1) { startIndex = i; foundStart = true; break; } } // if we didn't find an atom with unsaturated valence of ONE, // .. then find *something* if (!foundStart) { for (Index i = startIndex; i < atomCount(); ++i) { if (unsaturatedValence[i] > 0) { startIndex = i; foundStart = true; break; } } } if (foundStart) { // std::cerr << "Found start index " << startIndex << std::endl; // look at the neighbors of our start atom Index bestIndex = MaxIndex; unsigned bestValence = 256; // something impossible Real bestDistance = 100.0; // 10 Angstroms squared Vector3 startPosition = m_positions3d[startIndex]; // iterate through the Indexes of the neighbors for (auto neighbor : graph().neighbors(startIndex)) { // if this neighbor doesn't have an unsaturated valence, skip it if (unsaturatedValence[neighbor] == 0) { continue; } if (unsaturatedValence[neighbor] < bestValence) { bestIndex = neighbor; bestValence = unsaturatedValence[neighbor]; bestDistance = (m_positions3d[neighbor] - startPosition).squaredNorm(); } else if (unsaturatedValence[neighbor] == bestValence) { // check if this neighbor is closer Real distance = (m_positions3d[neighbor] - startPosition).squaredNorm(); if (distance < bestDistance) { bestIndex = neighbor; bestDistance = distance; } } } // if we found a neighbor, then we can assign a bond order and update // charges if (bestIndex != MaxIndex) { /*std::cerr << "Assigning bond " << startIndex << " " << bestIndex << std::endl; */ // assign the bond order m_bondOrders[bond(startIndex, bestIndex).index()] += 1; // update the unsaturated valence of the start atom unsaturatedValence[startIndex] -= 1; // update the unsaturated valence of the neighbor atom unsaturatedValence[bestIndex] -= 1; startIndex = 0; // we can now try placing bonds anywhere } else { startIndex += 1; } } // TODO: update the current formal charges anyUnsaturated = false; // check if we're done for (Index i = 0; i < atomCount(); ++i) { if (unsaturatedValence[i] > 0) { anyUnsaturated = true; break; } } if (!foundStart && anyUnsaturated) { // we've gone through and it's not working // try a new starting atom and reset the bond orders // std::cerr << " didn't work " << initialAtom << std::endl; initialAtom += 1; startIndex = initialAtom; for (Index i = 0; i < m_bondOrders.size(); ++i) { unsigned change = m_bondOrders[i] - originalBonds[i]; if (change > 0) { // update the valences unsaturatedValence[bond(i).atom1().index()] += change; unsaturatedValence[bond(i).atom2().index()] += change; } m_bondOrders[i] = originalBonds[i]; } } if (initialAtom >= atomCount()) { break; } } // keep going until we've assigned all the bond orders } void Molecule::perceiveBondsSimple(const double tolerance, const double min) { // check for coordinates if (m_positions3d.size() != atomCount() || m_positions3d.size() < 2) return; // cache atomic radii std::vector radii(atomCount()); double max_radius = 0.0; for (size_t i = 0; i < radii.size(); i++) { radii[i] = Elements::radiusCovalent(atomicNumber(i)); if (radii[i] <= 0.0) radii[i] = 2.0; if (radii[i] > max_radius) max_radius = radii[i]; } float maxDistance = 2.0 * max_radius + tolerance; auto neighborPerceiver = NeighborPerceiver(m_positions3d, maxDistance); // check for bonds // O(n) average-case, O(n^2) worst-case // note that the "worst case" here would need to be an invalid molecule Array neighbors; for (Index i = 0; i < atomCount(); i++) { Vector3 ipos = m_positions3d[i]; neighborPerceiver.getNeighborsInclusiveInPlace(neighbors, ipos); for (unsigned long j : neighbors) { double cutoff = radii[i] + radii[j] + tolerance; Vector3 jpos = m_positions3d[j]; Vector3 diff = jpos - ipos; // Don't automatically bond nobel gases to anything switch (atomicNumber(i)) { case 2: // He case 10: // Ne case 18: // Ar case 36: // Kr continue; default: break; } // now for the other atom switch (atomicNumber(j)) { case 2: // He case 10: // Ne case 18: // Ar case 36: // Kr continue; default: break; } if (std::fabs(diff[0]) > cutoff || std::fabs(diff[1]) > cutoff || std::fabs(diff[2]) > cutoff || (atomicNumber(i) == 1 && atomicNumber(j) == 1)) continue; // check radius and add bond if needed double cutoffSq = cutoff * cutoff; double diffsq = diff.squaredNorm(); if (diffsq < cutoffSq && diffsq > min * min) addBond(atom(i), atom(j), 1); } } } void Molecule::perceiveBondsFromResidueData() { for (auto& m_residue : m_residues) { m_residue.resolveResidueBonds(*this); } } size_t Molecule::coordinate3dCount() const { return m_coordinates3d.size(); } bool Molecule::setCoordinate3d(int coord) { if (coord >= 0 && coord < static_cast(m_coordinates3d.size())) { m_positions3d = m_coordinates3d[coord]; return true; } return false; } void Molecule::clearCoordinate3d() { m_coordinates3d.clear(); } Array Molecule::coordinate3d(size_t index) const { return m_coordinates3d[index]; } bool Molecule::setCoordinate3d(const Array& coords, size_t index) { if (m_coordinates3d.size() <= index) m_coordinates3d.resize(index + 1); m_coordinates3d[index] = coords; return true; } double Molecule::timeStep(int index, bool& status) { if (static_cast(m_timesteps.size()) <= index) { status = false; return 0.0; } status = true; return m_timesteps[index]; } bool Molecule::setTimeStep(double timestep, int index) { if (static_cast(m_timesteps.size()) <= index) m_timesteps.resize(index + 1); m_timesteps[index] = timestep; return true; } Array& Molecule::forceVectors() { return m_forceVectors; } const Array& Molecule::forceVectors() const { return m_forceVectors; } Residue& Molecule::addResidue(std::string& name, Index& number, char& id) { Residue newResidue(name, number, id); m_residues.push_back(newResidue); return m_residues[m_residues.size() - 1]; } void Molecule::addResidue(Residue& residue) { m_residues.push_back(residue); } Residue& Molecule::residue(Index index) { return m_residues[index]; } Index Molecule::residueCount() const { return static_cast(m_residues.size()); } bool Molecule::setBondPairs(const Array>& pairs) { if (pairs.size() == bondCount()) { Index bond = 0; for (const auto& pair : pairs) { setBondPair(bond, pair); ++bond; } return true; } return false; } bool Molecule::setBondPair(Index bondId, const std::pair& pair) { if (bondId < bondCount()) { m_graph.editEdgeInPlace(bondId, pair.first, pair.second); return true; } return false; } unsigned char Molecule::bondOrder(Index bondId) const { return bondId < m_bondOrders.size() ? m_bondOrders[bondId] : 0; } bool Molecule::setBondOrders(const Array& orders) { if (orders.size() == bondCount()) { m_bondOrders = orders; return true; } return false; } bool Molecule::setBondOrder(Index bondId, unsigned char order) { if (bondId < bondCount()) { m_bondOrders[bondId] = order; return true; } return false; } Index Molecule::atomCount(unsigned char number) const { Index count(0); for (unsigned char m_atomicNumber : m_atomicNumbers) { if (m_atomicNumber == number) ++count; } return count; } bool Molecule::setAtomicNumbers(const Core::Array& nums) { if (nums.size() == atomCount()) { m_atomicNumbers = nums; // update element mask m_elements.reset(); // update colors too if (nums.size() == m_colors.size()) { for (Index i = 0; i < nums.size(); ++i) { m_colors[i] = Vector3ub(Elements::color(m_atomicNumbers[i])); m_elements.set(m_atomicNumbers[i]); } } return true; } return false; } bool Molecule::setAtomicNumber(Index atomId, unsigned char number) { if (atomId < atomCount()) { m_atomicNumbers[atomId] = number; // recalculate the element mask m_elements.reset(); for (unsigned char m_atomicNumber : m_atomicNumbers) { m_elements.set(m_atomicNumber); } // update colors too if (atomId < m_colors.size()) m_colors[atomId] = Vector3ub(Elements::color(number)); return true; } return false; } bool Molecule::hasCustomElements() const { for (unsigned char m_atomicNumber : m_atomicNumbers) { if (Core::isCustomElement(m_atomicNumber)) return true; } return false; } std::map Molecule::composition() const { // A map of atomic symbols to their quantity. std::map composition; for (unsigned char m_atomicNumber : m_atomicNumbers) { ++composition[m_atomicNumber]; } return composition; } bool Molecule::removeBonds(Index atom) { if (atom >= atomCount()) return false; while (true) { const std::vector& bondList = m_graph.edges(atom); if (!bondList.size()) break; size_t bond = bondList[0]; removeBond(bond); } return true; } Array> Molecule::getAtomBonds(Index index) const { Array> result; const std::vector& edgeIndices = m_graph.edges(index); for (unsigned long edgeIndice : edgeIndices) { result.push_back(m_graph.endpoints(edgeIndice)); } return result; } Array Molecule::getAtomOrders(Index index) const { Array result; const std::vector& edgeIndices = m_graph.edges(index); for (unsigned long edgeIndice : edgeIndices) { result.push_back(m_bondOrders[edgeIndice]); } return result; } void Molecule::addBonds(const Array>& bonds, const Array& orders) { Index i = 0; for (auto p : bonds) { addBond(p.first, p.second, orders[i]); ++i; } } std::list Molecule::getAtomsAtLayer(size_t layer) { std::list result; // get the index in decreasing order so deleting won't corrupt data for (Index i = atomCount(); i > 0; --i) { if (m_layers.getLayerID(i - 1) == layer) { result.push_back(i - 1); } } return result; } void Molecule::boundingBox(Vector3& boxMin, Vector3& boxMax, const double radius) const { boxMin.setConstant(std::numeric_limits::max()); boxMax.setConstant(-std::numeric_limits::max()); const bool noSelection = isSelectionEmpty(); for (uint32_t i = 0; i < atomCount(); i++) { if (noSelection || m_selectedAtoms[i]) { const Vector3 boxMinBuffer = atom(i).position3d().array() - radius; const Vector3 boxMaxBuffer = atom(i).position3d().array() + radius; boxMin.x() = std::min(boxMinBuffer.x(), boxMin.x()); boxMin.y() = std::min(boxMinBuffer.y(), boxMin.y()); boxMin.z() = std::min(boxMinBuffer.z(), boxMin.z()); boxMax.x() = std::max(boxMaxBuffer.x(), boxMax.x()); boxMax.y() = std::max(boxMaxBuffer.y(), boxMax.y()); boxMax.z() = std::max(boxMaxBuffer.z(), boxMax.z()); } } } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/molecule.h000066400000000000000000001063111506155467400210550ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_MOLECULE_H #define AVOGADRO_CORE_MOLECULE_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include "array.h" #include "bond.h" #include "constraint.h" #include "elements.h" #include "graph.h" #include "layer.h" #include "variantmap.h" #include "vector.h" #include #include #include #include #include namespace Avogadro::Core { class BasisSet; class Cube; class Mesh; class Residue; class UnitCell; /** Concrete atom/bond proxy classes for Core::Molecule. @{ */ class Atom; class Bond; /** @} */ /** * @class Molecule molecule.h * @brief The Molecule class represents a chemical molecule. */ class AVOGADROCORE_EXPORT Molecule { public: /** Typedef for Atom class. */ using AtomType = Atom; /** Typedef for Bond class. */ using BondType = Bond; /** Type for custom element map. */ using CustomElementMap = std::map; /** Type for element masks (e.g., does this molecule contain certain elements) */ using ElementMask = std::bitset; /** Creates a new, empty molecule. */ Molecule(); /** Copy constructor */ Molecule(const Molecule& other); /** Move constructor */ Molecule(Molecule&& other) noexcept; /** Assignment operator */ Molecule& operator=(const Molecule& other); /** Move assignment operator */ Molecule& operator=(Molecule&& other) noexcept; /** Destroys the molecule object. */ virtual ~Molecule(); /** * Adds the properties from the supplied * molecule to this molecule. Does not otherwise * modify atoms / bonds / residues, etc. */ void readProperties(const Molecule& other); /** Sets the data value with @p name to @p value. */ void setData(const std::string& name, const Variant& value); /** @return the data value for @p name. */ Variant data(const std::string& name) const; /** * @return true if the molecule has data with the given key, false otherwise. */ bool hasData(const std::string& name) const; /** Set the molecule's variant data to the entries in map. */ void setDataMap(const VariantMap& map); /** @return the molecule's variant data. */ const VariantMap& dataMap() const; /** \overload */ VariantMap& dataMap(); /** @return a specific spectra entry */ MatrixX spectra(const std::string& name) const; /** Sets the spectra value with @p name to @p value. */ void setSpectra(const std::string& name, const MatrixX& value); /** @return the list of available spectra */ std::set spectraTypes() const; /** Sets atomic partial charges with @p type to @p value. */ void setPartialCharges(const std::string& type, const MatrixX& value); /** @return the atomic partial charges of type @p type */ MatrixX partialCharges(const std::string& type) const; /** @return the types of partial charges available stored with this molecule. */ std::set partialChargeTypes() const; /** @return a vector of hybridizations for the atoms in the molecule. */ Array& hybridizations(); /** \overload */ const Array& hybridizations() const; /** * Get the hybridization for the requested atom. * @param atomId The index of the atom. * @return The hybridization of the atom indexed at @a atomId, or * 0 if @a atomId is invalid. */ AtomHybridization hybridization(Index atomId) const; /** * Replace the current array of hybridizations. * @param hybs The new hybridization array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setHybridizations(const Core::Array& hybs); /** * Set the hybridization of a single atom. * @param atomId The index of the atom to modify. * @param hybridization The new hybridization. * @return True on success, false otherwise. */ bool setHybridization(Index atomId, AtomHybridization hybridization); /** @return a vector of formal charges for the atoms in the molecule. */ Array& formalCharges(); /** \overload */ const Array& formalCharges() const; /** * Get the total charge on the molecule. * The method will first check to see if a total charge has been set. If not, * it will calculate the total charge from the formal charges (if set). * If neither has been set, it will assume the total charge is zero. * @return The total charge of the molecule. */ signed char totalCharge() const; /** * Get the total spin multiplicity of the molecule. * The method will first check to see if a total spin has been set. If not, * it will either suggest a singlet if an even number of electrons are * present, or a doublet if an odd number of electrons are present. * @return The total spin multiplicity of the molecule. */ char totalSpinMultiplicity() const; /** * Get the formal charge for the requested atom. * @param atomId The index of the atom. * @return The formal charge of the atom indexed at @a atomId, or * 0 if @a atomId is invalid. */ signed char formalCharge(Index atomId) const; /** * Replace the current array of formal charges. * @param charges The new formal charge array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setFormalCharges(const Core::Array& charges); /** * Set the formal charge of a single atom. * @param atomId The index of the atom to modify. * @param charge The new formal charge. * @return True on success, false otherwise. */ bool setFormalCharge(Index atomId, signed char charge); /** \returns a vector of colors for the atoms in the moleucle. */ Array& colors(); /** \overload */ const Array& colors() const; /** * Get the color for the requested atom. * @param atomId The index of the atom. * @return The color of the atom indexed at @a atomId, or * (0,0,0) if @a atomId is invalid. If no color is set for the * given atomId, the default color for the atomic number of * the atomId is returned. */ Vector3ub color(Index atomId) const; /** * Replace the current array of colors. * @param colors The new color array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setColors(const Core::Array& colors); /** * Set the color of a single atom. * @param atomId The index of the atom to modify. * @param color The new color. * @return True on success, false otherwise. */ bool setColor(Index atomId, Vector3ub color); bool setLayer(Index atomId, size_t layer); size_t layer(Index atomId) const; /** @return a vector of 2d atom positions for the atoms in the molecule. */ const Array& atomPositions2d() const; /** \overload */ Array& atomPositions2d(); /** * Get the 2D position of a single atom. * @param atomId The index of the atom. * @return The position of the atom, or Vector3::Zero() if no position * information has been set. */ Vector2 atomPosition2d(Index atomId) const; /** * Replace the current array of 2D atomic coordinates. * @param pos The new coordinate array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setAtomPositions2d(const Core::Array& pos); /** * Set the 2D position of a single atom. * @param atomId The index of the atom to modify. * @param pos The new position of the atom. * @return True on success, false otherwise. */ bool setAtomPosition2d(Index atomId, const Vector2& pos); /** @return a vector of 3d atom positions for the atoms in the molecule. */ const Array& atomPositions3d() const; /** \overload */ Array& atomPositions3d(); /** * Get the 3D position of a single atom. * @param atomId The index of the atom. * @return The position of the atom, or Vector3::Zero() if no position * information has been set. */ Vector3 atomPosition3d(Index atomId) const; /** * Replace the current array of 3D atomic coordinates. * @param pos The new coordinate array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setAtomPositions3d(const Core::Array& pos); /** * Set the 3D position of a single atom. * @param atomId The index of the atom to modify. * @param pos The new position of the atom. * @return True on success, false otherwise. */ bool setAtomPosition3d(Index atomId, const Vector3& pos); /** * @return Any custom label for the requested atom. * @param atomId The index of the atom. */ std::string atomLabel(Index atomId) const; /** * Set the custom label of a single atom. * @param atomId The index of the atom to modify. * @param label The new label of the atom. * @return True on success, false otherwise. */ bool setAtomLabel(Index atomId, const std::string& label); const Core::Array atomLabels() const { return m_atomLabels; } /** * Set all the atom labels in the molecule. * @param label The new label array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setAtomLabels(const Core::Array& label); /** * Set whether the specified atom is selected or not. */ void setAtomSelected(Index atomId, bool selected); /** * Query whether the supplied atom index has been selected. */ bool atomSelected(Index atomId) const; /** @return whether the selection is empty or not */ bool isSelectionEmpty() const; /** A map of custom element atomic numbers to string identifiers. These ids * can be used to override the generic custom element names returned by the * Elements class, and should be somewhat meaningful to the user. * * @note Custom element atomic numbers lie between CustomElementMin and * CustomElementMax. * @sa Avogadro::QtGui::CustomElementDialog * @sa hasCustomElements * @{ */ const CustomElementMap& customElementMap() const; void setCustomElementMap(const CustomElementMap& map); /** @} */ /** @return the elements currently in this molecule */ const ElementMask elements() const; /** Adds an atom to the molecule. */ virtual AtomType addAtom(unsigned char atomicNumber); AtomType addAtom(unsigned char atomicNumber, Vector3 position3d); /** * @brief Remove the specified atom from the molecule. * @param index The index of the atom to be removed. * @return True on success, false if the atom was not found. */ virtual bool removeAtom(Index index); /** * @brief Remove the specified atom from the molecule. * @param atom The atom to be removed. * @return True on success, false if the atom was not found. * @overload */ virtual bool removeAtom(const AtomType& atom); /** * Remove all atoms from the molecule. */ virtual void clearAtoms(); /** * @return the atom at @p index in the molecule. */ AtomType atom(Index index) const; /** * Create a new bond in the molecule. * @param atom1 The first atom in the bond. * @param atom2 The second atom in the bond. * @param order The bond order. * @return The new bond object. Will be invalid if @a atom1 or @a atom2 does * not exist. * @{ */ virtual BondType addBond(Index atom1, Index atom2, unsigned char order = 1); virtual BondType addBond(const AtomType& atom1, const AtomType& atom2, unsigned char order = 1); /** @} */ /** * @brief Remove the specified bond. * @param index The index of the bond to be removed. * @return True on success, false if the bond was not found. */ virtual bool removeBond(Index index); /** * @brief Remove the specified bond. * @param bond The bond to be removed. * @return True on success, false if the bond was not found. * @overload */ virtual bool removeBond(const BondType& bond); /** * @brief Remove the specified bond. * @param atom1 One atom in the bond. * @param atom2 The other atom in the bond. * @return True on success, false if the bond was not found. * @overload * @{ */ virtual bool removeBond(Index atom1, Index atom2); virtual bool removeBond(const AtomType& atom1, const AtomType& atom2); /** @} */ /** * Remove all bonds from the molecule. */ virtual void clearBonds(); /** @return the bond at @p index in the molecule. */ BondType bond(Index index) const; /** @return the bond between atoms @p a and @p b. */ BondType bond(const AtomType& a, const AtomType& b) const; /** @return the bond between atomId1 and atomId2. */ BondType bond(Index atomId1, Index atomId2) const; /** * @return Any custom label for the requested bond. * @param bondIndex The index of the bond. */ std::string bondLabel(Index bondIndex) const; /** * Set the custom label of a single bond. * @param bondIndex The index of the bond to modify. * @param label The new label of the bond. * @return True on success, false otherwise. */ bool setBondLabel(Index bondIndex, const std::string& label); const Core::Array bondLabels() const { return m_bondLabels; } /** * Set all the atom labels in the molecule. * @param label The new label array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setBondLabels(const Core::Array& label); /** * @brief Get all bonds to @p a. * @return A vector of bonds to the supplied atom @p a. * @{ */ Array bonds(const AtomType& a); Array bonds(Index a); Array bonds(Index a) const; /** @} */ /** * @brief Add a mesh to the molecule. * @return The mesh object added to the molecule. */ Mesh* addMesh(); Mesh* mesh(Index index); const Mesh* mesh(Index index) const; Index meshCount() const { return static_cast(m_meshes.size()); } void clearMeshes(); /** * @brief Add a cube to the molecule. * @return The cube object added to the molecule. */ Cube* addCube(); Cube* cube(Index index); const Cube* cube(Index index) const; Index cubeCount() const { return static_cast(m_cubes.size()); } void clearCubes(); /** * @brief Get the cubes vector set (if present) for the molecule. * @return The cube vector for the molecule */ std::vector cubes() { return m_cubes; } const std::vector cubes() const { return m_cubes; } /** * @return the chemical formula of the molecule. * @param delimiter Delimiter to insert between tokens, defaults to none. * @param showCountsOver Show atom counts above this (defaults to 1). */ std::string formula(const std::string& delimiter = "", int showCountsOver = 1) const; /** * @return The mass of the molecule obtained by summing constituent atomic * masses. */ double mass() const; /** * @return The center of geometry of the molecule obtained by summing the * coordinates of the atoms. */ Vector3 centerOfGeometry() const; /** * @return The center of mass of the molecule obtained by summing the * coordinates of the atoms weighted by mass. */ Vector3 centerOfMass() const; /** * @return The minimum radius of a sphere centered on centerOfGeometry * containing all the centers of the atoms. */ double radius() const; /** * @return The (centroid, normal vector) pair of the best-fit plane of * the atoms of the molecule. */ std::pair bestFitPlane() const; /** * @return The normal vector of the best-fit plane of some specific atoms. */ static std::pair bestFitPlane(const Array& pos); /** * Set the basis set for the molecule, note that the molecule takes ownership * of the object. */ void setBasisSet(BasisSet* basis) { m_basisSet = basis; } /** * @return the basis set (if present) for the molecule. */ BasisSet* basisSet() { return m_basisSet; } const BasisSet* basisSet() const { return m_basisSet; } /** * The unit cell for this molecule. May be nullptr for non-periodic * structures. * @{ */ void setUnitCell(UnitCell* uc); UnitCell* unitCell() { return m_unitCell; } const UnitCell* unitCell() const { return m_unitCell; } /** @} */ /** * The space group for this molecule. It is updated after every * space group operation. * @{ */ void setHallNumber(unsigned short hallNumber) { m_hallNumber = hallNumber; } unsigned short hallNumber() const { return m_hallNumber; } /** @} */ Array vibrationFrequencies() const; void setVibrationFrequencies(const Array& freq); Array vibrationIRIntensities() const; void setVibrationIRIntensities(const Array& intensities); Array vibrationRamanIntensities() const; void setVibrationRamanIntensities(const Array& intensities); Array vibrationLx(int mode) const; void setVibrationLx(const Array>& lx); /** * Perceives bonds in the molecule based on the 3D coordinates of the atoms. * atoms are considered bonded if within the sum of radii * plus a small @p tolerance. * @param tolerance The calculation tolerance. * @param minDistance = atoms closer than the square of this are ignored */ void perceiveBondsSimple(const double tolerance = 0.45, const double minDistance = 0.32); /** * Perceives bonds in the molecule based on preset residue data. * * Use this if you have residue data available (e.g., reading PDB or MMTF * files) Otherwise consider @sa perceiveBondsSimple and @sa * perceiveBondOrders */ void perceiveBondsFromResidueData(); void perceiveBondOrders(); /** * Perceives all-carbon-substituted onium ions of nitrogen, oxygen, * phosphorus, sulfur, arsenic and selenium. */ void perceiveSubstitutedCations(); size_t coordinate3dCount() const; bool setCoordinate3d(int coord); Array coordinate3d(size_t index) const; bool setCoordinate3d(const Array& coords, size_t index); /** * Clear coordinate sets (except the default set) */ void clearCoordinate3d(); /** * Timestep property is used when molecular dynamics trajectories are read */ bool setTimeStep(double timestep, int index); double timeStep(int index, bool& status); /** @return a vector of forces for the atoms in the molecule. */ const Array& forceVectors() const; /** \overload */ Array& forceVectors(); /** * Get the force of a single atom. * @param atomId The index of the atom. * @return The force vector of the atom, or Vector3::Zero() if no force * information has been set. */ Vector3 forceVector(Index atomId) const; /** * Replace the current array of force vectors. * @param forces The new coordinate array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setForceVectors(const Core::Array& forces); /** * Set the 3D position of a single atom. * @param atomId The index of the atom to modify. * @param force The new position of the atom. * @return True on success, false otherwise. */ bool setForceVector(Index atomId, const Vector3& force); Residue& addResidue(std::string& name, Index& number, char& id); void addResidue(Residue& residue); Residue& residue(Index index); Array& residues() { return m_residues; } const Array& residues() const { return m_residues; } /** @return The number of residues in the molecule. */ Index residueCount() const; /** @return The number of atoms in the molecule. */ Index atomCount() const; /** * @brief Get the number of atoms in the molecule that match atomicNumber. * @param atomicNumber The atomic number to match on. * @return The number of atoms with the supplied atomic number. */ Index atomCount(unsigned char atomicNumber) const; /** @return the number of bonds in the molecule. */ inline Index bondCount() const; // getters and setters /** * Get the set of bonded atoms corresponding to @a bondId. * @param bondId The index of the requested bond. * @return The bonded atom pair, represented as a pair of atom indices. */ inline std::pair bondPair(Index bondId) const; /** * Replace the current array of bonded atoms. * @param pairs The array. * @return True on success, false on failure. * @note The bonded atoms are represented as a pair of bond indices. * @note If needed, the elements in @a pairs will be modified to ensure that * the first atom index is less than the second. */ bool setBondPairs(const Array>& pairs); /** @return a vector of pairs of atom indices of the bonds in the molecule. */ inline const Array>& bondPairs() const; /** @return a vector of the bond orders for the bonds in the molecule. */ inline const Array& bondOrders() const; /** @return the graph for the molecule. */ inline const Graph& graph() const; /** @return a vector of atomic numbers for the atoms in the molecule. */ inline const Array& atomicNumbers() const; /** * Get the atomic number for the requested atom. * @param atomId The index of the atom. * @return The atomic number of the atom indexed at @a atomId, or * Avogadro::InvalidElement if @a atomId is invalid. */ unsigned char atomicNumber(Index atomId) const; /** * Replace the current array of bond orders. * @param orders The new array. * @return True on success, false on failure. */ bool setBondOrders(const Array& orders); /** * Set the order of a bond in the molecule. * @param bondId The bond's index. * @param order The new order of the bond. * @return True on success, false on failure. */ bool setBondOrder(Index bondId, unsigned char order); /** * @return True if custom elements exist in the molecule. * @note Custom element atomic numbers lie between CustomElementMin and * CustomElementMax. */ bool hasCustomElements() const; /** * Set the bonded atoms for a bond. * @param bondId The bond to modify. * @param pair The new bond pair. * @return True on success, false otherwise. * @note If needed, @a pair will be modified to ensure that the first atom * index is less than the second. */ bool setBondPair(Index bondId, const std::pair& pair); /** * Get the order of a bond. * @param bondId The id of the bond. * @return The bond order. */ unsigned char bondOrder(Index bondId) const; /** * Replace the current array of atomic numbers. * @param nums The new atomic number array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setAtomicNumbers(const Core::Array& nums); /** * Set the atomic number of a single atom. * @param atomId The index of the atom to modify. * @param atomicNumber The new atomic number. * @return True on success, false otherwise. */ bool setAtomicNumber(Index atomId, unsigned char atomicNumber); /** @name Constraints * Methods for distance, angle, torsion, etc. constraints */ ///@{ void addConstraint(Real Value, Index a, Index b, Index c = MaxIndex, Index d = MaxIndex); void addConstraint(Constraint& c) { m_constraints.push_back(c); } void removeConstraint(Index a, Index b, Index c = MaxIndex, Index d = MaxIndex); void clearConstraints() { m_constraints.clear(); } void setConstraints(const std::vector& constraints) { m_constraints = constraints; } std::vector& constraints() { return m_constraints; }; const std::vector& constraints() const { return m_constraints; } /** * Freeze or unfreeze an atom for optimization */ void setFrozenAtom(Index atomId, bool frozen); /** * Get the frozen status of an atom */ bool frozenAtom(Index atomId) const; /** * Freeze or unfreeze X, Y, or Z coordinate of an atom for optimization * @param atomId The index of the atom to modify. * @param axis The axis to freeze (0, 1, or 2 for X, Y, or Z) * @param frozen True to freeze, false to unfreeze */ void setFrozenAtomAxis(Index atomId, int axis, bool frozen); /** * @return the frozen status of atoms (i.e., 3*N array of 1.0 or 0.0) * 0.0 means the atom is frozen, 1.0 means the atom is not frozen. * (i.e., multiply this mask with gradients to freeze atoms) */ Eigen::VectorXd frozenAtomMask() const { return m_frozenAtomMask; } ///@} end of constraint methods /** * @return a map of components and count. */ std::map composition() const; /** * @return the atom pairs for all bonds to the atom indexed at @a index. */ Array> getAtomBonds(Index index) const; /** * @return the bond orders for all bonds to the atom indexed at @a index. */ Array getAtomOrders(Index index) const; inline static std::pair makeBondPair(const Index& a, const Index& b); /** Remove bonds to @a atom */ bool removeBonds(Index atom); void addBonds(const Array>& bonds, const Array& orders); // chenge the bond index position void swapBond(Index a, Index b); // channge the Atom index position void swapAtom(Index a, Index b); std::list getAtomsAtLayer(size_t layer); Layer& layer(); const Layer& layer() const; /** * Calculte and return bounding box of the whole molecule or selected atoms * only. * @param boxMin [out] the minimum corner (first end of the box diagonal) * @param boxMax [out] the maximum corner (second end of the box diagonal) * @param radius [in] radius of a single sphere */ void boundingBox(Vector3& boxMin, Vector3& boxMax, const double radius = 1.0) const; protected: VariantMap m_data; std::map m_partialCharges; //!< Sets of atomic partial charges std::map m_spectra; //!< Sets of spectra CustomElementMap m_customElementMap; ElementMask m_elements; //!< Which elements this molecule contains (e.g., for //!< force fields) Array m_positions2d; Array m_positions3d; Array m_atomLabels; Array m_bondLabels; Array> m_coordinates3d; //!< Store conformers/trajectories. Array m_timesteps; Array m_hybridizations; Array m_formalCharges; Array m_forceVectors; Array m_colors; // Vibration data if available. Array m_vibrationFrequencies; Array m_vibrationIRIntensities; Array m_vibrationRamanIntensities; Array> m_vibrationLx; // Array declaring whether atoms are selected or not. std::vector m_selectedAtoms; std::vector m_meshes; std::vector m_cubes; BasisSet* m_basisSet; UnitCell* m_unitCell; Array m_residues; // This will be stored from the last space group operation unsigned short m_hallNumber = 0; std::vector m_constraints; Eigen::VectorXd m_frozenAtomMask; private: mutable Graph m_graph; // A transformation of the molecule to a graph. // edge information Array m_bondOrders; // vertex information Array m_atomicNumbers; Layer& m_layers; }; class AVOGADROCORE_EXPORT Atom : public AtomTemplate { public: Atom() = default; Atom(Molecule* m, Index i) : AtomTemplate(m, i) {} }; class AVOGADROCORE_EXPORT Bond : public BondTemplate { public: Bond() = default; Bond(Molecule* m, Index i) : BondTemplate(m, i) {} }; inline AtomHybridization Molecule::hybridization(Index atomId) const { AtomHybridization hyb = HybridizationUnknown; if (atomId < m_hybridizations.size()) return m_hybridizations[atomId]; return hyb; } inline bool Molecule::setHybridizations( const Core::Array& hybs) { if (hybs.size() == atomCount()) { m_hybridizations = hybs; return true; } return false; } inline bool Molecule::setHybridization(Index atomId, AtomHybridization hyb) { if (atomId < atomCount()) { if (atomId >= m_hybridizations.size()) m_hybridizations.resize(atomCount(), HybridizationUnknown); m_hybridizations[atomId] = hyb; return true; } return false; } inline signed char Molecule::formalCharge(Index atomId) const { return atomId < m_formalCharges.size() ? m_formalCharges[atomId] : 0; } inline bool Molecule::setFormalCharges(const Core::Array& charges) { if (charges.size() == atomCount()) { m_formalCharges = charges; return true; } return false; } inline bool Molecule::setFormalCharge(Index atomId, signed char charge) { if (atomId < atomCount()) { if (atomId >= m_formalCharges.size()) m_formalCharges.resize(atomCount(), 0); m_formalCharges[atomId] = charge; return true; } return false; } inline const Molecule::ElementMask Molecule::elements() const { return m_elements; } inline Vector3ub Molecule::color(Index atomId) const { if (atomId >= atomCount()) return Vector3ub(0, 0, 0); if (atomId < m_colors.size()) return m_colors[atomId]; return Vector3ub(Elements::color(atomicNumber(atomId))); } inline bool Molecule::setColors(const Core::Array& colors) { if (colors.size() == atomCount()) { m_colors = colors; return true; } return false; } inline bool Molecule::setColor(Index atomId, Vector3ub color) { if (atomId < atomCount()) { if (atomId >= m_colors.size()) { for (Index i = m_colors.size(); i < atomCount(); ++i) { m_colors.push_back(Vector3ub(Elements::color(atomicNumber(i)))); } } m_colors[atomId] = color; return true; } return false; } inline size_t Molecule::layer(Index atomId) const { return m_layers.getLayerID(atomId); } inline bool Molecule::setLayer(Index atomId, size_t layer) { if (atomId < atomCount()) { m_layers.addAtom(layer, atomId); return true; } return false; } inline Vector2 Molecule::atomPosition2d(Index atomId) const { return atomId < m_positions2d.size() ? m_positions2d[atomId] : Vector2(); } inline bool Molecule::setAtomPositions2d(const Core::Array& pos) { if (pos.size() == atomCount() || pos.size() == 0) { m_positions2d = pos; return true; } return false; } inline bool Molecule::setAtomPosition2d(Index atomId, const Vector2& pos) { if (atomId < atomCount()) { if (atomId >= m_positions2d.size()) m_positions2d.resize(atomCount(), Vector2::Zero()); m_positions2d[atomId] = pos; return true; } return false; } inline Vector3 Molecule::atomPosition3d(Index atomId) const { return atomId < m_positions3d.size() ? m_positions3d[atomId] : Vector3(); } inline bool Molecule::setAtomPositions3d(const Core::Array& pos) { if (pos.size() == atomCount() || pos.size() == 0) { m_positions3d = pos; return true; } return false; } inline bool Molecule::setAtomPosition3d(Index atomId, const Vector3& pos) { if (atomId < atomCount()) { if (atomId >= m_positions3d.size()) m_positions3d.resize(atomCount(), Vector3::Zero()); m_positions3d[atomId] = pos; return true; } return false; } inline std::string Molecule::atomLabel(Index atomId) const { return atomId < m_atomLabels.size() ? m_atomLabels[atomId] : ""; } inline bool Molecule::setAtomLabels(const Core::Array& labels) { if (labels.size() == atomCount() || labels.size() == 0) { m_atomLabels = labels; return true; } return false; } inline bool Molecule::setAtomLabel(Index atomId, const std::string& label) { if (atomId < atomCount()) { if (atomId >= m_atomLabels.size()) m_atomLabels.resize(atomCount(), ""); m_atomLabels[atomId] = label; return true; } return false; } inline void Molecule::setAtomSelected(Index atomId, bool selected) { if (atomId < atomCount()) { if (atomId >= m_selectedAtoms.size()) m_selectedAtoms.resize(atomCount(), false); m_selectedAtoms[atomId] = selected; } } inline bool Molecule::atomSelected(Index atomId) const { return atomId < m_selectedAtoms.size() ? m_selectedAtoms[atomId] : false; } inline bool Molecule::isSelectionEmpty() const { for (Index i = 0; i < m_selectedAtoms.size(); ++i) { if (m_selectedAtoms[i]) return false; } return true; } inline Vector3 Molecule::forceVector(Index atomId) const { return atomId < m_forceVectors.size() ? m_forceVectors[atomId] : Vector3(); } inline bool Molecule::setForceVectors(const Core::Array& forces) { if (forces.size() == atomCount() || forces.size() == 0) { m_forceVectors = forces; return true; } return false; } inline bool Molecule::setForceVector(Index atomId, const Vector3& force) { if (atomId < atomCount()) { if (atomId >= m_forceVectors.size()) m_forceVectors.resize(atomCount(), Vector3::Zero()); m_forceVectors[atomId] = force; return true; } return false; } // Make an std::pair where the lower index is always first in the pair. This // offers us the guarantee that any given pair of atoms will always result in // a pair that is the same no matter what the order of the atoms given. std::pair Molecule::makeBondPair(const Index& a, const Index& b) { return a < b ? std::make_pair(a, b) : std::make_pair(b, a); } inline Index Molecule::bondCount() const { assert(m_graph.edgeCount() == m_bondOrders.size()); return m_graph.edgeCount(); } inline const Array>& Molecule::bondPairs() const { return m_graph.edgePairs(); } inline const Array& Molecule::bondOrders() const { return m_bondOrders; } inline std::string Molecule::bondLabel(Index bondId) const { return bondId < m_bondLabels.size() ? m_bondLabels[bondId] : ""; } inline bool Molecule::setBondLabels(const Core::Array& labels) { if (labels.size() == atomCount() || labels.size() == 0) { m_bondLabels = labels; return true; } return false; } inline bool Molecule::setBondLabel(Index bondId, const std::string& label) { if (bondId < bondCount()) { if (bondId >= m_bondLabels.size()) m_bondLabels.resize(bondCount(), ""); m_bondLabels[bondId] = label; return true; } return false; } inline const Graph& Molecule::graph() const { return m_graph; } inline const Array& Molecule::atomicNumbers() const { return m_atomicNumbers; } inline std::pair Molecule::bondPair(Index bondId) const { return bondId < bondCount() ? m_graph.endpoints(bondId) : std::make_pair(MaxIndex, MaxIndex); } inline Index Molecule::atomCount() const { return static_cast(m_atomicNumbers.size()); } inline unsigned char Molecule::atomicNumber(Index atomId) const { return atomId < m_atomicNumbers.size() ? m_atomicNumbers[atomId] : InvalidElement; } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_MOLECULE_H avogadrolibs-1.101.0/avogadro/core/mutex.cpp000066400000000000000000000012471506155467400207470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "mutex.h" #include namespace Avogadro::Core { using std::mutex; class Mutex::PIMPL { public: PIMPL() {} mutex lock; }; Mutex::Mutex() : d(new PIMPL) {} Mutex::~Mutex() { delete d; } void Mutex::lock() { d->lock.lock(); } bool Mutex::tryLock() { return d->lock.try_lock(); } void Mutex::unlock() { d->lock.unlock(); } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/mutex.h000066400000000000000000000021361506155467400204120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_MUTEX_H #define AVOGADRO_CORE_MUTEX_H #include "avogadrocoreexport.h" namespace Avogadro::Core { /** * @class Mutex mutex.h * @brief The Mutex class provides a simple wrapper for the C++11 mutex * class * @author Marcus D. Hanwell * * A very simple, and thin wrapper around the C++11 mutex class, allowing for * lock, tryLock and unlock. */ class AVOGADROCORE_EXPORT Mutex { public: Mutex(); ~Mutex(); /** * @brief Obtain an exclusive lock. */ void lock(); /** * @brief Attempt to obtain an exclusive lock. * @return True on success, false on failure. */ bool tryLock(); /** * @brief Unlocks the lock. */ void unlock(); private: class PIMPL; PIMPL* d; }; } // end namespace Avogadro::Core #endif // AVOGADRO_CORE_MUTEX_H avogadrolibs-1.101.0/avogadro/core/nameatomtyper.cpp000066400000000000000000000011311506155467400224620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "nameatomtyper.h" #include "elements.h" namespace Avogadro::Core { NameAtomTyper::NameAtomTyper(const Molecule* mol) : AtomTyper(mol) { } std::string NameAtomTyper::type(const Atom& atom) { return std::string(Elements::name(atom.atomicNumber())); } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/nameatomtyper.h000066400000000000000000000017461506155467400221430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_NAMEATOMTYPER_H #define AVOGADRO_CORE_NAMEATOMTYPER_H #include "avogadrocoreexport.h" #include #include namespace Avogadro::Core { /** * @class NameAtomTyper nameatomtyper.h * @brief The NameAtomTyper class is a simple implementation of AtomTyper that * assigns element names to each atom. */ class AVOGADROCORE_EXPORT NameAtomTyper : public AtomTyper { public: explicit NameAtomTyper(const Molecule* mol = nullptr); ~NameAtomTyper() override = default; protected: std::string type(const Atom& atom) override; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_NAMEATOMTYPER_H avogadrolibs-1.101.0/avogadro/core/neighborperceiver.cpp000066400000000000000000000054631506155467400233130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "neighborperceiver.h" namespace Avogadro::Core { NeighborPerceiver::NeighborPerceiver(const Array points, float maxDistance) : m_maxDistance(maxDistance), m_cachedArray(nullptr) { if (!points.size()) return; // find bounding box m_minPos = points[0]; m_maxPos = points[0]; for (Index i = 1; i < points.size(); i++) { Vector3 ipos = points[i]; for (size_t c = 0; c < 3; c++) { m_minPos(c) = std::min(ipos(c), m_minPos(c)); m_maxPos(c) = std::max(ipos(c), m_maxPos(c)); } } // group points into cubic bins so that each point is only checked against // other points inside bins within a 3-dimensional Moore neighborhood for (size_t c = 0; c < 3; c++) m_binCount[c] = std::floor((m_maxPos(c) + 0.1 - m_minPos(c)) / m_maxDistance) + 1; std::vector>>> bins( m_binCount[0], std::vector>>( m_binCount[1], std::vector>( m_binCount[2], std::vector()))); m_bins = bins; for (Index i = 0; i < points.size(); i++) { std::array bin_index = getBinIndex(points[i]); m_bins.at(bin_index[0]).at(bin_index[1]).at(bin_index[2]).push_back(i); } } void NeighborPerceiver::getNeighborsInclusiveInPlace(Array& out, const Vector3& point) const { const std::array bin_index = getBinIndex(point); if (&out == m_cachedArray && bin_index == m_cachedIndex) return; m_cachedIndex = bin_index; out.clear(); for (int xi = std::max(int(1), bin_index[0]) - 1; xi < std::min(m_binCount[0], bin_index[0] + 2); xi++) { for (int yi = std::max(int(1), bin_index[1]) - 1; yi < std::min(m_binCount[1], bin_index[1] + 2); yi++) { for (int zi = std::max(int(1), bin_index[2]) - 1; zi < std::min(m_binCount[2], bin_index[2] + 2); zi++) { const std::vector& bin = m_bins[xi][yi][zi]; out.insert(out.end(), bin.begin(), bin.end()); } } } } Array NeighborPerceiver::getNeighborsInclusive( const Vector3& point) const { Array r; getNeighborsInclusiveInPlace(r, point); return r; } std::array NeighborPerceiver::getBinIndex(const Vector3& point) const { std::array r = {}; for (size_t c = 0; c < 3; c++) { r[c] = std::floor((point(c) - m_minPos(c)) / m_maxDistance); } return r; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/neighborperceiver.h000066400000000000000000000047431506155467400227600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_NEIGHBORPERCEIVER_H #define AVOGADRO_CORE_NEIGHBORPERCEIVER_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include "array.h" #include "vector.h" #include #include namespace Avogadro::Core { /** * @class NeighborPerceiver neighborperceiver.h * * @brief This class can be used to find physically neighboring points in linear * average time. */ class AVOGADROCORE_EXPORT NeighborPerceiver { public: /** * Creates a NeighborPerceiver that detects neighbors up to at least some * distance. * * @param points Positions in 3D space to detect neighbors among. * @param maxDistance All neighbors strictly within this distance will be * detected. Should be as low as possible for best * performance. */ NeighborPerceiver(const Array points, float maxDistance); /** * Returns a list of neighboring points. Linear time to number of neighbors. * Can include some neighbors up to 2*sqrt(3) times the maximum distance. * The list is newly allocated on every call; if performance/fragmentation * is a concern, prefer NeighborPerceiver::getNeighborsInclusiveInPlace(). * * @param point Position to return neighbors of, can be located anywhere. */ Array getNeighborsInclusive(const Vector3& point) const; /** * Fills an array with all neighboring points. Linear time to number of * neighbors. Can include some neighbors up to 2*sqrt(3) times the maximum * distance. * * @param out Array to output neighbor indices in. * @param point Position to return neighbors of, can be located anywhere. */ void getNeighborsInclusiveInPlace(Array& out, const Vector3& point) const; private: std::array getBinIndex(const Vector3& point) const; protected: float m_maxDistance; std::array m_binCount; std::vector>>> m_bins; Vector3 m_minPos; Vector3 m_maxPos; mutable Array* m_cachedArray; mutable std::array m_cachedIndex; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_NEIGHBORPERCEIVER_H avogadrolibs-1.101.0/avogadro/core/residue.cpp000066400000000000000000000107361506155467400212500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "residue.h" #include "molecule.h" #include "residuecolors.h" #include "residuedata.h" namespace Avogadro::Core { Residue::Residue(std::string& name) : m_residueName(name), m_residueId(0), m_chainId('A'), m_heterogen(false), m_color(0, 0, 0), m_customColorSet(false), m_secondaryStructure(undefined) { } Residue::Residue(std::string& name, Index& number) : m_residueName(name), m_residueId(number), m_chainId('A'), m_heterogen(false), m_color(0, 0, 0), m_customColorSet(false), m_secondaryStructure(undefined) { } Residue::Residue(std::string& name, Index& number, char& id) : m_residueName(name), m_residueId(number), m_chainId(id), m_heterogen(false), m_color(0, 0, 0), m_customColorSet(false), m_secondaryStructure(undefined) { } Residue::Residue(const Residue& other) : m_residueName(other.m_residueName), m_residueId(other.m_residueId), m_chainId(other.m_chainId), m_atomNameMap(other.m_atomNameMap), m_heterogen(other.m_heterogen), m_color(other.m_color), m_customColorSet(other.m_customColorSet), m_secondaryStructure(other.m_secondaryStructure) { } Residue& Residue::operator=(Residue other) { m_residueName = other.m_residueName; m_residueId = other.m_residueId; m_chainId = other.m_chainId; m_atomNameMap = other.m_atomNameMap; m_heterogen = other.m_heterogen; m_color = other.m_color; m_customColorSet = other.m_customColorSet; m_secondaryStructure = other.m_secondaryStructure; return *this; } void Residue::addResidueAtom(const std::string& name, const Atom& atom) { m_atomNameMap.insert(std::pair(name, atom)); } std::vector Residue::residueAtoms() const { std::vector res; for (const auto& it : m_atomNameMap) { res.push_back(it.second); } return res; } Atom Residue::getAtomByName(std::string name) const { Atom empty; auto search = m_atomNameMap.find(name); if (search != m_atomNameMap.end()) { return search->second; } return empty; } std::string Residue::getAtomName(const Atom atom) const { for (const auto& it : m_atomNameMap) { if (it.second == atom) { return it.first; } } return ""; } std::string Residue::getAtomName(const Index index) const { for (const auto& it : m_atomNameMap) { if (it.second.index() == index) { return it.first; } } return ""; } void Residue::resolveResidueBonds(Molecule& mol) { std::vector> bondSeq; if (residueDict.find(m_residueName) != residueDict.end()) { size_t i = 0; bondSeq = residueDict[m_residueName].residueSingleBonds(); for (i = 0; i < bondSeq.size(); ++i) { if (m_atomNameMap.find(bondSeq[i].first) != m_atomNameMap.end() && m_atomNameMap.find(bondSeq[i].second) != m_atomNameMap.end()) { mol.Avogadro::Core::Molecule::addBond( m_atomNameMap[bondSeq[i].first], m_atomNameMap[bondSeq[i].second], 1); } } bondSeq = residueDict[m_residueName].residueDoubleBonds(); for (i = 0; i < bondSeq.size(); ++i) { if (m_atomNameMap.find(bondSeq[i].first) != m_atomNameMap.end() && m_atomNameMap.find(bondSeq[i].second) != m_atomNameMap.end()) { mol.Avogadro::Core::Molecule::addBond( m_atomNameMap[bondSeq[i].first], m_atomNameMap[bondSeq[i].second], 2); } } } } int Residue::getAtomicNumber(std::string name) const { auto search = m_atomNameMap.find(name); if (search != m_atomNameMap.end()) { return search->second.atomicNumber(); } return 0; } void Residue::setColor(const Vector3ub color) { m_customColorSet = true; m_color = color; } Vector3ub Residue::color() const { if (m_customColorSet) return m_color; // default return a color for the chain int offset = 0; if (m_chainId >= 'A' && m_chainId <= 'Z') offset = m_chainId - 'A'; else if (m_chainId >= 'a' && m_chainId <= 'z') offset = m_chainId - 'a'; else if (m_chainId >= '0' && m_chainId <= '9') offset = m_chainId - '0' + 15; // starts at 'P' return Vector3ub(chain_color[offset]); } bool Residue::hasAtomByIndex(Index index) const { for (const auto& atom : residueAtoms()) { if (atom.index() == index) { return true; } } return false; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/residue.h000066400000000000000000000075411506155467400207150ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_RESIDUE_H #define AVOGADRO_CORE_RESIDUE_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include #include #include #include "vector.h" namespace Avogadro::Core { class Atom; class Molecule; /** * @class Residue residue.h * @brief The Residue class represents a chemical residue, used commonly in the * PDB format. */ class AVOGADROCORE_EXPORT Residue { public: /** Type for atom name map. */ using AtomNameMap = std::map; // using codes from MMTF specification // https://github.com/rcsb/mmtf/blob/master/spec.md#secstructlist enum SecondaryStructure { piHelix = 0, // DSSP "I" bend = 1, // DSSP "S" alphaHelix = 2, // DSSP "H" betaSheet = 3, // DSSP "E" helix310 = 4, // DSSP "G" betaBridge = 5, // DSSP "B" turn = 6, // DSSP "T" coil = 7, // DSSP "C" maybeBeta = -3, // potential beta strand undefined = -1 }; /** Creates a new, empty residue. */ Residue() = default; Residue(std::string& name); Residue(std::string& name, Index& number); Residue(std::string& name, Index& number, char& id); Residue(const Residue& other); Residue& operator=(Residue other); virtual ~Residue() = default; std::string residueName() const { return m_residueName; } void setResidueName(std::string& name) { m_residueName = name; } Index residueId() const { return m_residueId; } void setResidueId(Index& number) { m_residueId = number; } char chainId() const { return m_chainId; } void setChainId(const char& id) { m_chainId = id; } SecondaryStructure secondaryStructure() const { return m_secondaryStructure; } void setSecondaryStructure(const SecondaryStructure& ss) { m_secondaryStructure = ss; } /** Adds an atom to the residue class */ void addResidueAtom(const std::string& name, const Atom& atom); /** \return a vector containing the atoms added to the residue */ std::vector residueAtoms() const; /** \return the atom map for the residue */ AtomNameMap& atomNameMap() { return m_atomNameMap; } /** Sets bonds to atoms in the residue based on data from residuedata header */ void resolveResidueBonds(Molecule& mol); /** * \return the atom with the name specified (e.g., "CA") */ Atom getAtomByName(std::string name) const; /** * \return the atomic number of the atom with the name specified (e.g., "CA" = * "C") */ int getAtomicNumber(std::string name) const; /** * \return the name of @p atom or an empty string if not in this residue */ std::string getAtomName(const Atom atom) const; /** * \return the name of atom @p index or an empty string if not in this residue */ std::string getAtomName(const Index index) const; bool hasAtomByIndex(Index index) const; /** Set whether this residue is a "HET" / "HETATOM" ligand */ void setHeterogen(bool heterogen) { m_heterogen = heterogen; } /** \return is this residue a heterogen (HET / HETATM) */ bool isHeterogen() const { return m_heterogen; } /** Set a custom color for this residue */ void setColor(const Vector3ub color); /** \return the color set for this residue, or a default from the chain id */ Vector3ub color() const; protected: std::string m_residueName; Index m_residueId = std::numeric_limits::max(); char m_chainId; AtomNameMap m_atomNameMap; bool m_heterogen; Vector3ub m_color; bool m_customColorSet; SecondaryStructure m_secondaryStructure; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_RESIDUE_H avogadrolibs-1.101.0/avogadro/core/residuecolors.h000066400000000000000000000065641506155467400221430ustar00rootroot00000000000000#ifndef AVOGADRO_CORE_RESIDUE_COLORS #define AVOGADRO_CORE_RESIDUE_COLORS namespace Avogadro::Core { unsigned char chain_color[][3] = { // clang-format off // See, for example http://jmol.sourceforge.net/jscolors/index.en.html // # A # B # C # D { 192, 208, 255 },{ 176, 255, 176 },{ 255, 192, 200 },{ 204, 128, 255 }, // # E # F # G # H { 255, 192, 255 },{ 176, 240, 240 },{ 255, 208, 112 },{ 240, 128, 128 }, // # I # J # K # L { 245, 222, 179 },{ 0, 191, 255 }, { 205, 92, 92 }, { 102, 205, 170 }, // # M # N # O # P { 154, 205, 50 }, { 238, 130, 238 },{ 0, 206, 209 }, { 0, 255, 127 }, // # Q # R # S # T { 60, 179, 113 }, { 0, 0, 139 }, { 189, 183, 107 }, { 0, 100, 0 }, // # U # V # W # X { 128, 0, 0 }, { 128, 128, 0 }, { 128, 128, 0 }, { 0, 128, 128 }, // # Y # Z { 184, 134, 11 }, { 178, 34, 34 } // clang-format on }; unsigned char amino_color[][3] = { // clang-format off // See, for example http://jmol.sourceforge.net/jscolors/index.en.html // # Ala # Arg # Asn # Asp { 200, 200, 200 },{ 20, 90, 255 },{ 0, 220, 220 },{ 230, 10, 10 }, // # Cys # Gln # Glu # Gly { 230, 230, 0 },{ 0, 220, 220 },{ 230, 10, 10 },{ 235, 235, 235 }, // # His # Ile # Leu # Lys { 130, 130, 255 },{ 15, 130, 15 },{ 15, 130, 15 },{ 20, 90, 255 }, // # Met # Phe # Pro # Ser { 230, 230, 0 },{ 50, 50, 170 },{ 220, 150, 130 },{ 250, 150, 0 }, // # Thr # Trp # Tyr # Val { 250, 150, 0 },{ 180, 90, 180 },{ 50, 50, 170 },{ 15, 130, 15 }, // # Asx # Glx # Anything else { 255, 105, 180 },{ 255, 105, 180 }, { 190, 160, 110} // clang-format on }; unsigned char shapely_color[][3] = { // clang-format off // See, for example http://jmol.sourceforge.net/jscolors/index.en.html // # Ala # Arg # Asn # Asp { 140, 255, 140 },{ 0, 0, 124 },{ 255, 124, 112 },{ 160, 0, 66 }, // # Cys # Gln # Glu # Gly { 255, 255, 112 },{ 255, 76, 76 },{ 102, 0, 0 },{ 255, 255, 255 }, // # His # Ile # Leu # Lys { 112, 112, 255 },{ 0, 76, 0 },{ 69, 94, 69 },{ 71, 71, 184 }, // # Met # Phe # Pro # Ser { 184, 160, 66 },{ 83, 76, 82 },{ 82, 82, 82 },{ 255, 112, 66 }, // # Thr # Trp # Tyr # Val { 184, 76, 0 },{ 79, 70, 0 },{ 140, 112, 76 },{ 255, 140, 255 }, // # Asx # Glx # Anything else { 255, 0, 255 },{ 255, 0, 255 }, { 255, 0, 255} // clang-format on }; unsigned char secondary_color[][3] = { // clang-format off // See, for example http://jmol.sourceforge.net/jscolors/index.en.html // using codes from MMTF // 0 = pi "I" # bend "S" # alpha "H" # beta "E" { 96, 0, 128 },{ 255, 255, 255 },{ 255, 0, 128 }, { 255, 200, 0 }, // 4 = 3-10 "G" # bridge "B" # turn "T" # coil "C" { 160, 0, 128 },{ 255, 255, 255 },{ 96, 128, 255 }, { 255, 255, 255 } // clang-format on }; } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/residuedata.h000066400000000000000000003361561506155467400215560ustar00rootroot00000000000000 #ifndef AVOGADRO_CORE_RESIDUE_DATA #define AVOGADRO_CORE_RESIDUE_DATA #include #include #include #include namespace Avogadro::Core { class ResidueData { private: std::string m_residueName; std::map m_residueAtomNames; std::vector> m_residueSingleBonds; std::vector> m_residueDoubleBonds; public: ResidueData() = default; ResidueData(std::string name, std::map atomNames, std::vector> singleBonds, std::vector> doubleBonds) : m_residueName(std::move(name)), m_residueAtomNames(std::move(atomNames)), m_residueSingleBonds(std::move(singleBonds)), m_residueDoubleBonds(std::move(doubleBonds)) { } ResidueData(const ResidueData& other) : m_residueName(other.m_residueName), m_residueAtomNames(other.m_residueAtomNames), m_residueSingleBonds(other.m_residueSingleBonds), m_residueDoubleBonds(other.m_residueDoubleBonds) { } ResidueData(ResidueData&& other) noexcept : m_residueName(std::move(other.m_residueName)), m_residueAtomNames(std::move(other.m_residueAtomNames)), m_residueSingleBonds(std::move(other.m_residueSingleBonds)), m_residueDoubleBonds(std::move(other.m_residueDoubleBonds)) { } ResidueData& operator=(ResidueData other) { using std::swap; swap(*this, other); return *this; } std::map residueAtoms() { return m_residueAtomNames; } std::vector> residueSingleBonds() { return m_residueSingleBonds; } std::vector> residueDoubleBonds() { return m_residueDoubleBonds; } }; ResidueData ALAData("ALA", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB1", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HB3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "HB1" }, { "CB", "HB2" }, { "CB", "HB3" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData CYSData("CYS", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "SG", 16 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG", 1 }, { "HG", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "SG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "SG", "HG" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData ASPData("ASP", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "OD1", 8 }, { "OD2", 8 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HD2", 1 }, { "HD2", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "OD2" }, { "OD2", "HD2" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CG", "OD1" } }); ResidueData GLUData("GLU", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD", 6 }, { "OE1", 8 }, { "OE2", 8 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HE2", 1 }, { "HE2", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "CD", "OE2" }, { "OE2", "HE2" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CD", "OE1" } }); ResidueData PHEData( "PHE", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD1", 6 }, { "CD2", 6 }, { "CE1", 6 }, { "CE2", 6 }, { "CZ", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HD1", 1 }, { "HD2", 1 }, { "HE1", 1 }, { "HE2", 1 }, { "HZ", 1 }, { "HZ", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD2" }, { "CD1", "CE1" }, { "CD1", "HD1" }, { "CD2", "HD2" }, { "CE1", "HE1" }, { "CE2", "CZ" }, { "CE2", "HE2" }, { "CZ", "HZ" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CG", "CD1" }, { "CD2", "CE2" }, { "CE1", "CZ" } }); ResidueData GLYData("GLY", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA2", 1 }, { "HA3", 1 }, { "HA3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "HA2" }, { "CA", "HA3" }, { "C", "OXT" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData HISData("HIS", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "ND1", 7 }, { "CD2", 6 }, { "CE1", 6 }, { "NE2", 7 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HD1", 1 }, { "HD2", 1 }, { "HE1", 1 }, { "HE2", 1 }, { "HE2", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "ND1" }, { "ND1", "HD1" }, { "CD2", "NE2" }, { "CD2", "HD2" }, { "CE1", "NE2" }, { "CE1", "HE1" }, { "NE2", "HE2" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CG", "CD2" }, { "ND1", "CE1" } }); ResidueData ILEData( "ILE", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG1", 6 }, { "CG2", 6 }, { "CD1", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB", 1 }, { "HG12", 1 }, { "HG13", 1 }, { "HG21", 1 }, { "HG22", 1 }, { "HG23", 1 }, { "HD11", 1 }, { "HD12", 1 }, { "HD13", 1 }, { "HD13", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG1" }, { "CB", "CG2" }, { "CB", "HB" }, { "CG1", "CD1" }, { "CG1", "HG12" }, { "CG1", "HG13" }, { "CG2", "HG21" }, { "CG2", "HG22" }, { "CG2", "HG23" }, { "CD1", "HD11" }, { "CD1", "HD12" }, { "CD1", "HD13" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData LYSData( "LYS", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD", 6 }, { "CE", 6 }, { "NZ", 7 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HD2", 1 }, { "HD3", 1 }, { "HE2", 1 }, { "HE3", 1 }, { "HZ1", 1 }, { "HZ2", 1 }, { "HZ3", 1 }, { "HZ3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "CD", "CE" }, { "CD", "HD2" }, { "CD", "HD3" }, { "CE", "NZ" }, { "CE", "HE2" }, { "CE", "HE3" }, { "NZ", "HZ1" }, { "NZ", "HZ2" }, { "NZ", "HZ3" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData LEUData( "LEU", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD1", 6 }, { "CD2", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG", 1 }, { "HD11", 1 }, { "HD12", 1 }, { "HD13", 1 }, { "HD21", 1 }, { "HD22", 1 }, { "HD23", 1 }, { "HD23", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD1" }, { "CG", "CD2" }, { "CG", "HG" }, { "CD1", "HD11" }, { "CD1", "HD12" }, { "CD1", "HD13" }, { "CD2", "HD21" }, { "CD2", "HD22" }, { "CD2", "HD23" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData METData("MET", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "SD", 16 }, { "CE", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HE1", 1 }, { "HE2", 1 }, { "HE3", 1 }, { "HE3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "SD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "SD", "CE" }, { "CE", "HE1" }, { "CE", "HE2" }, { "CE", "HE3" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData ASNData("ASN", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "OD1", 8 }, { "ND2", 7 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HD21", 1 }, { "HD22", 1 }, { "HD22", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "ND2" }, { "ND2", "HD21" }, { "ND2", "HD22" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CG", "OD1" } }); ResidueData PROData("PRO", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD", 6 }, { "OXT", 8 }, { "H", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HD2", 1 }, { "HD3", 1 }, { "HD3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "CD" }, { "N", "H" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "CD", "HD2" }, { "CD", "HD3" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData GLNData( "GLN", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD", 6 }, { "OE1", 8 }, { "NE2", 7 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HE21", 1 }, { "HE22", 1 }, { "HE22", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "CD", "NE2" }, { "NE2", "HE21" }, { "NE2", "HE22" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CD", "OE1" } }); ResidueData ARGData( "ARG", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD", 6 }, { "NE", 7 }, { "CZ", 6 }, { "NH1", 7 }, { "NH2", 7 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HD2", 1 }, { "HD3", 1 }, { "HE", 1 }, { "HH11", 1 }, { "HH12", 1 }, { "HH21", 1 }, { "HH22", 1 }, { "HH22", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "CD", "NE" }, { "CD", "HD2" }, { "CD", "HD3" }, { "NE", "CZ" }, { "NE", "HE" }, { "CZ", "NH1" }, { "NH1", "HH11" }, { "NH1", "HH12" }, { "NH2", "HH21" }, { "NH2", "HH22" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CZ", "NH2" } }); ResidueData SERData("SER", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "OG", 8 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG", 1 }, { "HG", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "OG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "OG", "HG" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData THRData("THR", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "OG1", 8 }, { "CG2", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB", 1 }, { "HG1", 1 }, { "HG21", 1 }, { "HG22", 1 }, { "HG23", 1 }, { "HG23", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "OG1" }, { "CB", "CG2" }, { "CB", "HB" }, { "OG1", "HG1" }, { "CG2", "HG21" }, { "CG2", "HG22" }, { "CG2", "HG23" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData VALData("VAL", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG1", 6 }, { "CG2", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB", 1 }, { "HG11", 1 }, { "HG12", 1 }, { "HG13", 1 }, { "HG21", 1 }, { "HG22", 1 }, { "HG23", 1 }, { "HG23", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG1" }, { "CB", "CG2" }, { "CB", "HB" }, { "CG1", "HG11" }, { "CG1", "HG12" }, { "CG1", "HG13" }, { "CG2", "HG21" }, { "CG2", "HG22" }, { "CG2", "HG23" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData TRPData( "TRP", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD1", 6 }, { "CD2", 6 }, { "NE1", 7 }, { "CE2", 6 }, { "CE3", 6 }, { "CZ2", 6 }, { "CZ3", 6 }, { "CH2", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HD1", 1 }, { "HE1", 1 }, { "HE3", 1 }, { "HZ2", 1 }, { "HZ3", 1 }, { "HH2", 1 }, { "HH2", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD2" }, { "CD1", "NE1" }, { "CD1", "HD1" }, { "CD2", "CE3" }, { "NE1", "CE2" }, { "NE1", "HE1" }, { "CE2", "CZ2" }, { "CE3", "HE3" }, { "CZ2", "HZ2" }, { "CZ3", "CH2" }, { "CZ3", "HZ3" }, { "CH2", "HH2" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CG", "CD1" }, { "CD2", "CE2" }, { "CE3", "CZ3" }, { "CZ2", "CH2" } }); ResidueData TYRData( "TYR", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD1", 6 }, { "CD2", 6 }, { "CE1", 6 }, { "CE2", 6 }, { "CZ", 6 }, { "OH", 8 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HD1", 1 }, { "HD2", 1 }, { "HE1", 1 }, { "HE2", 1 }, { "HH", 1 }, { "HH", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD2" }, { "CD1", "CE1" }, { "CD1", "HD1" }, { "CD2", "HD2" }, { "CE1", "HE1" }, { "CE2", "CZ" }, { "CE2", "HE2" }, { "CZ", "OH" }, { "OH", "HH" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" }, { "CG", "CD1" }, { "CD2", "CE2" }, { "CE1", "CZ" } }); ResidueData DAData( "DA", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "H2''", 1 }, { "H1'", 1 }, { "H8", 1 }, { "H61", 1 }, { "H62", 1 }, { "H62", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "C2'", "H2''" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "H61" }, { "N6", "H62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "P", "OP1" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData DCData( "DC", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "C1'", 6 }, { "N1", 7 }, { "C2", 6 }, { "O2", 8 }, { "N3", 7 }, { "C4", 6 }, { "N4", 7 }, { "C5", 6 }, { "C6", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "H2''", 1 }, { "H1'", 1 }, { "H41", 1 }, { "H42", 1 }, { "H5", 1 }, { "H5", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "C2'", "H2''" }, { "C1'", "N1" }, { "C1'", "H1'" }, { "N1", "C2" }, { "N1", "C6" }, { "C2", "N3" }, { "C4", "N4" }, { "C4", "C5" }, { "N4", "H41" }, { "N4", "H42" }, { "C5", "H5" }, { "C6", "H6" } }, // Double Bonds { { "P", "OP1" }, { "C2", "O2" }, { "N3", "C4" }, { "C5", "C6" } }); ResidueData DGData( "DG", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "O6", 8 }, { "N1", 7 }, { "C2", 6 }, { "N2", 7 }, { "N3", 7 }, { "C4", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "H2''", 1 }, { "H1'", 1 }, { "H8", 1 }, { "H1", 1 }, { "H21", 1 }, { "H21", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "C2'", "H2''" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N1" }, { "N1", "C2" }, { "N1", "H1" }, { "C2", "N2" }, { "N2", "H21" }, { "N2", "H22" }, { "N3", "C4" } }, // Double Bonds { { "P", "OP1" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "O6" }, { "C2", "N3" } }); ResidueData DTData( "DT", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "C1'", 6 }, { "N1", 7 }, { "C2", 6 }, { "O2", 8 }, { "N3", 7 }, { "C4", 6 }, { "O4", 8 }, { "C5", 6 }, { "C7", 6 }, { "C6", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "H2''", 1 }, { "H1'", 1 }, { "H3", 1 }, { "H71", 1 }, { "H72", 1 }, { "H73", 1 }, { "H73", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "C2'", "H2''" }, { "C1'", "N1" }, { "C1'", "H1'" }, { "N1", "C2" }, { "N1", "C6" }, { "C2", "N3" }, { "N3", "C4" }, { "N3", "H3" }, { "C4", "C5" }, { "C5", "C7" }, { "C7", "H71" }, { "C7", "H72" }, { "C7", "H73" }, { "C6", "H6" } }, // Double Bonds { { "P", "OP1" }, { "C2", "O2" }, { "C4", "O4" }, { "C5", "C6" } }); ResidueData DIData( "DI", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "O6", 8 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "H2''", 1 }, { "H1'", 1 }, { "H8", 1 }, { "H1", 1 }, { "H1", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "C2'", "H2''" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N1" }, { "N1", "C2" }, { "N1", "H1" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "P", "OP1" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "O6" }, { "C2", "N3" } }); ResidueData AData( "A", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "H61", 1 }, { "H62", 1 }, { "H62", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "H61" }, { "N6", "H62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "P", "OP1" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData CData( "C", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N1", 7 }, { "C2", 6 }, { "O2", 8 }, { "N3", 7 }, { "C4", 6 }, { "N4", 7 }, { "C5", 6 }, { "C6", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H41", 1 }, { "H42", 1 }, { "H5", 1 }, { "H5", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N1" }, { "C1'", "H1'" }, { "N1", "C2" }, { "N1", "C6" }, { "C2", "N3" }, { "C4", "N4" }, { "C4", "C5" }, { "N4", "H41" }, { "N4", "H42" }, { "C5", "H5" }, { "C6", "H6" } }, // Double Bonds { { "P", "OP1" }, { "C2", "O2" }, { "N3", "C4" }, { "C5", "C6" } }); ResidueData GData( "G", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "O6", 8 }, { "N1", 7 }, { "C2", 6 }, { "N2", 7 }, { "N3", 7 }, { "C4", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "H1", 1 }, { "H21", 1 }, { "H21", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N1" }, { "N1", "C2" }, { "N1", "H1" }, { "C2", "N2" }, { "N2", "H21" }, { "N2", "H22" }, { "N3", "C4" } }, // Double Bonds { { "P", "OP1" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "O6" }, { "C2", "N3" } }); ResidueData UData( "U", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N1", 7 }, { "C2", 6 }, { "O2", 8 }, { "N3", 7 }, { "C4", 6 }, { "O4", 8 }, { "C5", 6 }, { "C6", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H3", 1 }, { "H5", 1 }, { "H5", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N1" }, { "C1'", "H1'" }, { "N1", "C2" }, { "N1", "C6" }, { "C2", "N3" }, { "N3", "C4" }, { "N3", "H3" }, { "C4", "C5" }, { "C5", "H5" }, { "C6", "H6" } }, // Double Bonds { { "P", "OP1" }, { "C2", "O2" }, { "C4", "O4" }, { "C5", "C6" } }); ResidueData IData( "I", // Atoms { { "OP3", 8 }, { "P", 15 }, { "OP1", 8 }, { "OP2", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "O6", 8 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOP3", 1 }, { "HOP2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "H1", 1 }, { "H1", 1 } }, // Single Bonds { { "OP3", "P" }, { "OP3", "HOP3" }, { "P", "OP2" }, { "P", "O5'" }, { "OP2", "HOP2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N1" }, { "N1", "C2" }, { "N1", "H1" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "P", "OP1" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "O6" }, { "C2", "N3" } }); ResidueData HEMData( "HEM", // Atoms { { "CHA", 6 }, { "CHB", 6 }, { "CHC", 6 }, { "CHD", 6 }, { "C1A", 6 }, { "C2A", 6 }, { "C3A", 6 }, { "C4A", 6 }, { "CMA", 6 }, { "CAA", 6 }, { "CBA", 6 }, { "CGA", 6 }, { "O1A", 8 }, { "O2A", 8 }, { "C1B", 6 }, { "C2B", 6 }, { "C3B", 6 }, { "C4B", 6 }, { "CMB", 6 }, { "CAB", 6 }, { "CBB", 6 }, { "C1C", 6 }, { "C2C", 6 }, { "C3C", 6 }, { "C4C", 6 }, { "CMC", 6 }, { "CAC", 6 }, { "CBC", 6 }, { "C1D", 6 }, { "C2D", 6 }, { "C3D", 6 }, { "C4D", 6 }, { "CMD", 6 }, { "CAD", 6 }, { "CBD", 6 }, { "CGD", 6 }, { "O1D", 8 }, { "O2D", 8 }, { "NA", 7 }, { "NB", 7 }, { "NC", 7 }, { "ND", 7 }, { "FE", 26 }, { "HHB", 1 }, { "HHC", 1 }, { "HHD", 1 }, { "HMA", 1 }, { "HMAA", 1 }, { "HMAB", 1 }, { "HAA", 1 }, { "HAAA", 1 }, { "HBA", 1 }, { "HBAA", 1 }, { "HMB", 1 }, { "HMBA", 1 }, { "HMBB", 1 }, { "HAB", 1 }, { "HBB", 1 }, { "HBBA", 1 }, { "HMC", 1 }, { "HMCA", 1 }, { "HMCB", 1 }, { "HAC", 1 }, { "HBC", 1 }, { "HBCA", 1 }, { "HMD", 1 }, { "HMDA", 1 }, { "HMDB", 1 }, { "HAD", 1 }, { "HADA", 1 }, { "HBD", 1 }, { "HBDA", 1 }, { "H2A", 1 }, { "H2D", 1 }, { "H2D", 1 } }, // Single Bonds { { "CHA", "C1A" }, { "CHA", "HHA" }, { "CHB", "C4A" }, { "CHB", "HHB" }, { "CHC", "C4B" }, { "CHC", "HHC" }, { "CHD", "C1D" }, { "CHD", "HHD" }, { "C1A", "NA" }, { "C2A", "C3A" }, { "C2A", "CAA" }, { "C3A", "CMA" }, { "C4A", "NA" }, { "CMA", "HMA" }, { "CMA", "HMAA" }, { "CMA", "HMAB" }, { "CAA", "CBA" }, { "CAA", "HAA" }, { "CAA", "HAAA" }, { "CBA", "CGA" }, { "CBA", "HBA" }, { "CBA", "HBAA" }, { "CGA", "O2A" }, { "O2A", "H2A" }, { "C1B", "C2B" }, { "C1B", "NB" }, { "C2B", "CMB" }, { "C3B", "C4B" }, { "C3B", "CAB" }, { "CMB", "HMB" }, { "CMB", "HMBA" }, { "CMB", "HMBB" }, { "CAB", "HAB" }, { "CBB", "HBB" }, { "CBB", "HBBA" }, { "C1C", "C2C" }, { "C1C", "NC" }, { "C2C", "CMC" }, { "C3C", "C4C" }, { "C3C", "CAC" }, { "C4C", "NC" }, { "CMC", "HMC" }, { "CMC", "HMCA" }, { "CMC", "HMCB" }, { "CAC", "HAC" }, { "CBC", "HBC" }, { "CBC", "HBCA" }, { "C1D", "C2D" }, { "C2D", "CMD" }, { "C3D", "C4D" }, { "C3D", "CAD" }, { "C4D", "ND" }, { "CMD", "HMD" }, { "CMD", "HMDA" }, { "CMD", "HMDB" }, { "CAD", "CBD" }, { "CAD", "HAD" }, { "CAD", "HADA" }, { "CBD", "CGD" }, { "CBD", "HBD" }, { "CBD", "HBDA" }, { "CGD", "O2D" }, { "O2D", "H2D" }, { "FE", "NA" }, { "FE", "NB" }, { "FE", "NC" }, { "FE", "ND" } }, // Double Bonds { { "CHA", "C4D" }, { "CHB", "C1B" }, { "CHC", "C1C" }, { "CHD", "C4C" }, { "C1A", "C2A" }, { "C3A", "C4A" }, { "CGA", "O1A" }, { "C2B", "C3B" }, { "C4B", "NB" }, { "CAB", "CBB" }, { "C2C", "C3C" }, { "CAC", "CBC" }, { "C1D", "ND" }, { "C2D", "C3D" }, { "CGD", "O1D" } }); ResidueData HOHData("HOH", // Atoms { { "O", 8 }, { "H1", 1 }, { "H1", 1 } }, // Single Bonds { { "O", "H1" }, { "O", "H2" } }, // Double Bonds {}); ResidueData SO4Data( "SO4", // Atoms { { "S", 16 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O3", 8 } }, // Single Bonds { { "S", "O3" }, { "S", "O4" } }, // Double Bonds { { "S", "O1" }, { "S", "O2" } }); ResidueData GOLData("GOL", // Atoms { { "C1", 6 }, { "O1", 8 }, { "C2", 6 }, { "O2", 8 }, { "C3", 6 }, { "O3", 8 }, { "H11", 1 }, { "H12", 1 }, { "HO1", 1 }, { "H2", 1 }, { "HO2", 1 }, { "H31", 1 }, { "H32", 1 }, { "H32", 1 } }, // Single Bonds { { "C1", "O1" }, { "C1", "C2" }, { "C1", "H11" }, { "C1", "H12" }, { "O1", "HO1" }, { "C2", "O2" }, { "C2", "C3" }, { "C2", "H2" }, { "O2", "HO2" }, { "C3", "O3" }, { "C3", "H31" }, { "C3", "H32" }, { "O3", "HO3" } }, // Double Bonds {}); ResidueData EDOData("EDO", // Atoms { { "C1", 6 }, { "O1", 8 }, { "C2", 6 }, { "O2", 8 }, { "H11", 1 }, { "H12", 1 }, { "HO1", 1 }, { "H21", 1 }, { "H22", 1 }, { "H22", 1 } }, // Single Bonds { { "C1", "O1" }, { "C1", "C2" }, { "C1", "H11" }, { "C1", "H12" }, { "O1", "HO1" }, { "C2", "O2" }, { "C2", "H21" }, { "C2", "H22" }, { "O2", "HO2" } }, // Double Bonds {}); ResidueData MSEData("MSE", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "CB", 6 }, { "CG", 6 }, { "SE", 34 }, { "CE", 6 }, { "H", 1 }, { "HN2", 1 }, { "HA", 1 }, { "HXT", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HE1", 1 }, { "HE2", 1 }, { "HE2", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "HN2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "OXT", "HXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "SE" }, { "CG", "HG2" }, { "CG", "HG3" }, { "SE", "CE" }, { "CE", "HE1" }, { "CE", "HE2" }, { "CE", "HE3" } }, // Double Bonds { { "C", "O" } }); ResidueData NAGData( "NAG", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "C4", 6 }, { "C5", 6 }, { "C6", 6 }, { "C7", 6 }, { "C8", 6 }, { "N2", 7 }, { "O1", 8 }, { "O3", 8 }, { "O4", 8 }, { "O5", 8 }, { "O6", 8 }, { "O7", 8 }, { "H1", 1 }, { "H2", 1 }, { "H3", 1 }, { "H4", 1 }, { "H5", 1 }, { "H61", 1 }, { "H62", 1 }, { "H81", 1 }, { "H82", 1 }, { "H83", 1 }, { "HN2", 1 }, { "HO1", 1 }, { "HO3", 1 }, { "HO4", 1 }, { "HO4", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "O1" }, { "C1", "O5" }, { "C1", "H1" }, { "C2", "C3" }, { "C2", "N2" }, { "C2", "H2" }, { "C3", "C4" }, { "C3", "O3" }, { "C3", "H3" }, { "C4", "C5" }, { "C4", "O4" }, { "C4", "H4" }, { "C5", "C6" }, { "C5", "O5" }, { "C5", "H5" }, { "C6", "O6" }, { "C6", "H61" }, { "C6", "H62" }, { "C7", "C8" }, { "C7", "N2" }, { "C8", "H81" }, { "C8", "H82" }, { "C8", "H83" }, { "N2", "HN2" }, { "O1", "HO1" }, { "O3", "HO3" }, { "O4", "HO4" }, { "O6", "HO6" } }, // Double Bonds { { "C7", "O7" } }); ResidueData PO4Data( "PO4", // Atoms { { "P", 15 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O3", 8 } }, // Single Bonds { { "P", "O2" }, { "P", "O3" }, { "P", "O4" } }, // Double Bonds { { "P", "O1" } }); ResidueData ACTData("ACT", // Atoms { { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "CH3", 6 }, { "H1", 1 }, { "H2", 1 }, { "H2", 1 } }, // Single Bonds { { "C", "OXT" }, { "C", "CH3" }, { "CH3", "H1" }, { "CH3", "H2" }, { "CH3", "H3" } }, // Double Bonds { { "C", "O" } }); ResidueData PEGData("PEG", // Atoms { { "C1", 6 }, { "O1", 8 }, { "C2", 6 }, { "O2", 8 }, { "C3", 6 }, { "C4", 6 }, { "O4", 8 }, { "H11", 1 }, { "H12", 1 }, { "HO1", 1 }, { "H21", 1 }, { "H22", 1 }, { "H31", 1 }, { "H32", 1 }, { "H41", 1 }, { "H42", 1 }, { "H42", 1 } }, // Single Bonds { { "C1", "O1" }, { "C1", "C2" }, { "C1", "H11" }, { "C1", "H12" }, { "O1", "HO1" }, { "C2", "O2" }, { "C2", "H21" }, { "C2", "H22" }, { "O2", "C3" }, { "C3", "C4" }, { "C3", "H31" }, { "C3", "H32" }, { "C4", "O4" }, { "C4", "H41" }, { "C4", "H42" }, { "O4", "HO4" } }, // Double Bonds {}); ResidueData BMAData( "BMA", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "C4", 6 }, { "C5", 6 }, { "C6", 6 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O4", 8 }, { "O5", 8 }, { "O6", 8 }, { "H1", 1 }, { "H2", 1 }, { "H3", 1 }, { "H4", 1 }, { "H5", 1 }, { "H61", 1 }, { "H62", 1 }, { "HO1", 1 }, { "HO2", 1 }, { "HO3", 1 }, { "HO4", 1 }, { "HO4", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "O1" }, { "C1", "O5" }, { "C1", "H1" }, { "C2", "C3" }, { "C2", "O2" }, { "C2", "H2" }, { "C3", "C4" }, { "C3", "O3" }, { "C3", "H3" }, { "C4", "C5" }, { "C4", "O4" }, { "C4", "H4" }, { "C5", "C6" }, { "C5", "O5" }, { "C5", "H5" }, { "C6", "O6" }, { "C6", "H61" }, { "C6", "H62" }, { "O1", "HO1" }, { "O2", "HO2" }, { "O3", "HO3" }, { "O4", "HO4" }, { "O6", "HO6" } }, // Double Bonds {}); ResidueData MANData( "MAN", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "C4", 6 }, { "C5", 6 }, { "C6", 6 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O4", 8 }, { "O5", 8 }, { "O6", 8 }, { "H1", 1 }, { "H2", 1 }, { "H3", 1 }, { "H4", 1 }, { "H5", 1 }, { "H61", 1 }, { "H62", 1 }, { "HO1", 1 }, { "HO2", 1 }, { "HO3", 1 }, { "HO4", 1 }, { "HO4", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "O1" }, { "C1", "O5" }, { "C1", "H1" }, { "C2", "C3" }, { "C2", "O2" }, { "C2", "H2" }, { "C3", "C4" }, { "C3", "O3" }, { "C3", "H3" }, { "C4", "C5" }, { "C4", "O4" }, { "C4", "H4" }, { "C5", "C6" }, { "C5", "O5" }, { "C5", "H5" }, { "C6", "O6" }, { "C6", "H61" }, { "C6", "H62" }, { "O1", "HO1" }, { "O2", "HO2" }, { "O3", "HO3" }, { "O4", "HO4" }, { "O6", "HO6" } }, // Double Bonds {}); ResidueData DMSData("DMS", // Atoms { { "S", 16 }, { "O", 8 }, { "C1", 6 }, { "C2", 6 }, { "H11", 1 }, { "H12", 1 }, { "H13", 1 }, { "H21", 1 }, { "H22", 1 }, { "H22", 1 } }, // Single Bonds { { "S", "C1" }, { "S", "C2" }, { "C1", "H11" }, { "C1", "H12" }, { "C1", "H13" }, { "C2", "H21" }, { "C2", "H22" }, { "C2", "H23" } }, // Double Bonds { { "S", "O" } }); ResidueData ADPData( "ADP", // Atoms { { "PB", 15 }, { "O1B", 8 }, { "O2B", 8 }, { "O3B", 8 }, { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O3A", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOB2", 1 }, { "HOB3", 1 }, { "HOA2", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN61", 1 }, { "HN62", 1 }, { "HN62", 1 } }, // Single Bonds { { "PB", "O2B" }, { "PB", "O3B" }, { "PB", "O3A" }, { "O2B", "HOB2" }, { "O3B", "HOB3" }, { "PA", "O2A" }, { "PA", "O3A" }, { "PA", "O5'" }, { "O2A", "HOA2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "HN61" }, { "N6", "HN62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "PB", "O1B" }, { "PA", "O1A" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData FADData( "FAD", // Atoms { { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O5B", 8 }, { "C5B", 6 }, { "C4B", 6 }, { "O4B", 8 }, { "C3B", 6 }, { "O3B", 8 }, { "C2B", 6 }, { "O2B", 8 }, { "C1B", 6 }, { "N9A", 7 }, { "C8A", 6 }, { "N7A", 7 }, { "C5A", 6 }, { "C6A", 6 }, { "N6A", 7 }, { "N1A", 7 }, { "C2A", 6 }, { "N3A", 7 }, { "C4A", 6 }, { "N1", 7 }, { "C2", 6 }, { "O2", 8 }, { "N3", 7 }, { "C4", 6 }, { "O4", 8 }, { "C4X", 6 }, { "N5", 7 }, { "C5X", 6 }, { "C6", 6 }, { "C7", 6 }, { "C7M", 6 }, { "C8", 6 }, { "C8M", 6 }, { "C9", 6 }, { "C9A", 6 }, { "N10", 7 }, { "C10", 6 }, { "C1'", 6 }, { "C2'", 6 }, { "O2'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C4'", 6 }, { "O4'", 8 }, { "C5'", 6 }, { "O5'", 8 }, { "P", 15 }, { "O1P", 8 }, { "O2P", 8 }, { "O3P", 8 }, { "HOA2", 1 }, { "H51A", 1 }, { "H52A", 1 }, { "H4B", 1 }, { "H3B", 1 }, { "HO3A", 1 }, { "H2B", 1 }, { "HO2A", 1 }, { "H1B", 1 }, { "H8A", 1 }, { "H61A", 1 }, { "H62A", 1 }, { "H2A", 1 }, { "HN3", 1 }, { "H6", 1 }, { "HM71", 1 }, { "HM72", 1 }, { "HM73", 1 }, { "HM81", 1 }, { "HM82", 1 }, { "HM83", 1 }, { "H9", 1 }, { "H1'1", 1 }, { "H1'2", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H4'", 1 }, { "HO4'", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "H5'2", 1 } }, // Single Bonds { { "PA", "O2A" }, { "PA", "O5B" }, { "PA", "O3P" }, { "O2A", "HOA2" }, { "O5B", "C5B" }, { "C5B", "C4B" }, { "C5B", "H51A" }, { "C5B", "H52A" }, { "C4B", "O4B" }, { "C4B", "C3B" }, { "C4B", "H4B" }, { "O4B", "C1B" }, { "C3B", "O3B" }, { "C3B", "C2B" }, { "C3B", "H3B" }, { "O3B", "HO3A" }, { "C2B", "O2B" }, { "C2B", "C1B" }, { "C2B", "H2B" }, { "O2B", "HO2A" }, { "C1B", "N9A" }, { "C1B", "H1B" }, { "N9A", "C8A" }, { "N9A", "C4A" }, { "C8A", "H8A" }, { "N7A", "C5A" }, { "C5A", "C6A" }, { "C6A", "N6A" }, { "N6A", "H61A" }, { "N6A", "H62A" }, { "N1A", "C2A" }, { "C2A", "H2A" }, { "N3A", "C4A" }, { "N1", "C2" }, { "C2", "N3" }, { "N3", "C4" }, { "N3", "HN3" }, { "C4", "C4X" }, { "C4X", "C10" }, { "N5", "C5X" }, { "C5X", "C9A" }, { "C6", "C7" }, { "C6", "H6" }, { "C7", "C7M" }, { "C7M", "HM71" }, { "C7M", "HM72" }, { "C7M", "HM73" }, { "C8", "C8M" }, { "C8", "C9" }, { "C8M", "HM81" }, { "C8M", "HM82" }, { "C8M", "HM83" }, { "C9", "H9" }, { "C9A", "N10" }, { "N10", "C10" }, { "N10", "C1'" }, { "C1'", "C2'" }, { "C1'", "H1'1" }, { "C1'", "H1'2" }, { "C2'", "O2'" }, { "C2'", "C3'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C3'", "O3'" }, { "C3'", "C4'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C4'", "O4'" }, { "C4'", "C5'" }, { "C4'", "H4'" }, { "O4'", "HO4'" }, { "C5'", "O5'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "O5'", "P" }, { "P", "O2P" }, { "P", "O3P" }, { "O2P", "HOP2" } }, // Double Bonds { { "PA", "O1A" }, { "C8A", "N7A" }, { "C5A", "C4A" }, { "C6A", "N1A" }, { "C2A", "N3A" }, { "N1", "C10" }, { "C2", "O2" }, { "C4", "O4" }, { "C4X", "N5" }, { "C5X", "C6" }, { "C7", "C8" }, { "C9", "C9A" }, { "P", "O1P" } }); ResidueData ACEData("ACE", // Atoms { { "C", 6 }, { "O", 8 }, { "CH3", 6 }, { "H", 1 }, { "H1", 1 }, { "H2", 1 }, { "H2", 1 } }, // Single Bonds { { "C", "CH3" }, { "C", "H" }, { "CH3", "H1" }, { "CH3", "H2" }, { "CH3", "H3" } }, // Double Bonds { { "C", "O" } }); ResidueData MPDData("MPD", // Atoms { { "C1", 6 }, { "C2", 6 }, { "O2", 8 }, { "CM", 6 }, { "C3", 6 }, { "C4", 6 }, { "O4", 8 }, { "C5", 6 }, { "H11", 1 }, { "H12", 1 }, { "H13", 1 }, { "HO2", 1 }, { "HM1", 1 }, { "HM2", 1 }, { "HM3", 1 }, { "H31", 1 }, { "H32", 1 }, { "H4", 1 }, { "HO4", 1 }, { "H51", 1 }, { "H52", 1 }, { "H52", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "H11" }, { "C1", "H12" }, { "C1", "H13" }, { "C2", "O2" }, { "C2", "CM" }, { "C2", "C3" }, { "O2", "HO2" }, { "CM", "HM1" }, { "CM", "HM2" }, { "CM", "HM3" }, { "C3", "C4" }, { "C3", "H31" }, { "C3", "H32" }, { "C4", "O4" }, { "C4", "C5" }, { "C4", "H4" }, { "O4", "HO4" }, { "C5", "H51" }, { "C5", "H52" }, { "C5", "H53" } }, // Double Bonds {}); ResidueData GLCData( "GLC", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "C4", 6 }, { "C5", 6 }, { "C6", 6 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O4", 8 }, { "O5", 8 }, { "O6", 8 }, { "H1", 1 }, { "H2", 1 }, { "H3", 1 }, { "H4", 1 }, { "H5", 1 }, { "H61", 1 }, { "H62", 1 }, { "HO1", 1 }, { "HO2", 1 }, { "HO3", 1 }, { "HO4", 1 }, { "HO4", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "O1" }, { "C1", "O5" }, { "C1", "H1" }, { "C2", "C3" }, { "C2", "O2" }, { "C2", "H2" }, { "C3", "C4" }, { "C3", "O3" }, { "C3", "H3" }, { "C4", "C5" }, { "C4", "O4" }, { "C4", "H4" }, { "C5", "C6" }, { "C5", "O5" }, { "C5", "H5" }, { "C6", "O6" }, { "C6", "H61" }, { "C6", "H62" }, { "O1", "HO1" }, { "O2", "HO2" }, { "O3", "HO3" }, { "O4", "HO4" }, { "O6", "HO6" } }, // Double Bonds {}); ResidueData ATPData( "ATP", // Atoms { { "PG", 15 }, { "O1G", 8 }, { "O2G", 8 }, { "O3G", 8 }, { "PB", 15 }, { "O1B", 8 }, { "O2B", 8 }, { "O3B", 8 }, { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O3A", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOG2", 1 }, { "HOG3", 1 }, { "HOB2", 1 }, { "HOA2", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN61", 1 }, { "HN62", 1 }, { "HN62", 1 } }, // Single Bonds { { "PG", "O2G" }, { "PG", "O3G" }, { "PG", "O3B" }, { "O2G", "HOG2" }, { "O3G", "HOG3" }, { "PB", "O2B" }, { "PB", "O3B" }, { "PB", "O3A" }, { "O2B", "HOB2" }, { "PA", "O2A" }, { "PA", "O3A" }, { "PA", "O5'" }, { "O2A", "HOA2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "HN61" }, { "N6", "HN62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "PG", "O1G" }, { "PB", "O1B" }, { "PA", "O1A" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData MESData( "MES", // Atoms { { "O1", 8 }, { "C2", 6 }, { "C3", 6 }, { "N4", 7 }, { "C5", 6 }, { "C6", 6 }, { "C7", 6 }, { "C8", 6 }, { "S", 16 }, { "O1S", 8 }, { "O2S", 8 }, { "O3S", 8 }, { "H21", 1 }, { "H22", 1 }, { "H31", 1 }, { "H32", 1 }, { "HN4", 1 }, { "H51", 1 }, { "H52", 1 }, { "H61", 1 }, { "H62", 1 }, { "H71", 1 }, { "H72", 1 }, { "H81", 1 }, { "H81", 1 } }, // Single Bonds { { "O1", "C2" }, { "O1", "C6" }, { "C2", "C3" }, { "C2", "H21" }, { "C2", "H22" }, { "C3", "N4" }, { "C3", "H31" }, { "C3", "H32" }, { "N4", "C5" }, { "N4", "C7" }, { "N4", "HN4" }, { "C5", "C6" }, { "C5", "H51" }, { "C5", "H52" }, { "C6", "H61" }, { "C6", "H62" }, { "C7", "C8" }, { "C7", "H71" }, { "C7", "H72" }, { "C8", "S" }, { "C8", "H81" }, { "C8", "H82" }, { "S", "O3S" } }, // Double Bonds { { "S", "O1S" }, { "S", "O2S" } }); ResidueData TRSData("TRS", // Atoms { { "C", 6 }, { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "N", 7 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "H11", 1 }, { "H12", 1 }, { "H21", 1 }, { "H22", 1 }, { "H31", 1 }, { "H32", 1 }, { "HN1", 1 }, { "HN2", 1 }, { "HN3", 1 }, { "HO1", 1 }, { "HO2", 1 }, { "HO2", 1 } }, // Single Bonds { { "C", "C1" }, { "C", "C2" }, { "C", "C3" }, { "C", "N" }, { "C1", "O1" }, { "C1", "H11" }, { "C1", "H12" }, { "C2", "O2" }, { "C2", "H21" }, { "C2", "H22" }, { "C3", "O3" }, { "C3", "H31" }, { "C3", "H32" }, { "N", "HN1" }, { "N", "HN2" }, { "N", "HN3" }, { "O1", "HO1" }, { "O2", "HO2" }, { "O3", "HO3" } }, // Double Bonds {}); ResidueData PG4Data( "PG4", // Atoms { { "O1", 8 }, { "C1", 6 }, { "C2", 6 }, { "O2", 8 }, { "C3", 6 }, { "C4", 6 }, { "O3", 8 }, { "C5", 6 }, { "C6", 6 }, { "O4", 8 }, { "C7", 6 }, { "C8", 6 }, { "O5", 8 }, { "HO1", 1 }, { "H11", 1 }, { "H12", 1 }, { "H21", 1 }, { "H22", 1 }, { "H31", 1 }, { "H32", 1 }, { "H41", 1 }, { "H42", 1 }, { "H51", 1 }, { "H52", 1 }, { "H61", 1 }, { "H62", 1 }, { "H71", 1 }, { "H72", 1 }, { "H81", 1 }, { "H82", 1 }, { "H82", 1 } }, // Single Bonds { { "O1", "C1" }, { "O1", "HO1" }, { "C1", "C2" }, { "C1", "H11" }, { "C1", "H12" }, { "C2", "O2" }, { "C2", "H21" }, { "C2", "H22" }, { "O2", "C3" }, { "C3", "C4" }, { "C3", "H31" }, { "C3", "H32" }, { "C4", "O3" }, { "C4", "H41" }, { "C4", "H42" }, { "O3", "C5" }, { "C5", "C6" }, { "C5", "H51" }, { "C5", "H52" }, { "C6", "O4" }, { "C6", "H61" }, { "C6", "H62" }, { "O4", "C7" }, { "C7", "C8" }, { "C7", "H71" }, { "C7", "H72" }, { "C8", "O5" }, { "C8", "H81" }, { "C8", "H82" }, { "O5", "HO5" } }, // Double Bonds {}); ResidueData PGEData( "PGE", // Atoms { { "C1", 6 }, { "O1", 8 }, { "C2", 6 }, { "O2", 8 }, { "C3", 6 }, { "C4", 6 }, { "O4", 8 }, { "C6", 6 }, { "C5", 6 }, { "O3", 8 }, { "H1", 1 }, { "H12", 1 }, { "HO1", 1 }, { "H2", 1 }, { "H22", 1 }, { "H3", 1 }, { "H32", 1 }, { "H4", 1 }, { "H42", 1 }, { "HO4", 1 }, { "H6", 1 }, { "H62", 1 }, { "H5", 1 }, { "H5", 1 } }, // Single Bonds { { "C1", "O1" }, { "C1", "C2" }, { "C1", "H1" }, { "C1", "H12" }, { "O1", "HO1" }, { "C2", "O2" }, { "C2", "H2" }, { "C2", "H22" }, { "O2", "C3" }, { "C3", "C4" }, { "C3", "H3" }, { "C3", "H32" }, { "C4", "O3" }, { "C4", "H4" }, { "C4", "H42" }, { "O4", "C6" }, { "O4", "HO4" }, { "C6", "C5" }, { "C6", "H6" }, { "C6", "H62" }, { "C5", "O3" }, { "C5", "H5" }, { "C5", "H52" } }, // Double Bonds {}); ResidueData NADData( "NAD", // Atoms { { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O5B", 8 }, { "C5B", 6 }, { "C4B", 6 }, { "O4B", 8 }, { "C3B", 6 }, { "O3B", 8 }, { "C2B", 6 }, { "O2B", 8 }, { "C1B", 6 }, { "N9A", 7 }, { "C8A", 6 }, { "N7A", 7 }, { "C5A", 6 }, { "C6A", 6 }, { "N6A", 7 }, { "N1A", 7 }, { "C2A", 6 }, { "N3A", 7 }, { "C4A", 6 }, { "O3", 8 }, { "PN", 15 }, { "O1N", 8 }, { "O2N", 8 }, { "O5D", 8 }, { "C5D", 6 }, { "C4D", 6 }, { "O4D", 8 }, { "C3D", 6 }, { "O3D", 8 }, { "C2D", 6 }, { "O2D", 8 }, { "C1D", 6 }, { "N1N", 7 }, { "C2N", 6 }, { "C3N", 6 }, { "C7N", 6 }, { "O7N", 8 }, { "N7N", 7 }, { "C4N", 6 }, { "C5N", 6 }, { "C6N", 6 }, { "HOA2", 1 }, { "H51A", 1 }, { "H52A", 1 }, { "H4B", 1 }, { "H3B", 1 }, { "HO3A", 1 }, { "H2B", 1 }, { "HO2A", 1 }, { "H1B", 1 }, { "H8A", 1 }, { "H61A", 1 }, { "H62A", 1 }, { "H2A", 1 }, { "H51N", 1 }, { "H52N", 1 }, { "H4D", 1 }, { "H3D", 1 }, { "HO3N", 1 }, { "H2D", 1 }, { "HO2N", 1 }, { "H1D", 1 }, { "H2N", 1 }, { "H71N", 1 }, { "H72N", 1 }, { "H4N", 1 }, { "H5N", 1 }, { "H5N", 1 } }, // Single Bonds { { "PA", "O2A" }, { "PA", "O5B" }, { "PA", "O3" }, { "O2A", "HOA2" }, { "O5B", "C5B" }, { "C5B", "C4B" }, { "C5B", "H51A" }, { "C5B", "H52A" }, { "C4B", "O4B" }, { "C4B", "C3B" }, { "C4B", "H4B" }, { "O4B", "C1B" }, { "C3B", "O3B" }, { "C3B", "C2B" }, { "C3B", "H3B" }, { "O3B", "HO3A" }, { "C2B", "O2B" }, { "C2B", "C1B" }, { "C2B", "H2B" }, { "O2B", "HO2A" }, { "C1B", "N9A" }, { "C1B", "H1B" }, { "N9A", "C8A" }, { "N9A", "C4A" }, { "C8A", "H8A" }, { "N7A", "C5A" }, { "C5A", "C6A" }, { "C6A", "N6A" }, { "N6A", "H61A" }, { "N6A", "H62A" }, { "N1A", "C2A" }, { "C2A", "H2A" }, { "N3A", "C4A" }, { "O3", "PN" }, { "PN", "O2N" }, { "PN", "O5D" }, { "O5D", "C5D" }, { "C5D", "C4D" }, { "C5D", "H51N" }, { "C5D", "H52N" }, { "C4D", "O4D" }, { "C4D", "C3D" }, { "C4D", "H4D" }, { "O4D", "C1D" }, { "C3D", "O3D" }, { "C3D", "C2D" }, { "C3D", "H3D" }, { "O3D", "HO3N" }, { "C2D", "O2D" }, { "C2D", "C1D" }, { "C2D", "H2D" }, { "O2D", "HO2N" }, { "C1D", "N1N" }, { "C1D", "H1D" }, { "N1N", "C2N" }, { "C2N", "H2N" }, { "C3N", "C7N" }, { "C3N", "C4N" }, { "C7N", "N7N" }, { "N7N", "H71N" }, { "N7N", "H72N" }, { "C4N", "H4N" }, { "C5N", "C6N" }, { "C5N", "H5N" }, { "C6N", "H6N" } }, // Double Bonds { { "PA", "O1A" }, { "C8A", "N7A" }, { "C5A", "C4A" }, { "C6A", "N1A" }, { "C2A", "N3A" }, { "PN", "O1N" }, { "N1N", "C6N" }, { "C2N", "C3N" }, { "C7N", "O7N" }, { "C4N", "C5N" } }); ResidueData FUCData( "FUC", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "C4", 6 }, { "C5", 6 }, { "C6", 6 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O4", 8 }, { "O5", 8 }, { "H1", 1 }, { "H2", 1 }, { "H3", 1 }, { "H4", 1 }, { "H5", 1 }, { "H61", 1 }, { "H62", 1 }, { "H63", 1 }, { "HO1", 1 }, { "HO2", 1 }, { "HO3", 1 }, { "HO3", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "O1" }, { "C1", "O5" }, { "C1", "H1" }, { "C2", "C3" }, { "C2", "O2" }, { "C2", "H2" }, { "C3", "C4" }, { "C3", "O3" }, { "C3", "H3" }, { "C4", "C5" }, { "C4", "O4" }, { "C4", "H4" }, { "C5", "C6" }, { "C5", "O5" }, { "C5", "H5" }, { "C6", "H61" }, { "C6", "H62" }, { "C6", "H63" }, { "O1", "HO1" }, { "O2", "HO2" }, { "O3", "HO3" }, { "O4", "HO4" } }, // Double Bonds {}); ResidueData NH2Data("NH2", // Atoms { { "N", 7 }, { "HN1", 1 }, { "HN1", 1 } }, // Single Bonds { { "N", "HN1" }, { "N", "HN2" } }, // Double Bonds {}); ResidueData FMTData( "FMT", // Atoms { { "C", 6 }, { "O1", 8 }, { "O2", 8 }, { "H", 1 }, { "H", 1 } }, // Single Bonds { { "C", "O2" }, { "C", "H" }, { "O2", "HO2" } }, // Double Bonds { { "C", "O1" } }); ResidueData NAPData( "NAP", // Atoms { { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O5B", 8 }, { "C5B", 6 }, { "C4B", 6 }, { "O4B", 8 }, { "C3B", 6 }, { "O3B", 8 }, { "C2B", 6 }, { "O2B", 8 }, { "C1B", 6 }, { "N9A", 7 }, { "C8A", 6 }, { "N7A", 7 }, { "C5A", 6 }, { "C6A", 6 }, { "N6A", 7 }, { "N1A", 7 }, { "C2A", 6 }, { "N3A", 7 }, { "C4A", 6 }, { "O3", 8 }, { "PN", 15 }, { "O1N", 8 }, { "O2N", 8 }, { "O5D", 8 }, { "C5D", 6 }, { "C4D", 6 }, { "O4D", 8 }, { "C3D", 6 }, { "O3D", 8 }, { "C2D", 6 }, { "O2D", 8 }, { "C1D", 6 }, { "N1N", 7 }, { "C2N", 6 }, { "C3N", 6 }, { "C7N", 6 }, { "O7N", 8 }, { "N7N", 7 }, { "C4N", 6 }, { "C5N", 6 }, { "C6N", 6 }, { "P2B", 15 }, { "O1X", 8 }, { "O2X", 8 }, { "O3X", 8 }, { "HOA2", 1 }, { "H51A", 1 }, { "H52A", 1 }, { "H4B", 1 }, { "H3B", 1 }, { "HO3A", 1 }, { "H2B", 1 }, { "H1B", 1 }, { "H8A", 1 }, { "H61A", 1 }, { "H62A", 1 }, { "H2A", 1 }, { "H51N", 1 }, { "H52N", 1 }, { "H4D", 1 }, { "H3D", 1 }, { "HO3N", 1 }, { "H2D", 1 }, { "HO2N", 1 }, { "H1D", 1 }, { "H2N", 1 }, { "H71N", 1 }, { "H72N", 1 }, { "H4N", 1 }, { "H5N", 1 }, { "H6N", 1 }, { "HOP2", 1 }, { "HOP2", 1 } }, // Single Bonds { { "PA", "O2A" }, { "PA", "O5B" }, { "PA", "O3" }, { "O2A", "HOA2" }, { "O5B", "C5B" }, { "C5B", "C4B" }, { "C5B", "H51A" }, { "C5B", "H52A" }, { "C4B", "O4B" }, { "C4B", "C3B" }, { "C4B", "H4B" }, { "O4B", "C1B" }, { "C3B", "O3B" }, { "C3B", "C2B" }, { "C3B", "H3B" }, { "O3B", "HO3A" }, { "C2B", "O2B" }, { "C2B", "C1B" }, { "C2B", "H2B" }, { "O2B", "P2B" }, { "C1B", "N9A" }, { "C1B", "H1B" }, { "N9A", "C8A" }, { "N9A", "C4A" }, { "C8A", "H8A" }, { "N7A", "C5A" }, { "C5A", "C6A" }, { "C6A", "N6A" }, { "N6A", "H61A" }, { "N6A", "H62A" }, { "N1A", "C2A" }, { "C2A", "H2A" }, { "N3A", "C4A" }, { "O3", "PN" }, { "PN", "O2N" }, { "PN", "O5D" }, { "O5D", "C5D" }, { "C5D", "C4D" }, { "C5D", "H51N" }, { "C5D", "H52N" }, { "C4D", "O4D" }, { "C4D", "C3D" }, { "C4D", "H4D" }, { "O4D", "C1D" }, { "C3D", "O3D" }, { "C3D", "C2D" }, { "C3D", "H3D" }, { "O3D", "HO3N" }, { "C2D", "O2D" }, { "C2D", "C1D" }, { "C2D", "H2D" }, { "O2D", "HO2N" }, { "C1D", "N1N" }, { "C1D", "H1D" }, { "N1N", "C2N" }, { "C2N", "H2N" }, { "C3N", "C7N" }, { "C3N", "C4N" }, { "C7N", "N7N" }, { "N7N", "H71N" }, { "N7N", "H72N" }, { "C4N", "H4N" }, { "C5N", "C6N" }, { "C5N", "H5N" }, { "C6N", "H6N" }, { "P2B", "O2X" }, { "P2B", "O3X" }, { "O2X", "HOP2" }, { "O3X", "HOP3" } }, // Double Bonds { { "PA", "O1A" }, { "C8A", "N7A" }, { "C5A", "C4A" }, { "C6A", "N1A" }, { "C2A", "N3A" }, { "PN", "O1N" }, { "N1N", "C6N" }, { "C2N", "C3N" }, { "C7N", "O7N" }, { "C4N", "C5N" }, { "P2B", "O1X" } }); ResidueData SEPData("SEP", // Atoms { { "N", 7 }, { "CA", 6 }, { "CB", 6 }, { "OG", 8 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "P", 15 }, { "O1P", 8 }, { "O2P", 8 }, { "O3P", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HXT", 1 }, { "HOP2", 1 }, { "HOP2", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "CB" }, { "CA", "C" }, { "CA", "HA" }, { "CB", "OG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "OG", "P" }, { "C", "OXT" }, { "OXT", "HXT" }, { "P", "O2P" }, { "P", "O3P" }, { "O2P", "HOP2" }, { "O3P", "HOP3" } }, // Double Bonds { { "C", "O" }, { "P", "O1P" } }); ResidueData GDPData( "GDP", // Atoms { { "PB", 15 }, { "O1B", 8 }, { "O2B", 8 }, { "O3B", 8 }, { "O3A", 8 }, { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "O6", 8 }, { "N1", 7 }, { "C2", 6 }, { "N2", 7 }, { "N3", 7 }, { "C4", 6 }, { "HOB2", 1 }, { "HOB3", 1 }, { "HOA2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN1", 1 }, { "HN21", 1 }, { "HN21", 1 } }, // Single Bonds { { "PB", "O2B" }, { "PB", "O3B" }, { "PB", "O3A" }, { "O2B", "HOB2" }, { "O3B", "HOB3" }, { "O3A", "PA" }, { "PA", "O2A" }, { "PA", "O5'" }, { "O2A", "HOA2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N1" }, { "N1", "C2" }, { "N1", "HN1" }, { "C2", "N2" }, { "N2", "HN21" }, { "N2", "HN22" }, { "N3", "C4" } }, // Double Bonds { { "PB", "O1B" }, { "PA", "O1A" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "O6" }, { "C2", "N3" } }); ResidueData GALData( "GAL", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "C4", 6 }, { "C5", 6 }, { "C6", 6 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O4", 8 }, { "O5", 8 }, { "O6", 8 }, { "H1", 1 }, { "H2", 1 }, { "H3", 1 }, { "H4", 1 }, { "H5", 1 }, { "H61", 1 }, { "H62", 1 }, { "HO1", 1 }, { "HO2", 1 }, { "HO3", 1 }, { "HO4", 1 }, { "HO4", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "O1" }, { "C1", "O5" }, { "C1", "H1" }, { "C2", "C3" }, { "C2", "O2" }, { "C2", "H2" }, { "C3", "C4" }, { "C3", "O3" }, { "C3", "H3" }, { "C4", "C5" }, { "C4", "O4" }, { "C4", "H4" }, { "C5", "C6" }, { "C5", "O5" }, { "C5", "H5" }, { "C6", "O6" }, { "C6", "H61" }, { "C6", "H62" }, { "O1", "HO1" }, { "O2", "HO2" }, { "O3", "HO3" }, { "O4", "HO4" }, { "O6", "HO6" } }, // Double Bonds {}); ResidueData SF4Data("SF4", // Atoms { { "FE1", 26 }, { "FE2", 26 }, { "FE3", 26 }, { "FE4", 26 }, { "S1", 16 }, { "S2", 16 }, { "S3", 16 }, { "S3", 16 } }, // Single Bonds { { "FE1", "S2" }, { "FE1", "S3" }, { "FE1", "S4" }, { "FE2", "S1" }, { "FE2", "S3" }, { "FE2", "S4" }, { "FE3", "S1" }, { "FE3", "S2" }, { "FE3", "S4" }, { "FE4", "S1" }, { "FE4", "S2" }, { "FE4", "S3" } }, // Double Bonds {}); ResidueData BGCData( "BGC", // Atoms { { "C2", 6 }, { "C3", 6 }, { "C4", 6 }, { "C5", 6 }, { "C6", 6 }, { "C1", 6 }, { "O1", 8 }, { "O2", 8 }, { "O3", 8 }, { "O4", 8 }, { "O5", 8 }, { "O6", 8 }, { "H2", 1 }, { "H3", 1 }, { "H4", 1 }, { "H5", 1 }, { "H61", 1 }, { "H62", 1 }, { "H1", 1 }, { "HO1", 1 }, { "HO2", 1 }, { "HO3", 1 }, { "HO4", 1 }, { "HO4", 1 } }, // Single Bonds { { "C2", "C3" }, { "C2", "C1" }, { "C2", "O2" }, { "C2", "H2" }, { "C3", "C4" }, { "C3", "O3" }, { "C3", "H3" }, { "C4", "C5" }, { "C4", "O4" }, { "C4", "H4" }, { "C5", "C6" }, { "C5", "O5" }, { "C5", "H5" }, { "C6", "O6" }, { "C6", "H61" }, { "C6", "H62" }, { "C1", "O1" }, { "C1", "O5" }, { "C1", "H1" }, { "O1", "HO1" }, { "O2", "HO2" }, { "O3", "HO3" }, { "O4", "HO4" }, { "O6", "HO6" } }, // Double Bonds {}); ResidueData FMNData( "FMN", // Atoms { { "N1", 7 }, { "C2", 6 }, { "O2", 8 }, { "N3", 7 }, { "C4", 6 }, { "O4", 8 }, { "C4A", 6 }, { "N5", 7 }, { "C5A", 6 }, { "C6", 6 }, { "C7", 6 }, { "C7M", 6 }, { "C8", 6 }, { "C8M", 6 }, { "C9", 6 }, { "C9A", 6 }, { "N10", 7 }, { "C10", 6 }, { "C1'", 6 }, { "C2'", 6 }, { "O2'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C4'", 6 }, { "O4'", 8 }, { "C5'", 6 }, { "O5'", 8 }, { "P", 15 }, { "O1P", 8 }, { "O2P", 8 }, { "O3P", 8 }, { "HN3", 1 }, { "H6", 1 }, { "HM71", 1 }, { "HM72", 1 }, { "HM73", 1 }, { "HM81", 1 }, { "HM82", 1 }, { "HM83", 1 }, { "H9", 1 }, { "H1'1", 1 }, { "H1'2", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H4'", 1 }, { "HO4'", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "HOP2", 1 }, { "HOP2", 1 } }, // Single Bonds { { "N1", "C2" }, { "C2", "N3" }, { "N3", "C4" }, { "N3", "HN3" }, { "C4", "C4A" }, { "C4A", "C10" }, { "N5", "C5A" }, { "C5A", "C9A" }, { "C6", "C7" }, { "C6", "H6" }, { "C7", "C7M" }, { "C7M", "HM71" }, { "C7M", "HM72" }, { "C7M", "HM73" }, { "C8", "C8M" }, { "C8", "C9" }, { "C8M", "HM81" }, { "C8M", "HM82" }, { "C8M", "HM83" }, { "C9", "H9" }, { "C9A", "N10" }, { "N10", "C10" }, { "N10", "C1'" }, { "C1'", "C2'" }, { "C1'", "H1'1" }, { "C1'", "H1'2" }, { "C2'", "O2'" }, { "C2'", "C3'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C3'", "O3'" }, { "C3'", "C4'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C4'", "O4'" }, { "C4'", "C5'" }, { "C4'", "H4'" }, { "O4'", "HO4'" }, { "C5'", "O5'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "O5'", "P" }, { "P", "O2P" }, { "P", "O3P" }, { "O2P", "HOP2" }, { "O3P", "HOP3" } }, // Double Bonds { { "N1", "C10" }, { "C2", "O2" }, { "C4", "O4" }, { "C4A", "N5" }, { "C5A", "C6" }, { "C7", "C8" }, { "C9", "C9A" }, { "P", "O1P" } }); ResidueData UNKData("UNK", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "CB", 6 }, { "CG", 6 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB1", 1 }, { "HB2", 1 }, { "HG1", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HG3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB1" }, { "CB", "HB2" }, { "CG", "HG1" }, { "CG", "HG2" }, { "CG", "HG3" }, { "OXT", "HXT" } }, // Double Bonds { { "C", "O" } }); ResidueData CITData("CIT", // Atoms { { "C1", 6 }, { "O1", 8 }, { "O2", 8 }, { "C2", 6 }, { "C3", 6 }, { "O7", 8 }, { "C4", 6 }, { "C5", 6 }, { "O3", 8 }, { "O4", 8 }, { "C6", 6 }, { "O5", 8 }, { "O6", 8 }, { "HO2", 1 }, { "H21", 1 }, { "H22", 1 }, { "HO7", 1 }, { "H41", 1 }, { "H42", 1 }, { "HO4", 1 }, { "HO4", 1 } }, // Single Bonds { { "C1", "O2" }, { "C1", "C2" }, { "O2", "HO2" }, { "C2", "C3" }, { "C2", "H21" }, { "C2", "H22" }, { "C3", "O7" }, { "C3", "C4" }, { "C3", "C6" }, { "O7", "HO7" }, { "C4", "C5" }, { "C4", "H41" }, { "C4", "H42" }, { "C5", "O4" }, { "O4", "HO4" }, { "C6", "O6" }, { "O6", "HO6" } }, // Double Bonds { { "C1", "O1" }, { "C5", "O3" }, { "C6", "O5" } }); ResidueData EPEData( "EPE", // Atoms { { "N1", 7 }, { "C2", 6 }, { "C3", 6 }, { "N4", 7 }, { "C5", 6 }, { "C6", 6 }, { "C7", 6 }, { "C8", 6 }, { "O8", 8 }, { "C9", 6 }, { "C10", 6 }, { "S", 16 }, { "O1S", 8 }, { "O2S", 8 }, { "O3S", 8 }, { "H21", 1 }, { "H22", 1 }, { "H31", 1 }, { "H32", 1 }, { "H51", 1 }, { "H52", 1 }, { "H61", 1 }, { "H62", 1 }, { "H71", 1 }, { "H72", 1 }, { "H81", 1 }, { "H82", 1 }, { "HO8", 1 }, { "H91", 1 }, { "H92", 1 }, { "H101", 1 }, { "H102", 1 }, { "H102", 1 } }, // Single Bonds { { "N1", "C2" }, { "N1", "C6" }, { "N1", "C9" }, { "C2", "C3" }, { "C2", "H21" }, { "C2", "H22" }, { "C3", "N4" }, { "C3", "H31" }, { "C3", "H32" }, { "N4", "C5" }, { "N4", "C7" }, { "C5", "C6" }, { "C5", "H51" }, { "C5", "H52" }, { "C6", "H61" }, { "C6", "H62" }, { "C7", "C8" }, { "C7", "H71" }, { "C7", "H72" }, { "C8", "O8" }, { "C8", "H81" }, { "C8", "H82" }, { "O8", "HO8" }, { "C9", "C10" }, { "C9", "H91" }, { "C9", "H92" }, { "C10", "S" }, { "C10", "H101" }, { "C10", "H102" }, { "S", "O3S" }, { "O3S", "HOS3" } }, // Double Bonds { { "S", "O1S" }, { "S", "O2S" } }); ResidueData TPOData( "TPO", // Atoms { { "N", 7 }, { "CA", 6 }, { "CB", 6 }, { "CG2", 6 }, { "OG1", 8 }, { "P", 15 }, { "O1P", 8 }, { "O2P", 8 }, { "O3P", 8 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "H", 1 }, { "H2", 1 }, { "HA", 1 }, { "HB", 1 }, { "HG21", 1 }, { "HG22", 1 }, { "HG23", 1 }, { "HOP2", 1 }, { "HOP3", 1 }, { "HOP3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "H2" }, { "CA", "CB" }, { "CA", "C" }, { "CA", "HA" }, { "CB", "CG2" }, { "CB", "OG1" }, { "CB", "HB" }, { "CG2", "HG21" }, { "CG2", "HG22" }, { "CG2", "HG23" }, { "OG1", "P" }, { "P", "O2P" }, { "P", "O3P" }, { "O2P", "HOP2" }, { "O3P", "HOP3" }, { "C", "OXT" }, { "OXT", "HXT" } }, // Double Bonds { { "P", "O1P" }, { "C", "O" } }); ResidueData PLPData( "PLP", // Atoms { { "N1", 7 }, { "C2", 6 }, { "C2A", 6 }, { "C3", 6 }, { "O3", 8 }, { "C4", 6 }, { "C4A", 6 }, { "O4A", 8 }, { "C5", 6 }, { "C6", 6 }, { "C5A", 6 }, { "O4P", 8 }, { "P", 15 }, { "O1P", 8 }, { "O2P", 8 }, { "O3P", 8 }, { "H2A1", 1 }, { "H2A2", 1 }, { "H2A3", 1 }, { "HO3", 1 }, { "H4A", 1 }, { "H6", 1 }, { "H5A1", 1 }, { "H5A2", 1 }, { "HOP2", 1 }, { "HOP2", 1 } }, // Single Bonds { { "N1", "C6" }, { "C2", "C2A" }, { "C2", "C3" }, { "C2A", "H2A1" }, { "C2A", "H2A2" }, { "C2A", "H2A3" }, { "C3", "O3" }, { "O3", "HO3" }, { "C4", "C4A" }, { "C4", "C5" }, { "C4A", "H4A" }, { "C5", "C5A" }, { "C6", "H6" }, { "C5A", "O4P" }, { "C5A", "H5A1" }, { "C5A", "H5A2" }, { "O4P", "P" }, { "P", "O2P" }, { "P", "O3P" }, { "O2P", "HOP2" }, { "O3P", "HOP3" } }, // Double Bonds { { "N1", "C2" }, { "C3", "C4" }, { "C4A", "O4A" }, { "C5", "C6" }, { "P", "O1P" } }); ResidueData GTPData( "GTP", // Atoms { { "PG", 15 }, { "O1G", 8 }, { "O2G", 8 }, { "O3G", 8 }, { "O3B", 8 }, { "PB", 15 }, { "O1B", 8 }, { "O2B", 8 }, { "O3A", 8 }, { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "O6", 8 }, { "N1", 7 }, { "C2", 6 }, { "N2", 7 }, { "N3", 7 }, { "C4", 6 }, { "HOG2", 1 }, { "HOG3", 1 }, { "HOB2", 1 }, { "HOA2", 1 }, { "H5'", 1 }, { "H5''", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN1", 1 }, { "HN21", 1 }, { "HN21", 1 } }, // Single Bonds { { "PG", "O2G" }, { "PG", "O3G" }, { "PG", "O3B" }, { "O2G", "HOG2" }, { "O3G", "HOG3" }, { "O3B", "PB" }, { "PB", "O2B" }, { "PB", "O3A" }, { "O2B", "HOB2" }, { "O3A", "PA" }, { "PA", "O2A" }, { "PA", "O5'" }, { "O2A", "HOA2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'" }, { "C5'", "H5''" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N1" }, { "N1", "C2" }, { "N1", "HN1" }, { "C2", "N2" }, { "N2", "HN21" }, { "N2", "HN22" }, { "N3", "C4" } }, // Double Bonds { { "PG", "O1G" }, { "PB", "O1B" }, { "PA", "O1A" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "O6" }, { "C2", "N3" } }); ResidueData SAHData( "SAH", // Atoms { { "N", 7 }, { "CA", 6 }, { "CB", 6 }, { "CG", 6 }, { "SD", 16 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HN1", 1 }, { "HN2", 1 }, { "HA", 1 }, { "HB1", 1 }, { "HB2", 1 }, { "HG1", 1 }, { "HG2", 1 }, { "HXT", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN61", 1 }, { "HN62", 1 }, { "HN62", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "HN1" }, { "N", "HN2" }, { "CA", "CB" }, { "CA", "C" }, { "CA", "HA" }, { "CB", "CG" }, { "CB", "HB1" }, { "CB", "HB2" }, { "CG", "SD" }, { "CG", "HG1" }, { "CG", "HG2" }, { "SD", "C5'" }, { "C", "OXT" }, { "OXT", "HXT" }, { "C5'", "C4'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "HN61" }, { "N6", "HN62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "C", "O" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData BMEData("BME", // Atoms { { "C1", 6 }, { "C2", 6 }, { "O1", 8 }, { "S2", 16 }, { "H11", 1 }, { "H12", 1 }, { "H21", 1 }, { "H22", 1 }, { "HO1", 1 }, { "HO1", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "O1" }, { "C1", "H11" }, { "C1", "H12" }, { "C2", "S2" }, { "C2", "H21" }, { "C2", "H22" }, { "O1", "HO1" }, { "S2", "HS2" } }, // Double Bonds {}); ResidueData ACYData("ACY", // Atoms { { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "CH3", 6 }, { "HXT", 1 }, { "H1", 1 }, { "H2", 1 }, { "H2", 1 } }, // Single Bonds { { "C", "OXT" }, { "C", "CH3" }, { "OXT", "HXT" }, { "CH3", "H1" }, { "CH3", "H2" }, { "CH3", "H3" } }, // Double Bonds { { "C", "O" } }); ResidueData ANPData( "ANP", // Atoms { { "PG", 15 }, { "O1G", 8 }, { "O2G", 8 }, { "O3G", 8 }, { "PB", 15 }, { "O1B", 8 }, { "O2B", 8 }, { "N3B", 7 }, { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O3A", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOG2", 1 }, { "HOG3", 1 }, { "HOB2", 1 }, { "HNB1", 1 }, { "HOA2", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN61", 1 }, { "HN62", 1 }, { "HN62", 1 } }, // Single Bonds { { "PG", "O2G" }, { "PG", "O3G" }, { "PG", "N3B" }, { "O2G", "HOG2" }, { "O3G", "HOG3" }, { "PB", "O2B" }, { "PB", "N3B" }, { "PB", "O3A" }, { "O2B", "HOB2" }, { "N3B", "HNB1" }, { "PA", "O2A" }, { "PA", "O3A" }, { "PA", "O5'" }, { "O2A", "HOA2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "HN61" }, { "N6", "HN62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "PG", "O1G" }, { "PB", "O1B" }, { "PA", "O1A" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData IMDData("IMD", // Atoms { { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "C5", 6 }, { "HN1", 1 }, { "H2", 1 }, { "HN3", 1 }, { "H4", 1 }, { "H4", 1 } }, // Single Bonds { { "N1", "C2" }, { "N1", "C5" }, { "N1", "HN1" }, { "C2", "H2" }, { "N3", "C4" }, { "N3", "HN3" }, { "C4", "H4" }, { "C5", "H5" } }, // Double Bonds { { "C2", "N3" }, { "C4", "C5" } }); ResidueData PCAData("PCA", // Atoms { { "N", 7 }, { "CA", 6 }, { "CB", 6 }, { "CG", 6 }, { "CD", 6 }, { "OE", 8 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "H", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HG3", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "CD" }, { "N", "H" }, { "CA", "CB" }, { "CA", "C" }, { "CA", "HA" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "C", "OXT" }, { "OXT", "HXT" } }, // Double Bonds { { "CD", "OE" }, { "C", "O" } }); ResidueData CSOData("CSO", // Atoms { { "N", 7 }, { "CA", 6 }, { "CB", 6 }, { "SG", 16 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "OD", 8 }, { "H", 1 }, { "HN2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HXT", 1 }, { "HXT", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "HN2" }, { "CA", "CB" }, { "CA", "C" }, { "CA", "HA" }, { "CB", "SG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "SG", "OD" }, { "C", "OXT" }, { "OXT", "HXT" }, { "OD", "HD" } }, // Double Bonds { { "C", "O" } }); ResidueData MLIData("MLI", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "O6", 8 }, { "O7", 8 }, { "O8", 8 }, { "O9", 8 }, { "H11", 1 }, { "H11", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "C3" }, { "C1", "H11" }, { "C1", "H12" }, { "C2", "O7" }, { "C3", "O9" } }, // Double Bonds { { "C2", "O6" }, { "C3", "O8" } }); ResidueData FESData( "FES", // Atoms { { "FE1", 26 }, { "FE2", 26 }, { "S1", 16 }, { "S1", 16 } }, // Single Bonds { { "FE1", "S1" }, { "FE1", "S2" }, { "FE2", "S1" }, { "FE2", "S2" } }, // Double Bonds {}); ResidueData NDPData( "NDP", // Atoms { { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O5B", 8 }, { "C5B", 6 }, { "C4B", 6 }, { "O4B", 8 }, { "C3B", 6 }, { "O3B", 8 }, { "C2B", 6 }, { "O2B", 8 }, { "C1B", 6 }, { "N9A", 7 }, { "C8A", 6 }, { "N7A", 7 }, { "C5A", 6 }, { "C6A", 6 }, { "N6A", 7 }, { "N1A", 7 }, { "C2A", 6 }, { "N3A", 7 }, { "C4A", 6 }, { "O3", 8 }, { "PN", 15 }, { "O1N", 8 }, { "O2N", 8 }, { "O5D", 8 }, { "C5D", 6 }, { "C4D", 6 }, { "O4D", 8 }, { "C3D", 6 }, { "O3D", 8 }, { "C2D", 6 }, { "O2D", 8 }, { "C1D", 6 }, { "N1N", 7 }, { "C2N", 6 }, { "C3N", 6 }, { "C7N", 6 }, { "O7N", 8 }, { "N7N", 7 }, { "C4N", 6 }, { "C5N", 6 }, { "C6N", 6 }, { "P2B", 15 }, { "O1X", 8 }, { "O2X", 8 }, { "O3X", 8 }, { "HOA2", 1 }, { "H51A", 1 }, { "H52A", 1 }, { "H4B", 1 }, { "H3B", 1 }, { "HO3A", 1 }, { "H2B", 1 }, { "H1B", 1 }, { "H8A", 1 }, { "H61A", 1 }, { "H62A", 1 }, { "H2A", 1 }, { "H21N", 1 }, { "H51N", 1 }, { "H52N", 1 }, { "H4D", 1 }, { "H3D", 1 }, { "HO3N", 1 }, { "H2D", 1 }, { "HO2N", 1 }, { "H1D", 1 }, { "H2N", 1 }, { "H71N", 1 }, { "H72N", 1 }, { "H41N", 1 }, { "H42N", 1 }, { "H5N", 1 }, { "H6N", 1 }, { "HOP2", 1 }, { "HOP2", 1 } }, // Single Bonds { { "PA", "O2A" }, { "PA", "O5B" }, { "PA", "O3" }, { "O2A", "HOA2" }, { "O5B", "C5B" }, { "C5B", "C4B" }, { "C5B", "H51A" }, { "C5B", "H52A" }, { "C4B", "O4B" }, { "C4B", "C3B" }, { "C4B", "H4B" }, { "O4B", "C1B" }, { "C3B", "O3B" }, { "C3B", "C2B" }, { "C3B", "H3B" }, { "O3B", "HO3A" }, { "C2B", "O2B" }, { "C2B", "C1B" }, { "C2B", "H2B" }, { "O2B", "P2B" }, { "C1B", "N9A" }, { "C1B", "H1B" }, { "N9A", "C8A" }, { "N9A", "C4A" }, { "C8A", "H8A" }, { "N7A", "C5A" }, { "C5A", "C6A" }, { "C6A", "N6A" }, { "N6A", "H61A" }, { "N6A", "H62A" }, { "N1A", "C2A" }, { "C2A", "H2A" }, { "N3A", "C4A" }, { "O3", "PN" }, { "PN", "O2N" }, { "PN", "O5D" }, { "O2N", "H21N" }, { "O5D", "C5D" }, { "C5D", "C4D" }, { "C5D", "H51N" }, { "C5D", "H52N" }, { "C4D", "O4D" }, { "C4D", "C3D" }, { "C4D", "H4D" }, { "O4D", "C1D" }, { "C3D", "O3D" }, { "C3D", "C2D" }, { "C3D", "H3D" }, { "O3D", "HO3N" }, { "C2D", "O2D" }, { "C2D", "C1D" }, { "C2D", "H2D" }, { "O2D", "HO2N" }, { "C1D", "N1N" }, { "C1D", "H1D" }, { "N1N", "C2N" }, { "N1N", "C6N" }, { "C2N", "H2N" }, { "C3N", "C7N" }, { "C3N", "C4N" }, { "C7N", "N7N" }, { "N7N", "H71N" }, { "N7N", "H72N" }, { "C4N", "C5N" }, { "C4N", "H41N" }, { "C4N", "H42N" }, { "C5N", "H5N" }, { "C6N", "H6N" }, { "P2B", "O2X" }, { "P2B", "O3X" }, { "O2X", "HOP2" }, { "O3X", "HOP3" } }, // Double Bonds { { "PA", "O1A" }, { "C8A", "N7A" }, { "C5A", "C4A" }, { "C6A", "N1A" }, { "C2A", "N3A" }, { "PN", "O1N" }, { "C2N", "C3N" }, { "C7N", "O7N" }, { "C5N", "C6N" }, { "P2B", "O1X" } }); ResidueData AMPData( "AMP", // Atoms { { "P", 15 }, { "O1P", 8 }, { "O2P", 8 }, { "O3P", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HOP2", 1 }, { "HOP3", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN61", 1 }, { "HN62", 1 }, { "HN62", 1 } }, // Single Bonds { { "P", "O2P" }, { "P", "O3P" }, { "P", "O5'" }, { "O2P", "HOP2" }, { "O3P", "HOP3" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "HN61" }, { "N6", "HN62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "P", "O1P" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData NO3Data("NO3", // Atoms { { "N", 7 }, { "O1", 8 }, { "O2", 8 }, { "O2", 8 } }, // Single Bonds { { "N", "O2" }, { "N", "O3" } }, // Double Bonds { { "N", "O1" } }); ResidueData PTRData( "PTR", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "CB", 6 }, { "CG", 6 }, { "CD1", 6 }, { "CD2", 6 }, { "CE1", 6 }, { "CE2", 6 }, { "CZ", 6 }, { "OH", 8 }, { "P", 15 }, { "O1P", 8 }, { "O2P", 8 }, { "O3P", 8 }, { "H", 1 }, { "HN2", 1 }, { "HA", 1 }, { "HXT", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HD1", 1 }, { "HD2", 1 }, { "HE1", 1 }, { "HE2", 1 }, { "HO2P", 1 }, { "HO2P", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "HN2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "OXT", "HXT" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD2" }, { "CD1", "CE1" }, { "CD1", "HD1" }, { "CD2", "HD2" }, { "CE1", "HE1" }, { "CE2", "CZ" }, { "CE2", "HE2" }, { "CZ", "OH" }, { "OH", "P" }, { "P", "O2P" }, { "P", "O3P" }, { "O2P", "HO2P" }, { "O3P", "HO3P" } }, // Double Bonds { { "C", "O" }, { "CG", "CD1" }, { "CD2", "CE2" }, { "CE1", "CZ" }, { "P", "O1P" } }); ResidueData IPAData("IPA", // Atoms { { "C1", 6 }, { "C2", 6 }, { "C3", 6 }, { "O2", 8 }, { "H11", 1 }, { "H12", 1 }, { "H13", 1 }, { "H2", 1 }, { "H31", 1 }, { "H32", 1 }, { "H33", 1 }, { "H33", 1 } }, // Single Bonds { { "C1", "C2" }, { "C1", "H11" }, { "C1", "H12" }, { "C1", "H13" }, { "C2", "C3" }, { "C2", "O2" }, { "C2", "H2" }, { "C3", "H31" }, { "C3", "H32" }, { "C3", "H33" }, { "O2", "HO2" } }, // Double Bonds {}); ResidueData COAData( "COA", // Atoms { { "N1A", 7 }, { "C2A", 6 }, { "N3A", 7 }, { "C4A", 6 }, { "C5A", 6 }, { "C6A", 6 }, { "N6A", 7 }, { "N7A", 7 }, { "C8A", 6 }, { "N9A", 7 }, { "C1B", 6 }, { "C2B", 6 }, { "O2B", 8 }, { "C3B", 6 }, { "O3B", 8 }, { "P3B", 15 }, { "O7A", 8 }, { "O8A", 8 }, { "O9A", 8 }, { "C4B", 6 }, { "O4B", 8 }, { "C5B", 6 }, { "O5B", 8 }, { "P1A", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O3A", 8 }, { "P2A", 15 }, { "O4A", 8 }, { "O5A", 8 }, { "O6A", 8 }, { "CBP", 6 }, { "CCP", 6 }, { "CDP", 6 }, { "CEP", 6 }, { "CAP", 6 }, { "OAP", 8 }, { "C9P", 6 }, { "O9P", 8 }, { "N8P", 7 }, { "C7P", 6 }, { "C6P", 6 }, { "C5P", 6 }, { "O5P", 8 }, { "N4P", 7 }, { "C3P", 6 }, { "C2P", 6 }, { "S1P", 16 }, { "H2A", 1 }, { "H61A", 1 }, { "H62A", 1 }, { "H8A", 1 }, { "H1B", 1 }, { "H2B", 1 }, { "HO2A", 1 }, { "H3B", 1 }, { "HOA8", 1 }, { "HOA9", 1 }, { "H4B", 1 }, { "H51A", 1 }, { "H52A", 1 }, { "HOA2", 1 }, { "HOA5", 1 }, { "H121", 1 }, { "H122", 1 }, { "H131", 1 }, { "H132", 1 }, { "H133", 1 }, { "H141", 1 }, { "H142", 1 }, { "H143", 1 }, { "H10", 1 }, { "HO1", 1 }, { "HN8", 1 }, { "H71", 1 }, { "H72", 1 }, { "H61", 1 }, { "H62", 1 }, { "HN4", 1 }, { "H31", 1 }, { "H32", 1 }, { "H21", 1 }, { "H22", 1 }, { "H22", 1 } }, // Single Bonds { { "N1A", "C2A" }, { "C2A", "H2A" }, { "N3A", "C4A" }, { "C4A", "N9A" }, { "C5A", "C6A" }, { "C5A", "N7A" }, { "C6A", "N6A" }, { "N6A", "H61A" }, { "N6A", "H62A" }, { "C8A", "N9A" }, { "C8A", "H8A" }, { "N9A", "C1B" }, { "C1B", "C2B" }, { "C1B", "O4B" }, { "C1B", "H1B" }, { "C2B", "O2B" }, { "C2B", "C3B" }, { "C2B", "H2B" }, { "O2B", "HO2A" }, { "C3B", "O3B" }, { "C3B", "C4B" }, { "C3B", "H3B" }, { "O3B", "P3B" }, { "P3B", "O8A" }, { "P3B", "O9A" }, { "O8A", "HOA8" }, { "O9A", "HOA9" }, { "C4B", "O4B" }, { "C4B", "C5B" }, { "C4B", "H4B" }, { "C5B", "O5B" }, { "C5B", "H51A" }, { "C5B", "H52A" }, { "O5B", "P1A" }, { "P1A", "O2A" }, { "P1A", "O3A" }, { "O2A", "HOA2" }, { "O3A", "P2A" }, { "P2A", "O5A" }, { "P2A", "O6A" }, { "O5A", "HOA5" }, { "O6A", "CCP" }, { "CBP", "CCP" }, { "CBP", "CDP" }, { "CBP", "CEP" }, { "CBP", "CAP" }, { "CCP", "H121" }, { "CCP", "H122" }, { "CDP", "H131" }, { "CDP", "H132" }, { "CDP", "H133" }, { "CEP", "H141" }, { "CEP", "H142" }, { "CEP", "H143" }, { "CAP", "OAP" }, { "CAP", "C9P" }, { "CAP", "H10" }, { "OAP", "HO1" }, { "C9P", "N8P" }, { "N8P", "C7P" }, { "N8P", "HN8" }, { "C7P", "C6P" }, { "C7P", "H71" }, { "C7P", "H72" }, { "C6P", "C5P" }, { "C6P", "H61" }, { "C6P", "H62" }, { "C5P", "N4P" }, { "N4P", "C3P" }, { "N4P", "HN4" }, { "C3P", "C2P" }, { "C3P", "H31" }, { "C3P", "H32" }, { "C2P", "S1P" }, { "C2P", "H21" }, { "C2P", "H22" }, { "S1P", "HS1" } }, // Double Bonds { { "N1A", "C6A" }, { "C2A", "N3A" }, { "C4A", "C5A" }, { "N7A", "C8A" }, { "P3B", "O7A" }, { "P1A", "O1A" }, { "P2A", "O4A" }, { "C9P", "O9P" }, { "C5P", "O5P" } }); ResidueData KCXData( "KCX", // Atoms { { "N", 7 }, { "CA", 6 }, { "CB", 6 }, { "CG", 6 }, { "CD", 6 }, { "CE", 6 }, { "NZ", 7 }, { "C", 6 }, { "O", 8 }, { "CX", 6 }, { "OXT", 8 }, { "OQ1", 8 }, { "OQ2", 8 }, { "H", 1 }, { "HN2", 1 }, { "HA", 1 }, { "HB2", 1 }, { "HB3", 1 }, { "HG2", 1 }, { "HG3", 1 }, { "HD2", 1 }, { "HD3", 1 }, { "HE2", 1 }, { "HE3", 1 }, { "HZ", 1 }, { "HXT", 1 }, { "HXT", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "H" }, { "N", "HN2" }, { "CA", "CB" }, { "CA", "C" }, { "CA", "HA" }, { "CB", "CG" }, { "CB", "HB2" }, { "CB", "HB3" }, { "CG", "CD" }, { "CG", "HG2" }, { "CG", "HG3" }, { "CD", "CE" }, { "CD", "HD2" }, { "CD", "HD3" }, { "CE", "NZ" }, { "CE", "HE2" }, { "CE", "HE3" }, { "NZ", "CX" }, { "NZ", "HZ" }, { "C", "OXT" }, { "CX", "OQ2" }, { "OXT", "HXT" }, { "OQ2", "HQ2" } }, // Double Bonds { { "C", "O" }, { "CX", "OQ1" } }); ResidueData H4BData( "H4B", // Atoms { { "N1", 7 }, { "C2", 6 }, { "N2", 7 }, { "N3", 7 }, { "C4", 6 }, { "O4", 8 }, { "C4A", 6 }, { "C8A", 6 }, { "N5", 7 }, { "N8", 7 }, { "C6", 6 }, { "C7", 6 }, { "C9", 6 }, { "O9", 8 }, { "C10", 6 }, { "C11", 6 }, { "O10", 8 }, { "HN21", 1 }, { "HN22", 1 }, { "HN3", 1 }, { "HN5", 1 }, { "HN8", 1 }, { "H6", 1 }, { "H71", 1 }, { "H72", 1 }, { "H9", 1 }, { "HO9", 1 }, { "H10", 1 }, { "H111", 1 }, { "H112", 1 }, { "H113", 1 }, { "H113", 1 } }, // Single Bonds { { "N1", "C8A" }, { "C2", "N2" }, { "C2", "N3" }, { "N2", "HN21" }, { "N2", "HN22" }, { "N3", "C4" }, { "N3", "HN3" }, { "C4", "C4A" }, { "C4A", "N5" }, { "C8A", "N8" }, { "N5", "C6" }, { "N5", "HN5" }, { "N8", "C7" }, { "N8", "HN8" }, { "C6", "C7" }, { "C6", "C9" }, { "C6", "H6" }, { "C7", "H71" }, { "C7", "H72" }, { "C9", "O9" }, { "C9", "C10" }, { "C9", "H9" }, { "O9", "HO9" }, { "C10", "C11" }, { "C10", "O10" }, { "C10", "H10" }, { "C11", "H111" }, { "C11", "H112" }, { "C11", "H113" }, { "O10", "HO0" } }, // Double Bonds { { "N1", "C2" }, { "C4", "O4" }, { "C4A", "C8A" } }); ResidueData SAMData( "SAM", // Atoms { { "N", 7 }, { "CA", 6 }, { "C", 6 }, { "O", 8 }, { "OXT", 8 }, { "CB", 6 }, { "CG", 6 }, { "SD", 16 }, { "CE", 6 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "N6", 7 }, { "N1", 7 }, { "C2", 6 }, { "N3", 7 }, { "C4", 6 }, { "HN1", 1 }, { "HN2", 1 }, { "HA", 1 }, { "HB1", 1 }, { "HB2", 1 }, { "HG1", 1 }, { "HG2", 1 }, { "HE1", 1 }, { "HE2", 1 }, { "HE3", 1 }, { "H5'1", 1 }, { "H5'2", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN61", 1 }, { "HN62", 1 }, { "HN62", 1 } }, // Single Bonds { { "N", "CA" }, { "N", "HN1" }, { "N", "HN2" }, { "CA", "C" }, { "CA", "CB" }, { "CA", "HA" }, { "C", "OXT" }, { "CB", "CG" }, { "CB", "HB1" }, { "CB", "HB2" }, { "CG", "SD" }, { "CG", "HG1" }, { "CG", "HG2" }, { "SD", "CE" }, { "SD", "C5'" }, { "CE", "HE1" }, { "CE", "HE2" }, { "CE", "HE3" }, { "C5'", "C4'" }, { "C5'", "H5'1" }, { "C5'", "H5'2" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N6" }, { "N6", "HN61" }, { "N6", "HN62" }, { "N1", "C2" }, { "C2", "H2" }, { "N3", "C4" } }, // Double Bonds { { "C", "O" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "N1" }, { "C2", "N3" } }); ResidueData GNPData( "GNP", // Atoms { { "PG", 15 }, { "O1G", 8 }, { "O2G", 8 }, { "O3G", 8 }, { "N3B", 7 }, { "PB", 15 }, { "O1B", 8 }, { "O2B", 8 }, { "O3A", 8 }, { "PA", 15 }, { "O1A", 8 }, { "O2A", 8 }, { "O5'", 8 }, { "C5'", 6 }, { "C4'", 6 }, { "O4'", 8 }, { "C3'", 6 }, { "O3'", 8 }, { "C2'", 6 }, { "O2'", 8 }, { "C1'", 6 }, { "N9", 7 }, { "C8", 6 }, { "N7", 7 }, { "C5", 6 }, { "C6", 6 }, { "O6", 8 }, { "N1", 7 }, { "C2", 6 }, { "N2", 7 }, { "N3", 7 }, { "C4", 6 }, { "HOG2", 1 }, { "HOG3", 1 }, { "HNB3", 1 }, { "HOB2", 1 }, { "HOA2", 1 }, { "H5'2", 1 }, { "H5'1", 1 }, { "H4'", 1 }, { "H3'", 1 }, { "HO3'", 1 }, { "H2'", 1 }, { "HO2'", 1 }, { "H1'", 1 }, { "H8", 1 }, { "HN1", 1 }, { "HN21", 1 }, { "HN21", 1 } }, // Single Bonds { { "PG", "O2G" }, { "PG", "O3G" }, { "PG", "N3B" }, { "O2G", "HOG2" }, { "O3G", "HOG3" }, { "N3B", "PB" }, { "N3B", "HNB3" }, { "PB", "O2B" }, { "PB", "O3A" }, { "O2B", "HOB2" }, { "O3A", "PA" }, { "PA", "O2A" }, { "PA", "O5'" }, { "O2A", "HOA2" }, { "O5'", "C5'" }, { "C5'", "C4'" }, { "C5'", "H5'2" }, { "C5'", "H5'1" }, { "C4'", "O4'" }, { "C4'", "C3'" }, { "C4'", "H4'" }, { "O4'", "C1'" }, { "C3'", "O3'" }, { "C3'", "C2'" }, { "C3'", "H3'" }, { "O3'", "HO3'" }, { "C2'", "O2'" }, { "C2'", "C1'" }, { "C2'", "H2'" }, { "O2'", "HO2'" }, { "C1'", "N9" }, { "C1'", "H1'" }, { "N9", "C8" }, { "N9", "C4" }, { "C8", "H8" }, { "N7", "C5" }, { "C5", "C6" }, { "C6", "N1" }, { "N1", "C2" }, { "N1", "HN1" }, { "C2", "N2" }, { "N2", "HN21" }, { "N2", "HN22" }, { "N3", "C4" } }, // Double Bonds { { "PG", "O1G" }, { "PB", "O1B" }, { "PA", "O1A" }, { "C8", "N7" }, { "C5", "C4" }, { "C6", "O6" }, { "C2", "N3" } }); ResidueData FLCData("FLC", // Atoms { { "CAC", 6 }, { "CA", 6 }, { "CB", 6 }, { "CBC", 6 }, { "CG", 6 }, { "CGC", 6 }, { "OA1", 8 }, { "OA2", 8 }, { "OB1", 8 }, { "OB2", 8 }, { "OG1", 8 }, { "OG2", 8 }, { "OHB", 8 }, { "HA1", 1 }, { "HA2", 1 }, { "HG1", 1 }, { "HG2", 1 }, { "HG2", 1 } }, // Single Bonds { { "CAC", "CA" }, { "CAC", "OA2" }, { "CA", "CB" }, { "CA", "HA1" }, { "CA", "HA2" }, { "CB", "CBC" }, { "CB", "CG" }, { "CB", "OHB" }, { "CBC", "OB2" }, { "CG", "CGC" }, { "CG", "HG1" }, { "CG", "HG2" }, { "CGC", "OG2" }, { "OHB", "HOB" } }, // Double Bonds { { "CAC", "OA1" }, { "CBC", "OB1" }, { "CGC", "OG1" } }); std::map residueDict = { { "ALA", ALAData }, { "CYS", CYSData }, { "ASP", ASPData }, { "GLU", GLUData }, { "PHE", PHEData }, { "GLY", GLYData }, { "HIS", HISData }, { "ILE", ILEData }, { "LYS", LYSData }, { "LEU", LEUData }, { "MET", METData }, { "ASN", ASNData }, { "PRO", PROData }, { "GLN", GLNData }, { "ARG", ARGData }, { "SER", SERData }, { "THR", THRData }, { "VAL", VALData }, { "TRP", TRPData }, { "TYR", TYRData }, { "DA", DAData }, { "DC", DCData }, { "DG", DGData }, { "DT", DTData }, { "DI", DIData }, { "A", AData }, { "C", CData }, { "G", GData }, { "U", UData }, { "I", IData }, { "HEM", HEMData }, { "HOH", HOHData }, { "SO4", SO4Data }, { "GOL", GOLData }, { "EDO", EDOData }, { "MSE", MSEData }, { "NAG", NAGData }, { "PO4", PO4Data }, { "ACT", ACTData }, { "PEG", PEGData }, { "BMA", BMAData }, { "MAN", MANData }, { "DMS", DMSData }, { "ADP", ADPData }, { "FAD", FADData }, { "ACE", ACEData }, { "MPD", MPDData }, { "GLC", GLCData }, { "ATP", ATPData }, { "MES", MESData }, { "TRS", TRSData }, { "PG4", PG4Data }, { "PGE", PGEData }, { "NAD", NADData }, { "FUC", FUCData }, { "NH2", NH2Data }, { "FMT", FMTData }, { "NAP", NAPData }, { "SEP", SEPData }, { "GDP", GDPData }, { "GAL", GALData }, { "SF4", SF4Data }, { "BGC", BGCData }, { "FMN", FMNData }, { "UNK", UNKData }, { "CIT", CITData }, { "EPE", EPEData }, { "TPO", TPOData }, { "PLP", PLPData }, { "GTP", GTPData }, { "SAH", SAHData }, { "BME", BMEData }, { "ACY", ACYData }, { "ANP", ANPData }, { "IMD", IMDData }, { "PCA", PCAData }, { "CSO", CSOData }, { "MLI", MLIData }, { "FES", FESData }, { "NDP", NDPData }, { "AMP", AMPData }, { "NO3", NO3Data }, { "PTR", PTRData }, { "IPA", IPAData }, { "COA", COAData }, { "KCX", KCXData }, { "H4B", H4BData }, { "SAM", SAMData }, { "GNP", GNPData }, { "FLC", FLCData }, }; } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/ringperceiver.cpp000066400000000000000000000323761506155467400224600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "ringperceiver.h" #include "molecule.h" #include #include #include #include #include #include namespace Avogadro::Core { namespace { // === DistanceMatrix ====================================================== // class DistanceMatrix { public: // swap function marked friend so it can only be found via Argument Dependent // Lookup (ADL) friend void swap(DistanceMatrix& first, DistanceMatrix& second) { // Enable ADL for the swap using std::swap; swap(first.m_size, second.m_size); swap(first.m_values, second.m_values); } // construction and destruction DistanceMatrix(size_t size); ~DistanceMatrix(); DistanceMatrix(const DistanceMatrix& other); // intentional pass-by-value to leverage previous copy ctor DistanceMatrix& operator=(DistanceMatrix other); // operators size_t operator()(size_t i, size_t j) const; size_t& operator()(size_t i, size_t j); private: size_t m_size; size_t* m_values; }; DistanceMatrix::DistanceMatrix(size_t size) : m_size(size), m_values(new size_t[size * size]) { memset(m_values, 0, size * size * sizeof(size_t)); } [[maybe_unused]] DistanceMatrix::DistanceMatrix(const DistanceMatrix& other) : m_size(other.m_size), m_values(other.m_size ? new size_t[other.m_size * other.m_size] : nullptr) { if (m_values) std::copy(other.m_values, other.m_values + (other.m_size * other.m_size), m_values); } [[maybe_unused]] DistanceMatrix& DistanceMatrix::operator=(DistanceMatrix other) { // will use friend swap function via Argument Dependent Lookup swap(*this, other); return *this; } DistanceMatrix::~DistanceMatrix() { delete[] m_values; } size_t& DistanceMatrix::operator()(size_t i, size_t j) { return m_values[i * m_size + j]; } // === PidMatrix =========================================================== // // The PidMatrix class implements a path-included distance matrix. class PidMatrix { public: // construction and destruction PidMatrix(size_t size); ~PidMatrix(); // paths std::vector>& paths(size_t i, size_t j); void addPaths(size_t i, size_t j, const std::vector>& paths); std::vector> splice(size_t i, size_t j, size_t k); // operators std::vector>& operator()(size_t i, size_t j); private: size_t m_size; std::vector>* m_values; }; // --- Construction and Destruction ---------------------------------------- // PidMatrix::PidMatrix(size_t size) : m_size(size), m_values(new std::vector>[size * size]) { } PidMatrix::~PidMatrix() { delete[] m_values; } // --- Paths --------------------------------------------------------------- // std::vector>& PidMatrix::paths(size_t i, size_t j) { return m_values[i * m_size + j]; } void PidMatrix::addPaths(size_t i, size_t j, const std::vector>& p) { std::vector>& current = m_values[i * m_size + j]; current.insert(current.end(), p.begin(), p.end()); } std::vector>& PidMatrix::operator()(size_t i, size_t j) { return paths(i, j); } std::vector> PidMatrix::splice(size_t i, size_t j, size_t k) { std::vector> splicedPaths; std::vector> ijPaths = paths(i, j); std::vector> jkPaths = paths(j, k); if (ijPaths.empty() && jkPaths.empty()) { std::vector path; path.push_back(j); splicedPaths.push_back(path); } else if (ijPaths.empty()) { for (auto& jkPath : jkPaths) { std::vector path; path.push_back(j); path.insert(path.end(), jkPath.begin(), jkPath.end()); splicedPaths.push_back(path); } } else if (jkPaths.empty()) { for (auto path : ijPaths) { path.push_back(j); splicedPaths.push_back(path); } } else { for (auto path : ijPaths) { for (auto& jkPath : jkPaths) { path.push_back(j); path.insert(path.end(), jkPath.begin(), jkPath.end()); splicedPaths.push_back(path); } } } return splicedPaths; } // === RingCandidate ======================================================= // class RingCandidate { public: // construction and destruction RingCandidate(size_t n, size_t s, size_t e); // properties size_t size() const; size_t start() const; size_t end() const; // static methods static bool compareSize(const RingCandidate& a, const RingCandidate& b); private: size_t m_size; size_t m_start; size_t m_end; }; // --- Construction and Destruction ---------------------------------------- // RingCandidate::RingCandidate(size_t n, size_t s, size_t e) : m_size(n), m_start(s), m_end(e) { } // --- Properties ---------------------------------------------------------- // size_t RingCandidate::size() const { return m_size; } size_t RingCandidate::start() const { return m_start; } size_t RingCandidate::end() const { return m_end; } // --- Static Methods ------------------------------------------------------ // bool RingCandidate::compareSize(const RingCandidate& a, const RingCandidate& b) { return a.size() < b.size(); } // === Sssr ================================================================ // class Sssr { public: // construction and destruction Sssr() = default; ~Sssr() = default; // properties size_t size() const; bool isEmpty() const; // rings const std::vector>& rings() const; void append(const std::vector& ring); bool isValid(const std::vector& ring) const; bool isUnique(const std::vector& ring) const; private: std::vector> m_rings; }; // --- Properties ---------------------------------------------------------- // size_t Sssr::size() const { return m_rings.size(); } bool Sssr::isEmpty() const { return m_rings.empty(); } // --- Rings --------------------------------------------------------------- // const std::vector>& Sssr::rings() const { return m_rings; } void Sssr::append(const std::vector& ring) { m_rings.push_back(ring); } bool Sssr::isValid(const std::vector& ring) const { // Check for any duplicate atoms. for (size_t i = 0; i < ring.size(); ++i) for (size_t j = i + 1; j < ring.size(); ++j) if (ring[i] == ring[j]) return false; return true; } bool Sssr::isUnique(const std::vector& path) const { // Must be unique if sssr is empty. if (isEmpty()) return true; // Check if a ring with the same atoms is already in the sssr. std::set pathSet; pathSet.insert(path.begin(), path.end()); for (const auto& ring : m_rings) { std::set ringSet; ringSet.insert(ring.begin(), ring.end()); std::vector sortedRing(ring.begin(), ring.end()); std::sort(sortedRing.begin(), sortedRing.end()); std::set intersection; std::set_intersection(pathSet.begin(), pathSet.end(), ringSet.begin(), ringSet.end(), std::inserter(intersection, intersection.begin())); if (intersection.size() == ring.size()) return false; } // Build the set of bonds in the path. std::set> pathBonds; for (size_t i = 0; i < path.size() - 1; i++) { pathBonds.insert(std::make_pair(std::min(path[i], path[i + 1]), std::max(path[i], path[i + 1]))); } pathBonds.insert(std::make_pair(std::min(path.front(), path.back()), std::max(path.front(), path.back()))); // Remove bonds from path bonds that are already in a smaller ring. for (const auto& ring : m_rings) { if (ring.size() >= path.size()) continue; for (size_t i = 0; i < ring.size(); i++) { pathBonds.erase(std::make_pair(std::min(ring[i], ring[i + 1]), std::max(ring[i], ring[i + 1]))); } pathBonds.erase(std::make_pair(std::min(ring.front(), ring.back()), std::max(ring.front(), ring.back()))); } // Check if any other ring contains the same bonds. for (const auto& ring : m_rings) { std::set> ringBonds; // Add ring bonds. for (size_t i = 0; i < ring.size() - 1; i++) { ringBonds.insert(std::make_pair(std::min(ring[i], ring[i + 1]), std::max(ring[i], ring[i + 1]))); } // Add closure bond. ringBonds.insert(std::make_pair(std::min(ring.front(), ring.back()), std::max(ring.front(), ring.back()))); // Check intersection. std::set> intersection; std::set_intersection(pathBonds.begin(), pathBonds.end(), ringBonds.begin(), ringBonds.end(), std::inserter(intersection, intersection.begin())); if (intersection.size() == pathBonds.size()) return false; } return true; } std::vector> perceiveRings(const Graph& graph) { size_t n = graph.size(); size_t ringCount = graph.vertexCount() - graph.edgeCount() + 1; if (ringCount == 0) return std::vector>(); // Algorithm 1 - create the distance and pid matrices. DistanceMatrix D(n); PidMatrix P(n); PidMatrix Pt(n); for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < n; ++j) { if (i == j) D(i, j) = 0; else if (graph.containsEdge(i, j)) D(i, j) = 1; else D(i, j) = std::numeric_limits::max() / 2; // ~ infinity } } for (size_t k = 0; k < n; ++k) { for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < n; ++j) { if (i == j || i == k || k == j) continue; if (D(i, j) > D(i, k) + D(k, j)) { if (D(i, j) == D(i, k) + D(k, j) + 1) Pt(i, j) = P(i, j); else Pt(i, j).clear(); D(i, j) = D(i, k) + D(k, j); P(i, j) = P.splice(i, k, j); } else if (D(i, j) == D(i, k) + D(k, j)) { P.addPaths(i, j, P.splice(i, k, j)); } else if (D(i, j) == D(i, k) + D(k, j) - 1) { Pt.addPaths(i, j, P.splice(i, k, j)); } } } } // Algorithm 2 - create the ring candidate set. std::vector candidates; for (size_t i = 0; i < n; i++) { for (size_t j = i + 1; j < n; j++) { if (P(i, j).size() == 1 && Pt(i, j).size() == 0) { continue; } else { size_t size; if (P(i, j).size() > 1) size = 2 * D(i, j); else size = 2 * D(i, j) + 1; if (size > 2) candidates.emplace_back(size, i, j); } } } // Sort the candidates. std::sort(candidates.begin(), candidates.end(), RingCandidate::compareSize); // Algorithm 3 - find sssr from the ring candidate set. Sssr sssr; for (auto& candidate : candidates) { // odd sized ring if (candidate.size() & 1) { for (size_t i = 0; i < Pt(candidate.start(), candidate.end()).size(); ++i) { std::vector ring; ring.push_back(candidate.start()); std::vector& path = Pt(candidate.start(), candidate.end())[i]; ring.insert(ring.end(), path.begin(), path.end()); ring.push_back(candidate.end()); if (!P(candidate.end(), candidate.start()).empty()) { path = P(candidate.end(), candidate.start())[0]; ring.insert(ring.end(), path.begin(), path.end()); } // Check if ring is valid and unique. if (sssr.isValid(ring) && sssr.isUnique(ring)) { sssr.append(ring); break; } } } // Even sized ring. else { for (size_t i = 0; i < P(candidate.start(), candidate.end()).size() - 1; ++i) { std::vector ring; ring.push_back(candidate.start()); std::vector& path = P(candidate.start(), candidate.end())[i]; ring.insert(ring.end(), path.begin(), path.end()); ring.push_back(candidate.end()); path = P(candidate.end(), candidate.start())[i + 1]; ring.insert(ring.end(), path.begin(), path.end()); // check if ring is valid and unique if (sssr.isValid(ring) && sssr.isUnique(ring)) { sssr.append(ring); break; } } } if (sssr.size() == ringCount) break; } return sssr.rings(); } } // end anonymous namespace RingPerceiver::RingPerceiver(const Molecule* m) : m_ringsPerceived(false), m_molecule(m) { } void RingPerceiver::setMolecule(const Molecule* m) { m_molecule = m; m_ringsPerceived = false; } const Molecule* RingPerceiver::molecule() const { return m_molecule; } std::vector>& RingPerceiver::rings() { if (!m_ringsPerceived) { if (m_molecule) m_rings = perceiveRings(m_molecule->graph()); else m_rings.clear(); m_ringsPerceived = true; } return m_rings; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/ringperceiver.h000066400000000000000000000017461506155467400221220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_RINGPERCEIVER_H #define AVOGADRO_CORE_RINGPERCEIVER_H #include "avogadrocoreexport.h" #include #include namespace Avogadro::Core { class Molecule; class AVOGADROCORE_EXPORT RingPerceiver { public: // construction and destruction explicit RingPerceiver(const Molecule* m = nullptr); ~RingPerceiver() = default; // properties void setMolecule(const Molecule* m); const Molecule* molecule() const; // ring perception std::vector>& rings(); private: bool m_ringsPerceived; const Molecule* m_molecule; std::vector> m_rings; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_RINGPERCEIVER_H avogadrolibs-1.101.0/avogadro/core/secondarystructure.cpp000066400000000000000000000235331506155467400235570ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "secondarystructure.h" #include #include #include #include namespace Avogadro::Core { SecondaryStructureAssigner::SecondaryStructureAssigner(Molecule* mol) : m_molecule(mol) { } SecondaryStructureAssigner::~SecondaryStructureAssigner() { for (auto* hBond : m_hBonds) delete hBond; m_hBonds.clear(); } //! Adapted from 3DMol.js parsers.js //! https://github.com/3dmol/3Dmol.js/blob/master/3Dmol/parsers.js void SecondaryStructureAssigner::assign(Molecule* mol) { m_molecule = mol; // Clear the current secondary structure auto residueCount = m_molecule->residues().size(); for (auto residue : m_molecule->residues()) residue.setSecondaryStructure(Residue::SecondaryStructure::undefined); // First assign the hydrogen bonds along the backbone assignBackboneHydrogenBonds(); float infinity = std::numeric_limits::max(); // Then assign the alpha helix by going through the hBond records for (auto* hBond : m_hBonds) { if (hBond->distSquared < infinity) { // check to see how far apart the residues are int separation = std::abs(int(hBond->residue - hBond->residuePair)); switch (separation) { case 3: { // 3-10 m_molecule->residue(hBond->residue) .setSecondaryStructure(Residue::SecondaryStructure::helix310); break; } case 4: { // alpha m_molecule->residue(hBond->residue) .setSecondaryStructure(Residue::SecondaryStructure::alphaHelix); break; } case 5: { // pi m_molecule->residue(hBond->residue) .setSecondaryStructure(Residue::SecondaryStructure::piHelix); break; } default: { break; } } } } // If there are less than 3 residues, don't do anything if (residueCount < 3) return; // Plug gaps in the helix for (size_t i = 1; i < residueCount - 1; ++i) { // check that before and after this residue are in the same chain if (m_molecule->residue(i).chainId() != m_molecule->residue(i - 1).chainId() || m_molecule->residue(i).chainId() != m_molecule->residue(i + 1).chainId()) continue; auto current = m_molecule->residue(i).secondaryStructure(); auto previous = m_molecule->residue(i - 1).secondaryStructure(); auto next = m_molecule->residue(i + 1).secondaryStructure(); // is there a gap in a helix (before and after are helix, so this should be) if (previous != Residue::SecondaryStructure::undefined && previous == next && current != previous) m_molecule->residue(i).setSecondaryStructure(previous); } // Then assign the beta sheet - but only if a residue isn't assigned for (auto* hBond : m_hBonds) { if (hBond->distSquared < infinity) { if (m_molecule->residue(hBond->residue).secondaryStructure() == Residue::SecondaryStructure::undefined) m_molecule->residue(hBond->residue) .setSecondaryStructure(Residue::SecondaryStructure::maybeBeta); } } // Check that sheets bond to other sheets for (auto* hBond : m_hBonds) { if (hBond->distSquared < infinity) { // find the match auto current = m_molecule->residue(hBond->residue); auto match = m_molecule->residue(hBond->residuePair); // if we're "maybe" beta see if the match is either beta or "maybe" if (current.secondaryStructure() == Residue::SecondaryStructure::maybeBeta && (match.secondaryStructure() == Residue::SecondaryStructure::maybeBeta || match.secondaryStructure() == Residue::SecondaryStructure::betaSheet)) { // we can be sure now m_molecule->residue(hBond->residue) .setSecondaryStructure(Residue::SecondaryStructure::betaSheet); m_molecule->residue(hBond->residuePair) .setSecondaryStructure(Residue::SecondaryStructure::betaSheet); } } } // Plug gaps in the beta sheet for (size_t i = 1; i < residueCount - 1; ++i) { // check that before and after this residue are in the same chain if (m_molecule->residue(i).chainId() != m_molecule->residue(i - 1).chainId() || m_molecule->residue(i).chainId() != m_molecule->residue(i + 1).chainId()) continue; auto current = m_molecule->residue(i).secondaryStructure(); auto previous = m_molecule->residue(i - 1).secondaryStructure(); auto next = m_molecule->residue(i + 1).secondaryStructure(); // is there a gap in a beta sheet? if (previous == Residue::SecondaryStructure::betaSheet && previous == next && current != previous) m_molecule->residue(i).setSecondaryStructure(previous); } // remove singletons for (size_t i = 1; i < residueCount - 1; ++i) { // check that before and after this residue are in the same chain if (m_molecule->residue(i).chainId() != m_molecule->residue(i - 1).chainId() || m_molecule->residue(i).chainId() != m_molecule->residue(i + 1).chainId()) continue; auto current = m_molecule->residue(i); // clear maybeBeta assignments (e.g. short bits) if (current.secondaryStructure() == Residue::SecondaryStructure::maybeBeta) m_molecule->residue(i).setSecondaryStructure( Residue::SecondaryStructure::undefined); if (current.secondaryStructure() != Residue::SecondaryStructure::undefined) { // make sure we don't have one lone odd assignment if (current.secondaryStructure() != m_molecule->residue(i - 1).secondaryStructure() && current.secondaryStructure() != m_molecule->residue(i + 1).secondaryStructure()) { m_molecule->residue(i).setSecondaryStructure( Residue::SecondaryStructure::undefined); } } } // end loop over residues (for singletons) } //! Adapted from 3DMol.js parsers.js assignBackboneHBond //! https://github.com/3dmol/3Dmol.js/blob/master/3Dmol/parsers.js void SecondaryStructureAssigner::assignBackboneHydrogenBonds() { if (m_molecule == nullptr) return; const float maxDist = 3.2; // in Angstroms const float maxDistSq = maxDist * maxDist; // 10.24 // delete any previous records for (auto* hBond : m_hBonds) delete hBond; m_hBonds.clear(); // Loop over the backbone atoms // we're just considering N and O (on a peptide) unsigned int idx = 0; // track the residue index for (const auto& residue : m_molecule->residues()) { unsigned int residueId = idx++; if (residue.isHeterogen()) continue; auto oxygen = residue.getAtomByName("O"); if (oxygen.isValid()) { auto* oRecord = new hBondRecord(); oRecord->atom = oxygen.index(); oRecord->atomZ = oxygen.position3d()[2]; oRecord->distSquared = std::numeric_limits::max(); oRecord->residue = residueId; oRecord->residuePair = residueId; // just a placeholder m_hBonds.push_back(oRecord); } auto nitrogen = residue.getAtomByName("N"); if (nitrogen.isValid()) { auto* nRecord = new hBondRecord(); nRecord->atom = nitrogen.index(); nRecord->atomZ = nitrogen.position3d()[2]; nRecord->distSquared = std::numeric_limits::max(); nRecord->residue = residueId; nRecord->residuePair = residueId; m_hBonds.push_back(nRecord); } } if (m_hBonds.size() == 0) return; // sort by z-coordinate std::sort(m_hBonds.begin(), m_hBonds.end(), // lambda for sorting by z-coordinate [2] [](const hBondRecord* a, const hBondRecord* b) { return a->atomZ < b->atomZ; }); // now loop through the sorted list (so we can exit quickly) int n = m_hBonds.size(); for (int i = 0; i < n; ++i) { auto* recordI = m_hBonds[i]; const Residue& residueI = m_molecule->residue(recordI->residue); for (int j = i + 1; j < n; ++j) { auto* recordJ = m_hBonds[j]; const Residue& residueJ = m_molecule->residue(recordJ->residue); // skip if we're not on the same chain if (residueI.chainId() != residueJ.chainId()) continue; if (residueI.chainId() == residueJ.chainId() && std::abs(int(residueI.residueId() - residueJ.residueId())) < 3) continue; // either the same or too close to each other // compute the distance between the two atoms float zDiff = fabs(m_molecule->atomPosition3d(recordJ->atom)[2] - m_molecule->atomPosition3d(recordI->atom)[2]); if (zDiff > maxDist) // everything else is too far away break; // x and y, we just skip this atom float yDiff = fabs(m_molecule->atomPosition3d(recordJ->atom)[1] - m_molecule->atomPosition3d(recordI->atom)[1]); if (yDiff > maxDist) continue; float xDiff = fabs(m_molecule->atomPosition3d(recordJ->atom)[0] - m_molecule->atomPosition3d(recordI->atom)[0]); if (xDiff > maxDist) continue; // compute the squared distance between the two atoms float distSq = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; if (distSq > maxDistSq) continue; // if we get here, we have a potential hydrogen bond // select the one with the shortest distance if (distSq < recordI->distSquared) { recordI->distSquared = distSq; recordI->residuePair = recordJ->residue; } if (distSq < recordJ->distSquared) { recordJ->distSquared = distSq; recordJ->residuePair = recordI->residue; } } // end for(j) } // end for(i) } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/secondarystructure.h000066400000000000000000000023361506155467400232220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_SECONDARYSTRUCTURE_H #define AVOGADRO_CORE_SECONDARYSTRUCTURE_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include namespace Avogadro::Core { class Molecule; //! \internal struct hBondRecord { //! The atom index we're examining Index atom; //! The z-coordinate of the atom float atomZ; //! The residue containing the atom Index residue; //! The residue we're paired through an hbond Index residuePair; //! The length (squared) of the hydrogen bond float distSquared; }; class AVOGADROCORE_EXPORT SecondaryStructureAssigner { public: // construction and destruction explicit SecondaryStructureAssigner(Molecule* m = nullptr); ~SecondaryStructureAssigner(); void assign(Molecule* mol); private: void assignBackboneHydrogenBonds(); Molecule* m_molecule; std::vector m_hBonds; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_ANGLEITERATOR_H avogadrolibs-1.101.0/avogadro/core/sharedmutex.cpp000066400000000000000000000017271506155467400221410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "sharedmutex.h" #include namespace Avogadro::Core { using std::shared_mutex; class SharedMutex::PIMPL { public: PIMPL() {} shared_mutex lock; }; SharedMutex::SharedMutex() : d(new PIMPL) {} SharedMutex::~SharedMutex() { delete d; } void SharedMutex::lockForRead() { d->lock.lock_shared(); } bool SharedMutex::tryLockForRead() { return d->lock.try_lock_shared(); } void SharedMutex::unlockForRead() { d->lock.unlock_shared(); } void SharedMutex::lockForWrite() { d->lock.lock(); } bool SharedMutex::tryLockForWrite() { return d->lock.try_lock(); } void SharedMutex::unlockForWrite() { d->lock.unlock(); } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/sharedmutex.h000066400000000000000000000027761506155467400216130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_SHAREDMUTEX_H #define AVOGADRO_CORE_SHAREDMUTEX_H #include "avogadrocoreexport.h" namespace Avogadro::Core { /** * @class SharedMutex sharedmutex.h * @brief The SharedMutex class provides a simple wrapper for the C++17 * shared_mutex class * @author Marcus D. Hanwell * * A very simple, and thin wrapper around the C++17 shared_mutex class, allowing * for lock, tryLock and unlock. */ class AVOGADROCORE_EXPORT SharedMutex { public: SharedMutex(); ~SharedMutex(); /** * @brief Obtain a shared read lock. */ void lockForRead(); /** * @brief Attempt to obtain a shared read lock. * @return True on success, false on failure. */ bool tryLockForRead(); /** * @brief Unlocks the exclusive write lock. */ void unlockForRead(); /** * @brief Obtain an exclusive write lock. */ void lockForWrite(); /** * @brief Attempt to obtain an exclusive write lock. * @return True on success, false on failure. */ bool tryLockForWrite(); /** * @brief Unlocks the exclusive write lock. */ void unlockForWrite(); private: class PIMPL; PIMPL* d; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_SHAREDMUTEX_H avogadrolibs-1.101.0/avogadro/core/slaterset.cpp000066400000000000000000000074251506155467400216170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "slaterset.h" #include #include #include namespace Avogadro::Core { bool SlaterSet::addSlaterIndices(const std::vector& i) { m_slaterIndices = i; return true; } bool SlaterSet::addSlaterTypes(const std::vector& t) { m_initialized = false; m_slaterTypes = t; return true; } bool SlaterSet::addZetas(const std::vector& z) { m_initialized = false; m_zetas = z; return true; } bool SlaterSet::addPQNs(const std::vector& pqns) { m_initialized = false; m_pqns = pqns; return true; } bool SlaterSet::addOverlapMatrix(const Eigen::MatrixXd& m) { m_initialized = false; m_overlap.resize(m.rows(), m.cols()); m_overlap = m; return true; } bool SlaterSet::addEigenVectors(const Eigen::MatrixXd& e) { m_eigenVectors.resize(e.rows(), e.cols()); m_eigenVectors = e; return true; } bool SlaterSet::addDensityMatrix(const Eigen::MatrixXd& d) { m_density.resize(d.rows(), d.cols()); m_density = d; return true; } unsigned int SlaterSet::molecularOrbitalCount(ElectronType) const { return static_cast(m_overlap.cols()); } void SlaterSet::outputAll() {} void SlaterSet::initCalculation() { if (m_initialized) return; m_normalized.resize(m_overlap.cols(), m_overlap.rows()); Eigen::SelfAdjointEigenSolver s(m_overlap); const MatrixX& p = s.eigenvectors(); MatrixX m = p * s.eigenvalues().array().inverse().array().sqrt().matrix().asDiagonal() * p.inverse(); m_normalized = m * m_eigenVectors; if (!(m_overlap * m * m).eval().isIdentity()) std::cout << "Identity test FAILED - do you need a newer version of Eigen?\n"; m_factors.resize(m_zetas.size()); m_PQNs = m_pqns; // Calculate the normalizations of the orbitals. for (size_t i = 0; i < m_zetas.size(); ++i) { switch (m_slaterTypes[i]) { case S: m_factors[i] = pow(2.0 * m_zetas[i], m_pqns[i] + 0.5) * sqrt(1.0 / (4.0 * M_PI) / factorial(2 * m_pqns[i])); m_PQNs[i] -= 1; break; case PX: case PY: case PZ: m_factors[i] = pow(2.0 * m_zetas[i], m_pqns[i] + 0.5) * sqrt(3.0 / (4.0 * M_PI) / factorial(2 * m_pqns[i])); m_PQNs[i] -= 2; break; case X2: m_factors[i] = 0.5 * pow(2.0 * m_zetas[i], m_pqns[i] + 0.5) * sqrt(15.0 / (4.0 * M_PI) / factorial(2 * m_pqns[i])); m_PQNs[i] -= 3; break; case XZ: m_factors[i] = pow(2.0 * m_zetas[i], m_pqns[i] + 0.5) * sqrt(15.0 / (4.0 * M_PI) / factorial(2 * m_pqns[i])); m_PQNs[i] -= 3; break; case Z2: m_factors[i] = (0.5 / sqrt(3.0)) * pow(2.0 * m_zetas[i], m_pqns[i] + 0.5) * sqrt(15.0 / (4.0 * M_PI) / factorial(2 * m_pqns[i])); m_PQNs[i] -= 3; break; case YZ: case XY: m_factors[i] = pow(2.0 * m_zetas[i], m_pqns[i] + 0.5) * sqrt(15.0 / (4.0 * M_PI) / factorial(2 * m_pqns[i])); m_PQNs[i] -= 3; break; default: std::cout << "Orbital " << i << " not handled, type " << m_slaterTypes[i] << std::endl; } } // Convert the exponents into Angstroms for (double& m_zeta : m_zetas) m_zeta = m_zeta / BOHR_TO_ANGSTROM_D; m_initialized = true; } inline unsigned int SlaterSet::factorial(unsigned int n) { if (n <= 1) return n; return (n * factorial(n - 1)); } } // End namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/slaterset.h000066400000000000000000000102561506155467400212600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_SLATERSET_H #define AVOGADRO_CORE_SLATERSET_H #include "basisset.h" #include #include #include namespace Avogadro::Core { /** * @class SlaterSet slaterset.h * @brief SlaterSet Class * @author Marcus D. Hanwell * * The SlaterSet class has a transparent data structure for storing the basis * sets output by many quantum mechanical codes. It has a certain hierarchy * where shells are built up from n primitives, in this case Slater Type * Orbitals (STOs). Each shell has a type (S, P, D, F, etc) and is composed of * one or more STOs. Each STO has a contraction coefficient, c, and an exponent, * a. * * When calculating Molecular Orbitals (MOs) each orthogonal shell has an * independent coefficient. That is the S type orbitals have one coefficient, * the P type orbitals have three coefficients (Px, Py and Pz), the D type * orbitals have five (or six if cartesian types) coefficients, and so on. */ struct SlaterShell; class AVOGADROCORE_EXPORT SlaterSet : public BasisSet { public: /** * Constructor. */ SlaterSet() = default; /** * Destructor. */ ~SlaterSet() override = default; /** * Clone. */ SlaterSet* clone() const override { return new SlaterSet(*this); } /** * Enumeration of the Slater orbital types. */ enum slater { S, PX, PY, PZ, X2, XZ, Z2, YZ, XY, UU }; /** * Add a basis to the basis set. * @param i Index of the atom to add the Basis too. * @return The index of the added Basis. */ bool addSlaterIndices(const std::vector& i); /** * Add the symmetry types for the orbitals. * @param t Vector containing the types of symmetry using the slater enum. */ bool addSlaterTypes(const std::vector& t); /** * Add a STO to the supplied basis. * @param zetas The exponents of the STOs * @return True if successful. */ bool addZetas(const std::vector& zetas); /** * The PQNs for the orbitals. */ bool addPQNs(const std::vector& pqns); /** * The overlap matrix. * @param m Matrix containing the overlap matrix for the basis. */ bool addOverlapMatrix(const Eigen::MatrixXd& m); /** * Add Eigen Vectors to the SlaterSet. * @param e Matrix of the eigen vectors for the SlaterSet. */ bool addEigenVectors(const Eigen::MatrixXd& e); /** * Add the density matrix to the SlaterSet. * @param d Density matrix for the SlaterSet. */ bool addDensityMatrix(const Eigen::MatrixXd& d); /** * @return The number of molecular orbitals in the BasisSet. */ unsigned int molecularOrbitalCount(ElectronType type = Paired) const override; /** * @return True of the basis set is valid, false otherwise. * Default is true, if false then the basis set is likely unusable. */ bool isValid() override { return true; } /** * Initialize the calculation, this must normally be done before anything. */ void initCalculation(); /** * Accessors for the various properties of the GaussianSet. */ std::vector& slaterIndices() { return m_slaterIndices; } std::vector& slaterTypes() { return m_slaterTypes; } std::vector& zetas() { return m_zetas; } std::vector& factors() { return m_factors; } std::vector& PQNs() { return m_PQNs; } MatrixX& normalizedMatrix() { return m_normalized; } MatrixX& densityMatrix() { return m_density; } // Nonfunctional - Included for backwards compatibility void outputAll(); private: std::vector m_slaterIndices; std::vector m_slaterTypes; std::vector m_zetas; std::vector m_pqns, m_PQNs; std::vector m_factors; MatrixX m_overlap; MatrixX m_eigenVectors; MatrixX m_density; MatrixX m_normalized; bool m_initialized = false; unsigned int factorial(unsigned int n); }; } // namespace Avogadro::Core #endif avogadrolibs-1.101.0/avogadro/core/slatersettools.cpp000066400000000000000000000105521506155467400226730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "slatersettools.h" #include "molecule.h" #include "slaterset.h" namespace Avogadro::Core { SlaterSetTools::SlaterSetTools(Molecule* mol) : m_molecule(mol) { if (m_molecule) m_basis = dynamic_cast(m_molecule->basisSet()); } double SlaterSetTools::calculateMolecularOrbital(const Vector3& position, int mo) const { if (mo > static_cast(m_basis->molecularOrbitalCount())) return 0.0; std::vector values(calculateValues(position)); const MatrixX& matrix = m_basis->normalizedMatrix(); int matrixSize(static_cast(matrix.rows())); int indexMO(mo - 1); // Now calculate the value of the density at this point in space double result(0.0); for (int i = 0; i < matrixSize; ++i) result += matrix(i, indexMO) * values[i]; return result; } double SlaterSetTools::calculateElectronDensity(const Vector3& position) const { const MatrixX& matrix = m_basis->densityMatrix(); int matrixSize(static_cast(m_basis->normalizedMatrix().rows())); if (matrix.rows() != matrixSize || matrix.cols() != matrixSize) return 0.0; std::vector values(calculateValues(position)); // Now calculate the value of the density at this point in space double rho(0.0); for (int i = 0; i < matrixSize; ++i) { // Calculate the off-diagonal parts of the matrix for (int j = 0; j < i; ++j) rho += 2.0 * matrix(i, j) * (values[i] * values[j]); // Now calculate the matrix diagonal rho += matrix(i, i) * (values[i] * values[i]); } return rho; } double SlaterSetTools::calculateSpinDensity(const Vector3&) const { return 0.0; } bool SlaterSetTools::isValid() const { return (m_molecule != nullptr) && (dynamic_cast(m_molecule->basisSet()) != nullptr); } inline bool SlaterSetTools::isSmall(double val) const { return std::abs(val) < 1e-20; } std::vector SlaterSetTools::calculateValues( const Vector3& position) const { m_basis->initCalculation(); Index atomsSize = m_molecule->atomCount(); size_t basisSize = m_basis->zetas().size(); const std::vector& slaterIndices = m_basis->slaterIndices(); const std::vector& slaterTypes = m_basis->slaterTypes(); const std::vector& PQNs = m_basis->PQNs(); const std::vector& factors = m_basis->factors(); const std::vector& zetas = m_basis->zetas(); std::vector deltas; std::vector dr2; deltas.reserve(atomsSize); dr2.reserve(atomsSize); // Calculate the deltas for the position for (Index i = 0; i < atomsSize; ++i) { deltas.emplace_back(position - m_molecule->atom(i).position3d()); dr2.push_back(deltas[i].squaredNorm()); } // Allocate space for the values to be calculated. std::vector values; values.resize(basisSize); // Now calculate the values at this point in space for (size_t i = 0; i < basisSize; ++i) { double dr(dr2[slaterIndices[i]]); Vector3 delta(deltas[slaterIndices[i]]); values[i] = factors[i] * exp(-zetas[i] * dr); // Radial part with effective PQNs for (int j = 0; j < PQNs[i]; ++j) values[i] *= dr; switch (slaterTypes[i]) { case SlaterSet::S: break; case SlaterSet::PX: values[i] *= delta.x(); break; case SlaterSet::PY: values[i] *= delta.y(); break; case SlaterSet::PZ: values[i] *= delta.z(); break; case SlaterSet::X2: // (x^2 - y^2)r^n values[i] *= delta.x() * delta.x() - delta.y() * delta.y(); break; case SlaterSet::XZ: // xzr^n values[i] *= delta.x() * delta.z(); break; case SlaterSet::Z2: // (2z^2 - x^2 - y^2)r^n values[i] *= 2.0 * delta.z() * delta.z() - delta.x() * delta.x() - delta.y() * delta.y(); break; case SlaterSet::YZ: // yzr^n values[i] *= delta.y() * delta.z(); break; case SlaterSet::XY: // xyr^n values[i] *= delta.x() * delta.y(); break; default: values[i] = 0.0; } } return values; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/slatersettools.h000066400000000000000000000051001506155467400223310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_SLATERSETTOOLS_H #define AVOGADRO_CORE_SLATERSETTOOLS_H #include "avogadrocoreexport.h" #include "vector.h" #include namespace Avogadro::Core { class Molecule; class SlaterSet; /** * @class SlaterSetTools slatersettools.h * @brief Provide tools to calculate molecular orbitals, electron densities and * other derived data stored in a GaussianSet result. * @author Marcus D. Hanwell */ class AVOGADROCORE_EXPORT SlaterSetTools { public: explicit SlaterSetTools(Molecule* mol = nullptr); ~SlaterSetTools() = default; /** * @brief Calculate the value of the specified molecular orbital at the * position specified. * @param position The position in space to calculate the value. * @param molecularOrbitalNumber The molecular orbital number. * @return The value of the molecular orbital at the position specified. */ double calculateMolecularOrbital(const Vector3& position, int molecularOrbitalNumber) const; /** * @brief Calculate the value of the electron density at the position * specified. * @param position The position in space to calculate the value. * @return The value of the electron density at the position specified. */ double calculateElectronDensity(const Vector3& position) const; /** * @brief Calculate the value of the electron spin density at the position * specified. * @param position The position in space to calculate the value. * @return The value of the spin density at the position specified. */ double calculateSpinDensity(const Vector3& position) const; /** * @brief Check that the basis set is valid and can be used. * @return True if valid, false otherwise. */ bool isValid() const; private: Molecule* m_molecule; SlaterSet* m_basis; bool isSmall(double value) const; /** * @brief Calculate the values at this position in space. The public calculate * functions call this function to prepare values before multiplying by the * molecular orbital or density matrix elements. * @param position The position in space to calculate the value. */ std::vector calculateValues(const Vector3& position) const; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_SlaterSetTools_H avogadrolibs-1.101.0/avogadro/core/spacegroupdata.h000066400000000000000000006376701506155467400222730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_SPACE_GROUP_DATA #define AVOGADRO_CORE_SPACE_GROUP_DATA // This file contains data for each hall number. // For example, [3] accesses data for hall number 3. // [0] is a dummy in every case. There are 530 entries in each one. namespace Avogadro::Core { const unsigned short space_group_international_number[] = { 0, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 17, 17, 17, 18, 18, 18, 19, 20, 20, 20, 21, 21, 21, 22, 23, 24, 25, 25, 25, 26, 26, 26, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 36, 36, 37, 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 48, 48, 49, 49, 49, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57, 57, 57, 57, 58, 58, 58, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 61, 61, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 65, 65, 65, 66, 66, 66, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 69, 70, 70, 71, 72, 72, 72, 73, 73, 74, 74, 74, 74, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 85, 86, 86, 87, 88, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 125, 126, 126, 127, 128, 129, 129, 130, 130, 131, 132, 133, 133, 134, 134, 135, 136, 137, 137, 138, 138, 139, 140, 141, 141, 142, 142, 143, 144, 145, 146, 146, 147, 148, 148, 149, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, 160, 161, 161, 162, 163, 164, 165, 166, 166, 167, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 201, 202, 203, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, 224, 225, 226, 227, 227, 228, 228, 229, 230 }; const char* space_group_schoenflies[] = { "", "C1^1", "Ci^1", "C2^1", "C2^1", "C2^1", "C2^2", "C2^2", "C2^2", "C2^3", "C2^3", "C2^3", "C2^3", "C2^3", "C2^3", "C2^3", "C2^3", "C2^3", "Cs^1", "Cs^1", "Cs^1", "Cs^2", "Cs^2", "Cs^2", "Cs^2", "Cs^2", "Cs^2", "Cs^2", "Cs^2", "Cs^2", "Cs^3", "Cs^3", "Cs^3", "Cs^3", "Cs^3", "Cs^3", "Cs^3", "Cs^3", "Cs^3", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "Cs^4", "C2h^1", "C2h^1", "C2h^1", "C2h^2", "C2h^2", "C2h^2", "C2h^3", "C2h^3", "C2h^3", "C2h^3", "C2h^3", "C2h^3", "C2h^3", "C2h^3", "C2h^3", "C2h^4", "C2h^4", "C2h^4", "C2h^4", "C2h^4", "C2h^4", "C2h^4", "C2h^4", "C2h^4", "C2h^5", "C2h^5", "C2h^5", "C2h^5", "C2h^5", "C2h^5", "C2h^5", "C2h^5", "C2h^5", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "C2h^6", "D2^1", "D2^2", "D2^2", "D2^2", "D2^3", "D2^3", "D2^3", "D2^4", "D2^5", "D2^5", "D2^5", "D2^6", "D2^6", "D2^6", "D2^7", "D2^8", "D2^9", "C2v^1", "C2v^1", "C2v^1", "C2v^2", "C2v^2", "C2v^2", "C2v^2", "C2v^2", "C2v^2", "C2v^3", "C2v^3", "C2v^3", "C2v^4", "C2v^4", "C2v^4", "C2v^4", "C2v^4", "C2v^4", "C2v^5", "C2v^5", "C2v^5", "C2v^5", "C2v^5", "C2v^5", "C2v^6", "C2v^6", "C2v^6", "C2v^6", "C2v^6", "C2v^6", "C2v^7", "C2v^7", "C2v^7", "C2v^7", "C2v^7", "C2v^7", "C2v^8", "C2v^8", "C2v^8", "C2v^9", "C2v^9", "C2v^9", "C2v^9", "C2v^9", "C2v^9", "C2v^10", "C2v^10", "C2v^10", "C2v^11", "C2v^11", "C2v^11", "C2v^12", "C2v^12", "C2v^12", "C2v^12", "C2v^12", "C2v^12", "C2v^13", "C2v^13", "C2v^13", "C2v^14", "C2v^14", "C2v^14", "C2v^14", "C2v^14", "C2v^14", "C2v^15", "C2v^15", "C2v^15", "C2v^15", "C2v^15", "C2v^15", "C2v^16", "C2v^16", "C2v^16", "C2v^16", "C2v^16", "C2v^16", "C2v^17", "C2v^17", "C2v^17", "C2v^17", "C2v^17", "C2v^17", "C2v^18", "C2v^18", "C2v^18", "C2v^19", "C2v^19", "C2v^19", "C2v^20", "C2v^20", "C2v^20", "C2v^21", "C2v^21", "C2v^21", "C2v^22", "C2v^22", "C2v^22", "C2v^22", "C2v^22", "C2v^22", "D2h^1", "D2h^2", "D2h^2", "D2h^3", "D2h^3", "D2h^3", "D2h^4", "D2h^4", "D2h^4", "D2h^4", "D2h^4", "D2h^4", "D2h^5", "D2h^5", "D2h^5", "D2h^5", "D2h^5", "D2h^5", "D2h^6", "D2h^6", "D2h^6", "D2h^6", "D2h^6", "D2h^6", "D2h^7", "D2h^7", "D2h^7", "D2h^7", "D2h^7", "D2h^7", "D2h^8", "D2h^8", "D2h^8", "D2h^8", "D2h^8", "D2h^8", "D2h^9", "D2h^9", "D2h^9", "D2h^10", "D2h^10", "D2h^10", "D2h^11", "D2h^11", "D2h^11", "D2h^11", "D2h^11", "D2h^11", "D2h^12", "D2h^12", "D2h^12", "D2h^13", "D2h^13", "D2h^13", "D2h^13", "D2h^13", "D2h^13", "D2h^14", "D2h^14", "D2h^14", "D2h^14", "D2h^14", "D2h^14", "D2h^15", "D2h^15", "D2h^16", "D2h^16", "D2h^16", "D2h^16", "D2h^16", "D2h^16", "D2h^17", "D2h^17", "D2h^17", "D2h^17", "D2h^17", "D2h^17", "D2h^18", "D2h^18", "D2h^18", "D2h^18", "D2h^18", "D2h^18", "D2h^19", "D2h^19", "D2h^19", "D2h^20", "D2h^20", "D2h^20", "D2h^21", "D2h^21", "D2h^21", "D2h^21", "D2h^21", "D2h^21", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^22", "D2h^23", "D2h^24", "D2h^24", "D2h^25", "D2h^26", "D2h^26", "D2h^26", "D2h^27", "D2h^27", "D2h^28", "D2h^28", "D2h^28", "D2h^28", "D2h^28", "D2h^28", "C4^1", "C4^2", "C4^3", "C4^4", "C4^5", "C4^6", "S4^1", "S4^2", "C4h^1", "C4h^2", "C4h^3", "C4h^3", "C4h^4", "C4h^4", "C4h^5", "C4h^6", "C4h^6", "D4^1", "D4^2", "D4^3", "D4^4", "D4^5", "D4^6", "D4^7", "D4^8", "D4^9", "D4^10", "C4v^1", "C4v^2", "C4v^3", "C4v^4", "C4v^5", "C4v^6", "C4v^7", "C4v^8", "C4v^9", "C4v^10", "C4v^11", "C4v^12", "D2d^1", "D2d^2", "D2d^3", "D2d^4", "D2d^5", "D2d^6", "D2d^7", "D2d^8", "D2d^9", "D2d^10", "D2d^11", "D2d^12", "D4h^1", "D4h^2", "D4h^3", "D4h^3", "D4h^4", "D4h^4", "D4h^5", "D4h^6", "D4h^7", "D4h^7", "D4h^8", "D4h^8", "D4h^9", "D4h^10", "D4h^11", "D4h^11", "D4h^12", "D4h^12", "D4h^13", "D4h^14", "D4h^15", "D4h^15", "D4h^16", "D4h^16", "D4h^17", "D4h^18", "D4h^19", "D4h^19", "D4h^20", "D4h^20", "C3^1", "C3^2", "C3^3", "C3^4", "C3^4", "C3i^1", "C3i^2", "C3i^2", "D3^1", "D3^2", "D3^3", "D3^4", "D3^5", "D3^6", "D3^7", "D3^7", "C3v^1", "C3v^2", "C3v^3", "C3v^4", "C3v^5", "C3v^5", "C3v^6", "C3v^6", "D3d^1", "D3d^2", "D3d^3", "D3d^4", "D3d^5", "D3d^5", "D3d^6", "D3d^6", "C6^1", "C6^2", "C6^3", "C6^4", "C6^5", "C6^6", "C3h^1", "C6h^1", "C6h^2", "D6^1", "D6^2", "D6^3", "D6^4", "D6^5", "D6^6", "C6v^1", "C6v^2", "C6v^3", "C6v^4", "D3h^1", "D3h^2", "D3h^3", "D3h^4", "D6h^1", "D6h^2", "D6h^3", "D6h^4", "T^1", "T^2", "T^3", "T^4", "T^5", "Th^1", "Th^2", "Th^2", "Th^3", "Th^4", "Th^4", "Th^5", "Th^6", "Th^7", "O^1", "O^2", "O^3", "O^4", "O^5", "O^6", "O^7", "O^8", "Td^1", "Td^2", "Td^3", "Td^4", "Td^5", "Td^6", "Oh^1", "Oh^2", "Oh^2", "Oh^3", "Oh^4", "Oh^4", "Oh^5", "Oh^6", "Oh^7", "Oh^7", "Oh^8", "Oh^8", "Oh^9", "Oh^10" }; const char* space_group_hall_symbol[] = { "", "P 1", "-P 1", "P 2y", "P 2", "P 2x", "P 2yb", "P 2c", "P 2xa", "C 2y", "A 2y", "I 2y", "A 2", "B 2", "I 2", "B 2x", "C 2x", "I 2x", "P -2y", "P -2", "P -2x", "P -2yc", "P -2yac", "P -2ya", "P -2a", "P -2ab", "P -2b", "P -2xb", "P -2xbc", "P -2xc", "C -2y", "A -2y", "I -2y", "A -2", "B -2", "I -2", "B -2x", "C -2x", "I -2x", "C -2yc", "A -2yac", "I -2ya", "A -2ya", "C -2ybc", "I -2yc", "A -2a", "B -2bc", "I -2b", "B -2b", "A -2ac", "I -2a", "B -2xb", "C -2xbc", "I -2xc", "C -2xc", "B -2xbc", "I -2xb", "-P 2y", "-P 2", "-P 2x", "-P 2yb", "-P 2c", "-P 2xa", "-C 2y", "-A 2y", "-I 2y", "-A 2", "-B 2", "-I 2", "-B 2x", "-C 2x", "-I 2x", "-P 2yc", "-P 2yac", "-P 2ya", "-P 2a", "-P 2ab", "-P 2b", "-P 2xb", "-P 2xbc", "-P 2xc", "-P 2ybc", "-P 2yn", "-P 2yab", "-P 2ac", "-P 2n", "-P 2bc", "-P 2xab", "-P 2xn", "-P 2xac", "-C 2yc", "-A 2yac", "-I 2ya", "-A 2ya", "-C 2ybc", "-I 2yc", "-A 2a", "-B 2bc", "-I 2b", "-B 2b", "-A 2ac", "-I 2a", "-B 2xb", "-C 2xbc", "-I 2xc", "-C 2xc", "-B 2xbc", "-I 2xb", "P 2 2", "P 2c 2", "P 2a 2a", "P 2 2b", "P 2 2ab", "P 2bc 2", "P 2ac 2ac", "P 2ac 2ab", "C 2c 2", "A 2a 2a", "B 2 2b", "C 2 2", "A 2 2", "B 2 2", "F 2 2", "I 2 2", "I 2b 2c", "P 2 -2", "P -2 2", "P -2 -2", "P 2c -2", "P 2c -2c", "P -2a 2a", "P -2 2a", "P -2 -2b", "P -2b -2", "P 2 -2c", "P -2a 2", "P -2b -2b", "P 2 -2a", "P 2 -2b", "P -2b 2", "P -2c 2", "P -2c -2c", "P -2a -2a", "P 2c -2ac", "P 2c -2b", "P -2b 2a", "P -2ac 2a", "P -2bc -2c", "P -2a -2ab", "P 2 -2bc", "P 2 -2ac", "P -2ac 2", "P -2ab 2", "P -2ab -2ab", "P -2bc -2bc", "P 2ac -2", "P 2bc -2bc", "P -2ab 2ab", "P -2 2ac", "P -2 -2bc", "P -2ab -2", "P 2 -2ab", "P -2bc 2", "P -2ac -2ac", "P 2c -2n", "P 2c -2ab", "P -2bc 2a", "P -2n 2a", "P -2n -2ac", "P -2ac -2n", "P 2 -2n", "P -2n 2", "P -2n -2n", "C 2 -2", "A -2 2", "B -2 -2", "C 2c -2", "C 2c -2c", "A -2a 2a", "A -2 2a", "B -2 -2b", "B -2b -2", "C 2 -2c", "A -2a 2", "B -2b -2b", "A 2 -2", "B 2 -2", "B -2 2", "C -2 2", "C -2 -2", "A -2 -2", "A 2 -2c", "B 2 -2c", "B -2c 2", "C -2b 2", "C -2b -2b", "A -2c -2c", "A 2 -2a", "B 2 -2b", "B -2b 2", "C -2c 2", "C -2c -2c", "A -2a -2a", "A 2 -2ac", "B 2 -2bc", "B -2bc 2", "C -2bc 2", "C -2bc -2bc", "A -2ac -2ac", "F 2 -2", "F -2 2", "F -2 -2", "F 2 -2d", "F -2d 2", "F -2d -2d", "I 2 -2", "I -2 2", "I -2 -2", "I 2 -2c", "I -2a 2", "I -2b -2b", "I 2 -2a", "I 2 -2b", "I -2b 2", "I -2c 2", "I -2c -2c", "I -2a -2a", "-P 2 2", "P 2 2 -1n", "-P 2ab 2bc", "-P 2 2c", "-P 2a 2", "-P 2b 2b", "P 2 2 -1ab", "-P 2ab 2b", "P 2 2 -1bc", "-P 2b 2bc", "P 2 2 -1ac", "-P 2a 2c", "-P 2a 2a", "-P 2b 2", "-P 2 2b", "-P 2c 2c", "-P 2c 2", "-P 2 2a", "-P 2a 2bc", "-P 2b 2n", "-P 2n 2b", "-P 2ab 2c", "-P 2ab 2n", "-P 2n 2bc", "-P 2ac 2", "-P 2bc 2bc", "-P 2ab 2ab", "-P 2 2ac", "-P 2 2bc", "-P 2ab 2", "-P 2a 2ac", "-P 2b 2c", "-P 2a 2b", "-P 2ac 2c", "-P 2bc 2b", "-P 2b 2ab", "-P 2 2ab", "-P 2bc 2", "-P 2ac 2ac", "-P 2ab 2ac", "-P 2ac 2bc", "-P 2bc 2ab", "-P 2c 2b", "-P 2c 2ac", "-P 2ac 2a", "-P 2b 2a", "-P 2a 2ab", "-P 2bc 2c", "-P 2 2n", "-P 2n 2", "-P 2n 2n", "P 2 2ab -1ab", "-P 2ab 2a", "P 2bc 2 -1bc", "-P 2c 2bc", "P 2ac 2ac -1ac", "-P 2c 2a", "-P 2n 2ab", "-P 2n 2c", "-P 2a 2n", "-P 2bc 2n", "-P 2ac 2b", "-P 2b 2ac", "-P 2ac 2ab", "-P 2bc 2ac", "-P 2ac 2n", "-P 2bc 2a", "-P 2c 2ab", "-P 2n 2ac", "-P 2n 2a", "-P 2c 2n", "-C 2c 2", "-C 2c 2c", "-A 2a 2a", "-A 2 2a", "-B 2 2b", "-B 2b 2", "-C 2bc 2", "-C 2bc 2bc", "-A 2ac 2ac", "-A 2 2ac", "-B 2 2bc", "-B 2bc 2", "-C 2 2", "-A 2 2", "-B 2 2", "-C 2 2c", "-A 2a 2", "-B 2b 2b", "-C 2b 2", "-C 2b 2b", "-A 2c 2c", "-A 2 2c", "-B 2 2c", "-B 2c 2", "C 2 2 -1bc", "-C 2b 2bc", "C 2 2 -1bc", "-C 2b 2c", "A 2 2 -1ac", "-A 2a 2c", "A 2 2 -1ac", "-A 2ac 2c", "B 2 2 -1bc", "-B 2bc 2b", "B 2 2 -1bc", "-B 2b 2bc", "-F 2 2", "F 2 2 -1d", "-F 2uv 2vw", "-I 2 2", "-I 2 2c", "-I 2a 2", "-I 2b 2b", "-I 2b 2c", "-I 2a 2b", "-I 2b 2", "-I 2a 2a", "-I 2c 2c", "-I 2 2b", "-I 2 2a", "-I 2c 2", "P 4", "P 4w", "P 4c", "P 4cw", "I 4", "I 4bw", "P -4", "I -4", "-P 4", "-P 4c", "P 4ab -1ab", "-P 4a", "P 4n -1n", "-P 4bc", "-I 4", "I 4bw -1bw", "-I 4ad", "P 4 2", "P 4ab 2ab", "P 4w 2c", "P 4abw 2nw", "P 4c 2", "P 4n 2n", "P 4cw 2c", "P 4nw 2abw", "I 4 2", "I 4bw 2bw", "P 4 -2", "P 4 -2ab", "P 4c -2c", "P 4n -2n", "P 4 -2c", "P 4 -2n", "P 4c -2", "P 4c -2ab", "I 4 -2", "I 4 -2c", "I 4bw -2", "I 4bw -2c", "P -4 2", "P -4 2c", "P -4 2ab", "P -4 2n", "P -4 -2", "P -4 -2c", "P -4 -2ab", "P -4 -2n", "I -4 -2", "I -4 -2c", "I -4 2", "I -4 2bw", "-P 4 2", "-P 4 2c", "P 4 2 -1ab", "-P 4a 2b", "P 4 2 -1n", "-P 4a 2bc", "-P 4 2ab", "-P 4 2n", "P 4ab 2ab -1ab", "-P 4a 2a", "P 4ab 2n -1ab", "-P 4a 2ac", "-P 4c 2", "-P 4c 2c", "P 4n 2c -1n", "-P 4ac 2b", "P 4n 2 -1n", "-P 4ac 2bc", "-P 4c 2ab", "-P 4n 2n", "P 4n 2n -1n", "-P 4ac 2a", "P 4n 2ab -1n", "-P 4ac 2ac", "-I 4 2", "-I 4 2c", "I 4bw 2bw -1bw", "-I 4bd 2", "I 4bw 2aw -1bw", "-I 4bd 2c", "P 3", "P 31", "P 32", "R 3", "P 3*", "-P 3", "-R 3", "-P 3*", "P 3 2", "P 3 2=", "P 31 2c (0 0 1)", "P 31 2=", "P 32 2c (0 0 -1)", "P 32 2=", "R 3 2=", "P 3* 2", "P 3 -2=", "P 3 -2", "P 3 -2=c", "P 3 -2c", "R 3 -2=", "P 3* -2", "R 3 -2=c", "P 3* -2n", "-P 3 2", "-P 3 2c", "-P 3 2=", "-P 3 2=c", "-R 3 2=", "-P 3* 2", "-R 3 2=c", "-P 3* 2n", "P 6", "P 61", "P 65", "P 62", "P 64", "P 6c", "P -6", "-P 6", "-P 6c", "P 6 2", "P 61 2 (0 0 -1)", "P 65 2 (0 0 1)", "P 62 2c (0 0 1)", "P 64 2c (0 0 -1)", "P 6c 2c", "P 6 -2", "P 6 -2c", "P 6c -2", "P 6c -2c", "P -6 2", "P -6c 2", "P -6 -2", "P -6c -2c", "-P 6 2", "-P 6 2c", "-P 6c 2", "-P 6c 2c", "P 2 2 3", "F 2 2 3", "I 2 2 3", "P 2ac 2ab 3", "I 2b 2c 3", "-P 2 2 3", "P 2 2 3 -1n", "-P 2ab 2bc 3", "-F 2 2 3", "F 2 2 3 -1d", "-F 2uv 2vw 3", "-I 2 2 3", "-P 2ac 2ab 3", "-I 2b 2c 3", "P 4 2 3", "P 4n 2 3", "F 4 2 3", "F 4d 2 3", "I 4 2 3", "P 4acd 2ab 3", "P 4bd 2ab 3", "I 4bd 2c 3", "P -4 2 3", "F -4 2 3", "I -4 2 3", "P -4n 2 3", "F -4c 2 3", "I -4bd 2c 3", "-P 4 2 3", "P 4 2 3 -1n", "-P 4a 2bc 3", "-P 4n 2 3", "P 4n 2 3 -1n", "-P 4bc 2bc 3", "-F 4 2 3", "-F 4c 2 3", "F 4d 2 3 -1d", "-F 4vw 2vw 3", "F 4d 2 3 -1cd", "-F 4cvw 2vw 3", "-I 4 2 3", "-I 4bd 2c 3" }; const char* space_group_international[] = { "", "P 1", "P -1", "P 2 = P 1 2 1", "P 2 = P 1 1 2", "P 2 = P 2 1 1", "P 2_1 = P 1 2_1 1", "P 2_1 = P 1 1 2_1", "P 2_1 = P 2_1 1 1", "C 2 = C 1 2 1", "C 2 = A 1 2 1", "C 2 = I 1 2 1", "C 2 = A 1 1 2", "C 2 = B 1 1 2 = B 2", "C 2 = I 1 1 2", "C 2 = B 2 1 1", "C 2 = C 2 1 1", "C 2 = I 2 1 1", "P m = P 1 m 1", "P m = P 1 1 m", "P m = P m 1 1", "P c = P 1 c 1", "P c = P 1 n 1", "P c = P 1 a 1", "P c = P 1 1 a", "P c = P 1 1 n", "P c = P 1 1 b = P b", "P c = P b 1 1", "P c = P n 1 1", "P c = P c 1 1", "C m = C 1 m 1", "C m = A 1 m 1", "C m = I 1 m 1", "C m = A 1 1 m", "C m = B 1 1 m = B m", "C m = I 1 1 m", "C m = B m 1 1", "C m = C m 1 1", "C m = I m 1 1", "C c = C 1 c 1", "C c = A 1 n 1", "C c = I 1 a 1", "C c = A 1 a 1", "C c = C 1 n 1", "C c = I 1 c 1", "C c = A 1 1 a", "C c = B 1 1 n", "C c = I 1 1 b", "C c = B 1 1 b = B b", "C c = A 1 1 n", "C c = I 1 1 a", "C c = B b 1 1", "C c = C n 1 1", "C c = I c 1 1", "C c = C c 1 1", "C c = B n 1 1", "C c = I b 1 1", "P 2/m = P 1 2/m 1", "P 2/m = P 1 1 2/m", "P 2/m = P 2/m 1 1", "P 2_1/m = P 1 2_1/m 1", "P 2_1/m = P 1 1 2_1/m", "P 2_1/m = P 2_1/m 1 1", "C 2/m = C 1 2/m 1", "C 2/m = A 1 2/m 1", "C 2/m = I 1 2/m 1", "C 2/m = A 1 1 2/m", "C 2/m = B 1 1 2/m = B 2/m", "C 2/m = I 1 1 2/m", "C 2/m = B 2/m 1 1", "C 2/m = C 2/m 1 1", "C 2/m = I 2/m 1 1", "P 2/c = P 1 2/c 1", "P 2/c = P 1 2/n 1", "P 2/c = P 1 2/a 1", "P 2/c = P 1 1 2/a", "P 2/c = P 1 1 2/n", "P 2/c = P 1 1 2/b = P 2/b", "P 2/c = P 2/b 1 1", "P 2/c = P 2/n 1 1", "P 2/c = P 2/c 1 1", "P 2_1/c = P 1 2_1/c 1", "P 2_1/c = P 1 2_1/n 1", "P 2_1/c = P 1 2_1/a 1", "P 2_1/c = P 1 1 2_1/a", "P 2_1/c = P 1 1 2_1/n", "P 2_1/c = P 1 1 2_1/b = P 2_1/b", "P 2_1/c = P 2_1/b 1 1", "P 2_1/c = P 2_1/n 1 1", "P 2_1/c = P 2_1/c 1 1", "C 2/c = C 1 2/c 1", "C 2/c = A 1 2/n 1", "C 2/c = I 1 2/a 1", "C 2/c = A 1 2/a 1", "C 2/c = C 1 2/n 1", "C 2/c = I 1 2/c 1", "C 2/c = A 1 1 2/a", "C 2/c = B 1 1 2/n", "C 2/c = I 1 1 2/b", "C 2/c = B 1 1 2/b = B 2/b", "C 2/c = A 1 1 2/n", "C 2/c = I 1 1 2/a", "C 2/c = B 2/b 1 1", "C 2/c = C 2/n 1 1", "C 2/c = I 2/c 1 1", "C 2/c = C 2/c 1 1", "C 2/c = B 2/n 1 1", "C 2/c = I 2/b 1 1", "P 2 2 2", "P 2 2 2_1", "P 2_1 2 2", "P 2 2_1 2", "P 2_1 2_1 2", "P 2 2_1 2_1", "P 2_1 2 2_1", "P 2_1 2_1 2_1", "C 2 2 2_1", "A 2_1 2 2", "B 2 2_1 2", "C 2 2 2", "A 2 2 2", "B 2 2 2", "F 2 2 2", "I 2 2 2", "I 2_1 2_1 2_1", "P m m 2", "P 2 m m", "P m 2 m", "P m c 2_1", "P c m 2_1", "P 2_1 m a", "P 2_1 a m", "P b 2_1 m", "P m 2_1 b", "P c c 2", "P 2 a a", "P b 2 b", "P m a 2", "P b m 2", "P 2 m b", "P 2 c m", "P c 2 m", "P m 2 a", "P c a 2_1", "P b c 2_1", "P 2_1 a b", "P 2_1 c a", "P c 2_1 b", "P b 2_1 a", "P n c 2", "P c n 2", "P 2 n a", "P 2 a n", "P b 2 n", "P n 2 b", "P m n 2_1", "P n m 2_1", "P 2_1 m n", "P 2_1 n m", "P n 2_1 m", "P m 2_1 n", "P b a 2", "P 2 c b", "P c 2 a", "P n a 2_1", "P b n 2_1", "P 2_1 n b", "P 2_1 c n", "P c 2_1 n", "P n 2_1 a", "P n n 2", "P 2 n n", "P n 2 n", "C m m 2", "A 2 m m", "B m 2 m", "C m c 2_1", "C c m 2_1", "A 2_1 m a", "A 2_1 a m", "B b 2_1 m", "B m 2_1 b", "C c c 2", "A 2 a a", "B b 2 b", "A m m 2", "B m m 2", "B 2 m m", "C 2 m m", "C m 2 m", "A m 2 m", "A e m 2", "B m e 2", "B 2 e m", "C 2 m e", "C m 2 e", "A e 2 m", "A m a 2", "B b m 2", "B 2 m b", "C 2 c m", "C c 2 m", "A m 2 a", "A e a 2", "B b e 2", "B 2 e b", "C 2 c e", "C c 2 e", "A e 2 a", "F m m 2", "F 2 m m", "F m 2 m", "F d d 2", "F 2 d d", "F d 2 d", "I m m 2", "I 2 m m", "I m 2 m", "I b a 2", "I 2 c b", "I c 2 a", "I m a 2", "I b m 2", "I 2 m b", "I 2 c m", "I c 2 m", "I m 2 a", "P m m m", "P n n n", "P n n n", "P c c m", "P m a a", "P b m b", "P b a n", "P b a n", "P n c b", "P n c b", "P c n a", "P c n a", "P m m a", "P m m b", "P b m m", "P c m m", "P m c m", "P m a m", "P n n a", "P n n b", "P b n n", "P c n n", "P n c n", "P n a n", "P m n a", "P n m b", "P b m n", "P c n m", "P n c m", "P m a n", "P c c a", "P c c b", "P b a a", "P c a a", "P b c b", "P b a b", "P b a m", "P m c b", "P c m a", "P c c n", "P n a a", "P b n b", "P b c m", "P c a m", "P m c a", "P m a b", "P b m a", "P c m b", "P n n m", "P m n n", "P n m n", "P m m n", "P m m n", "P n m m", "P n m m", "P m n m", "P m n m", "P b c n", "P c a n", "P n c a", "P n a b", "P b n a", "P c n b", "P b c a", "P c a b", "P n m a", "P m n b", "P b n m", "P c m n", "P m c n", "P n a m", "C m c m", "C c m m", "A m m a", "A m a m", "B b m m", "B m m b", "C m c e", "C c m e", "A e m a", "A e a m", "B b e m", "B m e b", "C m m m", "A m m m", "B m m m", "C c c m", "A m a a", "B b m b", "C m m e", "C m m e", "A e m m", "A e m m", "B m e m", "B m e m", "C c c e", "C c c e", "C c c e", "C c c e", "A e a a", "A e a a", "A e a a", "A e a a", "B b e b", "B b c b", "B b e b", "B b e b", "F m m m", "F d d d", "F d d d", "I m m m", "I b a m", "I m c b", "I c m a", "I b c a", "I c a b", "I m m a", "I m m b", "I b m m", "I c m m", "I m c m", "I m a m", "P 4", "P 4_1", "P 4_2", "P 4_3", "I 4", "I 4_1", "P -4", "I -4", "P 4/m", "P 4_2/m", "P 4/n", "P 4/n", "P 4_2/n", "P 4_2/n", "I 4/m", "I 4_1/a", "I 4_1/a", "P 4 2 2", "P 4 2_1 2", "P 4_1 2 2", "P 4_1 2_1 2", "P 4_2 2 2", "P 4_2 2_1 2", "P 4_3 2 2", "P 4_3 2_1 2", "I 4 2 2", "I 4_1 2 2", "P 4 m m", "P 4 b m", "P 4_2 c m", "P 4_2 n m", "P 4 c c", "P 4 n c", "P 4_2 m c", "P 4_2 b c", "I 4 m m", "I 4 c m", "I 4_1 m d", "I 4_1 c d", "P -4 2 m", "P -4 2 c", "P -4 2_1 m", "P -4 2_1 c", "P -4 m 2", "P -4 c 2", "P -4 b 2", "P -4 n 2", "I -4 m 2", "I -4 c 2", "I -4 2 m", "I -4 2 d", "P 4/m m m", "P 4/m c c", "P 4/n b m", "P 4/n b m", "P 4/n n c", "P 4/n n c", "P 4/m b m", "P 4/m n c", "P 4/n m m", "P 4/n m m", "P 4/n c c", "P 4/n c c", "P 4_2/m m c", "P 4_2/m c m", "P 4_2/n b c", "P 4_2/n b c", "P 4_2/n n m", "P 4_2/n n m", "P 4_2/m b c", "P 4_2/m n m", "P 4_2/n m c", "P 4_2/n m c", "P 4_2/n c m", "P 4_2/n c m", "I 4/m m m", "I 4/m c m", "I 4_1/a m d", "I 4_1/a m d", "I 4_1/a c d", "I 4_1/a c d", "P 3", "P 3_1", "P 3_2", "R 3", "R 3", "P -3", "R -3", "R -3", "P 3 1 2", "P 3 2 1", "P 3_1 1 2", "P 3_1 2 1", "P 3_2 1 2", "P 3_2 2 1", "R 3 2", "R 3 2", "P 3 m 1", "P 3 1 m", "P 3 c 1", "P 3 1 c", "R 3 m", "R 3 m", "R 3 c", "R 3 c", "P -3 1 m", "P -3 1 c", "P -3 m 1", "P -3 c 1", "R -3 m", "R -3 m", "R -3 c", "R -3 c", "P 6", "P 6_1", "P 6_5", "P 6_2", "P 6_4", "P 6_3", "P -6", "P 6/m", "P 6_3/m", "P 6 2 2", "P 6_1 2 2", "P 6_5 2 2", "P 6_2 2 2", "P 6_4 2 2", "P 6_3 2 2", "P 6 m m", "P 6 c c", "P 6_3 c m", "P 6_3 m c", "P -6 m 2", "P -6 c 2", "P -6 2 m", "P -6 2 c", "P 6/m m m", "P 6/m c c", "P 6_3/m c m", "P 6_3/m m c", "P 2 3", "F 2 3", "I 2 3", "P 2_1 3", "I 2_1 3", "P m 3", "P n 3", "P n 3", "F m 3", "F d 3", "F d 3", "I m 3", "P a 3", "I a 3", "P 4 3 2", "P 4_2 3 2", "F 4 3 2", "F 4_1 3 2", "I 4 3 2", "P 4_3 3 2", "P 4_1 3 2", "I 4_1 3 2", "P -4 3 m", "F -4 3 m", "I -4 3 m", "P -4 3 n", "F -4 3 c", "I -4 3 d", "P m -3 m", "P n -3 n", "P n -3 n", "P m -3 n", "P n -3 m", "P n -3 m", "F m -3 m", "F m -3 c", "F d -3 m", "F d -3 m", "F d -3 c", "F d -3 c", "I m -3 m", "I a -3 d" }; const char* space_group_international_full[] = { "", "P 1", "P -1", "P 1 2 1", "P 1 1 2", "P 2 1 1", "P 1 2_1 1", "P 1 1 2_1", "P 2_1 1 1", "C 1 2 1", "A 1 2 1", "I 1 2 1", "A 1 1 2", "B 1 1 2", "I 1 1 2", "B 2 1 1", "C 2 1 1", "I 2 1 1", "P 1 m 1", "P 1 1 m", "P m 1 1", "P 1 c 1", "P 1 n 1", "P 1 a 1", "P 1 1 a", "P 1 1 n", "P 1 1 b", "P b 1 1", "P n 1 1", "P c 1 1", "C 1 m 1", "A 1 m 1", "I 1 m 1", "A 1 1 m", "B 1 1 m", "I 1 1 m", "B m 1 1", "C m 1 1", "I m 1 1", "C 1 c 1", "A 1 n 1", "I 1 a 1", "A 1 a 1", "C 1 n 1", "I 1 c 1", "A 1 1 a", "B 1 1 n", "I 1 1 b", "B 1 1 b", "A 1 1 n", "I 1 1 a", "B b 1 1", "C n 1 1", "I c 1 1", "C c 1 1", "B n 1 1", "I b 1 1", "P 1 2/m 1", "P 1 1 2/m", "P 2/m 1 1", "P 1 2_1/m 1", "P 1 1 2_1/m", "P 2_1/m 1 1", "C 1 2/m 1", "A 1 2/m 1", "I 1 2/m 1", "A 1 1 2/m", "B 1 1 2/m", "I 1 1 2/m", "B 2/m 1 1", "C 2/m 1 1", "I 2/m 1 1", "P 1 2/c 1", "P 1 2/n 1", "P 1 2/a 1", "P 1 1 2/a", "P 1 1 2/n", "P 1 1 2/b", "P 2/b 1 1", "P 2/n 1 1", "P 2/c 1 1", "P 1 2_1/c 1", "P 1 2_1/n 1", "P 1 2_1/a 1", "P 1 1 2_1/a", "P 1 1 2_1/n", "P 1 1 2_1/b", "P 2_1/b 1 1", "P 2_1/n 1 1", "P 2_1/c 1 1", "C 1 2/c 1", "A 1 2/n 1", "I 1 2/a 1", "A 1 2/a 1", "C 1 2/n 1", "I 1 2/c 1", "A 1 1 2/a", "B 1 1 2/n", "I 1 1 2/b", "B 1 1 2/b", "A 1 1 2/n", "I 1 1 2/a", "B 2/b 1 1", "C 2/n 1 1", "I 2/c 1 1", "C 2/c 1 1", "B 2/n 1 1", "I 2/b 1 1", "P 2 2 2", "P 2 2 2_1", "P 2_1 2 2", "P 2 2_1 2", "P 2_1 2_1 2", "P 2 2_1 2_1", "P 2_1 2 2_1", "P 2_1 2_1 2_1", "C 2 2 2_1", "A 2_1 2 2", "B 2 2_1 2", "C 2 2 2", "A 2 2 2", "B 2 2 2", "F 2 2 2", "I 2 2 2", "I 2_1 2_1 2_1", "P m m 2", "P 2 m m", "P m 2 m", "P m c 2_1", "P c m 2_1", "P 2_1 m a", "P 2_1 a m", "P b 2_1 m", "P m 2_1 b", "P c c 2", "P 2 a a", "P b 2 b", "P m a 2", "P b m 2", "P 2 m b", "P 2 c m", "P c 2 m", "P m 2 a", "P c a 2_1", "P b c 2_1", "P 2_1 a b", "P 2_1 c a", "P c 2_1 b", "P b 2_1 a", "P n c 2", "P c n 2", "P 2 n a", "P 2 a n", "P b 2 n", "P n 2 b", "P m n 2_1", "P n m 2_1", "P 2_1 m n", "P 2_1 n m", "P n 2_1 m", "P m 2_1 n", "P b a 2", "P 2 c b", "P c 2 a", "P n a 2_1", "P b n 2_1", "P 2_1 n b", "P 2_1 c n", "P c 2_1 n", "P n 2_1 a", "P n n 2", "P 2 n n", "P n 2 n", "C m m 2", "A 2 m m", "B m 2 m", "C m c 2_1", "C c m 2_1", "A 2_1 m a", "A 2_1 a m", "B b 2_1 m", "B m 2_1 b", "C c c 2", "A 2 a a", "B b 2 b", "A m m 2", "B m m 2", "B 2 m m", "C 2 m m", "C m 2 m", "A m 2 m", "A e m 2", "B m e 2", "B 2 e m", "C 2 m e", "C m 2 e", "A e 2 m", "A m a 2", "B b m 2", "B 2 m b", "C 2 c m", "C c 2 m", "A m 2 a", "A e a 2", "B b e 2", "B 2 e b", "C 2 c e", "C c 2 e", "A e 2 a", "F m m 2", "F 2 m m", "F m 2 m", "F d d 2", "F 2 d d", "F d 2 d", "I m m 2", "I 2 m m", "I m 2 m", "I b a 2", "I 2 c b", "I c 2 a", "I m a 2", "I b m 2", "I 2 m b", "I 2 c m", "I c 2 m", "I m 2 a", "P 2/m 2/m 2/m", "P 2/n 2/n 2/n", "P 2/n 2/n 2/n", "P 2/c 2/c 2/m", "P 2/m 2/a 2/a", "P 2/b 2/m 2/b", "P 2/b 2/a 2/n", "P 2/b 2/a 2/n", "P 2/n 2/c 2/b", "P 2/n 2/c 2/b", "P 2/c 2/n 2/a", "P 2/c 2/n 2/a", "P 2_1/m 2/m 2/a", "P 2/m 2_1/m 2/b", "P 2/b 2_1/m 2/m", "P 2/c 2/m 2_1/m", "P 2/m 2/c 2_1/m", "P 2_1/m 2/a 2/m", "P 2/n 2_1/n 2/a", "P 2_1/n 2/n 2/b", "P 2/b 2/n 2_1/n", "P 2/c 2_1/n 2/n", "P 2_1/n 2/c 2/n", "P 2/n 2/a 2_1/n", "P 2/m 2/n 2_1/a", "P 2/n 2/m 2_1/b", "P 2_1/b 2/m 2/n", "P 2_1/c 2/n 2/m", "P 2/n 2_1/c 2/m", "P 2/m 2_1/a 2/n", "P 2_1/c 2/c 2/a", "P 2/c 2_1/c 2/b", "P 2/b 2_1/a 2/a", "P 2/c 2/a 2_1/a", "P 2/b 2/c 2_1/b", "P 2_1/b 2/a 2/b", "P 2_1/b 2_1/a 2/m", "P 2/m 2_1/c 2_1/b", "P 2_1/c 2/m 2_1/a", "P 2_1/c 2_1/c 2/n", "P 2/n 2_1/a 2_1/a", "P 2_1/b 2/n 2_1/b", "P 2/b 2_1/c 2_1/m", "P 2_1/c 2/a 2_1/m", "P 2_1/m 2/c 2_1/a", "P 2_1/m 2_1/a 2/b", "P 2_1/b 2_1/m 2/a", "P 2/c 2_1/m 2_1/b", "P 2_1/n 2_1/n 2/m", "P 2/m 2_1/n 2_1/n", "P 2_1/n 2/m 2_1/n", "P 2_1/m 2_1/m 2/n", "P 2_1/m 2_1/m 2/n", "P 2/n 2_1/m 2_1/m", "P 2/n 2_1/m 2_1/m", "P 2_1/m 2/n 2_1/m", "P 2_1/m 2/n 2_1/m", "P 2_1/b 2/c 2_1/n", "P 2/c 2_1/a 2_1/n", "P 2_1/n 2_1/c 2/a", "P 2_1/n 2/a 2_1/b", "P 2/b 2_1/n 2_1/a", "P 2_1/c 2_1/n 2/b", "P 2_1/b 2_1/c 2_1/a", "P 2_1/c 2_1/a 2_1/b", "P 2_1/n 2_1/m 2_1/a", "P 2_1/m 2_1/n 2_1/b", "P 2_1/b 2_1/n 2_1/m", "P 2_1/c 2_1/m 2_1/n", "P 2_1/m 2_1/c 2_1/n", "P 2_1/n 2_1/a 2_1/m", "C 2/m 2/c 2_1/m", "C 2/c 2/m 2_1/m", "A 2_1/m 2/m 2/a", "A 2_1/m 2/a 2/m", "B 2/b 2_1/m 2/m", "B 2/m 2_1/m 2/b", "C 2/m 2/c 2_1/e", "C 2/c 2/m 2_1/e", "A 2_1/e 2/m 2/a", "A 2_1/e 2/a 2/m", "B 2/b 2_1/e 2/m", "B 2/m 2_1/e 2/b", "C 2/m 2/m 2/m", "A 2/m 2/m 2/m", "B 2/m 2/m 2/m", "C 2/c 2/c 2/m", "A 2/m 2/a 2/a", "B 2/b 2/m 2/b", "C 2/m 2/m 2/e", "C 2/m 2/m 2/e", "A 2/e 2/m 2/m", "A 2/e 2/m 2/m", "B 2/m 2/e 2/m", "B 2/m 2/e 2/m", "C 2/c 2/c 2/e", "C 2/c 2/c 2/e", "C 2/c 2/c 2/e", "C 2/c 2/c 2/e", "A 2/e 2/a 2/a", "A 2/e 2/a 2/a", "A 2/e 2/a 2/a", "A 2/e 2/a 2/a", "B 2/b 2/e 2/b", "B 2/b 2/e 2/b", "B 2/b 2/e 2/b", "B 2/b 2/e 2/b", "F 2/m 2/m 2/m", "F 2/d 2/d 2/d", "F 2/d 2/d 2/d", "I 2/m 2/m 2/m", "I 2/b 2/a 2/m", "I 2/m 2/c 2/b", "I 2/c 2/m 2/a", "I 2/b 2/c 2/a", "I 2/c 2/a 2/b", "I 2/m 2/m 2/a", "I 2/m 2/m 2/b", "I 2/b 2/m 2/m", "I 2/c 2/m 2/m", "I 2/m 2/c 2/m", "I 2/m 2/a 2/m", "P 4", "P 4_1", "P 4_2", "P 4_3", "I 4", "I 4_1", "P -4", "I -4", "P 4/m", "P 4_2/m", "P 4/n", "P 4/n", "P 4_2/n", "P 4_2/n", "I 4/m", "I 4_1/a", "I 4_1/a", "P 4 2 2", "P 4 2_1 2", "P 4_1 2 2", "P 4_1 2_1 2", "P 4_2 2 2", "P 4_2 2_1 2", "P 4_3 2 2", "P 4_3 2_1 2", "I 4 2 2", "I 4_1 2 2", "P 4 m m", "P 4 b m", "P 4_2 c m", "P 4_2 n m", "P 4 c c", "P 4 n c", "P 4_2 m c", "P 4_2 b c", "I 4 m m", "I 4 c m", "I 4_1 m d", "I 4_1 c d", "P -4 2 m", "P -4 2 c", "P -4 2_1 m", "P -4 2_1 c", "P -4 m 2", "P -4 c 2", "P -4 b 2", "P -4 n 2", "I -4 m 2", "I -4 c 2", "I -4 2 m", "I -4 2 d", "P 4/m 2/m 2/m", "P 4/m 2/c 2/c", "P 4/n 2/b 2/m", "P 4/n 2/b 2/m", "P 4/n 2/n 2/c", "P 4/n 2/n 2/c", "P 4/m 2_1/b m", "P 4/m 2_1/n c", "P 4/n 2_1/m m", "P 4/n 2_1/m m", "P 4/n 2_1/c c", "P 4/n 2_1/c c", "P 4_2/m 2/m 2/c", "P 4_2/m 2/c 2/m", "P 4_2/n 2/b 2/c", "P 4_2/n 2/b 2/c", "P 4_2/n 2/n 2/m", "P 4_2/n 2/n 2/m", "P 4_2/m 2_1/b 2/c", "P 4_2/m 2_1/n 2/m", "P 4_2/n 2_1/m 2/c", "P 4_2/n 2_1/m 2/c", "P 4_2/n 2_1/c 2/m", "P 4_2/n 2_1/c 2/m", "I 4/m 2/m 2/m", "I 4/m 2/c 2/m", "I 4_1/a 2/m 2/d", "I 4_1/a 2/m 2/d", "I 4_1/a 2/c 2/d", "I 4_1/a 2/c 2/d", "P 3", "P 3_1", "P 3_2", "R 3", "R 3", "P -3", "R -3", "R -3", "P 3 1 2", "P 3 2 1", "P 3_1 1 2", "P 3_1 2 1", "P 3_2 1 2", "P 3_2 2 1", "R 3 2", "R 3 2", "P 3 m 1", "P 3 1 m", "P 3 c 1", "P 3 1 c", "R 3 m", "R 3 m", "R 3 c", "R 3 c", "P -3 1 2/m", "P -3 1 2/c", "P -3 2/m 1", "P -3 2/c 1", "R -3 2/m", "R -3 2/m", "R -3 2/c", "R -3 2/c", "P 6", "P 6_1", "P 6_5", "P 6_2", "P 6_4", "P 6_3", "P -6", "P 6/m", "P 6_3/m", "P 6 2 2", "P 6_1 2 2", "P 6_5 2 2", "P 6_2 2 2", "P 6_4 2 2", "P 6_3 2 2", "P 6 m m", "P 6 c c", "P 6_3 c m", "P 6_3 m c", "P -6 m 2", "P -6 c 2", "P -6 2 m", "P -6 2 c", "P 6/m 2/m 2/m", "P 6/m 2/c 2/c", "P 6_3/m 2/c 2/m", "P 6_3/m 2/m 2/c", "P 2 3", "F 2 3", "I 2 3", "P 2_1 3", "I 2_1 3", "P 2/m -3", "P 2/n -3", "P 2/n -3", "F 2/m -3", "F 2/d -3", "F 2/d -3", "I 2/m -3", "P 2_1/a -3", "I 2_1/a -3", "P 4 3 2", "P 4_2 3 2", "F 4 3 2", "F 4_1 3 2", "I 4 3 2", "P 4_3 3 2", "P 4_1 3 2", "I 4_1 3 2", "P -4 3 m", "F -4 3 m", "I -4 3 m", "P -4 3 n", "F -4 3 c", "I -4 3 d", "P 4/m -3 2/m", "P 4/n -3 2/n", "P 4/n -3 2/n", "P 4_2/m -3 2/n", "P 4_2/n -3 2/m", "P 4_2/n -3 2/m", "F 4/m -3 2/m", "F 4/m -3 2/c", "F 4_1/d -3 2/m", "F 4_1/d -3 2/m", "F 4_1/d -3 2/c", "F 4_1/d -3 2/c", "I 4/m -3 2/m", "I 4_1/a -3 2/d" }; const char* space_group_international_short[] = { "", "P1", "P-1", "P2", "P2", "P2", "P2_1", "P2_1", "P2_1", "C2", "C2", "C2", "C2", "C2", "C2", "C2", "C2", "C2", "Pm", "Pm", "Pm", "Pc", "Pc", "Pc", "Pc", "Pc", "Pc", "Pc", "Pc", "Pc", "Cm", "Cm", "Cm", "Cm", "Cm", "Cm", "Cm", "Cm", "Cm", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "Cc", "P2/m", "P2/m", "P2/m", "P2_1/m", "P2_1/m", "P2_1/m", "C2/m", "C2/m", "C2/m", "C2/m", "C2/m", "C2/m", "C2/m", "C2/m", "C2/m", "P2/c", "P2/c", "P2/c", "P2/c", "P2/c", "P2/c", "P2/c", "P2/c", "P2/c", "P2_1/c", "P2_1/c", "P2_1/c", "P2_1/c", "P2_1/c", "P2_1/c", "P2_1/c", "P2_1/c", "P2_1/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "C2/c", "P222", "P222_1", "P2_122", "P22_12", "P2_12_12", "P22_12_1", "P2_122_1", "P2_12_12_1", "C222_1", "A2_122", "B22_12", "C222", "A222", "B222", "F222", "I222", "I2_12_12_1", "Pmm2", "P2mm", "Pm2m", "Pmc2_1", "Pcm2_1", "P2_1ma", "P2_1am", "Pb2_1m", "Pm2_1b", "Pcc2", "P2aa", "Pb2b", "Pma2", "Pbm2", "P2mb", "P2cm", "Pc2m", "Pm2a", "Pca2_1", "Pbc2_1", "P2_1ab", "P2_1ca", "Pc2_1b", "Pb2_1a", "Pnc2", "Pcn2", "P2na", "P2an", "Pb2n", "Pn2b", "Pmn2_1", "Pnm2_1", "P2_1mn", "P2_1nm", "Pn2_1m", "Pm2_1n", "Pba2", "P2cb", "Pc2a", "Pna2_1", "Pbn2_1", "P2_1nb", "P2_1cn", "Pc2_1n", "Pn2_1a", "Pnn2", "P2nn", "Pn2n", "Cmm2", "A2mm", "Bm2m", "Cmc2_1", "Ccm2_1", "A2_1ma", "A2_1am", "Bb2_1m", "Bm2_1b", "Ccc2", "A2aa", "Bb2b", "Amm2", "Bmm2", "B2mm", "C2mm", "Cm2m", "Am2m", "Aem2", "Bme2", "B2em", "C2me", "Cm2e", "Ae2m", "Ama2", "Bbm2", "B2mb", "C2cm", "Cc2m", "Am2a", "Aea2", "Bbe2", "B2eb", "C2ce", "Cc2e", "Ae2a", "Fmm2", "F2mm", "Fm2m", "Fdd2", "F2dd", "Fd2d", "Imm2", "I2mm", "Im2m", "Iba2", "I2cb", "Ic2a", "Ima2", "Ibm2", "I2mb", "I2cm", "Ic2m", "Im2a", "Pmmm", "Pnnn", "Pnnn", "Pccm", "Pmaa", "Pbmb", "Pban", "Pban", "Pncb", "Pncb", "Pcna", "Pcna", "Pmma", "Pmmb", "Pbmm", "Pcmm", "Pmcm", "Pmam", "Pnna", "Pnnb", "Pbnn", "Pcnn", "Pncn", "Pnan", "Pmna", "Pnmb", "Pbmn", "Pcnm", "Pncm", "Pman", "Pcca", "Pccb", "Pbaa", "Pcaa", "Pbcb", "Pbab", "Pbam", "Pmcb", "Pcma", "Pccn", "Pnaa", "Pbnb", "Pbcm", "Pcam", "Pmca", "Pmab", "Pbma", "Pcmb", "Pnnm", "Pmnn", "Pnmn", "Pmmn", "Pmmn", "Pnmm", "Pnmm", "Pmnm", "Pmnm", "Pbcn", "Pcan", "Pnca", "Pnab", "Pbna", "Pcnb", "Pbca", "Pcab", "Pnma", "Pmnb", "Pbnm", "Pcmn", "Pmcn", "Pnam", "Cmcm", "Ccmm", "Amma", "Amam", "Bbmm", "Bmmb", "Cmce", "Ccme", "Aema", "Aeam", "Bbem", "Bmeb", "Cmmm", "Ammm", "Bmmm", "Cccm", "Amaa", "Bbmb", "Cmme", "Cmme", "Aemm", "Aemm", "Bmem", "Bmem", "Ccce", "Ccce", "Ccce", "Ccce", "Aeaa", "Aeaa", "Aeaa", "Aeaa", "Bbeb", "Bbcb", "Bbeb", "Bbeb", "Fmmm", "Fddd", "Fddd", "Immm", "Ibam", "Imcb", "Icma", "Ibca", "Icab", "Imma", "Immb", "Ibmm", "Icmm", "Imcm", "Imam", "P4", "P4_1", "P4_2", "P4_3", "I4", "I4_1", "P-4", "I-4", "P4/m", "P4_2/m", "P4/n", "P4/n", "P4_2/n", "P4_2/n", "I4/m", "I4_1/a", "I4_1/a", "P422", "P42_12", "P4_122", "P4_12_12", "P4_222", "P4_22_12", "P4_322", "P4_32_12", "I422", "I4_122", "P4mm", "P4bm", "P4_2cm", "P4_2nm", "P4cc", "P4nc", "P4_2mc", "P4_2bc", "I4mm", "I4cm", "I4_1md", "I4_1cd", "P-42m", "P-42c", "P-42_1m", "P-42_1c", "P-4m2", "P-4c2", "P-4b2", "P-4n2", "I-4m2", "I-4c2", "I-42m", "I-42d", "P4/mmm", "P4/mcc", "P4/nbm", "P4/nbm", "P4/nnc", "P4/nnc", "P4/mbm", "P4/mnc", "P4/nmm", "P4/nmm", "P4/ncc", "P4/ncc", "P4_2/mmc", "P4_2/mcm", "P4_2/nbc", "P4_2/nbc", "P4_2/nnm", "P4_2/nnm", "P4_2/mbc", "P4_2/mnm", "P4_2/nmc", "P4_2/nmc", "P4_2/ncm", "P4_2/ncm", "I4/mmm", "I4/mcm", "I4_1/amd", "I4_1/amd", "I4_1/acd", "I4_1/acd", "P3", "P3_1", "P3_2", "R3", "R3", "P-3", "R-3", "R-3", "P312", "P321", "P3_112", "P3_121", "P3_212", "P3_221", "R32", "R32", "P3m1", "P31m", "P3c1", "P31c", "R3m", "R3m", "R3c", "R3c", "P-31m", "P-31c", "P-3m1", "P-3c1", "R-3m", "R-3m", "R-3c", "R-3c", "P6", "P6_1", "P6_5", "P6_2", "P6_4", "P6_3", "P-6", "P6/m", "P6_3/m", "P622", "P6_122", "P6_522", "P6_222", "P6_422", "P6_322", "P6mm", "P6cc", "P6_3cm", "P6_3mc", "P-6m2", "P-6c2", "P-62m", "P-62c", "P6/mmm", "P6/mcc", "P6_3/mcm", "P6_3/mmc", "P23", "F23", "I23", "P2_13", "I2_13", "Pm3", "Pn3", "Pn3", "Fm3", "Fd3", "Fd3", "Im3", "Pa3", "Ia3", "P432", "P4_232", "F432", "F4_132", "I432", "P4_332", "P4_132", "I4_132", "P-43m", "F-43m", "I-43m", "P-43n", "F-43c", "I-43d", "Pm-3m", "Pn-3n", "Pn-3n", "Pm-3n", "Pn-3m", "Pn-3m", "Fm-3m", "Fm-3c", "Fd-3m", "Fd-3m", "Fd-3c", "Fd-3c", "Im-3m", "Ia-3d" }; const char* space_group_setting[] = { "", "", "", "b", "c", "a", "b", "c", "a", "b1", "b2", "b3", "c1", "c2", "c3", "a1", "a2", "a3", "b", "c", "a", "b1", "b2", "b3", "c1", "c2", "c3", "a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3", "a1", "a2", "a3", "b1", "b2", "b3", "-b1", "-b2", "-b3", "c1", "c2", "c3", "-c1", "-c2", "-c3", "a1", "a2", "a3", "-a1", "-a2", "-a3", "b", "c", "a", "b", "c", "a", "b1", "b2", "b3", "c1", "c2", "c3", "a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3", "a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3", "a1", "a2", "a3", "b1", "b2", "b3", "-b1", "-b2", "-b3", "c1", "c2", "c3", "-c1", "-c2", "-c3", "a1", "a2", "a3", "-a1", "-a2", "-a3", "", "", "cab", "bca", "", "cab", "bca", "", "", "cab", "bca", "", "cab", "bca", "", "", "", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "", "cab", "bca", "", "cab", "bca", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "1", "2", "", "cab", "bca", "1", "2", "1cab", "2cab", "1bca", "2bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "1", "2", "1cab", "2cab", "1bca", "2bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "cab", "bca", "", "cab", "bca", "", "ba-c", "cab", "-cba", "bca", "a-cb", "1", "2", "1ba-c", "2ba-c", "1cab", "2cab", "1-cba", "2-cba", "1bca", "2bca", "1a-cb", "2a-cb", "", "1", "2", "", "", "cab", "bca", "", "ba-c", "", "ba-c", "cab", "-cba", "bca", "a-cb", "", "", "", "", "", "", "", "", "", "", "1", "2", "1", "2", "", "1", "2", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "1", "2", "1", "2", "", "", "1", "2", "1", "2", "", "", "1", "2", "1", "2", "", "", "1", "2", "1", "2", "", "", "1", "2", "1", "2", "", "", "", "H", "R", "", "H", "R", "", "", "", "", "", "", "H", "R", "", "", "", "", "H", "R", "H", "R", "", "", "", "", "H", "R", "H", "R", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "1", "2", "", "1", "2", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "1", "2", "", "1", "2", "", "", "1", "2", "1", "2", "", "" }; const char* space_group_transforms[] = { "", "x,y,z", "x,y,z -x,-y,-z", "x,y,z -x,y,-z", "x,y,z -x,-y,z", "x,y,z x,-y,-z", "x,y,z -x,1/2+y,-z", "x,y,z -x,-y,1/2+z", "x,y,z 1/2+x,-y,-z", "x,y,z -x,y,-z 1/2+x,1/2+y,z 1/2-x,1/2+y,-z", "x,y,z -x,y,-z x,1/2+y,1/2+z -x,1/2+y,1/2-z", "x,y,z -x,y,-z 1/2+x,1/2+y,1/2+z 1/2-x,1/2+y,1/2-z", "x,y,z -x,-y,z x,1/2+y,1/2+z -x,1/2-y,1/2+z", "x,y,z -x,-y,z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z", "x,y,z -x,-y,z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z", "x,y,z x,-y,-z 1/2+x,y,1/2+z 1/2+x,-y,1/2-z", "x,y,z x,-y,-z 1/2+x,1/2+y,z 1/2+x,1/2-y,-z", "x,y,z x,-y,-z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z", "x,y,z x,-y,z", "x,y,z x,y,-z", "x,y,z -x,y,z", "x,y,z x,-y,1/2+z", "x,y,z 1/2+x,-y,1/2+z", "x,y,z 1/2+x,-y,z", "x,y,z 1/2+x,y,-z", "x,y,z 1/2+x,1/2+y,-z", "x,y,z x,1/2+y,-z", "x,y,z -x,1/2+y,z", "x,y,z -x,1/2+y,1/2+z", "x,y,z -x,y,1/2+z", "x,y,z x,-y,z 1/2+x,1/2+y,z 1/2+x,1/2-y,z", "x,y,z x,-y,z x,1/2+y,1/2+z x,1/2-y,1/2+z", "x,y,z x,-y,z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z", "x,y,z x,y,-z x,1/2+y,1/2+z x,1/2+y,1/2-z", "x,y,z x,y,-z 1/2+x,y,1/2+z 1/2+x,y,1/2-z", "x,y,z x,y,-z 1/2+x,1/2+y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,y,z 1/2+x,y,1/2+z 1/2-x,y,1/2+z", "x,y,z -x,y,z 1/2+x,1/2+y,z 1/2-x,1/2+y,z", "x,y,z -x,y,z 1/2+x,1/2+y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z x,-y,1/2+z 1/2+x,1/2+y,z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,1/2+z x,1/2+y,1/2+z 1/2+x,1/2-y,z", "x,y,z 1/2+x,-y,z 1/2+x,1/2+y,1/2+z x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,z x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,1/2+z 1/2+x,1/2+y,z x,1/2-y,1/2+z", "x,y,z x,-y,1/2+z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,z", "x,y,z 1/2+x,y,-z x,1/2+y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z x,1/2+y,1/2-z 1/2+x,y,1/2+z 1/2+x,1/2+y,-z", "x,y,z x,1/2+y,-z 1/2+x,1/2+y,1/2+z 1/2+x,y,1/2-z", "x,y,z x,1/2+y,-z 1/2+x,y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z 1/2+x,1/2+y,-z x,1/2+y,1/2+z 1/2+x,y,1/2-z", "x,y,z 1/2+x,y,-z 1/2+x,1/2+y,1/2+z x,1/2+y,1/2-z", "x,y,z -x,1/2+y,z 1/2+x,y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z -x,1/2+y,1/2+z 1/2+x,1/2+y,z 1/2-x,y,1/2+z", "x,y,z -x,y,1/2+z 1/2+x,1/2+y,1/2+z 1/2-x,1/2+y,z", "x,y,z -x,y,1/2+z 1/2+x,1/2+y,z 1/2-x,1/2+y,1/2+z", "x,y,z -x,1/2+y,1/2+z 1/2+x,y,1/2+z 1/2-x,1/2+y,z", "x,y,z -x,1/2+y,z 1/2+x,1/2+y,1/2+z 1/2-x,y,1/2+z", "x,y,z -x,y,-z -x,-y,-z x,-y,z", "x,y,z -x,-y,z -x,-y,-z x,y,-z", "x,y,z x,-y,-z -x,-y,-z -x,y,z", "x,y,z -x,1/2+y,-z -x,-y,-z x,1/2-y,z", "x,y,z -x,-y,1/2+z -x,-y,-z x,y,1/2-z", "x,y,z 1/2+x,-y,-z -x,-y,-z 1/2-x,y,z", "x,y,z -x,y,-z -x,-y,-z x,-y,z " "1/2+x,1/2+y,z 1/2-x,1/2+y,-z " "1/2-x,1/2-y,-z 1/2+x,1/2-y,z", "x,y,z -x,y,-z -x,-y,-z x,-y,z x,1/2+y,1/2+z -x,1/2+y,1/2-z -x,1/2-y,1/2-z " "x,1/2-y,1/2+z", "x,y,z -x,y,-z -x,-y,-z x,-y,z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2-z 1/2-x,1/2-y,1/2-z", "x,y,z -x,-y,z -x,-y,-z x,y,-z x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2-y,1/2-z " "x,1/2+y,1/2-z", "x,y,z -x,-y,z -x,-y,-z x,y,-z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,-y,1/2-z " "1/2+x,y,1/2-z", "x,y,z -x,-y,z -x,-y,-z x,y,-z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z", "x,y,z x,-y,-z -x,-y,-z -x,y,z 1/2+x,y,1/2+z 1/2+x,-y,1/2-z 1/2-x,-y,1/2-z " "1/2-x,y,1/2+z", "x,y,z x,-y,-z -x,-y,-z -x,y,z 1/2+x,1/2+y,z 1/2+x,1/2-y,-z 1/2-x,1/2-y,-z " "1/2-x,1/2+y,z", "x,y,z x,-y,-z -x,-y,-z -x,y,z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2-x,1/2+y,1/2+z", "x,y,z -x,y,1/2-z -x,-y,-z x,-y,1/2+z", "x,y,z 1/2-x,y,1/2-z -x,-y,-z 1/2+x,-y,1/2+z", "x,y,z 1/2-x,y,-z -x,-y,-z 1/2+x,-y,z", "x,y,z 1/2-x,-y,z -x,-y,-z 1/2+x,y,-z", "x,y,z 1/2-x,1/2-y,z -x,-y,-z 1/2+x,1/2+y,-z", "x,y,z -x,1/2-y,z -x,-y,-z x,1/2+y,-z", "x,y,z x,1/2-y,-z -x,-y,-z -x,1/2+y,z", "x,y,z x,1/2-y,1/2-z -x,-y,-z -x,1/2+y,1/2+z", "x,y,z x,-y,1/2-z -x,-y,-z -x,y,1/2+z", "x,y,z -x,1/2+y,1/2-z -x,-y,-z x,1/2-y,1/2+z", "x,y,z 1/2-x,1/2+y,1/2-z -x,-y,-z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2-x,1/2+y,-z -x,-y,-z 1/2+x,1/2-y,z", "x,y,z 1/2-x,-y,1/2+z -x,-y,-z 1/2+x,y,1/2-z", "x,y,z 1/2-x,1/2-y,1/2+z -x,-y,-z 1/2+x,1/2+y,1/2-z", "x,y,z -x,1/2-y,1/2+z -x,-y,-z x,1/2+y,1/2-z", "x,y,z 1/2+x,1/2-y,-z -x,-y,-z 1/2-x,1/2+y,z", "x,y,z 1/2+x,1/2-y,1/2-z -x,-y,-z 1/2-x,1/2+y,1/2+z", "x,y,z 1/2+x,-y,1/2-z -x,-y,-z 1/2-x,y,1/2+z", "x,y,z -x,y,1/2-z -x,-y,-z x,-y,1/2+z 1/2+x,1/2+y,z 1/2-x,1/2+y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2-x,y,1/2-z -x,-y,-z 1/2+x,-y,1/2+z x,1/2+y,1/2+z 1/2-x,1/2+y,-z " "-x,1/2-y,1/2-z 1/2+x,1/2-y,z", "x,y,z 1/2-x,y,-z -x,-y,-z 1/2+x,-y,z 1/2+x,1/2+y,1/2+z -x,1/2+y,1/2-z " "1/2-x,1/2-y,1/2-z x,1/2-y,1/2+z", "x,y,z 1/2-x,y,-z -x,-y,-z 1/2+x,-y,z x,1/2+y,1/2+z 1/2-x,1/2+y,1/2-z " "-x,1/2-y,1/2-z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2-x,y,1/2-z -x,-y,-z 1/2+x,-y,1/2+z 1/2+x,1/2+y,z -x,1/2+y,1/2-z " "1/2-x,1/2-y,-z x,1/2-y,1/2+z", "x,y,z -x,y,1/2-z -x,-y,-z x,-y,1/2+z 1/2+x,1/2+y,1/2+z 1/2-x,1/2+y,-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2-y,z", "x,y,z 1/2-x,-y,z -x,-y,-z 1/2+x,y,-z x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z", "x,y,z 1/2-x,1/2-y,z -x,-y,-z 1/2+x,1/2+y,-z 1/2+x,y,1/2+z -x,1/2-y,1/2+z " "1/2-x,-y,1/2-z x,1/2+y,1/2-z", "x,y,z -x,1/2-y,z -x,-y,-z x,1/2+y,-z 1/2+x,1/2+y,1/2+z 1/2-x,-y,1/2+z " "1/2-x,1/2-y,1/2-z 1/2+x,y,1/2-z", "x,y,z -x,1/2-y,z -x,-y,-z x,1/2+y,-z 1/2+x,y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-x,-y,1/2-z 1/2+x,1/2+y,1/2-z", "x,y,z 1/2-x,1/2-y,z -x,-y,-z 1/2+x,1/2+y,-z x,1/2+y,1/2+z 1/2-x,-y,1/2+z " "-x,1/2-y,1/2-z 1/2+x,y,1/2-z", "x,y,z 1/2-x,-y,z -x,-y,-z 1/2+x,y,-z 1/2+x,1/2+y,1/2+z -x,1/2-y,1/2+z " "1/2-x,1/2-y,1/2-z x,1/2+y,1/2-z", "x,y,z x,1/2-y,-z -x,-y,-z -x,1/2+y,z 1/2+x,y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2-x,-y,1/2-z 1/2-x,1/2+y,1/2+z", "x,y,z x,1/2-y,1/2-z -x,-y,-z -x,1/2+y,1/2+z 1/2+x,1/2+y,z 1/2+x,-y,1/2-z " "1/2-x,1/2-y,-z 1/2-x,y,1/2+z", "x,y,z x,-y,1/2-z -x,-y,-z -x,y,1/2+z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,-z " "1/2-x,1/2-y,1/2-z 1/2-x,1/2+y,z", "x,y,z x,-y,1/2-z -x,-y,-z -x,y,1/2+z 1/2+x,1/2+y,z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2-y,-z 1/2-x,1/2+y,1/2+z", "x,y,z x,1/2-y,1/2-z -x,-y,-z -x,1/2+y,1/2+z 1/2+x,y,1/2+z 1/2+x,1/2-y,-z " "1/2-x,-y,1/2-z 1/2-x,1/2+y,z", "x,y,z x,1/2-y,-z -x,-y,-z -x,1/2+y,z 1/2+x,1/2+y,1/2+z 1/2+x,-y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2-x,y,1/2+z", "x,y,z -x,-y,z -x,y,-z x,-y,-z", "x,y,z -x,-y,1/2+z -x,y,1/2-z x,-y,-z", "x,y,z 1/2+x,-y,-z 1/2-x,-y,z -x,y,-z", "x,y,z -x,1/2+y,-z x,1/2-y,-z -x,-y,z", "x,y,z -x,-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z", "x,y,z x,-y,-z -x,1/2-y,1/2+z -x,1/2+y,1/2-z", "x,y,z -x,y,-z 1/2+x,-y,1/2-z 1/2-x,-y,1/2+z", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z", "x,y,z -x,-y,1/2+z -x,y,1/2-z x,-y,-z 1/2+x,1/2+y,z 1/2-x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,-z", "x,y,z 1/2+x,-y,-z 1/2-x,-y,z -x,y,-z x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2-y,1/2+z -x,1/2+y,1/2-z", "x,y,z -x,1/2+y,-z x,1/2-y,-z -x,-y,z 1/2+x,y,1/2+z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2-z 1/2-x,-y,1/2+z", "x,y,z -x,-y,z -x,y,-z x,-y,-z 1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z " "1/2+x,1/2-y,-z", "x,y,z x,-y,-z -x,-y,z -x,y,-z x,1/2+y,1/2+z x,1/2-y,1/2-z -x,1/2-y,1/2+z " "-x,1/2+y,1/2-z", "x,y,z -x,y,-z x,-y,-z -x,-y,z 1/2+x,y,1/2+z 1/2-x,y,1/2-z 1/2+x,-y,1/2-z " "1/2-x,-y,1/2+z", "x,y,z -x,-y,z -x,y,-z x,-y,-z x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2+y,1/2-z " "x,1/2-y,1/2-z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z 1/2+x,-y,1/2-z " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z", "x,y,z -x,-y,z -x,y,-z x,-y,-z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z 1/2+x,1/2+y,1/2+z " "-x,1/2-y,z 1/2-x,y,-z x,-y,1/2-z", "x,y,z -x,-y,z x,-y,z -x,y,z", "x,y,z x,-y,-z x,y,-z x,-y,z", "x,y,z -x,y,-z -x,y,z x,y,-z", "x,y,z -x,-y,1/2+z x,-y,1/2+z -x,y,z", "x,y,z -x,-y,1/2+z -x,y,1/2+z x,-y,z", "x,y,z 1/2+x,-y,-z 1/2+x,y,-z x,-y,z", "x,y,z 1/2+x,-y,-z x,y,-z 1/2+x,-y,z", "x,y,z -x,1/2+y,-z x,y,-z -x,1/2+y,z", "x,y,z -x,1/2+y,-z x,1/2+y,-z -x,y,z", "x,y,z -x,-y,z x,-y,1/2+z -x,y,1/2+z", "x,y,z x,-y,-z 1/2+x,y,-z 1/2+x,-y,z", "x,y,z -x,y,-z -x,1/2+y,z x,1/2+y,-z", "x,y,z -x,-y,z 1/2+x,-y,z 1/2-x,y,z", "x,y,z -x,-y,z -x,1/2+y,z x,1/2-y,z", "x,y,z x,-y,-z x,1/2+y,-z x,1/2-y,z", "x,y,z x,-y,-z x,-y,1/2+z x,y,1/2-z", "x,y,z -x,y,-z -x,y,1/2+z x,y,1/2-z", "x,y,z -x,y,-z 1/2+x,y,-z 1/2-x,y,z", "x,y,z -x,-y,1/2+z 1/2+x,-y,z 1/2-x,y,1/2+z", "x,y,z -x,-y,1/2+z -x,1/2+y,z x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,-z x,1/2+y,-z 1/2+x,1/2-y,z", "x,y,z 1/2+x,-y,-z x,-y,1/2+z 1/2+x,y,1/2-z", "x,y,z -x,1/2+y,-z -x,y,1/2+z x,1/2+y,1/2-z", "x,y,z -x,1/2+y,-z 1/2+x,y,-z 1/2-x,1/2+y,z", "x,y,z -x,-y,z x,1/2-y,1/2+z -x,1/2+y,1/2+z", "x,y,z -x,-y,z 1/2-x,y,1/2+z 1/2+x,-y,1/2+z", "x,y,z x,-y,-z 1/2+x,y,1/2-z 1/2+x,-y,1/2+z", "x,y,z x,-y,-z 1/2+x,1/2-y,z 1/2+x,1/2+y,-z", "x,y,z -x,y,-z 1/2-x,1/2+y,z 1/2+x,1/2+y,-z", "x,y,z -x,y,-z x,1/2+y,1/2-z -x,1/2+y,1/2+z", "x,y,z 1/2-x,-y,1/2+z 1/2+x,-y,1/2+z -x,y,z", "x,y,z -x,1/2+y,1/2+z -x,1/2-y,1/2+z x,-y,z", "x,y,z 1/2+x,1/2-y,-z 1/2+x,1/2+y,-z x,-y,z", "x,y,z 1/2+x,-y,1/2-z 1/2+x,-y,1/2+z x,y,-z", "x,y,z -x,1/2+y,1/2-z -x,1/2+y,1/2+z x,y,-z", "x,y,z 1/2-x,1/2+y,-z 1/2+x,1/2+y,-z -x,y,z", "x,y,z -x,-y,z 1/2+x,1/2-y,z 1/2-x,1/2+y,z", "x,y,z x,-y,-z x,1/2+y,1/2-z x,1/2-y,1/2+z", "x,y,z -x,y,-z 1/2-x,y,1/2+z 1/2+x,y,1/2-z", "x,y,z -x,-y,1/2+z 1/2+x,1/2-y,z 1/2-x,1/2+y,1/2+z", "x,y,z -x,-y,1/2+z 1/2-x,1/2+y,z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,-z x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,-z x,1/2-y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,1/2+y,-z 1/2-x,y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,1/2+y,-z 1/2+x,y,1/2-z 1/2-x,1/2+y,1/2+z", "x,y,z -x,-y,z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z x,-y,-z 1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z", "x,y,z -x,y,-z 1/2-x,1/2+y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,-y,z x,-y,z -x,y,z 1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2+x,1/2-y,z " "1/2-x,1/2+y,z", "x,y,z x,-y,-z x,y,-z x,-y,z x,1/2+y,1/2+z x,1/2-y,1/2+z x,1/2-y,1/2-z " "x,1/2+y,1/2-z", "x,y,z -x,y,-z -x,y,z x,y,-z 1/2+x,y,1/2+z 1/2-x,y,1/2-z 1/2-x,y,1/2+z " "1/2+x,y,1/2-z", "x,y,z -x,-y,1/2+z x,-y,1/2+z -x,y,z 1/2+x,1/2+y,z 1/2-x,1/2-y,1/2+z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,z", "x,y,z -x,-y,1/2+z -x,y,1/2+z x,-y,z 1/2+x,1/2+y,z 1/2-x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,z", "x,y,z 1/2+x,-y,-z 1/2+x,y,-z x,-y,z x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2+x,1/2+y,1/2-z x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,-z 1/2+x,-y,z x,y,-z x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2+x,1/2-y,1/2+z x,1/2+y,1/2-z", "x,y,z -x,1/2+y,-z -x,1/2+y,z x,y,-z 1/2+x,y,1/2+z 1/2-x,1/2+y,1/2-z " "1/2-x,1/2+y,1/2+z 1/2+x,y,1/2-z", "x,y,z -x,1/2+y,-z x,1/2+y,-z -x,y,z 1/2+x,y,1/2+z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2+y,1/2-z 1/2-x,y,1/2+z", "x,y,z -x,-y,z -x,y,1/2+z x,-y,1/2+z 1/2+x,1/2+y,z 1/2-x,1/2-y,z " "1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z", "x,y,z x,-y,-z 1/2+x,-y,z 1/2+x,y,-z x,1/2+y,1/2+z x,1/2-y,1/2-z " "1/2+x,1/2-y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,y,-z x,1/2+y,-z -x,1/2+y,z 1/2+x,y,1/2+z 1/2-x,y,1/2-z " "1/2+x,1/2+y,1/2-z 1/2-x,1/2+y,1/2+z", "x,y,z -x,-y,z x,-y,z -x,y,z x,1/2+y,1/2+z -x,1/2-y,1/2+z x,1/2-y,1/2+z " "-x,1/2+y,1/2+z", "x,y,z -x,-y,z -x,y,z x,-y,z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2+z " "1/2+x,-y,1/2+z", "x,y,z x,-y,-z x,y,-z x,-y,z 1/2+x,y,1/2+z 1/2+x,-y,1/2-z 1/2+x,y,1/2-z " "1/2+x,-y,1/2+z", "x,y,z x,-y,-z x,-y,z x,y,-z 1/2+x,1/2+y,z 1/2+x,1/2-y,-z 1/2+x,1/2-y,z " "1/2+x,1/2+y,-z", "x,y,z -x,y,-z -x,y,z x,y,-z 1/2+x,1/2+y,z 1/2-x,1/2+y,-z 1/2-x,1/2+y,z " "1/2+x,1/2+y,-z", "x,y,z -x,y,-z x,y,-z -x,y,z x,1/2+y,1/2+z -x,1/2+y,1/2-z x,1/2+y,1/2-z " "-x,1/2+y,1/2+z", "x,y,z -x,-y,z x,1/2-y,z -x,1/2+y,z x,1/2+y,1/2+z -x,1/2-y,1/2+z x,-y,1/2+z " "-x,y,1/2+z", "x,y,z -x,-y,z 1/2-x,y,z 1/2+x,-y,z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z -x,y,1/2+z " "x,-y,1/2+z", "x,y,z x,-y,-z x,y,1/2-z x,-y,1/2+z 1/2+x,y,1/2+z 1/2+x,-y,1/2-z 1/2+x,y,-z " "1/2+x,-y,z", "x,y,z x,-y,-z x,1/2-y,z x,1/2+y,-z 1/2+x,1/2+y,z 1/2+x,1/2-y,-z 1/2+x,-y,z " "1/2+x,y,-z", "x,y,z -x,y,-z 1/2-x,y,z 1/2+x,y,-z 1/2+x,1/2+y,z 1/2-x,1/2+y,-z -x,1/2+y,z " "x,1/2+y,-z", "x,y,z -x,y,-z x,y,1/2-z -x,y,1/2+z x,1/2+y,1/2+z -x,1/2+y,1/2-z x,1/2+y,-z " "-x,1/2+y,z", "x,y,z -x,-y,z 1/2+x,-y,z 1/2-x,y,z x,1/2+y,1/2+z -x,1/2-y,1/2+z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z -x,-y,z -x,1/2+y,z x,1/2-y,z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z " "1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z", "x,y,z x,-y,-z x,1/2+y,-z x,1/2-y,z 1/2+x,y,1/2+z 1/2+x,-y,1/2-z " "1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z", "x,y,z x,-y,-z x,-y,1/2+z x,y,1/2-z 1/2+x,1/2+y,z 1/2+x,1/2-y,-z " "1/2+x,1/2-y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,y,-z -x,y,1/2+z x,y,1/2-z 1/2+x,1/2+y,z 1/2-x,1/2+y,-z " "1/2-x,1/2+y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,y,-z 1/2+x,y,-z 1/2-x,y,z x,1/2+y,1/2+z -x,1/2+y,1/2-z " "1/2+x,1/2+y,1/2-z 1/2-x,1/2+y,1/2+z", "x,y,z -x,-y,z 1/2+x,1/2-y,z 1/2-x,1/2+y,z x,1/2+y,1/2+z -x,1/2-y,1/2+z " "1/2+x,-y,1/2+z 1/2-x,y,1/2+z", "x,y,z -x,-y,z 1/2-x,1/2+y,z 1/2+x,1/2-y,z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z " "-x,1/2+y,1/2+z x,1/2-y,1/2+z", "x,y,z x,-y,-z x,1/2+y,1/2-z x,1/2-y,1/2+z 1/2+x,y,1/2+z 1/2+x,-y,1/2-z " "1/2+x,1/2+y,-z 1/2+x,1/2-y,z", "x,y,z x,-y,-z x,1/2-y,1/2+z x,1/2+y,1/2-z 1/2+x,1/2+y,z 1/2+x,1/2-y,-z " "1/2+x,-y,1/2+z 1/2+x,y,1/2-z", "x,y,z -x,y,-z 1/2-x,y,1/2+z 1/2+x,y,1/2-z 1/2+x,1/2+y,z 1/2-x,1/2+y,-z " "-x,1/2+y,1/2+z x,1/2+y,1/2-z", "x,y,z -x,y,-z 1/2+x,y,1/2-z 1/2-x,y,1/2+z x,1/2+y,1/2+z -x,1/2+y,1/2-z " "1/2+x,1/2+y,-z 1/2-x,1/2+y,z", "x,y,z -x,-y,z x,-y,z -x,y,z x,1/2+y,1/2+z -x,1/2-y,1/2+z x,1/2-y,1/2+z " "-x,1/2+y,1/2+z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2+x,-y,1/2+z 1/2-x,y,1/2+z " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2+x,1/2-y,z 1/2-x,1/2+y,z", "x,y,z x,-y,-z x,y,-z x,-y,z 1/2+x,y,1/2+z 1/2+x,-y,1/2-z 1/2+x,y,1/2-z " "1/2+x,-y,1/2+z 1/2+x,1/2+y,z 1/2+x,1/2-y,-z 1/2+x,1/2+y,-z 1/2+x,1/2-y,z " "x,1/2+y,1/2+z x,1/2-y,1/2-z x,1/2+y,1/2-z x,1/2-y,1/2+z", "x,y,z -x,y,-z -x,y,z x,y,-z 1/2+x,1/2+y,z 1/2-x,1/2+y,-z 1/2-x,1/2+y,z " "1/2+x,1/2+y,-z x,1/2+y,1/2+z -x,1/2+y,1/2-z -x,1/2+y,1/2+z x,1/2+y,1/2-z " "1/2+x,y,1/2+z 1/2-x,y,1/2-z 1/2-x,y,1/2+z 1/2+x,y,1/2-z", "x,y,z -x,-y,z 1/4+x,1/4-y,1/4+z 1/4-x,1/4+y,1/4+z 1/2+x,y,1/2+z " "1/2-x,-y,1/2+z 3/4+x,1/4-y,3/4+z 3/4-x,1/4+y,3/4+z x,1/2+y,1/2+z " "-x,1/2-y,1/2+z 1/4+x,3/4-y,3/4+z 1/4-x,3/4+y,3/4+z 1/2+x,1/2+y,z " "1/2-x,1/2-y,z 3/4+x,3/4-y,1/4+z 3/4-x,3/4+y,1/4+z", "x,y,z x,-y,-z 1/4+x,1/4+y,1/4-z 1/4+x,1/4-y,1/4+z 1/2+x,1/2+y,z " "1/2+x,1/2-y,-z 3/4+x,3/4+y,1/4-z 3/4+x,3/4-y,1/4+z 1/2+x,y,1/2+z " "1/2+x,-y,1/2-z 3/4+x,1/4+y,3/4-z 3/4+x,1/4-y,3/4+z x,1/2+y,1/2+z " "x,1/2-y,1/2-z 1/4+x,3/4+y,3/4-z 1/4+x,3/4-y,3/4+z", "x,y,z -x,y,-z 1/4-x,1/4+y,1/4+z 1/4+x,1/4+y,1/4-z x,1/2+y,1/2+z " "-x,1/2+y,1/2-z 1/4-x,3/4+y,3/4+z 1/4+x,3/4+y,3/4-z 1/2+x,1/2+y,z " "1/2-x,1/2+y,-z 3/4-x,3/4+y,1/4+z 3/4+x,3/4+y,1/4-z 1/2+x,y,1/2+z " "1/2-x,y,1/2-z 3/4-x,1/4+y,3/4+z 3/4+x,1/4+y,3/4-z", "x,y,z -x,-y,z x,-y,z -x,y,z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z x,-y,-z x,y,-z x,-y,z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z", "x,y,z -x,y,-z -x,y,z x,y,-z 1/2+x,1/2+y,1/2+z 1/2-x,1/2+y,1/2-z " "1/2-x,1/2+y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,-y,z 1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2+x,1/2+y,1/2+z " "1/2-x,1/2-y,1/2+z x,-y,1/2+z -x,y,1/2+z", "x,y,z x,-y,-z x,1/2+y,1/2-z x,1/2-y,1/2+z 1/2+x,1/2+y,1/2+z " "1/2+x,1/2-y,1/2-z 1/2+x,y,-z 1/2+x,-y,z", "x,y,z -x,y,-z 1/2-x,y,1/2+z 1/2+x,y,1/2-z 1/2+x,1/2+y,1/2+z " "1/2-x,1/2+y,1/2-z -x,1/2+y,z x,1/2+y,-z", "x,y,z -x,-y,z 1/2+x,-y,z 1/2-x,y,z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "x,1/2-y,1/2+z -x,1/2+y,1/2+z", "x,y,z -x,-y,z -x,1/2+y,z x,1/2-y,z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-x,y,1/2+z 1/2+x,-y,1/2+z", "x,y,z x,-y,-z x,1/2+y,-z x,1/2-y,z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2+x,y,1/2-z 1/2+x,-y,1/2+z", "x,y,z x,-y,-z x,-y,1/2+z x,y,1/2-z 1/2+x,1/2+y,1/2+z 1/2+x,1/2-y,1/2-z " "1/2+x,1/2-y,z 1/2+x,1/2+y,-z", "x,y,z -x,y,-z -x,y,1/2+z x,y,1/2-z 1/2+x,1/2+y,1/2+z 1/2-x,1/2+y,1/2-z " "1/2-x,1/2+y,z 1/2+x,1/2+y,-z", "x,y,z -x,y,-z 1/2+x,y,-z 1/2-x,y,z 1/2+x,1/2+y,1/2+z 1/2-x,1/2+y,1/2-z " "x,1/2+y,1/2-z -x,1/2+y,1/2+z", "x,y,z -x,-y,z -x,y,-z x,-y,-z -x,-y,-z x,y,-z x,-y,z -x,y,z", "x,y,z -x,-y,z -x,y,-z x,-y,-z 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z 1/2-x,1/2-y,z 1/2-x,y,1/2-z x,1/2-y,1/2-z -x,-y,-z 1/2+x,1/2+y,-z " "1/2+x,-y,1/2+z -x,1/2+y,1/2+z", "x,y,z -x,-y,z -x,y,1/2-z x,-y,1/2-z -x,-y,-z x,y,-z x,-y,1/2+z -x,y,1/2+z", "x,y,z x,-y,-z 1/2-x,-y,z 1/2-x,y,-z -x,-y,-z -x,y,z 1/2+x,y,-z 1/2+x,-y,z", "x,y,z -x,y,-z x,1/2-y,-z -x,1/2-y,z -x,-y,-z x,-y,z -x,1/2+y,z x,1/2+y,-z", "x,y,z -x,-y,z -x,y,-z x,-y,-z 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z 1/2+x,1/2-y,z " "1/2-x,1/2+y,z", "x,y,z 1/2-x,1/2-y,z 1/2-x,y,-z x,1/2-y,-z -x,-y,-z 1/2+x,1/2+y,-z " "1/2+x,-y,z -x,1/2+y,z", "x,y,z -x,1/2+y,1/2+z x,1/2-y,1/2+z x,1/2+y,1/2-z -x,1/2-y,1/2-z x,-y,-z " "-x,y,-z -x,-y,z", "x,y,z -x,1/2+y,1/2+z x,-y,1/2+z x,1/2+y,-z x,1/2-y,1/2-z -x,-y,-z " "-x,y,1/2-z -x,1/2-y,z", "x,y,z 1/2-x,y,1/2+z 1/2+x,-y,1/2+z 1/2+x,y,1/2-z 1/2-x,-y,1/2-z x,-y,-z " "-x,y,-z -x,-y,z", "x,y,z -x,y,1/2+z 1/2+x,-y,1/2+z 1/2+x,y,-z x,-y,1/2-z -x,-y,-z " "1/2-x,y,1/2-z 1/2-x,-y,z", "x,y,z 1/2-x,-y,z -x,y,-z 1/2+x,-y,-z -x,-y,-z 1/2+x,y,-z x,-y,z 1/2-x,y,z", "x,y,z -x,1/2-y,z x,-y,-z -x,1/2+y,-z -x,-y,-z x,1/2+y,-z -x,y,z x,1/2-y,z", "x,y,z x,1/2-y,-z -x,-y,z -x,1/2+y,-z -x,-y,-z -x,1/2+y,z x,y,-z x,1/2-y,z", "x,y,z x,-y,1/2-z -x,y,-z -x,-y,1/2+z -x,-y,-z -x,y,1/2+z x,-y,z x,y,1/2-z", "x,y,z -x,y,1/2-z x,-y,-z -x,-y,1/2+z -x,-y,-z x,-y,1/2+z -x,y,z x,y,1/2-z", "x,y,z 1/2-x,y,-z -x,-y,z 1/2+x,-y,-z -x,-y,-z 1/2+x,-y,z x,y,-z 1/2-x,y,z", "x,y,z 1/2-x,-y,z 1/2-x,1/2+y,1/2-z x,1/2-y,1/2-z -x,-y,-z 1/2+x,y,-z " "1/2+x,1/2-y,1/2+z -x,1/2+y,1/2+z", "x,y,z -x,1/2-y,z 1/2+x,1/2-y,1/2-z 1/2-x,y,1/2-z -x,-y,-z x,1/2+y,-z " "1/2-x,1/2+y,1/2+z 1/2+x,-y,1/2+z", "x,y,z x,1/2-y,-z 1/2-x,1/2-y,1/2+z 1/2-x,y,1/2-z -x,-y,-z -x,1/2+y,z " "1/2+x,1/2+y,1/2-z 1/2+x,-y,1/2+z", "x,y,z x,-y,1/2-z 1/2-x,1/2+y,1/2-z 1/2-x,1/2-y,z -x,-y,-z -x,y,1/2+z " "1/2+x,1/2-y,1/2+z 1/2+x,1/2+y,-z", "x,y,z -x,y,1/2-z 1/2+x,1/2-y,1/2-z 1/2-x,1/2-y,z -x,-y,-z x,-y,1/2+z " "1/2-x,1/2+y,1/2+z 1/2+x,1/2+y,-z", "x,y,z 1/2-x,y,-z 1/2-x,1/2-y,1/2+z x,1/2-y,1/2-z -x,-y,-z 1/2+x,-y,z " "1/2+x,1/2+y,1/2-z -x,1/2+y,1/2+z", "x,y,z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z x,-y,-z -x,-y,-z 1/2+x,y,1/2-z " "1/2+x,-y,1/2+z -x,y,z", "x,y,z -x,1/2-y,1/2+z x,1/2-y,1/2-z -x,y,-z -x,-y,-z x,1/2+y,1/2-z " "-x,1/2+y,1/2+z x,-y,z", "x,y,z 1/2+x,1/2-y,-z 1/2-x,1/2-y,z -x,y,-z -x,-y,-z 1/2-x,1/2+y,z " "1/2+x,1/2+y,-z x,-y,z", "x,y,z 1/2+x,-y,1/2-z 1/2-x,y,1/2-z -x,-y,z -x,-y,-z 1/2-x,y,1/2+z " "1/2+x,-y,1/2+z x,y,-z", "x,y,z -x,1/2+y,1/2-z x,1/2-y,1/2-z -x,-y,z -x,-y,-z x,1/2-y,1/2+z " "-x,1/2+y,1/2+z x,y,-z", "x,y,z 1/2-x,1/2+y,-z 1/2-x,1/2-y,z x,-y,-z -x,-y,-z 1/2+x,1/2-y,z " "1/2+x,1/2+y,-z -x,y,z", "x,y,z 1/2-x,-y,z -x,y,1/2-z 1/2+x,-y,1/2-z -x,-y,-z 1/2+x,y,-z x,-y,1/2+z " "1/2-x,y,1/2+z", "x,y,z -x,1/2-y,z x,-y,1/2-z -x,1/2+y,1/2-z -x,-y,-z x,1/2+y,-z -x,y,1/2+z " "x,1/2-y,1/2+z", "x,y,z x,1/2-y,-z 1/2-x,-y,z 1/2-x,1/2+y,-z -x,-y,-z -x,1/2+y,z 1/2+x,y,-z " "1/2+x,1/2-y,z", "x,y,z x,-y,1/2-z 1/2-x,y,-z 1/2-x,-y,1/2+z -x,-y,-z -x,y,1/2+z 1/2+x,-y,z " "1/2+x,y,1/2-z", "x,y,z -x,y,1/2-z x,1/2-y,-z -x,1/2-y,1/2+z -x,-y,-z x,-y,1/2+z -x,1/2+y,z " "x,1/2+y,1/2-z", "x,y,z 1/2-x,y,-z -x,1/2-y,z 1/2+x,1/2-y,-z -x,-y,-z 1/2+x,-y,z x,1/2+y,-z " "1/2-x,1/2+y,z", "x,y,z -x,-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z -x,-y,-z x,y,-z 1/2+x,1/2-y,z " "1/2-x,1/2+y,z", "x,y,z x,-y,-z -x,1/2-y,1/2+z -x,1/2+y,1/2-z -x,-y,-z -x,y,z x,1/2+y,1/2-z " "x,1/2-y,1/2+z", "x,y,z -x,y,-z 1/2+x,-y,1/2-z 1/2-x,-y,1/2+z -x,-y,-z x,-y,z 1/2-x,y,1/2+z " "1/2+x,y,1/2-z", "x,y,z 1/2-x,1/2-y,z -x,1/2+y,1/2-z 1/2+x,-y,1/2-z -x,-y,-z 1/2+x,1/2+y,-z " "x,1/2-y,1/2+z 1/2-x,y,1/2+z", "x,y,z x,1/2-y,1/2-z 1/2-x,-y,1/2+z 1/2-x,1/2+y,-z -x,-y,-z -x,1/2+y,1/2+z " "1/2+x,y,1/2-z 1/2+x,1/2-y,z", "x,y,z 1/2-x,y,1/2-z 1/2+x,1/2-y,-z -x,1/2-y,1/2+z -x,-y,-z 1/2+x,-y,1/2+z " "1/2-x,1/2+y,z x,1/2+y,1/2-z", "x,y,z -x,-y,1/2+z -x,1/2+y,1/2-z x,1/2-y,-z -x,-y,-z x,y,1/2-z " "x,1/2-y,1/2+z -x,1/2+y,z", "x,y,z -x,-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,y,-z -x,-y,-z x,y,1/2-z " "1/2-x,y,1/2+z 1/2+x,-y,z", "x,y,z 1/2+x,-y,-z 1/2-x,-y,1/2+z -x,y,1/2-z -x,-y,-z 1/2-x,y,z " "1/2+x,y,1/2-z x,-y,1/2+z", "x,y,z 1/2+x,-y,-z 1/2-x,1/2+y,-z -x,1/2-y,z -x,-y,-z 1/2-x,y,z " "1/2+x,1/2-y,z x,1/2+y,-z", "x,y,z -x,1/2+y,-z 1/2+x,1/2-y,-z 1/2-x,-y,z -x,-y,-z x,1/2-y,z " "1/2-x,1/2+y,z 1/2+x,y,-z", "x,y,z -x,1/2+y,-z -x,1/2-y,1/2+z x,-y,1/2-z -x,-y,-z x,1/2-y,z " "x,1/2+y,1/2-z -x,y,1/2+z", "x,y,z -x,-y,z 1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z -x,-y,-z x,y,-z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z x,-y,-z 1/2-x,1/2-y,1/2+z 1/2-x,1/2+y,1/2-z -x,-y,-z -x,y,z " "1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z", "x,y,z -x,y,-z 1/2+x,1/2-y,1/2-z 1/2-x,1/2-y,1/2+z -x,-y,-z x,-y,z " "1/2-x,1/2+y,1/2+z 1/2+x,1/2+y,1/2-z", "x,y,z -x,-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z " "x,-y,z -x,y,z", "x,y,z 1/2-x,1/2-y,z -x,1/2+y,-z 1/2+x,-y,-z -x,-y,-z 1/2+x,1/2+y,-z " "x,1/2-y,z 1/2-x,y,z", "x,y,z -x,1/2+y,1/2+z x,-y,z x,y,-z -x,1/2-y,1/2-z x,-y,-z -x,1/2+y,1/2-z " "-x,1/2-y,1/2+z", "x,y,z -x,1/2+y,1/2+z x,1/2-y,z x,y,1/2-z -x,-y,-z x,1/2-y,1/2-z -x,1/2+y,-z " "-x,-y,1/2+z", "x,y,z -x,y,z 1/2+x,-y,1/2+z x,y,-z 1/2-x,-y,1/2-z 1/2+x,-y,1/2-z -x,y,-z " "1/2-x,-y,1/2+z", "x,y,z 1/2-x,y,z 1/2+x,-y,1/2+z x,y,1/2-z -x,-y,-z 1/2+x,-y,-z 1/2-x,y,1/2-z " "-x,-y,1/2+z", "x,y,z 1/2-x,1/2-y,1/2+z -x,y,1/2-z 1/2+x,1/2-y,-z -x,-y,-z " "1/2+x,1/2+y,1/2-z x,-y,1/2+z 1/2-x,1/2+y,z", "x,y,z 1/2-x,1/2-y,1/2+z x,-y,1/2-z 1/2-x,1/2+y,-z -x,-y,-z " "1/2+x,1/2+y,1/2-z -x,y,1/2+z 1/2+x,1/2-y,z", "x,y,z 1/2+x,1/2-y,1/2-z 1/2-x,-y,z -x,1/2+y,1/2-z -x,-y,-z " "1/2-x,1/2+y,1/2+z 1/2+x,y,-z x,1/2-y,1/2+z", "x,y,z 1/2+x,1/2-y,1/2-z 1/2-x,y,-z -x,1/2-y,1/2+z -x,-y,-z " "1/2-x,1/2+y,1/2+z 1/2+x,-y,z x,1/2+y,1/2-z", "x,y,z 1/2-x,1/2+y,1/2-z x,1/2-y,-z 1/2-x,-y,1/2+z -x,-y,-z " "1/2+x,1/2-y,1/2+z -x,1/2+y,z 1/2+x,y,1/2-z", "x,y,z 1/2-x,1/2+y,1/2-z -x,1/2-y,z 1/2+x,-y,1/2-z -x,-y,-z " "1/2+x,1/2-y,1/2+z x,1/2+y,-z 1/2-x,y,1/2+z", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z -x,-y,-z 1/2+x,y,1/2-z " "x,1/2-y,1/2+z 1/2-x,1/2+y,z", "x,y,z -x,1/2-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,1/2+y,-z -x,-y,-z x,1/2+y,1/2-z " "1/2-x,y,1/2+z 1/2+x,1/2-y,z", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,-z 1/2+x,1/2-y,1/2-z -x,-y,-z 1/2+x,y,1/2-z " "x,1/2-y,z 1/2-x,1/2+y,1/2+z", "x,y,z -x,1/2-y,1/2+z 1/2+x,-y,-z 1/2-x,1/2+y,1/2-z -x,-y,-z x,1/2+y,1/2-z " "1/2-x,y,z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2+x,1/2-y,-z -x,-y,1/2+z 1/2-x,1/2+y,1/2-z -x,-y,-z 1/2-x,1/2+y,z " "x,y,1/2-z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2+x,-y,1/2-z -x,1/2+y,-z 1/2-x,1/2-y,1/2+z -x,-y,-z 1/2-x,y,1/2+z " "x,1/2-y,z 1/2+x,1/2+y,1/2-z", "x,y,z 1/2-x,y,z x,1/2-y,1/2+z 1/2+x,1/2+y,1/2-z -x,-y,-z 1/2+x,-y,-z " "-x,1/2+y,1/2-z 1/2-x,1/2-y,1/2+z", "x,y,z 1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,z x,y,1/2-z -x,-y,-z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2+y,-z -x,-y,1/2+z", "x,y,z -x,-y,1/2+z -x,y,1/2-z x,-y,-z -x,-y,-z x,y,1/2-z x,-y,1/2+z -x,y,z " "1/2+x,1/2+y,z 1/2-x,1/2-y,1/2+z 1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,-z " "1/2-x,1/2-y,-z 1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,z", "x,y,z -x,y,1/2+z x,-y,z x,y,1/2-z -x,-y,-z x,-y,1/2-z -x,y,-z -x,-y,1/2+z " "1/2+x,1/2+y,z 1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,1/2+y,1/2-z 1/2-x,1/2+y,-z 1/2-x,1/2-y,1/2+z", "x,y,z 1/2-x,y,z x,-y,z 1/2+x,-y,-z -x,-y,-z 1/2+x,y,-z -x,y,-z 1/2-x,-y,z " "x,1/2+y,1/2+z 1/2-x,1/2+y,1/2+z x,1/2-y,1/2+z 1/2+x,1/2-y,1/2-z " "-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z -x,1/2+y,1/2-z 1/2-x,1/2-y,1/2+z", "x,y,z 1/2-x,y,z 1/2+x,-y,z x,y,-z -x,-y,-z 1/2+x,-y,-z 1/2-x,y,-z -x,-y,z " "x,1/2+y,1/2+z 1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z x,1/2+y,1/2-z " "-x,1/2-y,1/2-z 1/2+x,1/2-y,1/2-z 1/2-x,1/2+y,1/2-z -x,1/2-y,1/2+z", "x,y,z -x,1/2+y,z x,1/2-y,z x,y,-z -x,-y,-z x,1/2-y,-z -x,1/2+y,-z -x,-y,z " "1/2+x,y,1/2+z 1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z 1/2+x,y,1/2-z " "1/2-x,-y,1/2-z 1/2+x,1/2-y,1/2-z 1/2-x,1/2+y,1/2-z 1/2-x,-y,1/2+z", "x,y,z -x,y,z x,1/2-y,z x,1/2+y,-z -x,-y,-z x,-y,-z -x,1/2+y,-z -x,1/2-y,z " "1/2+x,y,1/2+z 1/2-x,y,1/2+z 1/2+x,1/2-y,1/2+z 1/2+x,1/2+y,1/2-z " "1/2-x,-y,1/2-z 1/2+x,-y,1/2-z 1/2-x,1/2+y,1/2-z 1/2-x,1/2-y,1/2+z", "x,y,z -x,1/2-y,1/2+z -x,1/2+y,1/2-z x,-y,-z -x,-y,-z x,1/2+y,1/2-z " "x,1/2-y,1/2+z -x,y,z 1/2+x,1/2+y,z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z " "1/2+x,1/2-y,-z 1/2-x,1/2-y,-z 1/2+x,y,1/2-z 1/2+x,-y,1/2+z 1/2-x,1/2+y,z", "x,y,z -x,1/2+y,1/2+z x,-y,z x,1/2+y,1/2-z -x,-y,-z x,1/2-y,1/2-z -x,y,-z " "-x,1/2-y,1/2+z 1/2+x,1/2+y,z 1/2-x,y,1/2+z 1/2+x,1/2-y,z 1/2+x,y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,-y,1/2-z 1/2-x,1/2+y,-z 1/2-x,-y,1/2+z", "x,y,z 1/2-x,1/2+y,z x,-y,z 1/2+x,1/2+y,-z -x,-y,-z 1/2+x,1/2-y,-z -x,y,-z " "1/2-x,1/2-y,z x,1/2+y,1/2+z 1/2-x,y,1/2+z x,1/2-y,1/2+z 1/2+x,y,1/2-z " "-x,1/2-y,1/2-z 1/2+x,-y,1/2-z -x,1/2+y,1/2-z 1/2-x,-y,1/2+z", "x,y,z 1/2-x,1/2+y,z 1/2+x,1/2-y,z x,y,-z -x,-y,-z 1/2+x,1/2-y,-z " "1/2-x,1/2+y,-z -x,-y,z x,1/2+y,1/2+z 1/2-x,y,1/2+z 1/2+x,-y,1/2+z " "x,1/2+y,1/2-z -x,1/2-y,1/2-z 1/2+x,-y,1/2-z 1/2-x,y,1/2-z -x,1/2-y,1/2+z", "x,y,z -x,1/2+y,1/2+z x,1/2-y,1/2+z x,y,-z -x,-y,-z x,1/2-y,1/2-z " "-x,1/2+y,1/2-z -x,-y,z 1/2+x,y,1/2+z 1/2-x,1/2+y,z 1/2+x,1/2-y,z " "1/2+x,y,1/2-z 1/2-x,-y,1/2-z 1/2+x,1/2-y,-z 1/2-x,1/2+y,-z 1/2-x,-y,1/2+z", "x,y,z -x,y,z x,1/2-y,1/2+z x,1/2+y,1/2-z -x,-y,-z x,-y,-z -x,1/2+y,1/2-z " "-x,1/2-y,1/2+z 1/2+x,y,1/2+z 1/2-x,y,1/2+z 1/2+x,1/2-y,z 1/2+x,1/2+y,-z " "1/2-x,-y,1/2-z 1/2+x,-y,1/2-z 1/2-x,1/2+y,-z 1/2-x,1/2-y,z", "x,y,z -x,-y,z -x,y,-z x,-y,-z -x,-y,-z x,y,-z x,-y,z -x,y,z 1/2+x,1/2+y,z " "1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z " "1/2+x,1/2-y,z 1/2-x,1/2+y,z", "x,y,z -x,-y,z x,-y,-z -x,y,-z -x,-y,-z x,y,-z -x,y,z x,-y,z x,1/2+y,1/2+z " "-x,1/2-y,1/2+z x,1/2-y,1/2-z -x,1/2+y,1/2-z -x,1/2-y,1/2-z x,1/2+y,1/2-z " "-x,1/2+y,1/2+z x,1/2-y,1/2+z", "x,y,z -x,-y,z x,-y,-z -x,y,-z -x,-y,-z x,y,-z -x,y,z x,-y,z 1/2+x,y,1/2+z " "1/2-x,-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,y,1/2-z 1/2-x,-y,1/2-z 1/2+x,y,1/2-z " "1/2-x,y,1/2+z 1/2+x,-y,1/2+z", "x,y,z -x,-y,z -x,y,1/2-z x,-y,1/2-z -x,-y,-z x,y,-z x,-y,1/2+z -x,y,1/2+z " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,1/2+y,-z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z 1/2-x,-y,z x,-y,-z 1/2-x,y,-z -x,-y,-z 1/2+x,y,-z -x,y,z 1/2+x,-y,z " "x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z x,1/2-y,1/2-z 1/2-x,1/2+y,1/2-z " "-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z -x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z", "x,y,z -x,1/2-y,z x,1/2-y,-z -x,y,-z -x,-y,-z x,1/2+y,-z -x,1/2+y,z x,-y,z " "1/2+x,y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+x,1/2-y,1/2-z 1/2-x,y,1/2-z " "1/2-x,-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2-x,1/2+y,1/2+z 1/2+x,-y,1/2+z", "x,y,z -x,1/2-y,z -x,1/2+y,-z x,-y,-z -x,-y,-z x,1/2+y,-z x,1/2-y,z -x,y,z " "1/2+x,1/2+y,z 1/2-x,-y,z 1/2-x,y,-z 1/2+x,1/2-y,-z 1/2-x,1/2-y,-z " "1/2+x,y,-z 1/2+x,-y,z 1/2-x,1/2+y,z", "x,y,z -x,1/2-y,z x,1/2-y,-z -x,y,-z -x,-y,-z x,1/2+y,-z -x,1/2+y,z x,-y,z " "1/2+x,1/2+y,z 1/2-x,-y,z 1/2+x,-y,-z 1/2-x,1/2+y,-z 1/2-x,1/2-y,-z " "1/2+x,y,-z 1/2-x,y,z 1/2+x,1/2-y,z", "x,y,z -x,-y,1/2+z x,-y,1/2-z -x,y,-z -x,-y,-z x,y,1/2-z -x,y,1/2+z x,-y,z " "x,1/2+y,1/2+z -x,1/2-y,z x,1/2-y,-z -x,1/2+y,1/2-z -x,1/2-y,1/2-z " "x,1/2+y,-z -x,1/2+y,z x,1/2-y,1/2+z", "x,y,z -x,-y,z x,-y,1/2-z -x,y,1/2-z -x,-y,-z x,y,-z -x,y,1/2+z x,-y,1/2+z " "x,1/2+y,1/2+z -x,1/2-y,1/2+z x,1/2-y,-z -x,1/2+y,-z -x,1/2-y,1/2-z " "x,1/2+y,1/2-z -x,1/2+y,z x,1/2-y,z", "x,y,z -x,-y,z x,-y,1/2-z -x,y,1/2-z -x,-y,-z x,y,-z -x,y,1/2+z x,-y,1/2+z " "1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2+x,-y,-z 1/2-x,y,-z 1/2-x,-y,1/2-z " "1/2+x,y,1/2-z 1/2-x,y,z 1/2+x,-y,z", "x,y,z -x,-y,1/2+z x,-y,-z -x,y,1/2-z -x,-y,-z x,y,1/2-z -x,y,z x,-y,1/2+z " "1/2+x,y,1/2+z 1/2-x,-y,z 1/2+x,-y,1/2-z 1/2-x,y,-z 1/2-x,-y,1/2-z " "1/2+x,y,-z 1/2-x,y,1/2+z 1/2+x,-y,z", "x,y,z -x,1/2-y,1/2-z -x,-y,z x,-y,-z -x,y,-z x,1/2+y,1/2-z -x,1/2+y,1/2+z " "x,1/2-y,1/2+z 1/2+x,1/2+y,z 1/2-x,-y,1/2-z 1/2-x,1/2-y,z 1/2+x,1/2-y,-z " "1/2-x,1/2+y,-z 1/2+x,y,1/2-z 1/2-x,y,1/2+z 1/2+x,-y,1/2+z", "x,y,z 1/2-x,-y,z -x,y,1/2-z 1/2+x,-y,1/2-z -x,-y,-z 1/2+x,y,-z x,-y,1/2+z " "1/2-x,y,1/2+z 1/2+x,1/2+y,z -x,1/2-y,z 1/2-x,1/2+y,1/2-z x,1/2-y,1/2-z " "1/2-x,1/2-y,-z x,1/2+y,-z 1/2+x,1/2-y,1/2+z -x,1/2+y,1/2+z", "x,y,z -x,1/2-y,1/2-z -x,-y,z x,-y,-z -x,y,-z x,1/2+y,1/2-z -x,1/2+y,1/2+z " "x,1/2-y,1/2+z 1/2+x,1/2+y,z 1/2-x,-y,1/2-z 1/2-x,1/2-y,z 1/2+x,1/2-y,-z " "1/2-x,1/2+y,-z 1/2+x,y,1/2-z 1/2-x,y,1/2+z 1/2+x,-y,1/2+z", "x,y,z -x,1/2-y,z x,-y,1/2-z -x,1/2+y,1/2-z -x,-y,-z x,1/2+y,-z -x,y,1/2+z " "x,1/2-y,1/2+z 1/2+x,1/2+y,z 1/2-x,-y,z 1/2+x,1/2-y,1/2-z 1/2-x,y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,y,-z 1/2-x,1/2+y,1/2+z 1/2+x,-y,1/2+z", "x,y,z -x,-y,z x,-y,-z -x,y,-z 1/2-x,-y,1/2-z 1/2+x,y,1/2-z 1/2-x,y,1/2+z " "1/2+x,-y,1/2+z x,1/2+y,1/2+z -x,1/2-y,1/2+z x,1/2-y,1/2-z -x,1/2+y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,1/2+y,-z 1/2-x,1/2+y,z 1/2+x,1/2-y,z", "x,y,z 1/2-x,-y,z x,-y,1/2-z 1/2-x,y,1/2-z -x,-y,-z 1/2+x,y,-z -x,y,1/2+z " "1/2+x,-y,1/2+z x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z x,1/2-y,-z 1/2-x,1/2+y,-z " "-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z -x,1/2+y,z 1/2+x,1/2-y,z", "x,y,z -x,-y,z x,-y,-z -x,y,-z 1/2-x,-y,1/2-z 1/2+x,y,1/2-z 1/2-x,y,1/2+z " "1/2+x,-y,1/2+z x,1/2+y,1/2+z -x,1/2-y,1/2+z x,1/2-y,1/2-z -x,1/2+y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,1/2+y,-z 1/2-x,1/2+y,z 1/2+x,1/2-y,z", "x,y,z 1/2-x,-y,1/2+z x,-y,1/2-z 1/2-x,y,-z -x,-y,-z 1/2+x,y,1/2-z " "-x,y,1/2+z 1/2+x,-y,z x,1/2+y,1/2+z 1/2-x,1/2-y,z x,1/2-y,-z " "1/2-x,1/2+y,1/2-z -x,1/2-y,1/2-z 1/2+x,1/2+y,-z -x,1/2+y,z " "1/2+x,1/2-y,1/2+z", "x,y,z -x,-y,z x,-y,-z -x,y,-z -x,1/2-y,1/2-z x,1/2+y,1/2-z -x,1/2+y,1/2+z " "x,1/2-y,1/2+z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,1/2+y,-z 1/2-x,1/2+y,z 1/2+x,1/2-y,z", "x,y,z -x,1/2-y,1/2+z x,1/2-y,-z -x,y,1/2-z -x,-y,-z x,1/2+y,1/2-z " "-x,1/2+y,z x,-y,1/2+z 1/2+x,y,1/2+z 1/2-x,1/2-y,z 1/2+x,1/2-y,1/2-z " "1/2-x,y,-z 1/2-x,-y,1/2-z 1/2+x,1/2+y,-z 1/2-x,1/2+y,1/2+z 1/2+x,-y,z", "x,y,z -x,-y,z x,-y,-z -x,y,-z -x,1/2-y,1/2-z x,1/2+y,1/2-z -x,1/2+y,1/2+z " "x,1/2-y,1/2+z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,y,1/2-z " "1/2-x,1/2-y,-z 1/2+x,1/2+y,-z 1/2-x,1/2+y,z 1/2+x,1/2-y,z", "x,y,z -x,1/2-y,z x,1/2-y,1/2-z -x,y,1/2-z -x,-y,-z x,1/2+y,-z " "-x,1/2+y,1/2+z x,-y,1/2+z 1/2+x,y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+x,1/2-y,-z " "1/2-x,y,-z 1/2-x,-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2-x,1/2+y,z 1/2+x,-y,z", "x,y,z -x,-y,z -x,y,-z x,-y,-z -x,-y,-z x,y,-z x,-y,z -x,y,z x,1/2+y,1/2+z " "-x,1/2-y,1/2+z -x,1/2+y,1/2-z x,1/2-y,1/2-z -x,1/2-y,1/2-z x,1/2+y,1/2-z " "x,1/2-y,1/2+z -x,1/2+y,1/2+z 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z " "1/2+x,-y,1/2-z 1/2-x,-y,1/2-z 1/2+x,y,1/2-z 1/2+x,-y,1/2+z 1/2-x,y,1/2+z " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2-x,1/2-y,-z " "1/2+x,1/2+y,-z 1/2+x,1/2-y,z 1/2-x,1/2+y,z", "x,y,z -x,y,-z 1/4-x,1/4-y,1/4-z 1/4+x,1/4-y,1/4+z x,-y,-z -x,-y,z " "1/4-x,1/4+y,1/4+z 1/4+x,1/4+y,1/4-z x,1/2+y,1/2+z 1/2+x,y,1/2+z " "1/2+x,1/2+y,z -x,1/2+y,1/2-z 1/2-x,y,1/2-z 1/2-x,1/2+y,-z 1/4-x,3/4-y,3/4-z " "3/4-x,1/4-y,3/4-z 3/4-x,3/4-y,1/4-z 1/4+x,3/4-y,3/4+z 3/4+x,1/4-y,3/4+z " "3/4+x,3/4-y,1/4+z x,1/2-y,1/2-z 1/2+x,-y,1/2-z 1/2+x,1/2-y,-z " "-x,1/2-y,1/2+z 1/2-x,-y,1/2+z 1/2-x,1/2-y,z 1/4-x,3/4+y,3/4+z " "3/4-x,1/4+y,3/4+z 3/4-x,3/4+y,1/4+z 1/4+x,3/4+y,3/4-z 3/4+x,1/4+y,3/4-z " "3/4+x,3/4+y,1/4-z", "x,y,z 1/4-x,1/4-y,z x,1/4-y,1/4-z 1/4-x,y,1/4-z -x,-y,-z 3/4+x,3/4+y,-z " "-x,3/4+y,3/4+z 3/4+x,-y,3/4+z x,1/2+y,1/2+z 1/4-x,3/4-y,1/2+z x,3/4-y,3/4-z " "1/4-x,1/2+y,3/4-z -x,1/2-y,1/2-z 3/4+x,1/4+y,1/2-z -x,1/4+y,1/4+z " "3/4+x,1/2-y,1/4+z 1/2+x,y,1/2+z 3/4-x,1/4-y,1/2+z 1/2+x,1/4-y,3/4-z " "3/4-x,y,3/4-z 1/2-x,-y,1/2-z 1/4+x,3/4+y,1/2-z 1/2-x,3/4+y,1/4+z " "1/4+x,-y,1/4+z 1/2+x,1/2+y,z 3/4-x,3/4-y,z 1/2+x,3/4-y,1/4-z " "3/4-x,1/2+y,1/4-z 1/2-x,1/2-y,-z 1/4+x,1/4+y,-z 1/2-x,1/4+y,3/4+z " "1/4+x,1/2-y,3/4+z", "x,y,z -x,-y,z -x,y,-z x,-y,-z -x,-y,-z x,y,-z x,-y,z -x,y,z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z -x,-y,z x,-y,1/2-z -x,y,1/2-z -x,-y,-z x,y,-z -x,y,1/2+z x,-y,1/2+z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+x,1/2-y,-z 1/2-x,1/2+y,-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2-x,1/2+y,z 1/2+x,1/2-y,z", "x,y,z 1/2-x,-y,z x,-y,-z 1/2-x,y,-z -x,-y,-z 1/2+x,y,-z -x,y,z 1/2+x,-y,z " "1/2+x,1/2+y,1/2+z -x,1/2-y,1/2+z 1/2+x,1/2-y,1/2-z -x,1/2+y,1/2-z " "1/2-x,1/2-y,1/2-z x,1/2+y,1/2-z 1/2-x,1/2+y,1/2+z x,1/2-y,1/2+z", "x,y,z -x,1/2-y,z x,1/2-y,-z -x,y,-z -x,-y,-z x,1/2+y,-z -x,1/2+y,z x,-y,z " "1/2+x,1/2+y,1/2+z 1/2-x,-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,1/2+y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2+x,y,1/2-z 1/2-x,y,1/2+z 1/2+x,1/2-y,1/2+z", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z -x,-y,-z 1/2+x,y,1/2-z " "x,1/2-y,1/2+z 1/2-x,1/2+y,z 1/2+x,1/2+y,1/2+z -x,1/2-y,z 1/2-x,y,-z " "x,-y,1/2-z 1/2-x,1/2-y,1/2-z x,1/2+y,-z 1/2+x,-y,z -x,y,1/2+z", "x,y,z 1/2-x,-y,z x,1/2-y,-z -x,y,1/2-z -x,-y,-z 1/2+x,y,-z -x,1/2+y,z " "x,-y,1/2+z 1/2+x,1/2+y,1/2+z -x,1/2-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,1/2+y,-z " "1/2-x,1/2-y,1/2-z x,1/2+y,1/2-z 1/2-x,y,1/2+z 1/2+x,1/2-y,z", "x,y,z -x,1/2-y,z -x,1/2+y,-z x,-y,-z -x,-y,-z x,1/2+y,-z x,1/2-y,z -x,y,z " "1/2+x,1/2+y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z 1/2+x,1/2-y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2+x,y,1/2-z 1/2+x,-y,1/2+z 1/2-x,1/2+y,1/2+z", "x,y,z 1/2-x,-y,z 1/2+x,-y,-z -x,y,-z -x,-y,-z 1/2+x,y,-z 1/2-x,y,z x,-y,z " "1/2+x,1/2+y,1/2+z -x,1/2-y,1/2+z x,1/2-y,1/2-z 1/2-x,1/2+y,1/2-z " "1/2-x,1/2-y,1/2-z x,1/2+y,1/2-z -x,1/2+y,1/2+z 1/2+x,1/2-y,1/2+z", "x,y,z -x,-y,1/2+z x,-y,1/2-z -x,y,-z -x,-y,-z x,y,1/2-z -x,y,1/2+z x,-y,z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,z 1/2+x,1/2-y,-z 1/2-x,1/2+y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,-z 1/2-x,1/2+y,z 1/2+x,1/2-y,1/2+z", "x,y,z -x,-y,z x,1/2-y,-z -x,1/2+y,-z -x,-y,-z x,y,-z -x,1/2+y,z x,1/2-y,z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+x,-y,1/2-z 1/2-x,y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2-x,y,1/2+z 1/2+x,-y,1/2+z", "x,y,z -x,-y,z 1/2+x,-y,-z 1/2-x,y,-z -x,-y,-z x,y,-z 1/2-x,y,z 1/2+x,-y,z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z x,1/2-y,1/2-z -x,1/2+y,1/2-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z -x,1/2+y,1/2+z x,1/2-y,1/2+z", "x,y,z -x,-y,1/2+z x,-y,-z -x,y,1/2-z -x,-y,-z x,y,1/2-z -x,y,z x,-y,1/2+z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,z 1/2+x,1/2-y,1/2-z 1/2-x,1/2+y,-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,-z 1/2-x,1/2+y,1/2+z 1/2+x,1/2-y,z", "x,y,z -x,-y,z -y,x,z y,-x,z", "x,y,z -x,-y,1/2+z -y,x,1/4+z y,-x,3/4+z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z", "x,y,z -x,-y,1/2+z -y,x,3/4+z y,-x,1/4+z", "x,y,z -x,-y,z -y,x,z y,-x,z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z", "x,y,z 1/2-x,1/2-y,1/2+z -y,1/2+x,1/4+z 1/2+y,-x,3/4+z 1/2+x,1/2+y,1/2+z " "-x,-y,z 1/2-y,x,3/4+z y,1/2-x,1/4+z", "x,y,z -x,-y,z y,-x,-z -y,x,-z", "x,y,z -x,-y,z y,-x,-z -y,x,-z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,-y,-z x,y,-z y,-x,-z -y,x,-z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z -x,-y,-z x,y,-z y,-x,1/2-z -y,x,1/2-z", "x,y,z -x,-y,z 1/2-y,1/2+x,z 1/2+y,1/2-x,z 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z " "y,-x,-z -y,x,-z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,z y,1/2-x,z -x,-y,-z 1/2+x,1/2+y,-z 1/2+y,-x,-z " "-y,1/2+x,-z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2-x,1/2-y,1/2-z " "1/2+x,1/2+y,1/2-z y,-x,-z -y,x,-z", "x,y,z 1/2-x,1/2-y,z -y,1/2+x,1/2+z 1/2+y,-x,1/2+z -x,-y,-z 1/2+x,1/2+y,-z " "y,1/2-x,1/2-z 1/2-y,x,1/2-z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,-y,-z x,y,-z y,-x,-z -y,x,-z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z", "x,y,z 1/2-x,1/2-y,1/2+z -y,1/2+x,1/4+z 1/2+y,-x,3/4+z -x,1/2-y,1/4-z " "1/2+x,y,3/4-z y,-x,-z 1/2-y,1/2+x,1/2-z 1/2+x,1/2+y,1/2+z -x,-y,z " "1/2-y,x,3/4+z y,1/2-x,1/4+z 1/2-x,-y,3/4-z x,1/2+y,1/4-z 1/2+y,1/2-x,1/2-z " "-y,x,-z", "x,y,z 1/2-x,-y,1/2+z 3/4-y,1/4+x,1/4+z 3/4+y,3/4-x,3/4+z -x,-y,-z " "1/2+x,y,1/2-z 1/4+y,3/4-x,3/4-z 1/4-y,1/4+x,1/4-z 1/2+x,1/2+y,1/2+z " "-x,1/2-y,z 1/4-y,3/4+x,3/4+z 1/4+y,1/4-x,1/4+z 1/2-x,1/2-y,1/2-z x,1/2+y,-z " "3/4+y,1/4-x,1/4-z 3/4-y,3/4+x,3/4-z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,y,-z x,-y,-z y,x,-z -y,-x,-z", "x,y,z -x,-y,z 1/2-y,1/2+x,z 1/2+y,1/2-x,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z " "y,x,-z -y,-x,-z", "x,y,z -x,-y,1/2+z -y,x,1/4+z y,-x,3/4+z -x,y,-z x,-y,1/2-z y,x,3/4-z " "-y,-x,1/4-z", "x,y,z -x,-y,1/2+z 1/2-y,1/2+x,1/4+z 1/2+y,1/2-x,3/4+z 1/2-x,1/2+y,1/4-z " "1/2+x,1/2-y,3/4-z y,x,-z -y,-x,1/2-z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z -x,y,-z x,-y,-z y,x,1/2-z -y,-x,1/2-z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2-z y,x,-z -y,-x,-z", "x,y,z -x,-y,1/2+z -y,x,3/4+z y,-x,1/4+z -x,y,-z x,-y,1/2-z y,x,1/4-z " "-y,-x,3/4-z", "x,y,z -x,-y,1/2+z 1/2-y,1/2+x,3/4+z 1/2+y,1/2-x,1/4+z 1/2-x,1/2+y,3/4-z " "1/2+x,1/2-y,1/4-z y,x,-z -y,-x,1/2-z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,y,-z x,-y,-z y,x,-z -y,-x,-z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z " "1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z", "x,y,z 1/2-x,1/2-y,1/2+z -y,1/2+x,1/4+z 1/2+y,-x,3/4+z 1/2-x,y,3/4-z " "x,1/2-y,1/4-z 1/2+y,1/2+x,1/2-z -y,-x,-z 1/2+x,1/2+y,1/2+z -x,-y,z " "1/2-y,x,3/4+z y,1/2-x,1/4+z -x,1/2+y,1/4-z 1/2+x,-y,3/4-z y,x,-z " "1/2-y,1/2-x,1/2-z", "x,y,z -x,-y,z -y,x,z y,-x,z x,-y,z -x,y,z -y,-x,z y,x,z", "x,y,z -x,-y,z -y,x,z y,-x,z 1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2-y,1/2-x,z " "1/2+y,1/2+x,z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z x,-y,1/2+z -x,y,1/2+z -y,-x,z y,x,z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2+x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2+z -y,-x,z y,x,z", "x,y,z -x,-y,z -y,x,z y,-x,z x,-y,1/2+z -x,y,1/2+z -y,-x,1/2+z y,x,1/2+z", "x,y,z -x,-y,z -y,x,z y,-x,z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z " "1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z x,-y,z -x,y,z -y,-x,1/2+z y,x,1/2+z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z 1/2+x,1/2-y,z 1/2-x,1/2+y,z " "1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z -y,x,z y,-x,z x,-y,z -x,y,z -y,-x,z y,x,z 1/2+x,1/2+y,1/2+z " "1/2-x,1/2-y,1/2+z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2+x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2+z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z -y,x,z y,-x,z x,-y,1/2+z -x,y,1/2+z -y,-x,1/2+z y,x,1/2+z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z " "1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2-y,1/2-x,z 1/2+y,1/2+x,z", "x,y,z 1/2-x,1/2-y,1/2+z -y,1/2+x,1/4+z 1/2+y,-x,3/4+z x,-y,z " "1/2-x,1/2+y,1/2+z -y,1/2-x,1/4+z 1/2+y,x,3/4+z 1/2+x,1/2+y,1/2+z -x,-y,z " "1/2-y,x,3/4+z y,1/2-x,1/4+z 1/2+x,1/2-y,1/2+z -x,y,z 1/2-y,-x,3/4+z " "y,1/2+x,1/4+z", "x,y,z 1/2-x,1/2-y,1/2+z -y,1/2+x,1/4+z 1/2+y,-x,3/4+z x,-y,1/2+z " "1/2-x,1/2+y,z -y,1/2-x,3/4+z 1/2+y,x,1/4+z 1/2+x,1/2+y,1/2+z -x,-y,z " "1/2-y,x,3/4+z y,1/2-x,1/4+z 1/2+x,1/2-y,z -x,y,1/2+z 1/2-y,-x,1/4+z " "y,1/2+x,3/4+z", "x,y,z -x,-y,z y,-x,-z -y,x,-z -x,y,-z x,-y,-z -y,-x,z y,x,z", "x,y,z -x,-y,z y,-x,-z -y,x,-z -x,y,1/2-z x,-y,1/2-z -y,-x,1/2+z y,x,1/2+z", "x,y,z -x,-y,z y,-x,-z -y,x,-z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2-y,1/2-x,z " "1/2+y,1/2+x,z", "x,y,z -x,-y,z y,-x,-z -y,x,-z 1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z " "1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z y,-x,-z -y,x,-z x,-y,z -x,y,z y,x,-z -y,-x,-z", "x,y,z -x,-y,z y,-x,-z -y,x,-z x,-y,1/2+z -x,y,1/2+z y,x,1/2-z -y,-x,1/2-z", "x,y,z -x,-y,z y,-x,-z -y,x,-z 1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2+y,1/2+x,-z " "1/2-y,1/2-x,-z", "x,y,z -x,-y,z y,-x,-z -y,x,-z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z " "1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z", "x,y,z -x,-y,z y,-x,-z -y,x,-z x,-y,z -x,y,z y,x,-z -y,-x,-z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z", "x,y,z -x,-y,z y,-x,-z -y,x,-z x,-y,1/2+z -x,y,1/2+z y,x,1/2-z -y,-x,1/2-z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z " "1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2+y,1/2+x,-z 1/2-y,1/2-x,-z", "x,y,z -x,-y,z y,-x,-z -y,x,-z -x,y,-z x,-y,-z -y,-x,z y,x,z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z " "1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z y,-x,-z -y,x,-z 1/2-x,y,3/4-z 1/2+x,-y,3/4-z 1/2-y,-x,3/4+z " "1/2+y,x,3/4+z 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2+y,1/2-x,1/2-z " "1/2-y,1/2+x,1/2-z -x,1/2+y,1/4-z x,1/2-y,1/4-z -y,1/2-x,1/4+z y,1/2+x,1/4+z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,y,-z x,-y,-z y,x,-z -y,-x,-z -x,-y,-z x,y,-z " "y,-x,-z -y,x,-z x,-y,z -x,y,z -y,-x,z y,x,z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,y,1/2-z x,-y,1/2-z y,x,1/2-z -y,-x,1/2-z " "-x,-y,-z x,y,-z y,-x,-z -y,x,-z x,-y,1/2+z -x,y,1/2+z -y,-x,1/2+z y,x,1/2+z", "x,y,z -y,x,z -x,-y,z y,-x,z x,-y,-z y,x,-z -x,y,-z -y,-x,-z 1/2-x,1/2-y,-z " "1/2+y,1/2-x,-z 1/2+x,1/2+y,-z 1/2-y,1/2+x,-z 1/2-x,1/2+y,z 1/2-y,1/2-x,z " "1/2+x,1/2-y,z 1/2+y,1/2+x,z", "x,y,z 1/2-y,x,z 1/2-x,1/2-y,z y,1/2-x,z x,1/2-y,-z y,x,-z 1/2-x,y,-z " "1/2-y,1/2-x,-z -x,-y,-z 1/2+y,-x,-z 1/2+x,1/2+y,-z -y,1/2+x,-z -x,1/2+y,z " "-y,-x,z 1/2+x,-y,z 1/2+y,1/2+x,z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,y,-z x,-y,-z y,x,-z -y,-x,-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,z y,1/2-x,z 1/2-x,y,1/2-z x,1/2-y,1/2-z " "y,x,1/2-z 1/2-y,1/2-x,1/2-z -x,-y,-z 1/2+x,1/2+y,-z 1/2+y,-x,-z -y,1/2+x,-z " "1/2+x,-y,1/2+z -x,1/2+y,1/2+z -y,-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z -y,x,z y,-x,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+y,1/2+x,-z " "1/2-y,1/2-x,-z -x,-y,-z x,y,-z y,-x,-z -y,x,-z 1/2+x,1/2-y,z 1/2-x,1/2+y,z " "1/2-y,1/2-x,z 1/2+y,1/2+x,z", "x,y,z -x,-y,z -y,x,z y,-x,z 1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z " "1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z -x,-y,-z x,y,-z y,-x,-z -y,x,-z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z 1/2-y,1/2+x,z 1/2+y,1/2-x,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z " "y,x,-z -y,-x,-z 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z y,-x,-z -y,x,-z x,-y,z -x,y,z " "1/2-y,1/2-x,z 1/2+y,1/2+x,z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,z y,1/2-x,z -x,1/2+y,-z 1/2+x,-y,-z " "1/2+y,1/2+x,-z -y,-x,-z -x,-y,-z 1/2+x,1/2+y,-z 1/2+y,-x,-z -y,1/2+x,-z " "x,1/2-y,z 1/2-x,y,z 1/2-y,1/2-x,z y,x,z", "x,y,z -x,-y,z 1/2-y,1/2+x,z 1/2+y,1/2-x,z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2-z y,x,1/2-z -y,-x,1/2-z 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z " "y,-x,-z -y,x,-z x,-y,1/2+z -x,y,1/2+z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,z y,1/2-x,z -x,1/2+y,1/2-z 1/2+x,-y,1/2-z " "1/2+y,1/2+x,1/2-z -y,-x,1/2-z -x,-y,-z 1/2+x,1/2+y,-z 1/2+y,-x,-z " "-y,1/2+x,-z x,1/2-y,1/2+z 1/2-x,y,1/2+z 1/2-y,1/2-x,1/2+z y,x,1/2+z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z -x,y,-z x,-y,-z y,x,1/2-z -y,-x,1/2-z " "-x,-y,-z x,y,-z y,-x,1/2-z -y,x,1/2-z x,-y,z -x,y,z -y,-x,1/2+z y,x,1/2+z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z -x,y,1/2-z x,-y,1/2-z y,x,-z -y,-x,-z " "-x,-y,-z x,y,-z y,-x,1/2-z -y,x,1/2-z x,-y,1/2+z -x,y,1/2+z -y,-x,z y,x,z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z -x,y,1/2-z x,-y,1/2-z " "1/2+y,1/2+x,-z 1/2-y,1/2-x,-z 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z y,-x,-z " "-y,x,-z 1/2+x,1/2-y,z 1/2-x,1/2+y,z -y,-x,1/2+z y,x,1/2+z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,1/2+z y,1/2-x,1/2+z 1/2-x,y,-z x,1/2-y,-z " "y,x,1/2-z 1/2-y,1/2-x,1/2-z -x,-y,-z 1/2+x,1/2+y,-z 1/2+y,-x,1/2-z " "-y,1/2+x,1/2-z 1/2+x,-y,z -x,1/2+y,z -y,-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z -x,y,-z x,-y,-z " "1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z " "y,-x,-z -y,x,-z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z -y,-x,z y,x,z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,1/2+z y,1/2-x,1/2+z 1/2-x,y,1/2-z x,1/2-y,1/2-z " "y,x,-z 1/2-y,1/2-x,-z -x,-y,-z 1/2+x,1/2+y,-z 1/2+y,-x,1/2-z -y,1/2+x,1/2-z " "1/2+x,-y,1/2+z -x,1/2+y,1/2+z -y,-x,z 1/2+y,1/2+x,z", "x,y,z -x,-y,z -y,x,1/2+z y,-x,1/2+z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z " "1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z -x,-y,-z x,y,-z y,-x,1/2-z -y,x,1/2-z " "1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2-z y,x,-z -y,-x,-z -x,-y,-z x,y,-z 1/2+y,1/2-x,1/2-z " "1/2-y,1/2+x,1/2-z 1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z -y,-x,z y,x,z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2-z y,x,-z -y,-x,-z 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z " "y,-x,-z -y,x,-z x,-y,z -x,y,z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,1/2+z y,1/2-x,1/2+z -x,1/2+y,-z 1/2+x,-y,-z " "1/2+y,1/2+x,1/2-z -y,-x,1/2-z -x,-y,-z 1/2+x,1/2+y,-z 1/2+y,-x,1/2-z " "-y,1/2+x,1/2-z x,1/2-y,z 1/2-x,y,z 1/2-y,1/2-x,1/2+z y,x,1/2+z", "x,y,z -x,-y,z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2-x,1/2+y,-z " "1/2+x,1/2-y,-z y,x,1/2-z -y,-x,1/2-z 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z " "y,-x,-z -y,x,-z x,-y,1/2+z -x,y,1/2+z 1/2-y,1/2-x,z 1/2+y,1/2+x,z", "x,y,z 1/2-x,1/2-y,z 1/2-y,x,1/2+z y,1/2-x,1/2+z -x,1/2+y,1/2-z " "1/2+x,-y,1/2-z 1/2+y,1/2+x,-z -y,-x,-z -x,-y,-z 1/2+x,1/2+y,-z " "1/2+y,-x,1/2-z -y,1/2+x,1/2-z x,1/2-y,1/2+z 1/2-x,y,1/2+z 1/2-y,1/2-x,z " "y,x,z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,y,-z x,-y,-z y,x,-z -y,-x,-z -x,-y,-z x,y,-z " "y,-x,-z -y,x,-z x,-y,z -x,y,z -y,-x,z y,x,z 1/2+x,1/2+y,1/2+z " "1/2-x,1/2-y,1/2+z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2-z 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z 1/2-x,1/2-y,1/2-z " "1/2+x,1/2+y,1/2-z 1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z 1/2+x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2+z 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z", "x,y,z -x,-y,z -y,x,z y,-x,z -x,y,1/2-z x,-y,1/2-z y,x,1/2-z -y,-x,1/2-z " "-x,-y,-z x,y,-z y,-x,-z -y,x,-z x,-y,1/2+z -x,y,1/2+z -y,-x,1/2+z y,x,1/2+z " "1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2-y,1/2+x,1/2+z 1/2+y,1/2-x,1/2+z " "1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+y,1/2+x,-z 1/2-y,1/2-x,-z " "1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z " "1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2-y,1/2-x,z 1/2+y,1/2+x,z", "x,y,z 1/2-x,1/2-y,1/2+z -y,1/2+x,1/4+z 1/2+y,-x,3/4+z 1/2-x,y,3/4-z " "x,1/2-y,1/4-z 1/2+y,1/2+x,1/2-z -y,-x,-z -x,1/2-y,1/4-z 1/2+x,y,3/4-z " "y,-x,-z 1/2-y,1/2+x,1/2-z 1/2+x,1/2-y,1/2+z -x,y,z 1/2-y,-x,3/4+z " "y,1/2+x,1/4+z 1/2+x,1/2+y,1/2+z -x,-y,z 1/2-y,x,3/4+z y,1/2-x,1/4+z " "-x,1/2+y,1/4-z 1/2+x,-y,3/4-z y,x,-z 1/2-y,1/2-x,1/2-z 1/2-x,-y,3/4-z " "x,1/2+y,1/4-z 1/2+y,1/2-x,1/2-z -y,x,-z x,-y,z 1/2-x,1/2+y,1/2+z " "-y,1/2-x,1/4+z 1/2+y,x,3/4+z", "x,y,z 1/2-x,-y,1/2+z 1/4-y,3/4+x,1/4+z 1/4+y,1/4-x,3/4+z 1/2-x,y,1/2-z " "x,-y,-z 1/4+y,3/4+x,1/4-z 1/4-y,1/4-x,3/4-z -x,-y,-z 1/2+x,y,1/2-z " "3/4+y,1/4-x,3/4-z 3/4-y,3/4+x,1/4-z 1/2+x,-y,1/2+z -x,y,z 3/4-y,1/4-x,3/4+z " "3/4+y,3/4+x,1/4+z 1/2+x,1/2+y,1/2+z -x,1/2-y,z 3/4-y,1/4+x,3/4+z " "3/4+y,3/4-x,1/4+z -x,1/2+y,-z 1/2+x,1/2-y,1/2-z 3/4+y,1/4+x,3/4-z " "3/4-y,3/4-x,1/4-z 1/2-x,1/2-y,1/2-z x,1/2+y,-z 1/4+y,3/4-x,1/4-z " "1/4-y,1/4+x,3/4-z x,1/2-y,z 1/2-x,1/2+y,1/2+z 1/4-y,3/4-x,1/4+z " "1/4+y,1/4+x,3/4+z", "x,y,z 1/2-x,1/2-y,1/2+z -y,1/2+x,1/4+z 1/2+y,-x,3/4+z 1/2-x,y,1/4-z " "x,1/2-y,3/4-z 1/2+y,1/2+x,-z -y,-x,1/2-z -x,1/2-y,1/4-z 1/2+x,y,3/4-z " "y,-x,-z 1/2-y,1/2+x,1/2-z 1/2+x,1/2-y,z -x,y,1/2+z 1/2-y,-x,1/4+z " "y,1/2+x,3/4+z 1/2+x,1/2+y,1/2+z -x,-y,z 1/2-y,x,3/4+z y,1/2-x,1/4+z " "-x,1/2+y,3/4-z 1/2+x,-y,1/4-z y,x,1/2-z 1/2-y,1/2-x,-z 1/2-x,-y,3/4-z " "x,1/2+y,1/4-z 1/2+y,1/2-x,1/2-z -y,x,-z x,-y,1/2+z 1/2-x,1/2+y,z " "-y,1/2-x,3/4+z 1/2+y,x,1/4+z", "x,y,z 1/2-x,-y,1/2+z 1/4-y,3/4+x,1/4+z 1/4+y,1/4-x,3/4+z 1/2-x,y,-z " "x,-y,1/2-z 1/4+y,3/4+x,3/4-z 1/4-y,1/4-x,1/4-z -x,-y,-z 1/2+x,y,1/2-z " "3/4+y,1/4-x,3/4-z 3/4-y,3/4+x,1/4-z 1/2+x,-y,z -x,y,1/2+z 3/4-y,1/4-x,1/4+z " "3/4+y,3/4+x,3/4+z 1/2+x,1/2+y,1/2+z -x,1/2-y,z 3/4-y,1/4+x,3/4+z " "3/4+y,3/4-x,1/4+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z 3/4+y,1/4+x,1/4-z " "3/4-y,3/4-x,3/4-z 1/2-x,1/2-y,1/2-z x,1/2+y,-z 1/4+y,3/4-x,1/4-z " "1/4-y,1/4+x,3/4-z x,1/2-y,1/2+z 1/2-x,1/2+y,z 1/4-y,3/4-x,3/4+z " "1/4+y,1/4+x,1/4+z", "x,y,z -y,x-y,z y-x,-x,z", "x,y,z -y,x-y,1/3+z y-x,-x,2/3+z", "x,y,z -y,x-y,2/3+z y-x,-x,1/3+z", "x,y,z -y,x-y,z y-x,-x,z " "2/3+x,1/3+y,1/3+z 2/3-y,1/3+x-y,1/3+z " "2/3+y-x,1/3-x,1/3+z 1/3+x,2/3+y,2/3+z " "1/3-y,2/3+x-y,2/3+z 1/3+y-x,2/3-x,2/3+z", "x,y,z z,x,y y,z,x", "x,y,z -y,x-y,z y-x,-x,z -x,-y,-z y,y-x,-z x-y,x,-z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,-z y,y-x,-z x-y,x,-z 2/3+x,1/3+y,1/3+z " "2/3-y,1/3+x-y,1/3+z 2/3+y-x,1/3-x,1/3+z 2/3-x,1/3-y,1/3-z " "2/3+y,1/3+y-x,1/3-z 2/3+x-y,1/3+x,1/3-z 1/3+x,2/3+y,2/3+z " "1/3-y,2/3+x-y,2/3+z 1/3+y-x,2/3-x,2/3+z 1/3-x,2/3-y,2/3-z " "1/3+y,2/3+y-x,2/3-z 1/3+x-y,2/3+x,2/3-z", "x,y,z z,x,y y,z,x -x,-y,-z -z,-x,-y -y,-z,-x", "x,y,z -y,x-y,z y-x,-x,z -y,-x,-z y-x,y,-z x,x-y,-z", "x,y,z -y,x-y,z y-x,-x,z y,x,-z x-y,-y,-z -x,y-x,-z", "x,y,z -y,x-y,1/3+z y-x,-x,2/3+z -y,-x,2/3-z y-x,y,1/3-z x,x-y,-z", "x,y,z -y,x-y,1/3+z y-x,-x,2/3+z y,x,-z x-y,-y,2/3-z -x,y-x,1/3-z", "x,y,z -y,x-y,2/3+z y-x,-x,1/3+z -y,-x,1/3-z y-x,y,2/3-z x,x-y,-z", "x,y,z -y,x-y,2/3+z y-x,-x,1/3+z y,x,-z x-y,-y,1/3-z -x,y-x,2/3-z", "x,y,z -y,x-y,z -x+y,-x,z y,x,-z -x,-x+y,-z x-y,-y,-z 2/3+x,1/3+y,1/3+z " "2/3-y,1/3+x-y,1/3+z 2/3-x+y,1/3-x,1/3+z 2/3+y,1/3+x,1/3-z " "2/3-x,1/3-x+y,1/3-z 2/3+x-y,1/3-y,1/3-z 1/3+x,2/3+y,2/3+z " "1/3-y,2/3+x-y,2/3+z 1/3-x+y,2/3-x,2/3+z 1/3+y,2/3+x,2/3-z " "1/3-x,2/3-x+y,2/3-z 1/3+x-y,2/3-y,2/3-z", "x,y,z z,x,y y,z,x -y,-x,-z -z,-y,-x -x,-z,-y", "x,y,z -y,x-y,z y-x,-x,z -y,-x,z y-x,y,z x,x-y,z", "x,y,z -y,x-y,z y-x,-x,z y,x,z x-y,-y,z -x,y-x,z", "x,y,z -y,x-y,z y-x,-x,z -y,-x,1/2+z y-x,y,1/2+z x,x-y,1/2+z", "x,y,z -y,x-y,z y-x,-x,z y,x,1/2+z x-y,-y,1/2+z -x,y-x,1/2+z", "x,y,z -y,x-y,z y-x,-x,z -y,-x,z y-x,y,z x,x-y,z 2/3+x,1/3+y,1/3+z " "2/3-y,1/3+x-y,1/3+z 2/3+y-x,1/3-x,1/3+z 2/3-y,1/3-x,1/3+z " "2/3+y-x,1/3+y,1/3+z 2/3+x,1/3+x-y,1/3+z 1/3+x,2/3+y,2/3+z " "1/3-y,2/3+x-y,2/3+z 1/3+y-x,2/3-x,2/3+z 1/3-y,2/3-x,2/3+z " "1/3+y-x,2/3+y,2/3+z 1/3+x,2/3+x-y,2/3+z", "x,y,z z,x,y y,z,x y,x,z x,z,y z,y,x", "x,y,z -y,x-y,z y-x,-x,z -y,-x,1/2+z y-x,y,1/2+z x,x-y,1/2+z " "2/3+x,1/3+y,1/3+z 2/3-y,1/3+x-y,1/3+z 2/3+y-x,1/3-x,1/3+z 2/3-y,1/3-x,5/6+z " "2/3+y-x,1/3+y,5/6+z 2/3+x,1/3+x-y,5/6+z 1/3+x,2/3+y,2/3+z " "1/3-y,2/3+x-y,2/3+z 1/3+y-x,2/3-x,2/3+z 1/3-y,2/3-x,1/6+z " "1/3+y-x,2/3+y,1/6+z 1/3+x,2/3+x-y,1/6+z", "x,y,z z,x,y y,z,x 1/2+y,1/2+x,1/2+z 1/2+x,1/2+z,1/2+y 1/2+z,1/2+y,1/2+x", "x,y,z -y,x-y,z y-x,-x,z -y,-x,-z y-x,y,-z x,x-y,-z -x,-y,-z y,y-x,-z " "x-y,x,-z y,x,z x-y,-y,z -x,y-x,z", "x,y,z -y,x-y,z y-x,-x,z -y,-x,1/2-z y-x,y,1/2-z x,x-y,1/2-z -x,-y,-z " "y,y-x,-z x-y,x,-z y,x,1/2+z x-y,-y,1/2+z -x,y-x,1/2+z", "x,y,z -y,x-y,z y-x,-x,z y,x,-z x-y,-y,-z -x,y-x,-z -x,-y,-z y,y-x,-z " "x-y,x,-z -y,-x,z y-x,y,z x,x-y,z", "x,y,z -y,x-y,z y-x,-x,z y,x,1/2-z x-y,-y,1/2-z -x,y-x,1/2-z -x,-y,-z " "y,y-x,-z x-y,x,-z -y,-x,1/2+z y-x,y,1/2+z x,x-y,1/2+z", "x,y,z -y,x-y,z y-x,-x,z y,x,-z x-y,-y,-z -x,y-x,-z -x,-y,-z y,y-x,-z " "x-y,x,-z -y,-x,z y-x,y,z x,x-y,z 2/3+x,1/3+y,1/3+z 2/3-y,1/3+x-y,1/3+z " "2/3+y-x,1/3-x,1/3+z 2/3+y,1/3+x,1/3-z 2/3+x-y,1/3-y,1/3-z " "2/3-x,1/3+y-x,1/3-z 2/3-x,1/3-y,1/3-z 2/3+y,1/3+y-x,1/3-z " "2/3+x-y,1/3+x,1/3-z 2/3-y,1/3-x,1/3+z 2/3+y-x,1/3+y,1/3+z " "2/3+x,1/3+x-y,1/3+z 1/3+x,2/3+y,2/3+z 1/3-y,2/3+x-y,2/3+z " "1/3+y-x,2/3-x,2/3+z 1/3+y,2/3+x,2/3-z 1/3+x-y,2/3-y,2/3-z " "1/3-x,2/3+y-x,2/3-z 1/3-x,2/3-y,2/3-z 1/3+y,2/3+y-x,2/3-z " "1/3+x-y,2/3+x,2/3-z 1/3-y,2/3-x,2/3+z 1/3+y-x,2/3+y,2/3+z " "1/3+x,2/3+x-y,2/3+z", "x,y,z z,x,y y,z,x -y,-x,-z -x,-z,-y -z,-y,-x -x,-y,-z -z,-x,-y -y,-z,-x " "y,x,z x,z,y z,y,x", "x,y,z -y,x-y,z y-x,-x,z y,x,1/2-z x-y,-y,1/2-z -x,y-x,1/2-z -x,-y,-z " "y,y-x,-z x-y,x,-z -y,-x,1/2+z y-x,y,1/2+z x,x-y,1/2+z 2/3+x,1/3+y,1/3+z " "2/3-y,1/3+x-y,1/3+z 2/3+y-x,1/3-x,1/3+z 2/3+y,1/3+x,5/6-z " "2/3+x-y,1/3-y,5/6-z 2/3-x,1/3+y-x,5/6-z 2/3-x,1/3-y,1/3-z " "2/3+y,1/3+y-x,1/3-z 2/3+x-y,1/3+x,1/3-z 2/3-y,1/3-x,5/6+z " "2/3+y-x,1/3+y,5/6+z 2/3+x,1/3+x-y,5/6+z 1/3+x,2/3+y,2/3+z " "1/3-y,2/3+x-y,2/3+z 1/3+y-x,2/3-x,2/3+z 1/3+y,2/3+x,1/6-z " "1/3+x-y,2/3-y,1/6-z 1/3-x,2/3+y-x,1/6-z 1/3-x,2/3-y,2/3-z " "1/3+y,2/3+y-x,2/3-z 1/3+x-y,2/3+x,2/3-z 1/3-y,2/3-x,1/6+z " "1/3+y-x,2/3+y,1/6+z 1/3+x,2/3+x-y,1/6+z", "x,y,z z,x,y y,z,x 1/2-y,1/2-x,1/2-z 1/2-x,1/2-z,1/2-y 1/2-z,1/2-y,1/2-x " "-x,-y,-z -z,-x,-y -y,-z,-x 1/2+y,1/2+x,1/2+z 1/2+x,1/2+z,1/2+y " "1/2+z,1/2+y,1/2+x", "x,y,z -y,x-y,z y-x,-x,z -x,-y,z y,y-x,z x-y,x,z", "x,y,z -y,x-y,1/3+z y-x,-x,2/3+z -x,-y,1/2+z y,y-x,5/6+z x-y,x,1/6+z", "x,y,z -y,x-y,2/3+z y-x,-x,1/3+z -x,-y,1/2+z y,y-x,1/6+z x-y,x,5/6+z", "x,y,z -y,x-y,2/3+z y-x,-x,1/3+z -x,-y,z y,y-x,2/3+z x-y,x,1/3+z", "x,y,z -y,x-y,1/3+z y-x,-x,2/3+z -x,-y,z y,y-x,1/3+z x-y,x,2/3+z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,1/2+z y,y-x,1/2+z x-y,x,1/2+z", "x,y,z -y,x-y,z y-x,-x,z x,y,-z -y,x-y,-z y-x,-x,-z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,z y,y-x,z x-y,x,z -x,-y,-z y,y-x,-z x-y,x,-z " "x,y,-z -y,x-y,-z y-x,-x,-z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,1/2+z y,y-x,1/2+z x-y,x,1/2+z -x,-y,-z " "y,y-x,-z x-y,x,-z x,y,1/2-z -y,x-y,1/2-z y-x,-x,1/2-z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,z y,y-x,z x-y,x,z y,x,-z x-y,-y,-z -x,y-x,-z " "-y,-x,-z y-x,y,-z x,x-y,-z", "x,y,z -y,x-y,1/3+z y-x,-x,2/3+z -x,-y,1/2+z y,y-x,5/6+z x-y,x,1/6+z " "y,x,1/3-z x-y,-y,-z -x,y-x,2/3-z -y,-x,5/6-z y-x,y,1/2-z x,x-y,1/6-z", "x,y,z -y,x-y,2/3+z y-x,-x,1/3+z -x,-y,1/2+z y,y-x,1/6+z x-y,x,5/6+z " "y,x,2/3-z x-y,-y,-z -x,y-x,1/3-z -y,-x,1/6-z y-x,y,1/2-z x,x-y,5/6-z", "x,y,z -y,x-y,2/3+z y-x,-x,1/3+z -x,-y,z y,y-x,2/3+z x-y,x,1/3+z y,x,2/3-z " "x-y,-y,-z -x,y-x,1/3-z -y,-x,2/3-z y-x,y,-z x,x-y,1/3-z", "x,y,z -y,x-y,1/3+z y-x,-x,2/3+z -x,-y,z y,y-x,1/3+z x-y,x,2/3+z y,x,1/3-z " "x-y,-y,-z -x,y-x,2/3-z -y,-x,1/3-z y-x,y,-z x,x-y,2/3-z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,1/2+z y,y-x,1/2+z x-y,x,1/2+z y,x,-z " "x-y,-y,-z -x,y-x,-z -y,-x,1/2-z y-x,y,1/2-z x,x-y,1/2-z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,z y,y-x,z x-y,x,z -y,-x,z y-x,y,z x,x-y,z " "y,x,z x-y,-y,z -x,y-x,z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,z y,y-x,z x-y,x,z -y,-x,1/2+z y-x,y,1/2+z " "x,x-y,1/2+z y,x,1/2+z x-y,-y,1/2+z -x,y-x,1/2+z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,1/2+z y,y-x,1/2+z x-y,x,1/2+z -y,-x,1/2+z " "y-x,y,1/2+z x,x-y,1/2+z y,x,z x-y,-y,z -x,y-x,z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,1/2+z y,y-x,1/2+z x-y,x,1/2+z -y,-x,z y-x,y,z " "x,x-y,z y,x,1/2+z x-y,-y,1/2+z -x,y-x,1/2+z", "x,y,z -y,x-y,z y-x,-x,z x,y,-z -y,x-y,-z y-x,-x,-z -y,-x,z y-x,y,z x,x-y,z " "-y,-x,-z y-x,y,-z x,x-y,-z", "x,y,z -y,x-y,z y-x,-x,z x,y,1/2-z -y,x-y,1/2-z y-x,-x,1/2-z -y,-x,1/2+z " "y-x,y,1/2+z x,x-y,1/2+z -y,-x,-z y-x,y,-z x,x-y,-z", "x,y,z -y,x-y,z y-x,-x,z x,y,-z -y,x-y,-z y-x,-x,-z y,x,-z x-y,-y,-z " "-x,y-x,-z y,x,z x-y,-y,z -x,y-x,z", "x,y,z -y,x-y,z y-x,-x,z x,y,1/2-z -y,x-y,1/2-z y-x,-x,1/2-z y,x,-z " "x-y,-y,-z -x,y-x,-z y,x,1/2+z x-y,-y,1/2+z -x,y-x,1/2+z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,z y,y-x,z x-y,x,z y,x,-z x-y,-y,-z -x,y-x,-z " "-y,-x,-z y-x,y,-z x,x-y,-z -x,-y,-z y,y-x,-z x-y,x,-z x,y,-z -y,x-y,-z " "y-x,-x,-z -y,-x,z y-x,y,z x,x-y,z y,x,z x-y,-y,z -x,y-x,z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,z y,y-x,z x-y,x,z y,x,1/2-z x-y,-y,1/2-z " "-x,y-x,1/2-z -y,-x,1/2-z y-x,y,1/2-z x,x-y,1/2-z -x,-y,-z y,y-x,-z x-y,x,-z " "x,y,-z -y,x-y,-z y-x,-x,-z -y,-x,1/2+z y-x,y,1/2+z x,x-y,1/2+z y,x,1/2+z " "x-y,-y,1/2+z -x,y-x,1/2+z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,1/2+z y,y-x,1/2+z x-y,x,1/2+z y,x,1/2-z " "x-y,-y,1/2-z -x,y-x,1/2-z -y,-x,-z y-x,y,-z x,x-y,-z -x,-y,-z y,y-x,-z " "x-y,x,-z x,y,1/2-z -y,x-y,1/2-z y-x,-x,1/2-z -y,-x,1/2+z y-x,y,1/2+z " "x,x-y,1/2+z y,x,z x-y,-y,z -x,y-x,z", "x,y,z -y,x-y,z y-x,-x,z -x,-y,1/2+z y,y-x,1/2+z x-y,x,1/2+z y,x,-z " "x-y,-y,-z -x,y-x,-z -y,-x,1/2-z y-x,y,1/2-z x,x-y,1/2-z -x,-y,-z y,y-x,-z " "x-y,x,-z x,y,1/2-z -y,x-y,1/2-z y-x,-x,1/2-z -y,-x,z y-x,y,z x,x-y,z " "y,x,1/2+z x-y,-y,1/2+z -x,y-x,1/2+z", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2+y,1/2-z x,1/2-y,1/2-z " "z,1/2+x,1/2+y z,1/2-x,1/2-y -z,1/2-x,1/2+y -z,1/2+x,1/2-y y,1/2+z,1/2+x " "-y,1/2+z,1/2-x y,1/2-z,1/2-x -y,1/2-z,1/2+x 1/2+x,y,1/2+z 1/2-x,-y,1/2+z " "1/2-x,y,1/2-z 1/2+x,-y,1/2-z 1/2+z,x,1/2+y 1/2+z,-x,1/2-y 1/2-z,-x,1/2+y " "1/2-z,x,1/2-y 1/2+y,z,1/2+x 1/2-y,z,1/2-x 1/2+y,-z,1/2-x 1/2-y,-z,1/2+x " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+z,1/2+x,y " "1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x 1/2-y,1/2+z,-x " "1/2+y,1/2-z,-x 1/2-y,1/2-z,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z 1/2-x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2-z 1/2+z,1/2+x,1/2+y 1/2+z,1/2-x,1/2-y 1/2-z,1/2-x,1/2+y " "1/2-z,1/2+x,1/2-y 1/2+y,1/2+z,1/2+x 1/2-y,1/2+z,1/2-x 1/2+y,1/2-z,1/2-x " "1/2-y,1/2-z,1/2+x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x 1/2+x,1/2+y,1/2+z -x,1/2-y,z 1/2-x,y,-z x,-y,1/2-z " "1/2+z,1/2+x,1/2+y z,-x,1/2-y -z,1/2-x,y 1/2-z,x,-y 1/2+y,1/2+z,1/2+x " "1/2-y,z,-x y,-z,1/2-x -y,1/2-z,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x -x,-y,-z x,y,-z x,-y,z -x,y,z -z,-x,-y -z,x,y z,x,-y z,-x,y " "-y,-z,-x y,-z,x -y,z,x y,z,-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2+z 1/2-z,1/2-x,1/2-y 1/2-z,1/2+x,1/2+y 1/2+z,1/2+x,1/2-y " "1/2+z,1/2-x,1/2+y 1/2-y,1/2-z,1/2-x 1/2+y,1/2-z,1/2+x 1/2-y,1/2+z,1/2+x " "1/2+y,1/2+z,1/2-x", "x,y,z 1/2-x,1/2-y,z 1/2-x,y,1/2-z x,1/2-y,1/2-z z,x,y z,1/2-x,1/2-y " "1/2-z,1/2-x,y 1/2-z,x,1/2-y y,z,x 1/2-y,z,1/2-x y,1/2-z,1/2-x 1/2-y,1/2-z,x " "-x,-y,-z 1/2+x,1/2+y,-z 1/2+x,-y,1/2+z -x,1/2+y,1/2+z -z,-x,-y " "-z,1/2+x,1/2+y 1/2+z,1/2+x,-y 1/2+z,-x,1/2+y -y,-z,-x 1/2+y,-z,1/2+x " "-y,1/2+z,1/2+x 1/2+y,1/2+z,-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x -x,-y,-z x,y,-z x,-y,z -x,y,z -z,-x,-y -z,x,y z,x,-y z,-x,y " "-y,-z,-x y,-z,x -y,z,x y,z,-x x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2+y,1/2-z " "x,1/2-y,1/2-z z,1/2+x,1/2+y z,1/2-x,1/2-y -z,1/2-x,1/2+y -z,1/2+x,1/2-y " "y,1/2+z,1/2+x -y,1/2+z,1/2-x y,1/2-z,1/2-x -y,1/2-z,1/2+x -x,1/2-y,1/2-z " "x,1/2+y,1/2-z x,1/2-y,1/2+z -x,1/2+y,1/2+z -z,1/2-x,1/2-y -z,1/2+x,1/2+y " "z,1/2+x,1/2-y z,1/2-x,1/2+y -y,1/2-z,1/2-x y,1/2-z,1/2+x -y,1/2+z,1/2+x " "y,1/2+z,1/2-x 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z 1/2+x,-y,1/2-z " "1/2+z,x,1/2+y 1/2+z,-x,1/2-y 1/2-z,-x,1/2+y 1/2-z,x,1/2-y 1/2+y,z,1/2+x " "1/2-y,z,1/2-x 1/2+y,-z,1/2-x 1/2-y,-z,1/2+x 1/2-x,-y,1/2-z 1/2+x,y,1/2-z " "1/2+x,-y,1/2+z 1/2-x,y,1/2+z 1/2-z,-x,1/2-y 1/2-z,x,1/2+y 1/2+z,x,1/2-y " "1/2+z,-x,1/2+y 1/2-y,-z,1/2-x 1/2+y,-z,1/2+x 1/2-y,z,1/2+x 1/2+y,z,1/2-x " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+z,1/2+x,y " "1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x 1/2-y,1/2+z,-x " "1/2+y,1/2-z,-x 1/2-y,1/2-z,x 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z 1/2+x,1/2-y,z " "1/2-x,1/2+y,z 1/2-z,1/2-x,-y 1/2-z,1/2+x,y 1/2+z,1/2+x,-y 1/2+z,1/2-x,y " "1/2-y,1/2-z,-x 1/2+y,1/2-z,x 1/2-y,1/2+z,x 1/2+y,1/2+z,-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/4-x,1/4-y,1/4-z 1/4+x,1/4+y,1/4-z 1/4+x,1/4-y,1/4+z " "1/4-x,1/4+y,1/4+z 1/4-z,1/4-x,1/4-y 1/4-z,1/4+x,1/4+y 1/4+z,1/4+x,1/4-y " "1/4+z,1/4-x,1/4+y 1/4-y,1/4-z,1/4-x 1/4+y,1/4-z,1/4+x 1/4-y,1/4+z,1/4+x " "1/4+y,1/4+z,1/4-x x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2+y,1/2-z x,1/2-y,1/2-z " "z,1/2+x,1/2+y z,1/2-x,1/2-y -z,1/2-x,1/2+y -z,1/2+x,1/2-y y,1/2+z,1/2+x " "-y,1/2+z,1/2-x y,1/2-z,1/2-x -y,1/2-z,1/2+x 1/4-x,3/4-y,3/4-z " "1/4+x,3/4+y,3/4-z 1/4+x,3/4-y,3/4+z 1/4-x,3/4+y,3/4+z 1/4-z,3/4-x,3/4-y " "1/4-z,3/4+x,3/4+y 1/4+z,3/4+x,3/4-y 1/4+z,3/4-x,3/4+y 1/4-y,3/4-z,3/4-x " "1/4+y,3/4-z,3/4+x 1/4-y,3/4+z,3/4+x 1/4+y,3/4+z,3/4-x 1/2+x,y,1/2+z " "1/2-x,-y,1/2+z 1/2-x,y,1/2-z 1/2+x,-y,1/2-z 1/2+z,x,1/2+y 1/2+z,-x,1/2-y " "1/2-z,-x,1/2+y 1/2-z,x,1/2-y 1/2+y,z,1/2+x 1/2-y,z,1/2-x 1/2+y,-z,1/2-x " "1/2-y,-z,1/2+x 3/4-x,1/4-y,3/4-z 3/4+x,1/4+y,3/4-z 3/4+x,1/4-y,3/4+z " "3/4-x,1/4+y,3/4+z 3/4-z,1/4-x,3/4-y 3/4-z,1/4+x,3/4+y 3/4+z,1/4+x,3/4-y " "3/4+z,1/4-x,3/4+y 3/4-y,1/4-z,3/4-x 3/4+y,1/4-z,3/4+x 3/4-y,1/4+z,3/4+x " "3/4+y,1/4+z,3/4-x 1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z " "1/2+z,1/2+x,y 1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x " "1/2-y,1/2+z,-x 1/2+y,1/2-z,-x 1/2-y,1/2-z,x 3/4-x,3/4-y,1/4-z " "3/4+x,3/4+y,1/4-z 3/4+x,3/4-y,1/4+z 3/4-x,3/4+y,1/4+z 3/4-z,3/4-x,1/4-y " "3/4-z,3/4+x,1/4+y 3/4+z,3/4+x,1/4-y 3/4+z,3/4-x,1/4+y 3/4-y,3/4-z,1/4-x " "3/4+y,3/4-z,1/4+x 3/4-y,3/4+z,1/4+x 3/4+y,3/4+z,1/4-x", "x,y,z 1/4-x,1/4-y,z 1/4-x,y,1/4-z x,1/4-y,1/4-z z,x,y z,1/4-x,1/4-y " "1/4-z,1/4-x,y 1/4-z,x,1/4-y y,z,x 1/4-y,z,1/4-x y,1/4-z,1/4-x 1/4-y,1/4-z,x " "-x,-y,-z 3/4+x,3/4+y,-z 3/4+x,-y,3/4+z -x,3/4+y,3/4+z -z,-x,-y " "-z,3/4+x,3/4+y 3/4+z,3/4+x,-y 3/4+z,-x,3/4+y -y,-z,-x 3/4+y,-z,3/4+x " "-y,3/4+z,3/4+x 3/4+y,3/4+z,-x x,1/2+y,1/2+z 1/4-x,3/4-y,1/2+z " "1/4-x,1/2+y,3/4-z x,3/4-y,3/4-z z,1/2+x,1/2+y z,3/4-x,3/4-y " "1/4-z,3/4-x,1/2+y 1/4-z,1/2+x,3/4-y y,1/2+z,1/2+x 1/4-y,1/2+z,3/4-x " "y,3/4-z,3/4-x 1/4-y,3/4-z,1/2+x -x,1/2-y,1/2-z 3/4+x,1/4+y,1/2-z " "3/4+x,1/2-y,1/4+z -x,1/4+y,1/4+z -z,1/2-x,1/2-y -z,1/4+x,1/4+y " "3/4+z,1/4+x,1/2-y 3/4+z,1/2-x,1/4+y -y,1/2-z,1/2-x 3/4+y,1/2-z,1/4+x " "-y,1/4+z,1/4+x 3/4+y,1/4+z,1/2-x 1/2+x,y,1/2+z 3/4-x,1/4-y,1/2+z " "3/4-x,y,3/4-z 1/2+x,1/4-y,3/4-z 1/2+z,x,1/2+y 1/2+z,1/4-x,3/4-y " "3/4-z,1/4-x,1/2+y 3/4-z,x,3/4-y 1/2+y,z,1/2+x 3/4-y,z,3/4-x " "1/2+y,1/4-z,3/4-x 3/4-y,1/4-z,1/2+x 1/2-x,-y,1/2-z 1/4+x,3/4+y,1/2-z " "1/4+x,-y,1/4+z 1/2-x,3/4+y,1/4+z 1/2-z,-x,1/2-y 1/2-z,3/4+x,1/4+y " "1/4+z,3/4+x,1/2-y 1/4+z,-x,1/4+y 1/2-y,-z,1/2-x 1/4+y,-z,1/4+x " "1/2-y,3/4+z,1/4+x 1/4+y,3/4+z,1/2-x 1/2+x,1/2+y,z 3/4-x,3/4-y,z " "3/4-x,1/2+y,1/4-z 1/2+x,3/4-y,1/4-z 1/2+z,1/2+x,y 1/2+z,3/4-x,1/4-y " "3/4-z,3/4-x,y 3/4-z,1/2+x,1/4-y 1/2+y,1/2+z,x 3/4-y,1/2+z,1/4-x " "1/2+y,3/4-z,1/4-x 3/4-y,3/4-z,x 1/2-x,1/2-y,-z 1/4+x,1/4+y,-z " "1/4+x,1/2-y,3/4+z 1/2-x,1/4+y,3/4+z 1/2-z,1/2-x,-y 1/2-z,1/4+x,3/4+y " "1/4+z,1/4+x,-y 1/4+z,1/2-x,3/4+y 1/2-y,1/2-z,-x 1/4+y,1/2-z,3/4+x " "1/2-y,1/4+z,3/4+x 1/4+y,1/4+z,-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x -x,-y,-z x,y,-z x,-y,z -x,y,z -z,-x,-y -z,x,y z,x,-y z,-x,y " "-y,-z,-x y,-z,x -y,z,x y,z,-x 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z 1/2+z,1/2+x,1/2+y 1/2+z,1/2-x,1/2-y " "1/2-z,1/2-x,1/2+y 1/2-z,1/2+x,1/2-y 1/2+y,1/2+z,1/2+x 1/2-y,1/2+z,1/2-x " "1/2+y,1/2-z,1/2-x 1/2-y,1/2-z,1/2+x 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z " "1/2+x,1/2-y,1/2+z 1/2-x,1/2+y,1/2+z 1/2-z,1/2-x,1/2-y 1/2-z,1/2+x,1/2+y " "1/2+z,1/2+x,1/2-y 1/2+z,1/2-x,1/2+y 1/2-y,1/2-z,1/2-x 1/2+y,1/2-z,1/2+x " "1/2-y,1/2+z,1/2+x 1/2+y,1/2+z,1/2-x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x -x,-y,-z 1/2+x,y,1/2-z x,1/2-y,1/2+z 1/2-x,1/2+y,z -z,-x,-y " "1/2-z,1/2+x,y 1/2+z,x,1/2-y z,1/2-x,1/2+y -y,-z,-x y,1/2-z,1/2+x " "1/2-y,1/2+z,x 1/2+y,z,1/2-x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x -x,-y,-z 1/2+x,y,1/2-z x,1/2-y,1/2+z 1/2-x,1/2+y,z -z,-x,-y " "1/2-z,1/2+x,y 1/2+z,x,1/2-y z,1/2-x,1/2+y -y,-z,-x y,1/2-z,1/2+x " "1/2-y,1/2+z,x 1/2+y,z,1/2-x 1/2+x,1/2+y,1/2+z -x,1/2-y,z 1/2-x,y,-z " "x,-y,1/2-z 1/2+z,1/2+x,1/2+y z,-x,1/2-y -z,1/2-x,y 1/2-z,x,-y " "1/2+y,1/2+z,1/2+x 1/2-y,z,-x y,-z,1/2-x -y,1/2-z,x 1/2-x,1/2-y,1/2-z " "x,1/2+y,-z 1/2+x,-y,z -x,y,1/2+z 1/2-z,1/2-x,1/2-y -z,x,1/2+y z,1/2+x,-y " "1/2+z,-x,y 1/2-y,1/2-z,1/2-x 1/2+y,-z,x -y,z,1/2+x y,1/2+z,-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,-z -y,-x,-z y,-x,z -y,x,z x,z,-y -x,z,y -x,-z,-y x,-z,y " "z,y,-x z,-y,x -z,y,x -z,-y,-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z 1/2+y,1/2-x,1/2+z " "1/2-y,1/2+x,1/2+z 1/2+x,1/2+z,1/2-y 1/2-x,1/2+z,1/2+y 1/2-x,1/2-z,1/2-y " "1/2+x,1/2-z,1/2+y 1/2+z,1/2+y,1/2-x 1/2+z,1/2-y,1/2+x 1/2-z,1/2+y,1/2+x " "1/2-z,1/2-y,1/2-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,-z -y,-x,-z y,-x,z -y,x,z x,z,-y -x,z,y -x,-z,-y x,-z,y " "z,y,-x z,-y,x -z,y,x -z,-y,-x x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2+y,1/2-z " "x,1/2-y,1/2-z z,1/2+x,1/2+y z,1/2-x,1/2-y -z,1/2-x,1/2+y -z,1/2+x,1/2-y " "y,1/2+z,1/2+x -y,1/2+z,1/2-x y,1/2-z,1/2-x -y,1/2-z,1/2+x y,1/2+x,1/2-z " "-y,1/2-x,1/2-z y,1/2-x,1/2+z -y,1/2+x,1/2+z x,1/2+z,1/2-y -x,1/2+z,1/2+y " "-x,1/2-z,1/2-y x,1/2-z,1/2+y z,1/2+y,1/2-x z,1/2-y,1/2+x -z,1/2+y,1/2+x " "-z,1/2-y,1/2-x 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z 1/2+x,-y,1/2-z " "1/2+z,x,1/2+y 1/2+z,-x,1/2-y 1/2-z,-x,1/2+y 1/2-z,x,1/2-y 1/2+y,z,1/2+x " "1/2-y,z,1/2-x 1/2+y,-z,1/2-x 1/2-y,-z,1/2+x 1/2+y,x,1/2-z 1/2-y,-x,1/2-z " "1/2+y,-x,1/2+z 1/2-y,x,1/2+z 1/2+x,z,1/2-y 1/2-x,z,1/2+y 1/2-x,-z,1/2-y " "1/2+x,-z,1/2+y 1/2+z,y,1/2-x 1/2+z,-y,1/2+x 1/2-z,y,1/2+x 1/2-z,-y,1/2-x " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+z,1/2+x,y " "1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x 1/2-y,1/2+z,-x " "1/2+y,1/2-z,-x 1/2-y,1/2-z,x 1/2+y,1/2+x,-z 1/2-y,1/2-x,-z 1/2+y,1/2-x,z " "1/2-y,1/2+x,z 1/2+x,1/2+z,-y 1/2-x,1/2+z,y 1/2-x,1/2-z,-y 1/2+x,1/2-z,y " "1/2+z,1/2+y,-x 1/2+z,1/2-y,x 1/2-z,1/2+y,x 1/2-z,1/2-y,-x", "x,y,z -x,1/2-y,1/2+z 1/2-x,1/2+y,-z 1/2+x,-y,1/2-z z,x,y 1/2+z,-x,1/2-y " "-z,1/2-x,1/2+y 1/2-z,1/2+x,-y y,z,x 1/2-y,1/2+z,-x 1/2+y,-z,1/2-x " "-y,1/2-z,1/2+x 3/4+y,1/4+x,3/4-z 1/4-y,1/4-x,1/4-z 1/4+y,3/4-x,3/4+z " "3/4-y,3/4+x,1/4+z 3/4+x,1/4+z,3/4-y 3/4-x,3/4+z,1/4+y 1/4-x,1/4-z,1/4-y " "1/4+x,3/4-z,3/4+y 3/4+z,1/4+y,3/4-x 1/4+z,3/4-y,3/4+x 3/4-z,3/4+y,1/4+x " "1/4-z,1/4-y,1/4-x x,1/2+y,1/2+z -x,-y,z 1/2-x,y,1/2-z 1/2+x,1/2-y,-z " "z,1/2+x,1/2+y 1/2+z,1/2-x,-y -z,-x,y 1/2-z,x,1/2-y y,1/2+z,1/2+x " "1/2-y,z,1/2-x 1/2+y,1/2-z,-x -y,-z,x 3/4+y,3/4+x,1/4-z 1/4-y,3/4-x,3/4-z " "1/4+y,1/4-x,1/4+z 3/4-y,1/4+x,3/4+z 3/4+x,3/4+z,1/4-y 3/4-x,1/4+z,3/4+y " "1/4-x,3/4-z,3/4-y 1/4+x,1/4-z,1/4+y 3/4+z,3/4+y,1/4-x 1/4+z,1/4-y,1/4+x " "3/4-z,1/4+y,3/4+x 1/4-z,3/4-y,3/4-x 1/2+x,y,1/2+z 1/2-x,1/2-y,z " "-x,1/2+y,1/2-z x,-y,-z 1/2+z,x,1/2+y z,-x,-y 1/2-z,1/2-x,y -z,1/2+x,1/2-y " "1/2+y,z,1/2+x -y,1/2+z,1/2-x y,-z,-x 1/2-y,1/2-z,x 1/4+y,1/4+x,1/4-z " "3/4-y,1/4-x,3/4-z 3/4+y,3/4-x,1/4+z 1/4-y,3/4+x,3/4+z 1/4+x,1/4+z,1/4-y " "1/4-x,3/4+z,3/4+y 3/4-x,1/4-z,3/4-y 3/4+x,3/4-z,1/4+y 1/4+z,1/4+y,1/4-x " "3/4+z,3/4-y,1/4+x 1/4-z,3/4+y,3/4+x 3/4-z,1/4-y,3/4-x 1/2+x,1/2+y,z " "1/2-x,-y,1/2+z -x,y,-z x,1/2-y,1/2-z 1/2+z,1/2+x,y z,1/2-x,1/2-y " "1/2-z,-x,1/2+y -z,x,-y 1/2+y,1/2+z,x -y,z,-x y,1/2-z,1/2-x 1/2-y,-z,1/2+x " "1/4+y,3/4+x,3/4-z 3/4-y,3/4-x,1/4-z 3/4+y,1/4-x,3/4+z 1/4-y,1/4+x,1/4+z " "1/4+x,3/4+z,3/4-y 1/4-x,1/4+z,1/4+y 3/4-x,3/4-z,1/4-y 3/4+x,1/4-z,3/4+y " "1/4+z,3/4+y,3/4-x 3/4+z,1/4-y,3/4+x 1/4-z,1/4+y,1/4+x 3/4-z,3/4-y,1/4-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,-z -y,-x,-z y,-x,z -y,x,z x,z,-y -x,z,y -x,-z,-y x,-z,y " "z,y,-x z,-y,x -z,y,x -z,-y,-x 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z 1/2+z,1/2+x,1/2+y 1/2+z,1/2-x,1/2-y " "1/2-z,1/2-x,1/2+y 1/2-z,1/2+x,1/2-y 1/2+y,1/2+z,1/2+x 1/2-y,1/2+z,1/2-x " "1/2+y,1/2-z,1/2-x 1/2-y,1/2-z,1/2+x 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z " "1/2+y,1/2-x,1/2+z 1/2-y,1/2+x,1/2+z 1/2+x,1/2+z,1/2-y 1/2-x,1/2+z,1/2+y " "1/2-x,1/2-z,1/2-y 1/2+x,1/2-z,1/2+y 1/2+z,1/2+y,1/2-x 1/2+z,1/2-y,1/2+x " "1/2-z,1/2+y,1/2+x 1/2-z,1/2-y,1/2-x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x 1/4+y,3/4+x,3/4-z 1/4-y,1/4-x,1/4-z 3/4+y,3/4-x,1/4+z " "3/4-y,1/4+x,3/4+z 1/4+x,3/4+z,3/4-y 3/4-x,1/4+z,3/4+y 1/4-x,1/4-z,1/4-y " "3/4+x,3/4-z,1/4+y 1/4+z,3/4+y,3/4-x 3/4+z,3/4-y,1/4+x 3/4-z,1/4+y,3/4+x " "1/4-z,1/4-y,1/4-x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x 3/4+y,1/4+x,1/4-z 3/4-y,3/4-x,3/4-z 1/4+y,1/4-x,3/4+z " "1/4-y,3/4+x,1/4+z 3/4+x,1/4+z,1/4-y 1/4-x,3/4+z,1/4+y 3/4-x,3/4-z,3/4-y " "1/4+x,1/4-z,3/4+y 3/4+z,1/4+y,1/4-x 1/4+z,1/4-y,3/4+x 1/4-z,3/4+y,1/4+x " "3/4-z,3/4-y,3/4-x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x 3/4+y,1/4+x,1/4-z 3/4-y,3/4-x,3/4-z 1/4+y,1/4-x,3/4+z " "1/4-y,3/4+x,1/4+z 3/4+x,1/4+z,1/4-y 1/4-x,3/4+z,1/4+y 3/4-x,3/4-z,3/4-y " "1/4+x,1/4-z,3/4+y 3/4+z,1/4+y,1/4-x 1/4+z,1/4-y,3/4+x 1/4-z,3/4+y,1/4+x " "3/4-z,3/4-y,3/4-x 1/2+x,1/2+y,1/2+z -x,1/2-y,z 1/2-x,y,-z x,-y,1/2-z " "1/2+z,1/2+x,1/2+y z,-x,1/2-y -z,1/2-x,y 1/2-z,x,-y 1/2+y,1/2+z,1/2+x " "1/2-y,z,-x y,-z,1/2-x -y,1/2-z,x 1/4+y,3/4+x,3/4-z 1/4-y,1/4-x,1/4-z " "3/4+y,3/4-x,1/4+z 3/4-y,1/4+x,3/4+z 1/4+x,3/4+z,3/4-y 3/4-x,1/4+z,3/4+y " "1/4-x,1/4-z,1/4-y 3/4+x,3/4-z,1/4+y 1/4+z,3/4+y,3/4-x 3/4+z,3/4-y,1/4+x " "3/4-z,1/4+y,3/4+x 1/4-z,1/4-y,1/4-x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,z -y,-x,z y,-x,-z -y,x,-z x,z,y -x,z,-y -x,-z,y x,-z,-y " "z,y,x z,-y,-x -z,y,-x -z,-y,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,z -y,-x,z y,-x,-z -y,x,-z x,z,y -x,z,-y -x,-z,y x,-z,-y " "z,y,x z,-y,-x -z,y,-x -z,-y,x x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2+y,1/2-z " "x,1/2-y,1/2-z z,1/2+x,1/2+y z,1/2-x,1/2-y -z,1/2-x,1/2+y -z,1/2+x,1/2-y " "y,1/2+z,1/2+x -y,1/2+z,1/2-x y,1/2-z,1/2-x -y,1/2-z,1/2+x y,1/2+x,1/2+z " "-y,1/2-x,1/2+z y,1/2-x,1/2-z -y,1/2+x,1/2-z x,1/2+z,1/2+y -x,1/2+z,1/2-y " "-x,1/2-z,1/2+y x,1/2-z,1/2-y z,1/2+y,1/2+x z,1/2-y,1/2-x -z,1/2+y,1/2-x " "-z,1/2-y,1/2+x 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z 1/2+x,-y,1/2-z " "1/2+z,x,1/2+y 1/2+z,-x,1/2-y 1/2-z,-x,1/2+y 1/2-z,x,1/2-y 1/2+y,z,1/2+x " "1/2-y,z,1/2-x 1/2+y,-z,1/2-x 1/2-y,-z,1/2+x 1/2+y,x,1/2+z 1/2-y,-x,1/2+z " "1/2+y,-x,1/2-z 1/2-y,x,1/2-z 1/2+x,z,1/2+y 1/2-x,z,1/2-y 1/2-x,-z,1/2+y " "1/2+x,-z,1/2-y 1/2+z,y,1/2+x 1/2+z,-y,1/2-x 1/2-z,y,1/2-x 1/2-z,-y,1/2+x " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+z,1/2+x,y " "1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x 1/2-y,1/2+z,-x " "1/2+y,1/2-z,-x 1/2-y,1/2-z,x 1/2+y,1/2+x,z 1/2-y,1/2-x,z 1/2+y,1/2-x,-z " "1/2-y,1/2+x,-z 1/2+x,1/2+z,y 1/2-x,1/2+z,-y 1/2-x,1/2-z,y 1/2+x,1/2-z,-y " "1/2+z,1/2+y,x 1/2+z,1/2-y,-x 1/2-z,1/2+y,-x 1/2-z,1/2-y,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,z -y,-x,z y,-x,-z -y,x,-z x,z,y -x,z,-y -x,-z,y x,-z,-y " "z,y,x z,-y,-x -z,y,-x -z,-y,x 1/2+x,1/2+y,1/2+z 1/2-x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2-z 1/2+x,1/2-y,1/2-z 1/2+z,1/2+x,1/2+y 1/2+z,1/2-x,1/2-y " "1/2-z,1/2-x,1/2+y 1/2-z,1/2+x,1/2-y 1/2+y,1/2+z,1/2+x 1/2-y,1/2+z,1/2-x " "1/2+y,1/2-z,1/2-x 1/2-y,1/2-z,1/2+x 1/2+y,1/2+x,1/2+z 1/2-y,1/2-x,1/2+z " "1/2+y,1/2-x,1/2-z 1/2-y,1/2+x,1/2-z 1/2+x,1/2+z,1/2+y 1/2-x,1/2+z,1/2-y " "1/2-x,1/2-z,1/2+y 1/2+x,1/2-z,1/2-y 1/2+z,1/2+y,1/2+x 1/2+z,1/2-y,1/2-x " "1/2-z,1/2+y,1/2-x 1/2-z,1/2-y,1/2+x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2+y,1/2+x,1/2+z 1/2-y,1/2-x,1/2+z 1/2+y,1/2-x,1/2-z " "1/2-y,1/2+x,1/2-z 1/2+x,1/2+z,1/2+y 1/2-x,1/2+z,1/2-y 1/2-x,1/2-z,1/2+y " "1/2+x,1/2-z,1/2-y 1/2+z,1/2+y,1/2+x 1/2+z,1/2-y,1/2-x 1/2-z,1/2+y,1/2-x " "1/2-z,1/2-y,1/2+x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2+y,1/2+x,1/2+z 1/2-y,1/2-x,1/2+z 1/2+y,1/2-x,1/2-z " "1/2-y,1/2+x,1/2-z 1/2+x,1/2+z,1/2+y 1/2-x,1/2+z,1/2-y 1/2-x,1/2-z,1/2+y " "1/2+x,1/2-z,1/2-y 1/2+z,1/2+y,1/2+x 1/2+z,1/2-y,1/2-x 1/2-z,1/2+y,1/2-x " "1/2-z,1/2-y,1/2+x x,1/2+y,1/2+z -x,1/2-y,1/2+z -x,1/2+y,1/2-z x,1/2-y,1/2-z " "z,1/2+x,1/2+y z,1/2-x,1/2-y -z,1/2-x,1/2+y -z,1/2+x,1/2-y y,1/2+z,1/2+x " "-y,1/2+z,1/2-x y,1/2-z,1/2-x -y,1/2-z,1/2+x 1/2+y,x,z 1/2-y,-x,z " "1/2+y,-x,-z 1/2-y,x,-z 1/2+x,z,y 1/2-x,z,-y 1/2-x,-z,y 1/2+x,-z,-y " "1/2+z,y,x 1/2+z,-y,-x 1/2-z,y,-x 1/2-z,-y,x 1/2+x,y,1/2+z 1/2-x,-y,1/2+z " "1/2-x,y,1/2-z 1/2+x,-y,1/2-z 1/2+z,x,1/2+y 1/2+z,-x,1/2-y 1/2-z,-x,1/2+y " "1/2-z,x,1/2-y 1/2+y,z,1/2+x 1/2-y,z,1/2-x 1/2+y,-z,1/2-x 1/2-y,-z,1/2+x " "y,1/2+x,z -y,1/2-x,z y,1/2-x,-z -y,1/2+x,-z x,1/2+z,y -x,1/2+z,-y " "-x,1/2-z,y x,1/2-z,-y z,1/2+y,x z,1/2-y,-x -z,1/2+y,-x -z,1/2-y,x " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+z,1/2+x,y " "1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x 1/2-y,1/2+z,-x " "1/2+y,1/2-z,-x 1/2-y,1/2-z,x y,x,1/2+z -y,-x,1/2+z y,-x,1/2-z -y,x,1/2-z " "x,z,1/2+y -x,z,1/2-y -x,-z,1/2+y x,-z,1/2-y z,y,1/2+x z,-y,1/2-x -z,y,1/2-x " "-z,-y,1/2+x", "x,y,z 1/2-x,-y,1/2+z -x,1/2+y,1/2-z 1/2+x,1/2-y,-z z,x,y 1/2+z,1/2-x,-y " "1/2-z,-x,1/2+y -z,1/2+x,1/2-y y,z,x -y,1/2+z,1/2-x 1/2+y,1/2-z,-x " "1/2-y,-z,1/2+x 1/4+y,1/4+x,1/4+z 1/4-y,3/4-x,3/4+z 3/4+y,1/4-x,3/4-z " "3/4-y,3/4+x,1/4-z 1/4+x,1/4+z,1/4+y 3/4-x,3/4+z,1/4-y 1/4-x,3/4-z,3/4+y " "3/4+x,1/4-z,3/4-y 1/4+z,1/4+y,1/4+x 3/4+z,1/4-y,3/4-x 3/4-z,3/4+y,1/4-x " "1/4-z,3/4-y,3/4+x 1/2+x,1/2+y,1/2+z -x,1/2-y,z 1/2-x,y,-z x,-y,1/2-z " "1/2+z,1/2+x,1/2+y z,-x,1/2-y -z,1/2-x,y 1/2-z,x,-y 1/2+y,1/2+z,1/2+x " "1/2-y,z,-x y,-z,1/2-x -y,1/2-z,x 3/4+y,3/4+x,3/4+z 3/4-y,1/4-x,1/4+z " "1/4+y,3/4-x,1/4-z 1/4-y,1/4+x,3/4-z 3/4+x,3/4+z,3/4+y 1/4-x,1/4+z,3/4-y " "3/4-x,1/4-z,1/4+y 1/4+x,3/4-z,1/4-y 3/4+z,3/4+y,3/4+x 1/4+z,3/4-y,1/4-x " "1/4-z,1/4+y,3/4-x 3/4-z,1/4-y,1/4+x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,-z -y,-x,-z y,-x,z -y,x,z x,z,-y -x,z,y -x,-z,-y x,-z,y " "z,y,-x z,-y,x -z,y,x -z,-y,-x -x,-y,-z x,y,-z x,-y,z -x,y,z -z,-x,-y -z,x,y " "z,x,-y z,-x,y -y,-z,-x y,-z,x -y,z,x y,z,-x -y,-x,z y,x,z -y,x,-z y,-x,-z " "-x,-z,y x,-z,-y x,z,y -x,z,-y -z,-y,x -z,y,-x z,-y,-x z,y,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,-z -y,-x,-z y,-x,z -y,x,z x,z,-y -x,z,y -x,-z,-y x,-z,y " "z,y,-x z,-y,x -z,y,x -z,-y,-x -x+1/2,-y+1/2,-z+1/2 x+1/2,y+1/2,-z+1/2 " "x+1/2,-y+1/2,z+1/2 -x+1/2,y+1/2,z+1/2 -z+1/2,-x+1/2,-y+1/2 " "-z+1/2,x+1/2,y+1/2 z+1/2,x+1/2,-y+1/2 z+1/2,-x+1/2,y+1/2 " "-y+1/2,-z+1/2,-x+1/2 y+1/2,-z+1/2,x+1/2 -y+1/2,z+1/2,x+1/2 " "y+1/2,z+1/2,-x+1/2 -y+1/2,-x+1/2,z+1/2 y+1/2,x+1/2,z+1/2 " "-y+1/2,x+1/2,-z+1/2 y+1/2,-x+1/2,-z+1/2 -x+1/2,-z+1/2,y+1/2 " "x+1/2,-z+1/2,-y+1/2 x+1/2,z+1/2,y+1/2 -x+1/2,z+1/2,-y+1/2 " "-z+1/2,-y+1/2,x+1/2 -z+1/2,y+1/2,-x+1/2 z+1/2,-y+1/2,-x+1/2 " "z+1/2,y+1/2,x+1/2", "x,y,z 1/2-x,1/2-y,z 1/2-x,y,1/2-z x,1/2-y,1/2-z z,x,y z,1/2-x,1/2-y " "1/2-z,1/2-x,y 1/2-z,x,1/2-y y,z,x 1/2-y,z,1/2-x y,1/2-z,1/2-x 1/2-y,1/2-z,x " "y,x,1/2-z 1/2-y,1/2-x,1/2-z y,1/2-x,z 1/2-y,x,z x,z,1/2-y 1/2-x,z,y " "1/2-x,1/2-z,1/2-y x,1/2-z,y z,y,1/2-x z,1/2-y,x 1/2-z,y,x 1/2-z,1/2-y,1/2-x " "-x,-y,-z 1/2+x,1/2+y,-z 1/2+x,-y,1/2+z -x,1/2+y,1/2+z -z,-x,-y " "-z,1/2+x,1/2+y 1/2+z,1/2+x,-y 1/2+z,-x,1/2+y -y,-z,-x 1/2+y,-z,1/2+x " "-y,1/2+z,1/2+x 1/2+y,1/2+z,-x -y,-x,1/2+z 1/2+y,1/2+x,1/2+z -y,1/2+x,-z " "1/2+y,-x,-z -x,-z,1/2+y 1/2+x,-z,-y 1/2+x,1/2+z,1/2+y -x,1/2+z,-y " "-z,-y,1/2+x -z,1/2+y,-x 1/2+z,-y,-x 1/2+z,1/2+y,1/2+x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z 1/2+y,1/2-x,1/2+z " "1/2-y,1/2+x,1/2+z 1/2+x,1/2+z,1/2-y 1/2-x,1/2+z,1/2+y 1/2-x,1/2-z,1/2-y " "1/2+x,1/2-z,1/2+y 1/2+z,1/2+y,1/2-x 1/2+z,1/2-y,1/2+x 1/2-z,1/2+y,1/2+x " "1/2-z,1/2-y,1/2-x -x,-y,-z x,y,-z x,-y,z -x,y,z -z,-x,-y -z,x,y z,x,-y " "z,-x,y -y,-z,-x y,-z,x -y,z,x y,z,-x 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z " "1/2-y,1/2+x,1/2-z 1/2+y,1/2-x,1/2-z 1/2-x,1/2-z,1/2+y 1/2+x,1/2-z,1/2-y " "1/2+x,1/2+z,1/2+y 1/2-x,1/2+z,1/2-y 1/2-z,1/2-y,1/2+x 1/2-z,1/2+y,1/2-x " "1/2+z,1/2-y,1/2-x 1/2+z,1/2+y,1/2+x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z 1/2+y,1/2-x,1/2+z " "1/2-y,1/2+x,1/2+z 1/2+x,1/2+z,1/2-y 1/2-x,1/2+z,1/2+y 1/2-x,1/2-z,1/2-y " "1/2+x,1/2-z,1/2+y 1/2+z,1/2+y,1/2-x 1/2+z,1/2-y,1/2+x 1/2-z,1/2+y,1/2+x " "1/2-z,1/2-y,1/2-x 1/2-x,1/2-y,1/2-z 1/2+x,1/2+y,1/2-z 1/2+x,1/2-y,1/2+z " "1/2-x,1/2+y,1/2+z 1/2-z,1/2-x,1/2-y 1/2-z,1/2+x,1/2+y 1/2+z,1/2+x,1/2-y " "1/2+z,1/2-x,1/2+y 1/2-y,1/2-z,1/2-x 1/2+y,1/2-z,1/2+x 1/2-y,1/2+z,1/2+x " "1/2+y,1/2+z,1/2-x -y,-x,z y,x,z -y,x,-z y,-x,-z -x,-z,y x,-z,-y x,z,y " "-x,z,-y -z,-y,x -z,y,-x z,-y,-x z,y,x", "x,y,z 1/2-x,1/2-y,z 1/2-x,y,1/2-z x,1/2-y,1/2-z z,x,y z,1/2-x,1/2-y " "1/2-z,1/2-x,y 1/2-z,x,1/2-y y,z,x 1/2-y,z,1/2-x y,1/2-z,1/2-x 1/2-y,1/2-z,x " "1/2+y,1/2+x,-z -y,-x,-z 1/2+y,-x,1/2+z -y,1/2+x,1/2+z 1/2+x,1/2+z,-y " "-x,1/2+z,1/2+y -x,-z,-y 1/2+x,-z,1/2+y 1/2+z,1/2+y,-x 1/2+z,-y,1/2+x " "-z,1/2+y,1/2+x -z,-y,-x -x,-y,-z 1/2+x,1/2+y,-z 1/2+x,-y,1/2+z " "-x,1/2+y,1/2+z -z,-x,-y -z,1/2+x,1/2+y 1/2+z,1/2+x,-y 1/2+z,-x,1/2+y " "-y,-z,-x 1/2+y,-z,1/2+x -y,1/2+z,1/2+x 1/2+y,1/2+z,-x 1/2-y,1/2-x,z y,x,z " "1/2-y,x,1/2-z y,1/2-x,1/2-z 1/2-x,1/2-z,y x,1/2-z,1/2-y x,z,y 1/2-x,z,1/2-y " "1/2-z,1/2-y,x 1/2-z,y,1/2-x z,1/2-y,1/2-x z,y,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x y,x,-z -y,-x,-z y,-x,z -y,x,z x,z,-y -x,z,y -x,-z,-y x,-z,y " "z,y,-x z,-y,x -z,y,x -z,-y,-x -x,-y,-z x,y,-z x,-y,z -x,y,z -z,-x,-y -z,x,y " "z,x,-y z,-x,y -y,-z,-x y,-z,x -y,z,x y,z,-x -y,-x,z y,x,z -y,x,-z y,-x,-z " "-x,-z,y x,-z,-y x,z,y -x,z,-y -z,-y,x -z,y,-x z,-y,-x z,y,x x,1/2+y,1/2+z " "-x,1/2-y,1/2+z -x,1/2+y,1/2-z x,1/2-y,1/2-z z,1/2+x,1/2+y z,1/2-x,1/2-y " "-z,1/2-x,1/2+y -z,1/2+x,1/2-y y,1/2+z,1/2+x -y,1/2+z,1/2-x y,1/2-z,1/2-x " "-y,1/2-z,1/2+x y,1/2+x,1/2-z -y,1/2-x,1/2-z y,1/2-x,1/2+z -y,1/2+x,1/2+z " "x,1/2+z,1/2-y -x,1/2+z,1/2+y -x,1/2-z,1/2-y x,1/2-z,1/2+y z,1/2+y,1/2-x " "z,1/2-y,1/2+x -z,1/2+y,1/2+x -z,1/2-y,1/2-x -x,1/2-y,1/2-z x,1/2+y,1/2-z " "x,1/2-y,1/2+z -x,1/2+y,1/2+z -z,1/2-x,1/2-y -z,1/2+x,1/2+y z,1/2+x,1/2-y " "z,1/2-x,1/2+y -y,1/2-z,1/2-x y,1/2-z,1/2+x -y,1/2+z,1/2+x y,1/2+z,1/2-x " "-y,1/2-x,1/2+z y,1/2+x,1/2+z -y,1/2+x,1/2-z y,1/2-x,1/2-z -x,1/2-z,1/2+y " "x,1/2-z,1/2-y x,1/2+z,1/2+y -x,1/2+z,1/2-y -z,1/2-y,1/2+x -z,1/2+y,1/2-x " "z,1/2-y,1/2-x z,1/2+y,1/2+x 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z " "1/2+x,-y,1/2-z 1/2+z,x,1/2+y 1/2+z,-x,1/2-y 1/2-z,-x,1/2+y 1/2-z,x,1/2-y " "1/2+y,z,1/2+x 1/2-y,z,1/2-x 1/2+y,-z,1/2-x 1/2-y,-z,1/2+x 1/2+y,x,1/2-z " "1/2-y,-x,1/2-z 1/2+y,-x,1/2+z 1/2-y,x,1/2+z 1/2+x,z,1/2-y 1/2-x,z,1/2+y " "1/2-x,-z,1/2-y 1/2+x,-z,1/2+y 1/2+z,y,1/2-x 1/2+z,-y,1/2+x 1/2-z,y,1/2+x " "1/2-z,-y,1/2-x 1/2-x,-y,1/2-z 1/2+x,y,1/2-z 1/2+x,-y,1/2+z 1/2-x,y,1/2+z " "1/2-z,-x,1/2-y 1/2-z,x,1/2+y 1/2+z,x,1/2-y 1/2+z,-x,1/2+y 1/2-y,-z,1/2-x " "1/2+y,-z,1/2+x 1/2-y,z,1/2+x 1/2+y,z,1/2-x 1/2-y,-x,1/2+z 1/2+y,x,1/2+z " "1/2-y,x,1/2-z 1/2+y,-x,1/2-z 1/2-x,-z,1/2+y 1/2+x,-z,1/2-y 1/2+x,z,1/2+y " "1/2-x,z,1/2-y 1/2-z,-y,1/2+x 1/2-z,y,1/2-x 1/2+z,-y,1/2-x 1/2+z,y,1/2+x " "1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z 1/2+z,1/2+x,y " "1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x 1/2-y,1/2+z,-x " "1/2+y,1/2-z,-x 1/2-y,1/2-z,x 1/2+y,1/2+x,-z 1/2-y,1/2-x,-z 1/2+y,1/2-x,z " "1/2-y,1/2+x,z 1/2+x,1/2+z,-y 1/2-x,1/2+z,y 1/2-x,1/2-z,-y 1/2+x,1/2-z,y " "1/2+z,1/2+y,-x 1/2+z,1/2-y,x 1/2-z,1/2+y,x 1/2-z,1/2-y,-x 1/2-x,1/2-y,-z " "1/2+x,1/2+y,-z 1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2-z,1/2-x,-y 1/2-z,1/2+x,y " "1/2+z,1/2+x,-y 1/2+z,1/2-x,y 1/2-y,1/2-z,-x 1/2+y,1/2-z,x 1/2-y,1/2+z,x " "1/2+y,1/2+z,-x 1/2-y,1/2-x,z 1/2+y,1/2+x,z 1/2-y,1/2+x,-z 1/2+y,1/2-x,-z " "1/2-x,1/2-z,y 1/2+x,1/2-z,-y 1/2+x,1/2+z,y 1/2-x,1/2+z,-y 1/2-z,1/2-y,x " "1/2-z,1/2+y,-x 1/2+z,1/2-y,-x 1/2+z,1/2+y,x", "x,y,z -x,-y,z -x,y,-z x,-y,-z z,x,y z,-x,-y -z,-x,y -z,x,-y y,z,x -y,z,-x " "y,-z,-x -y,-z,x 1/2+y,1/2+x,1/2-z 1/2-y,1/2-x,1/2-z 1/2+y,1/2-x,1/2+z " "1/2-y,1/2+x,1/2+z 1/2+x,1/2+z,1/2-y 1/2-x,1/2+z,1/2+y 1/2-x,1/2-z,1/2-y " "1/2+x,1/2-z,1/2+y 1/2+z,1/2+y,1/2-x 1/2+z,1/2-y,1/2+x 1/2-z,1/2+y,1/2+x " "1/2-z,1/2-y,1/2-x -x,-y,-z x,y,-z x,-y,z -x,y,z -z,-x,-y -z,x,y z,x,-y " "z,-x,y -y,-z,-x y,-z,x -y,z,x y,z,-x 1/2-y,1/2-x,1/2+z 1/2+y,1/2+x,1/2+z " "1/2-y,1/2+x,1/2-z 1/2+y,1/2-x,1/2-z 1/2-x,1/2-z,1/2+y 1/2+x,1/2-z,1/2-y " "1/2+x,1/2+z,1/2+y 1/2-x,1/2+z,1/2-y 1/2-z,1/2-y,1/2+x 1/2-z,1/2+y,1/2-x " "1/2+z,1/2-y,1/2-x 1/2+z,1/2+y,1/2+x x,1/2+y,1/2+z -x,1/2-y,1/2+z " "-x,1/2+y,1/2-z x,1/2-y,1/2-z z,1/2+x,1/2+y z,1/2-x,1/2-y -z,1/2-x,1/2+y " "-z,1/2+x,1/2-y y,1/2+z,1/2+x -y,1/2+z,1/2-x y,1/2-z,1/2-x -y,1/2-z,1/2+x " "1/2+y,x,-z 1/2-y,-x,-z 1/2+y,-x,z 1/2-y,x,z 1/2+x,z,-y 1/2-x,z,y " "1/2-x,-z,-y 1/2+x,-z,y 1/2+z,y,-x 1/2+z,-y,x 1/2-z,y,x 1/2-z,-y,-x " "-x,1/2-y,1/2-z x,1/2+y,1/2-z x,1/2-y,1/2+z -x,1/2+y,1/2+z -z,1/2-x,1/2-y " "-z,1/2+x,1/2+y z,1/2+x,1/2-y z,1/2-x,1/2+y -y,1/2-z,1/2-x y,1/2-z,1/2+x " "-y,1/2+z,1/2+x y,1/2+z,1/2-x 1/2-y,-x,z 1/2+y,x,z 1/2-y,x,-z 1/2+y,-x,-z " "1/2-x,-z,y 1/2+x,-z,-y 1/2+x,z,y 1/2-x,z,-y 1/2-z,-y,x 1/2-z,y,-x " "1/2+z,-y,-x 1/2+z,y,x 1/2+x,y,1/2+z 1/2-x,-y,1/2+z 1/2-x,y,1/2-z " "1/2+x,-y,1/2-z 1/2+z,x,1/2+y 1/2+z,-x,1/2-y 1/2-z,-x,1/2+y 1/2-z,x,1/2-y " "1/2+y,z,1/2+x 1/2-y,z,1/2-x 1/2+y,-z,1/2-x 1/2-y,-z,1/2+x y,1/2+x,-z " "-y,1/2-x,-z y,1/2-x,z -y,1/2+x,z x,1/2+z,-y -x,1/2+z,y -x,1/2-z,-y " "x,1/2-z,y z,1/2+y,-x z,1/2-y,x -z,1/2+y,x -z,1/2-y,-x 1/2-x,-y,1/2-z " "1/2+x,y,1/2-z 1/2+x,-y,1/2+z 1/2-x,y,1/2+z 1/2-z,-x,1/2-y 1/2-z,x,1/2+y " "1/2+z,x,1/2-y 1/2+z,-x,1/2+y 1/2-y,-z,1/2-x 1/2+y,-z,1/2+x 1/2-y,z,1/2+x " "1/2+y,z,1/2-x -y,1/2-x,z y,1/2+x,z -y,1/2+x,-z y,1/2-x,-z -x,1/2-z,y " "x,1/2-z,-y x,1/2+z,y -x,1/2+z,-y -z,1/2-y,x -z,1/2+y,-x z,1/2-y,-x " "z,1/2+y,x 1/2+x,1/2+y,z 1/2-x,1/2-y,z 1/2-x,1/2+y,-z 1/2+x,1/2-y,-z " "1/2+z,1/2+x,y 1/2+z,1/2-x,-y 1/2-z,1/2-x,y 1/2-z,1/2+x,-y 1/2+y,1/2+z,x " "1/2-y,1/2+z,-x 1/2+y,1/2-z,-x 1/2-y,1/2-z,x y,x,1/2-z -y,-x,1/2-z " "y,-x,1/2+z -y,x,1/2+z x,z,1/2-y -x,z,1/2+y -x,-z,1/2-y x,-z,1/2+y z,y,1/2-x " "z,-y,1/2+x -z,y,1/2+x -z,-y,1/2-x 1/2-x,1/2-y,-z 1/2+x,1/2+y,-z " "1/2+x,1/2-y,z 1/2-x,1/2+y,z 1/2-z,1/2-x,-y 1/2-z,1/2+x,y 1/2+z,1/2+x,-y " "1/2+z,1/2-x,y 1/2-y,1/2-z,-x 1/2+y,1/2-z,x 1/2-y,1/2+z,x 1/2+y,1/2+z,-x " "-y,-x,1/2+z y,x,1/2+z -y,x,1/2-z y,-x,1/2-z -x,-z,1/2+y x,-z,1/2-y " "x,z,1/2+y -x,z,1/2-y -z,-y,1/2+x -z,y,1/2-x z,-y,1/2-x z,y,1/2+x", "x,y,z -x,1/2-y,1/2+z 1/2-x,1/2+y,-z 1/2+x,-y,1/2-z z,x,y 1/2+z,-x,1/2-y " "-z,1/2-x,1/2+y 1/2-z,1/2+x,-y y,z,x 1/2-y,1/2+z,-x 1/2+y,-z,1/2-x " "-y,1/2-z,1/2+x 3/4+y,1/4+x,3/4-z 1/4-y,1/4-x,1/4-z 1/4+y,3/4-x,3/4+z " "3/4-y,3/4+x,1/4+z 3/4+x,1/4+z,3/4-y 3/4-x,3/4+z,1/4+y 1/4-x,1/4-z,1/4-y " "1/4+x,3/4-z,3/4+y 3/4+z,1/4+y,3/4-x 1/4+z,3/4-y,3/4+x 3/4-z,3/4+y,1/4+x " "1/4-z,1/4-y,1/4-x 1/4-x,1/4-y,1/4-z 1/4+x,3/4+y,3/4-z 3/4+x,3/4-y,1/4+z " "3/4-x,1/4+y,3/4+z 1/4-z,1/4-x,1/4-y 3/4-z,1/4+x,3/4+y 1/4+z,3/4+x,3/4-y " "3/4+z,3/4-x,1/4+y 1/4-y,1/4-z,1/4-x 3/4+y,3/4-z,1/4+x 3/4-y,1/4+z,3/4+x " "1/4+y,3/4+z,3/4-x 1/2-y,-x,1/2+z y,x,z -y,1/2+x,1/2-z 1/2+y,1/2-x,-z " "1/2-x,-z,1/2+y 1/2+x,1/2-z,-y x,z,y -x,1/2+z,1/2-y 1/2-z,-y,1/2+x " "-z,1/2+y,1/2-x 1/2+z,1/2-y,-x z,y,x x,1/2+y,1/2+z -x,-y,z 1/2-x,y,1/2-z " "1/2+x,1/2-y,-z z,1/2+x,1/2+y 1/2+z,1/2-x,-y -z,-x,y 1/2-z,x,1/2-y " "y,1/2+z,1/2+x 1/2-y,z,1/2-x 1/2+y,1/2-z,-x -y,-z,x 3/4+y,3/4+x,1/4-z " "1/4-y,3/4-x,3/4-z 1/4+y,1/4-x,1/4+z 3/4-y,1/4+x,3/4+z 3/4+x,3/4+z,1/4-y " "3/4-x,1/4+z,3/4+y 1/4-x,3/4-z,3/4-y 1/4+x,1/4-z,1/4+y 3/4+z,3/4+y,1/4-x " "1/4+z,1/4-y,1/4+x 3/4-z,1/4+y,3/4+x 1/4-z,3/4-y,3/4-x 1/4-x,3/4-y,3/4-z " "1/4+x,1/4+y,1/4-z 3/4+x,1/4-y,3/4+z 3/4-x,3/4+y,1/4+z 1/4-z,3/4-x,3/4-y " "3/4-z,3/4+x,1/4+y 1/4+z,1/4+x,1/4-y 3/4+z,1/4-x,3/4+y 1/4-y,3/4-z,3/4-x " "3/4+y,1/4-z,3/4+x 3/4-y,3/4+z,1/4+x 1/4+y,1/4+z,1/4-x 1/2-y,1/2-x,z " "y,1/2+x,1/2+z -y,x,-z 1/2+y,-x,1/2-z 1/2-x,1/2-z,y 1/2+x,-z,1/2-y " "x,1/2+z,1/2+y -x,z,-y 1/2-z,1/2-y,x -z,y,-x 1/2+z,-y,1/2-x z,1/2+y,1/2+x " "1/2+x,y,1/2+z 1/2-x,1/2-y,z -x,1/2+y,1/2-z x,-y,-z 1/2+z,x,1/2+y z,-x,-y " "1/2-z,1/2-x,y -z,1/2+x,1/2-y 1/2+y,z,1/2+x -y,1/2+z,1/2-x y,-z,-x " "1/2-y,1/2-z,x 1/4+y,1/4+x,1/4-z 3/4-y,1/4-x,3/4-z 3/4+y,3/4-x,1/4+z " "1/4-y,3/4+x,3/4+z 1/4+x,1/4+z,1/4-y 1/4-x,3/4+z,3/4+y 3/4-x,1/4-z,3/4-y " "3/4+x,3/4-z,1/4+y 1/4+z,1/4+y,1/4-x 3/4+z,3/4-y,1/4+x 1/4-z,3/4+y,3/4+x " "3/4-z,1/4-y,3/4-x 3/4-x,1/4-y,3/4-z 3/4+x,3/4+y,1/4-z 1/4+x,3/4-y,3/4+z " "1/4-x,1/4+y,1/4+z 3/4-z,1/4-x,3/4-y 1/4-z,1/4+x,1/4+y 3/4+z,3/4+x,1/4-y " "1/4+z,3/4-x,3/4+y 3/4-y,1/4-z,3/4-x 1/4+y,3/4-z,3/4+x 1/4-y,1/4+z,1/4+x " "3/4+y,3/4+z,1/4-x -y,-x,z 1/2+y,x,1/2+z 1/2-y,1/2+x,-z y,1/2-x,1/2-z " "-x,-z,y x,1/2-z,1/2-y 1/2+x,z,1/2+y 1/2-x,1/2+z,-y -z,-y,x 1/2-z,1/2+y,-x " "z,1/2-y,1/2-x 1/2+z,y,1/2+x 1/2+x,1/2+y,z 1/2-x,-y,1/2+z -x,y,-z " "x,1/2-y,1/2-z 1/2+z,1/2+x,y z,1/2-x,1/2-y 1/2-z,-x,1/2+y -z,x,-y " "1/2+y,1/2+z,x -y,z,-x y,1/2-z,1/2-x 1/2-y,-z,1/2+x 1/4+y,3/4+x,3/4-z " "3/4-y,3/4-x,1/4-z 3/4+y,1/4-x,3/4+z 1/4-y,1/4+x,1/4+z 1/4+x,3/4+z,3/4-y " "1/4-x,1/4+z,1/4+y 3/4-x,3/4-z,1/4-y 3/4+x,1/4-z,3/4+y 1/4+z,3/4+y,3/4-x " "3/4+z,1/4-y,3/4+x 1/4-z,1/4+y,1/4+x 3/4-z,3/4-y,1/4-x 3/4-x,3/4-y,1/4-z " "3/4+x,1/4+y,3/4-z 1/4+x,1/4-y,1/4+z 1/4-x,3/4+y,3/4+z 3/4-z,3/4-x,1/4-y " "1/4-z,3/4+x,3/4+y 3/4+z,1/4+x,3/4-y 1/4+z,1/4-x,1/4+y 3/4-y,3/4-z,1/4-x " "1/4+y,1/4-z,1/4+x 1/4-y,3/4+z,3/4+x 3/4+y,1/4+z,3/4-x -y,1/2-x,1/2+z " "1/2+y,1/2+x,z 1/2-y,x,1/2-z y,-x,-z -x,1/2-z,1/2+y x,-z,-y 1/2+x,1/2+z,y " "1/2-x,z,1/2-y -z,1/2-y,1/2+x 1/2-z,y,1/2-x z,-y,-x 1/2+z,1/2+y,x", "x,y,z 3/4-x,1/4-y,1/2+z 1/4-x,1/2+y,3/4-z 1/2+x,3/4-y,1/4-z z,x,y " "1/2+z,3/4-x,1/4-y 3/4-z,1/4-x,1/2+y 1/4-z,1/2+x,3/4-y y,z,x " "1/4-y,1/2+z,3/4-x 1/2+y,3/4-z,1/4-x 3/4-y,1/4-z,1/2+x 3/4+y,1/4+x,1/2-z " "-y,-x,-z 1/4+y,1/2-x,3/4+z 1/2-y,3/4+x,1/4+z 3/4+x,1/4+z,1/2-y " "1/2-x,3/4+z,1/4+y -x,-z,-y 1/4+x,1/2-z,3/4+y 3/4+z,1/4+y,1/2-x " "1/4+z,1/2-y,3/4+x 1/2-z,3/4+y,1/4+x -z,-y,-x -x,-y,-z 1/4+x,3/4+y,1/2-z " "3/4+x,1/2-y,1/4+z 1/2-x,1/4+y,3/4+z -z,-x,-y 1/2-z,1/4+x,3/4+y " "1/4+z,3/4+x,1/2-y 3/4+z,1/2-x,1/4+y -y,-z,-x 3/4+y,1/2-z,1/4+x " "1/2-y,1/4+z,3/4+x 1/4+y,3/4+z,1/2-x 1/4-y,3/4-x,1/2+z y,x,z " "3/4-y,1/2+x,1/4-z 1/2+y,1/4-x,3/4-z 1/4-x,3/4-z,1/2+y 1/2+x,1/4-z,3/4-y " "x,z,y 3/4-x,1/2+z,1/4-y 1/4-z,3/4-y,1/2+x 3/4-z,1/2+y,1/4-x " "1/2+z,1/4-y,3/4-x z,y,x x,1/2+y,1/2+z 3/4-x,3/4-y,z 1/4-x,y,1/4-z " "1/2+x,1/4-y,3/4-z z,1/2+x,1/2+y 1/2+z,1/4-x,3/4-y 3/4-z,3/4-x,y " "1/4-z,x,1/4-y y,1/2+z,1/2+x 1/4-y,z,1/4-x 1/2+y,1/4-z,3/4-x 3/4-y,3/4-z,x " "3/4+y,3/4+x,-z -y,1/2-x,1/2-z 1/4+y,-x,1/4+z 1/2-y,1/4+x,3/4+z " "3/4+x,3/4+z,-y 1/2-x,1/4+z,3/4+y -x,1/2-z,1/2-y 1/4+x,-z,1/4+y " "3/4+z,3/4+y,-x 1/4+z,-y,1/4+x 1/2-z,1/4+y,3/4+x -z,1/2-y,1/2-x " "-x,1/2-y,1/2-z 1/4+x,1/4+y,-z 3/4+x,-y,3/4+z 1/2-x,3/4+y,1/4+z " "-z,1/2-x,1/2-y 1/2-z,3/4+x,1/4+y 1/4+z,1/4+x,-y 3/4+z,-x,3/4+y " "-y,1/2-z,1/2-x 3/4+y,-z,3/4+x 1/2-y,3/4+z,1/4+x 1/4+y,1/4+z,-x " "1/4-y,1/4-x,z y,1/2+x,1/2+z 3/4-y,x,3/4-z 1/2+y,3/4-x,1/4-z 1/4-x,1/4-z,y " "1/2+x,3/4-z,1/4-y x,1/2+z,1/2+y 3/4-x,z,3/4-y 1/4-z,1/4-y,x 3/4-z,y,3/4-x " "1/2+z,3/4-y,1/4-x z,1/2+y,1/2+x 1/2+x,y,1/2+z 1/4-x,1/4-y,z " "3/4-x,1/2+y,1/4-z x,3/4-y,3/4-z 1/2+z,x,1/2+y z,3/4-x,3/4-y 1/4-z,1/4-x,y " "3/4-z,1/2+x,1/4-y 1/2+y,z,1/2+x 3/4-y,1/2+z,1/4-x y,3/4-z,3/4-x " "1/4-y,1/4-z,x 1/4+y,1/4+x,-z 1/2-y,-x,1/2-z 3/4+y,1/2-x,1/4+z " "-y,3/4+x,3/4+z 1/4+x,1/4+z,-y -x,3/4+z,3/4+y 1/2-x,-z,1/2-y " "3/4+x,1/2-z,1/4+y 1/4+z,1/4+y,-x 3/4+z,1/2-y,1/4+x -z,3/4+y,3/4+x " "1/2-z,-y,1/2-x 1/2-x,-y,1/2-z 3/4+x,3/4+y,-z 1/4+x,1/2-y,3/4+z " "-x,1/4+y,1/4+z 1/2-z,-x,1/2-y -z,1/4+x,1/4+y 3/4+z,3/4+x,-y " "1/4+z,1/2-x,3/4+y 1/2-y,-z,1/2-x 1/4+y,1/2-z,3/4+x -y,1/4+z,1/4+x " "3/4+y,3/4+z,-x 3/4-y,3/4-x,z 1/2+y,x,1/2+z 1/4-y,1/2+x,3/4-z y,1/4-x,1/4-z " "3/4-x,3/4-z,y x,1/4-z,1/4-y 1/2+x,z,1/2+y 1/4-x,1/2+z,3/4-y 3/4-z,3/4-y,x " "1/4-z,1/2+y,3/4-x z,1/4-y,1/4-x 1/2+z,y,1/2+x 1/2+x,1/2+y,z " "1/4-x,3/4-y,1/2+z 3/4-x,y,3/4-z x,1/4-y,1/4-z 1/2+z,1/2+x,y z,1/4-x,1/4-y " "1/4-z,3/4-x,1/2+y 3/4-z,x,3/4-y 1/2+y,1/2+z,x 3/4-y,z,3/4-x y,1/4-z,1/4-x " "1/4-y,3/4-z,1/2+x 1/4+y,3/4+x,1/2-z 1/2-y,1/2-x,-z 3/4+y,-x,3/4+z " "-y,1/4+x,1/4+z 1/4+x,3/4+z,1/2-y -x,1/4+z,1/4+y 1/2-x,1/2-z,-y " "3/4+x,-z,3/4+y 1/4+z,3/4+y,1/2-x 3/4+z,-y,3/4+x -z,1/4+y,1/4+x " "1/2-z,1/2-y,-x 1/2-x,1/2-y,-z 3/4+x,1/4+y,1/2-z 1/4+x,-y,1/4+z " "-x,3/4+y,3/4+z 1/2-z,1/2-x,-y -z,3/4+x,3/4+y 3/4+z,1/4+x,1/2-y " "1/4+z,-x,1/4+y 1/2-y,1/2-z,-x 1/4+y,-z,1/4+x -y,3/4+z,3/4+x " "3/4+y,1/4+z,1/2-x 3/4-y,1/4-x,1/2+z 1/2+y,1/2+x,z 1/4-y,x,1/4-z " "y,3/4-x,3/4-z 3/4-x,1/4-z,1/2+y x,3/4-z,3/4-y 1/2+x,1/2+z,y 1/4-x,z,1/4-y " "3/4-z,1/4-y,1/2+x 1/4-z,y,1/4-x z,3/4-y,3/4-x 1/2+z,1/2+y,x", "x,y,z -x,1/2-y,1/2+z 1/2-x,1/2+y,-z 1/2+x,-y,1/2-z z,x,y 1/2+z,-x,1/2-y " "-z,1/2-x,1/2+y 1/2-z,1/2+x,-y y,z,x 1/2-y,1/2+z,-x 1/2+y,-z,1/2-x " "-y,1/2-z,1/2+x 3/4+y,1/4+x,3/4-z 1/4-y,1/4-x,1/4-z 1/4+y,3/4-x,3/4+z " "3/4-y,3/4+x,1/4+z 3/4+x,1/4+z,3/4-y 3/4-x,3/4+z,1/4+y 1/4-x,1/4-z,1/4-y " "1/4+x,3/4-z,3/4+y 3/4+z,1/4+y,3/4-x 1/4+z,3/4-y,3/4+x 3/4-z,3/4+y,1/4+x " "1/4-z,1/4-y,1/4-x 3/4-x,3/4-y,3/4-z 3/4+x,1/4+y,1/4-z 1/4+x,1/4-y,3/4+z " "1/4-x,3/4+y,1/4+z 3/4-z,3/4-x,3/4-y 1/4-z,3/4+x,1/4+y 3/4+z,1/4+x,1/4-y " "1/4+z,1/4-x,3/4+y 3/4-y,3/4-z,3/4-x 1/4+y,1/4-z,3/4+x 1/4-y,3/4+z,1/4+x " "3/4+y,1/4+z,1/4-x -y,1/2-x,z 1/2+y,1/2+x,1/2+z 1/2-y,x,-z y,-x,1/2-z " "-x,1/2-z,y x,-z,1/2-y 1/2+x,1/2+z,1/2+y 1/2-x,z,-y -z,1/2-y,x 1/2-z,y,-x " "z,-y,1/2-x 1/2+z,1/2+y,1/2+x x,1/2+y,1/2+z -x,-y,z 1/2-x,y,1/2-z " "1/2+x,1/2-y,-z z,1/2+x,1/2+y 1/2+z,1/2-x,-y -z,-x,y 1/2-z,x,1/2-y " "y,1/2+z,1/2+x 1/2-y,z,1/2-x 1/2+y,1/2-z,-x -y,-z,x 3/4+y,3/4+x,1/4-z " "1/4-y,3/4-x,3/4-z 1/4+y,1/4-x,1/4+z 3/4-y,1/4+x,3/4+z 3/4+x,3/4+z,1/4-y " "3/4-x,1/4+z,3/4+y 1/4-x,3/4-z,3/4-y 1/4+x,1/4-z,1/4+y 3/4+z,3/4+y,1/4-x " "1/4+z,1/4-y,1/4+x 3/4-z,1/4+y,3/4+x 1/4-z,3/4-y,3/4-x 3/4-x,1/4-y,1/4-z " "3/4+x,3/4+y,3/4-z 1/4+x,3/4-y,1/4+z 1/4-x,1/4+y,3/4+z 3/4-z,1/4-x,1/4-y " "1/4-z,1/4+x,3/4+y 3/4+z,3/4+x,3/4-y 1/4+z,3/4-x,1/4+y 3/4-y,1/4-z,1/4-x " "1/4+y,3/4-z,1/4+x 1/4-y,1/4+z,3/4+x 3/4+y,3/4+z,3/4-x -y,-x,1/2+z 1/2+y,x,z " "1/2-y,1/2+x,1/2-z y,1/2-x,-z -x,-z,1/2+y x,1/2-z,-y 1/2+x,z,y " "1/2-x,1/2+z,1/2-y -z,-y,1/2+x 1/2-z,1/2+y,1/2-x z,1/2-y,-x 1/2+z,y,x " "1/2+x,y,1/2+z 1/2-x,1/2-y,z -x,1/2+y,1/2-z x,-y,-z 1/2+z,x,1/2+y z,-x,-y " "1/2-z,1/2-x,y -z,1/2+x,1/2-y 1/2+y,z,1/2+x -y,1/2+z,1/2-x y,-z,-x " "1/2-y,1/2-z,x 1/4+y,1/4+x,1/4-z 3/4-y,1/4-x,3/4-z 3/4+y,3/4-x,1/4+z " "1/4-y,3/4+x,3/4+z 1/4+x,1/4+z,1/4-y 1/4-x,3/4+z,3/4+y 3/4-x,1/4-z,3/4-y " "3/4+x,3/4-z,1/4+y 1/4+z,1/4+y,1/4-x 3/4+z,3/4-y,1/4+x 1/4-z,3/4+y,3/4+x " "3/4-z,1/4-y,3/4-x 1/4-x,3/4-y,1/4-z 1/4+x,1/4+y,3/4-z 3/4+x,1/4-y,1/4+z " "3/4-x,3/4+y,3/4+z 1/4-z,3/4-x,1/4-y 3/4-z,3/4+x,3/4+y 1/4+z,1/4+x,3/4-y " "3/4+z,1/4-x,1/4+y 1/4-y,3/4-z,1/4-x 3/4+y,1/4-z,1/4+x 3/4-y,3/4+z,3/4+x " "1/4+y,1/4+z,3/4-x 1/2-y,1/2-x,1/2+z y,1/2+x,z -y,x,1/2-z 1/2+y,-x,-z " "1/2-x,1/2-z,1/2+y 1/2+x,-z,-y x,1/2+z,y -x,z,1/2-y 1/2-z,1/2-y,1/2+x " "-z,y,1/2-x 1/2+z,-y,-x z,1/2+y,x 1/2+x,1/2+y,z 1/2-x,-y,1/2+z -x,y,-z " "x,1/2-y,1/2-z 1/2+z,1/2+x,y z,1/2-x,1/2-y 1/2-z,-x,1/2+y -z,x,-y " "1/2+y,1/2+z,x -y,z,-x y,1/2-z,1/2-x 1/2-y,-z,1/2+x 1/4+y,3/4+x,3/4-z " "3/4-y,3/4-x,1/4-z 3/4+y,1/4-x,3/4+z 1/4-y,1/4+x,1/4+z 1/4+x,3/4+z,3/4-y " "1/4-x,1/4+z,1/4+y 3/4-x,3/4-z,1/4-y 3/4+x,1/4-z,3/4+y 1/4+z,3/4+y,3/4-x " "3/4+z,1/4-y,3/4+x 1/4-z,1/4+y,1/4+x 3/4-z,3/4-y,1/4-x 1/4-x,1/4-y,3/4-z " "1/4+x,3/4+y,1/4-z 3/4+x,3/4-y,3/4+z 3/4-x,1/4+y,1/4+z 1/4-z,1/4-x,3/4-y " "3/4-z,1/4+x,1/4+y 1/4+z,3/4+x,1/4-y 3/4+z,3/4-x,3/4+y 1/4-y,1/4-z,3/4-x " "3/4+y,3/4-z,3/4+x 3/4-y,1/4+z,1/4+x 1/4+y,3/4+z,1/4-x 1/2-y,-x,z y,x,1/2+z " "-y,1/2+x,-z 1/2+y,1/2-x,1/2-z 1/2-x,-z,y 1/2+x,1/2-z,1/2-y x,z,1/2+y " "-x,1/2+z,-y 1/2-z,-y,x -z,1/2+y,-x 1/2+z,1/2-y,1/2-x z,y,1/2+x", "x,y,z 1/4-x,3/4-y,1/2+z 3/4-x,1/2+y,1/4-z 1/2+x,1/4-y,3/4-z z,x,y " "1/2+z,1/4-x,3/4-y 1/4-z,3/4-x,1/2+y 3/4-z,1/2+x,1/4-y y,z,x " "3/4-y,1/2+z,1/4-x 1/2+y,1/4-z,3/4-x 1/4-y,3/4-z,1/2+x 3/4+y,1/4+x,-z " "1/2-y,1/2-x,1/2-z 1/4+y,-x,3/4+z -y,3/4+x,1/4+z 3/4+x,1/4+z,-y " "-x,3/4+z,1/4+y 1/2-x,1/2-z,1/2-y 1/4+x,-z,3/4+y 3/4+z,1/4+y,-x " "1/4+z,-y,3/4+x -z,3/4+y,1/4+x 1/2-z,1/2-y,1/2-x -x,-y,-z 3/4+x,1/4+y,1/2-z " "1/4+x,1/2-y,3/4+z 1/2-x,3/4+y,1/4+z -z,-x,-y 1/2-z,3/4+x,1/4+y " "3/4+z,1/4+x,1/2-y 1/4+z,1/2-x,3/4+y -y,-z,-x 1/4+y,1/2-z,3/4+x " "1/2-y,3/4+z,1/4+x 3/4+y,1/4+z,1/2-x 1/4-y,3/4-x,z 1/2+y,1/2+x,1/2+z " "3/4-y,x,1/4-z y,1/4-x,3/4-z 1/4-x,3/4-z,y x,1/4-z,3/4-y 1/2+x,1/2+z,1/2+y " "3/4-x,z,1/4-y 1/4-z,3/4-y,x 3/4-z,y,1/4-x z,1/4-y,3/4-x 1/2+z,1/2+y,1/2+x " "x,1/2+y,1/2+z 1/4-x,1/4-y,z 3/4-x,y,3/4-z 1/2+x,3/4-y,1/4-z z,1/2+x,1/2+y " "1/2+z,3/4-x,1/4-y 1/4-z,1/4-x,y 3/4-z,x,3/4-y y,1/2+z,1/2+x 3/4-y,z,3/4-x " "1/2+y,3/4-z,1/4-x 1/4-y,1/4-z,x 3/4+y,3/4+x,1/2-z 1/2-y,-x,-z " "1/4+y,1/2-x,1/4+z -y,1/4+x,3/4+z 3/4+x,3/4+z,1/2-y -x,1/4+z,3/4+y " "1/2-x,-z,-y 1/4+x,1/2-z,1/4+y 3/4+z,3/4+y,1/2-x 1/4+z,1/2-y,1/4+x " "-z,1/4+y,3/4+x 1/2-z,-y,-x -x,1/2-y,1/2-z 3/4+x,3/4+y,-z 1/4+x,-y,1/4+z " "1/2-x,1/4+y,3/4+z -z,1/2-x,1/2-y 1/2-z,1/4+x,3/4+y 3/4+z,3/4+x,-y " "1/4+z,-x,1/4+y -y,1/2-z,1/2-x 1/4+y,-z,1/4+x 1/2-y,1/4+z,3/4+x " "3/4+y,3/4+z,-x 1/4-y,1/4-x,1/2+z 1/2+y,x,z 3/4-y,1/2+x,3/4-z y,3/4-x,1/4-z " "1/4-x,1/4-z,1/2+y x,3/4-z,1/4-y 1/2+x,z,y 3/4-x,1/2+z,3/4-y " "1/4-z,1/4-y,1/2+x 3/4-z,1/2+y,3/4-x z,3/4-y,1/4-x 1/2+z,y,x 1/2+x,y,1/2+z " "3/4-x,3/4-y,z 1/4-x,1/2+y,3/4-z x,1/4-y,1/4-z 1/2+z,x,1/2+y z,1/4-x,1/4-y " "3/4-z,3/4-x,y 1/4-z,1/2+x,3/4-y 1/2+y,z,1/2+x 1/4-y,1/2+z,3/4-x " "y,1/4-z,1/4-x 3/4-y,3/4-z,x 1/4+y,1/4+x,1/2-z -y,1/2-x,-z 3/4+y,-x,1/4+z " "1/2-y,3/4+x,3/4+z 1/4+x,1/4+z,1/2-y 1/2-x,3/4+z,3/4+y -x,1/2-z,-y " "3/4+x,-z,1/4+y 1/4+z,1/4+y,1/2-x 3/4+z,-y,1/4+x 1/2-z,3/4+y,3/4+x " "-z,1/2-y,-x 1/2-x,-y,1/2-z 1/4+x,1/4+y,-z 3/4+x,1/2-y,1/4+z -x,3/4+y,3/4+z " "1/2-z,-x,1/2-y -z,3/4+x,3/4+y 1/4+z,1/4+x,-y 3/4+z,1/2-x,1/4+y " "1/2-y,-z,1/2-x 3/4+y,1/2-z,1/4+x -y,3/4+z,3/4+x 1/4+y,1/4+z,-x " "3/4-y,3/4-x,1/2+z y,1/2+x,z 1/4-y,x,3/4-z 1/2+y,1/4-x,1/4-z " "3/4-x,3/4-z,1/2+y 1/2+x,1/4-z,1/4-y x,1/2+z,y 1/4-x,z,3/4-y " "3/4-z,3/4-y,1/2+x 1/4-z,y,3/4-x 1/2+z,1/4-y,1/4-x z,1/2+y,x 1/2+x,1/2+y,z " "3/4-x,1/4-y,1/2+z 1/4-x,y,1/4-z x,3/4-y,3/4-z 1/2+z,1/2+x,y z,3/4-x,3/4-y " "3/4-z,1/4-x,1/2+y 1/4-z,x,1/4-y 1/2+y,1/2+z,x 1/4-y,z,1/4-x y,3/4-z,3/4-x " "3/4-y,1/4-z,1/2+x 1/4+y,3/4+x,-z -y,-x,1/2-z 3/4+y,1/2-x,3/4+z " "1/2-y,1/4+x,1/4+z 1/4+x,3/4+z,-y 1/2-x,1/4+z,1/4+y -x,-z,1/2-y " "3/4+x,1/2-z,3/4+y 1/4+z,3/4+y,-x 3/4+z,1/2-y,3/4+x 1/2-z,1/4+y,1/4+x " "-z,-y,1/2-x 1/2-x,1/2-y,-z 1/4+x,3/4+y,1/2-z 3/4+x,-y,3/4+z -x,1/4+y,1/4+z " "1/2-z,1/2-x,-y -z,1/4+x,1/4+y 1/4+z,3/4+x,1/2-y 3/4+z,-x,3/4+y " "1/2-y,1/2-z,-x 3/4+y,-z,3/4+x -y,1/4+z,1/4+x 1/4+y,3/4+z,1/2-x " "3/4-y,1/4-x,z y,x,1/2+z 1/4-y,1/2+x,1/4-z 1/2+y,3/4-x,3/4-z 3/4-x,1/4-z,y " "1/2+x,3/4-z,3/4-y x,z,1/2+y 1/4-x,1/2+z,1/4-y 3/4-z,1/4-y,x " "1/4-z,1/2+y,1/4-x 1/2+z,3/4-y,3/4-x z,y,1/2+x", "x,y,z -y,x,z -x,-y,z y,-x,z x,-y,-z y,x,-z -x,y,-z -y,-x,-z z,x,y -x,z,y " "-z,-x,y x,-z,y z,-x,-y x,z,-y -z,x,-y -x,-z,-y y,z,x y,-z,-x z,y,-x -y,z,-x " "-z,-y,-x -y,-z,x z,-y,x -z,y,x -x,-y,-z y,-x,-z x,y,-z -y,x,-z -x,y,z " "-y,-x,z x,-y,z y,x,z -z,-x,-y x,-z,-y z,x,-y -x,z,-y -z,x,y -x,-z,y z,-x,y " "x,z,y -y,-z,-x -y,z,x -z,-y,x y,-z,x z,y,x y,z,-x -z,y,-x z,-y,-x " "x+1/2,y+1/2,z+1/2 -y+1/2,x+1/2,z+1/2 -x+1/2,-y+1/2,z+1/2 y+1/2,-x+1/2,z+1/2 " "x+1/2,-y+1/2,-z+1/2 y+1/2,x+1/2,-z+1/2 -x+1/2,y+1/2,-z+1/2 " "-y+1/2,-x+1/2,-z+1/2 z+1/2,x+1/2,y+1/2 -x+1/2,z+1/2,y+1/2 " "-z+1/2,-x+1/2,y+1/2 x+1/2,-z+1/2,y+1/2 z+1/2,-x+1/2,-y+1/2 " "x+1/2,z+1/2,-y+1/2 -z+1/2,x+1/2,-y+1/2 -x+1/2,-z+1/2,-y+1/2 " "y+1/2,z+1/2,x+1/2 y+1/2,-z+1/2,-x+1/2 z+1/2,y+1/2,-x+1/2 " "-y+1/2,z+1/2,-x+1/2 -z+1/2,-y+1/2,-x+1/2 -y+1/2,-z+1/2,x+1/2 " "z+1/2,-y+1/2,x+1/2 -z+1/2,y+1/2,x+1/2 -x+1/2,-y+1/2,-z+1/2 " "y+1/2,-x+1/2,-z+1/2 x+1/2,y+1/2,-z+1/2 -y+1/2,x+1/2,-z+1/2 " "-x+1/2,y+1/2,z+1/2 -y+1/2,-x+1/2,z+1/2 x+1/2,-y+1/2,z+1/2 y+1/2,x+1/2,z+1/2 " "-z+1/2,-x+1/2,-y+1/2 x+1/2,-z+1/2,-y+1/2 z+1/2,x+1/2,-y+1/2 " "-x+1/2,z+1/2,-y+1/2 -z+1/2,x+1/2,y+1/2 -x+1/2,-z+1/2,y+1/2 " "z+1/2,-x+1/2,y+1/2 x+1/2,z+1/2,y+1/2 -y+1/2,-z+1/2,-x+1/2 " "-y+1/2,z+1/2,x+1/2 -z+1/2,-y+1/2,x+1/2 y+1/2,-z+1/2,x+1/2 z+1/2,y+1/2,x+1/2 " "y+1/2,z+1/2,-x+1/2 -z+1/2,y+1/2,-x+1/2 z+1/2,-y+1/2,-x+1/2", "x,y,z 1/2+x,1/2+y,1/2+z z,1/2-x,1/2+y 1/2+z,-x,y -y,1/2+z,1/2-x 1/2-y,z,-x " "x,1/2-y,1/2+z 1/2+x,-y,z -z,1/2+x,1/2-y 1/2-z,x,-y y,1/2-z,1/2+x 1/2+y,-z,x " "-x,1/2+y,1/2-z 1/2-x,y,-z 3/4+x,1/4-z,3/4-y 1/4+x,3/4-z,1/4-y " "3/4-z,1/4+y,3/4+x 1/4-z,3/4+y,1/4+x 3/4+y,1/4-x,3/4-z 1/4+y,3/4-x,1/4-z " "3/4-x,1/4+z,3/4+y 1/4-x,3/4+z,1/4+y 3/4+z,1/4-y,3/4-x 1/4+z,3/4-y,1/4-x " "3/4-y,1/4+x,3/4+z 1/4-y,3/4+x,1/4+z 1/4+x,1/4+z,1/4+y 3/4+x,3/4+z,3/4+y " "1/4-z,1/4-y,1/4-x 3/4-z,3/4-y,3/4-x 1/4+y,1/4+x,1/4+z 3/4+y,3/4+x,3/4+z " "1/4-x,1/4-z,1/4-y 3/4-x,3/4-z,3/4-y 1/4+z,1/4+y,1/4+x 3/4+z,3/4+y,3/4+x " "1/4-y,1/4-x,1/4-z 3/4-y,3/4-x,3/4-z 1/2+z,x,1/2-y z,1/2+x,-y 1/2-y,-z,1/2+x " "-y,1/2-z,x 1/2+x,y,1/2-z x,1/2+y,-z 1/2-z,-x,1/2+y -z,1/2-x,y 1/2+y,z,1/2-x " "y,1/2+z,-x 1/2-x,-y,1/2+z -x,1/2-y,z 1/2-z,1/2+x,y -z,x,1/2+y " "1/2+y,1/2-z,-x y,-z,1/2-x 1/2-x,1/2+y,z -x,y,1/2+z 1/2+z,1/2-x,-y " "z,-x,1/2-y 1/2-y,1/2+z,x -y,z,1/2+x 1/2+x,1/2-y,-z x,-y,1/2-z " "3/4-x,3/4+z,1/4-y 1/4-x,1/4+z,3/4-y 3/4+z,3/4-y,1/4+x 1/4+z,1/4-y,3/4+x " "3/4-y,3/4+x,1/4-z 1/4-y,1/4+x,3/4-z 3/4+x,3/4-z,1/4+y 1/4+x,1/4-z,3/4+y " "3/4-z,3/4+y,1/4-x 1/4-z,1/4+y,3/4-x 3/4+y,3/4-x,1/4+z 1/4+y,1/4-x,3/4+z " "1/4-x,3/4-z,3/4+y 3/4-x,1/4-z,1/4+y 1/4+z,3/4+y,3/4-x 3/4+z,1/4+y,1/4-x " "1/4-y,3/4-x,3/4+z 3/4-y,1/4-x,1/4+z 1/4+x,3/4+z,3/4-y 3/4+x,1/4+z,1/4-y " "1/4-z,3/4-y,3/4+x 3/4-z,1/4-y,1/4+x 1/4+y,3/4+x,3/4-z 3/4+y,1/4+x,1/4-z " "-z,-x,-y 1/2-z,1/2-x,1/2-y y,z,x 1/2+y,1/2+z,1/2+x -x,-y,-z " "1/2-x,1/2-y,1/2-z z,x,y 1/2+z,1/2+x,1/2+y -y,-z,-x 1/2-y,1/2-z,1/2-x" }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_SPACE_GROUP_DATA avogadrolibs-1.101.0/avogadro/core/spacegroups.cpp000066400000000000000000000317171506155467400221450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "spacegroups.h" #include "crystaltools.h" #include "molecule.h" #include "spacegroupdata.h" #include "unitcell.h" #include "utilities.h" #include // for std::count() #include #include // for isdigit() #include namespace Avogadro::Core { unsigned short SpaceGroups::hallNumber(const std::string& spaceGroup) { unsigned short hall = 0; // can't find anything const unsigned short hall_count = 531; // 530 but first one is empty // some files use " instead of = for the space group symbol std::string sg = spaceGroup; std::replace(sg.begin(), sg.end(), '"', '='); // space_group_hall_symbol for (unsigned short i = 0; i < hall_count; ++i) { if (sg == space_group_hall_symbol[i]) { return i; // found a match } } // space_group_international for (unsigned short i = 0; i < hall_count; ++i) { if (sg == space_group_international[i]) { return i; // found a match } } // space_group_international_short for (unsigned short i = 0; i < hall_count; ++i) { if (sg == space_group_international_short[i]) { return i; // found a match } } // space_group_international_full for (unsigned short i = 0; i < hall_count; ++i) { if (sg == space_group_international_full[i]) { return i; // found a match } } return hall; // can't find anything } CrystalSystem SpaceGroups::crystalSystem(unsigned short hallNumber) { if (hallNumber == 1 || hallNumber == 2) return Triclinic; if (hallNumber >= 3 && hallNumber <= 107) return Monoclinic; if (hallNumber >= 108 && hallNumber <= 348) return Orthorhombic; if (hallNumber >= 349 && hallNumber <= 429) return Tetragonal; if (hallNumber >= 430 && hallNumber <= 461) { // 14 of these are rhombohedral and the rest are trigonal switch (hallNumber) { case 433: case 434: case 436: case 437: case 444: case 445: case 450: case 451: case 452: case 453: case 458: case 459: case 460: case 461: return Rhombohedral; default: return Trigonal; } } if (hallNumber >= 462 && hallNumber <= 488) return Hexagonal; if (hallNumber >= 489 && hallNumber <= 530) return Cubic; // hallNumber must be 0 or > 531 return None; // for (unsigned short i = 0; i < hallNumberCount; ++i) } unsigned short SpaceGroups::internationalNumber(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_international_number[hallNumber]; else return space_group_international_number[0]; } const char* SpaceGroups::schoenflies(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_schoenflies[hallNumber]; else return space_group_schoenflies[0]; } const char* SpaceGroups::hallSymbol(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_hall_symbol[hallNumber]; else return space_group_hall_symbol[0]; } const char* SpaceGroups::international(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_international[hallNumber]; else return space_group_international[0]; } const char* SpaceGroups::internationalFull(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_international_full[hallNumber]; else return space_group_international_full[0]; } const char* SpaceGroups::internationalShort(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_international_short[hallNumber]; else return space_group_international_short[0]; } const char* SpaceGroups::setting(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_setting[hallNumber]; else return space_group_setting[0]; } unsigned short SpaceGroups::transformsCount(unsigned short hallNumber) { if (hallNumber <= 530) { std::string s = transformsString(hallNumber); return std::count(s.begin(), s.end(), ' ') + 1; } else { return 0; } } Real readTransformCoordinate(const std::string& coordinate, const Vector3& v) { // The coordinate should be at least 1 character assert(coordinate.size() != 0); Real ret = 0.0; Index i = 0; while (i < coordinate.size()) { bool isNeg = false; if (coordinate[i] == '-') { isNeg = true; ++i; assert(i < coordinate.size()); } // We assume we are adding, so no need for a boolean here else if (coordinate[i] == '+') { ++i; assert(i < coordinate.size()); } // Check to see if we have a digit if (isdigit(coordinate[i])) { // We SHOULD have a fraction. Also, we SHOULD only deal with single // digit numbers. Add assertions to make sure this is the case. assert(i + 2 < coordinate.size()); assert(coordinate[i + 1] == '/'); assert(isdigit(coordinate[i + 2])); // Assert that this is a single digit number if (coordinate.size() > i + 3) assert(!isdigit(coordinate[i + 3])); // Ancient methods used by our forefathers to cast a char to an int Real numerator = coordinate[i] - '0'; Real denominator = coordinate[i + 2] - '0'; Real fraction = numerator / denominator; fraction *= (isNeg) ? -1.0 : 1.0; ret += fraction; i += 3; } else if (coordinate[i] == 'x') { ret += (isNeg) ? -1.0 * v[0] : v[0]; ++i; } else if (coordinate[i] == 'y') { ret += (isNeg) ? -1.0 * v[1] : v[1]; ++i; } else if (coordinate[i] == 'z') { ret += (isNeg) ? -1.0 * v[2] : v[2]; ++i; } else { std::cerr << "In " << __FUNCTION__ << ", error reading string: '" << coordinate << "'\n"; return 0; } } return ret; } Vector3 getSingleTransform(const std::string& transform, const Vector3& v) { Vector3 ret; std::vector coordinates = split(transform, ','); // This should be 3 in size. Something very bad happened if it is not. assert(coordinates.size() == 3); ret[0] = readTransformCoordinate(coordinates[0], v); ret[1] = readTransformCoordinate(coordinates[1], v); ret[2] = readTransformCoordinate(coordinates[2], v); return ret; } Array SpaceGroups::getTransforms(unsigned short hallNumber, const Vector3& v) { if (hallNumber == 0 || hallNumber > 530) return Array(); Array ret; std::string transformsStr = transformsString(hallNumber); // These transforms are separated by spaces std::vector transforms = split(transformsStr, ' '); for (auto& transform : transforms) ret.push_back(getSingleTransform(transform, v)); return ret; } void SpaceGroups::fillUnitCell(Molecule& mol, unsigned short hallNumber, double cartTol, bool wrapToCell, bool allCopies) { if (!mol.unitCell()) return; UnitCell* uc = mol.unitCell(); Array atomicNumbers = mol.atomicNumbers(); Array positions = mol.atomPositions3d(); Index numAtoms = mol.atomCount(); // We are going to loop through the original atoms. That is why // we have numAtoms cached instead of using atomCount(). for (Index i = 0; i < numAtoms; ++i) { unsigned char atomicNum = atomicNumbers[i]; Vector3 pos = uc->toFractional(positions[i]); Array newAtoms = getTransforms(hallNumber, pos); // We skip 0 because it is the original atom. for (Index j = 1; j < newAtoms.size(); ++j) { // The new atoms are in fractional coordinates. Convert to cartesian. Vector3 newCandidate = uc->toCartesian(newAtoms[j]); // if we are wrapping to the cell, we need to wrap the new atom if (wrapToCell) newCandidate = uc->wrapCartesian(newCandidate); // If there is already an atom in this location within a // certain tolerance, do not add the atom. bool atomAlreadyPresent = false; for (Index k = 0; k < mol.atomCount(); k++) { // If it does not have the same atomic number, skip over it. if (mol.atomicNumber(k) != atomicNum) continue; Real distance = uc->distance(mol.atomPosition3d(k), newCandidate); if (distance <= cartTol) atomAlreadyPresent = true; } // If there is already an atom present here, just continue if (atomAlreadyPresent) continue; // If we got this far, add the atom! Atom newAtom = mol.addAtom(atomicNum); newAtom.setPosition3d(newCandidate); } } // Now we generate any copies on the unit boundary // We need to loop through all the atoms again // if a fractional coordinate contains 0.0, we need to generate a copy // of the atom at 1.0 if (!allCopies) return; atomicNumbers = mol.atomicNumbers(); positions = mol.atomPositions3d(); numAtoms = mol.atomCount(); for (Index i = 0; i < numAtoms; ++i) { unsigned char atomicNum = atomicNumbers[i]; Vector3 pos = uc->toFractional(positions[i]); Array newAtoms; // We need to check each coordinate to see if it is 0.0 or 1.0 // .. if so, we need to generate the symmetric copy for (Index j = 0; j < 3; ++j) { Vector3 newPos = pos; if (fabs(pos[j]) < 0.001) { newPos[j] = 1.0; newAtoms.push_back(newPos); } if (fabs(pos[j] - 1.0) < 0.001) { newPos[j] = 0.0; newAtoms.push_back(newPos); } for (Index k = j + 1; k < 3; ++k) { if (fabs(pos[k]) < 0.001) { newPos[k] = 1.0; newAtoms.push_back(newPos); newPos[k] = 0.0; // revert for the other coords } if (fabs(pos[k] - 1.0) < 0.001) { newPos[k] = 0.0; newAtoms.push_back(newPos); newPos[k] = 1.0; // revert } } } // finally, check if all coordinates are 0.0 if (fabs(pos[0]) < 0.001 && fabs(pos[1]) < 0.001 && fabs(pos[2]) < 0.001) newAtoms.push_back(Vector3(1.0, 1.0, 1.0)); // or 1.0 .. unlikely, but possible if (fabs(pos[0] - 1.0) < 0.001 && fabs(pos[1] - 1.0) < 0.001 && fabs(pos[2] - 1.0) < 0.001) newAtoms.push_back(Vector3(0.0, 0.0, 0.0)); for (const auto& atomMat : newAtoms) { // The new atoms are in fractional coordinates. Convert to cartesian. Vector3 newCandidate = uc->toCartesian(atomMat); // If there is already an atom in this location within a // certain tolerance, do not add the atom. bool atomAlreadyPresent = false; for (Index k = 0; k < mol.atomCount(); k++) { // If it does not have the same atomic number, skip over it. if (mol.atomicNumber(k) != atomicNum) continue; Real distance = (mol.atomPosition3d(k) - newCandidate).norm(); if (distance <= cartTol) { atomAlreadyPresent = true; break; } } // If there is already an atom present here, just continue if (atomAlreadyPresent) continue; // If we got this far, add the atom! Atom newAtom = mol.addAtom(atomicNum); newAtom.setPosition3d(newCandidate); } } } void SpaceGroups::reduceToAsymmetricUnit(Molecule& mol, unsigned short hallNumber, double cartTol) { if (!mol.unitCell()) return; UnitCell* uc = mol.unitCell(); // The number of atoms may change as we remove atoms, so don't cache // the number of atoms, atomic positions, or atomic numbers // There's no point in looking at the last atom for (Index i = 0; i + 1 < mol.atomCount(); ++i) { unsigned char atomicNum = mol.atomicNumber(i); Vector3 pos = uc->toFractional(mol.atomPosition3d(i)); Array transformAtoms = getTransforms(hallNumber, pos); // Loop through the rest of the atoms in this crystal and see if any match // up with a transform for (Index j = i + 1; j < mol.atomCount(); ++j) { // If the atomic number does not match, skip over it if (mol.atomicNumber(j) != atomicNum) continue; Vector3 trialPos = mol.atomPosition3d(j); // Loop through the transform atoms // We skip 0 because it is the original atom. for (Index k = 1; k < transformAtoms.size(); ++k) { // The transform atoms are in fractional coordinates. Convert to // cartesian. Vector3 transformPos = uc->toCartesian(transformAtoms[k]); Real distance = uc->distance(trialPos, transformPos); // Is the atom within the cartesian tolerance distance? if (distance <= cartTol) { // Remove this atom and adjust the index mol.removeAtom(j); --j; break; } } } } } const char* SpaceGroups::transformsString(unsigned short hallNumber) { if (hallNumber <= 530) return space_group_transforms[hallNumber]; else return ""; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/spacegroups.h000066400000000000000000000113131506155467400216000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_SPACE_GROUPS_H #define AVOGADRO_CORE_SPACE_GROUPS_H #include "avogadrocoreexport.h" #include "array.h" #include "vector.h" #include namespace Avogadro::Core { class Molecule; /** * Enumeration of the crystal system. */ enum CrystalSystem { None, Triclinic, Monoclinic, Orthorhombic, Tetragonal, Trigonal, Rhombohedral, Hexagonal, Cubic }; /** * @class SpaceGroups spacegroups.h * @brief The Spacegroups class stores basic data about crystal spacegroups. * * The spacegroups class gives a simple interface to basic data about crystal * spacegroups. The data is generated from information in spglib. */ class AVOGADROCORE_EXPORT SpaceGroups { public: SpaceGroups() = default; ~SpaceGroups() = default; /** * @return The hall number of the matching space group string or 0 if not * found */ static unsigned short hallNumber(const std::string& spaceGroup); /** * @return an enum representing the crystal system for a given hall number. * If an invalid hall number is given, None will be returned. */ static CrystalSystem crystalSystem(unsigned short hallNumber); /** * @return the international number for a given hall number. * If an invalid hall number is given, 0 will be returned. */ static unsigned short internationalNumber(unsigned short hallNumber); /** * @return the Schoenflies symbol for a given hall number. * If an invalid hall number is given, an empty string will be returned. */ static const char* schoenflies(unsigned short hallNumber); /** * @return the Hall symbol for a given hall number. '=' is used instead of * '"'. If an invalid hall number is given, an empty string will be returned. */ static const char* hallSymbol(unsigned short hallNumber); /** * @return the international symbol for a given hall number. * If an invalid hall number is given, an empty string will be returned. */ static const char* international(unsigned short hallNumber); /** * @return the full international symbol for a given hall number. * If an invalid hall number is given, an empty string will be returned. */ static const char* internationalFull(unsigned short hallNumber); /** * @return the short international symbol for a given hall number. * If an invalid hall number is given, an empty string will be returned. */ static const char* internationalShort(unsigned short hallNumber); /** * @return the setting for a given hall number. * If an invalid hall number is given, an empty string will be returned. * An empty string may also be returned if there are no settings for this * space group. */ static const char* setting(unsigned short hallNumber); /** * @return the number of transforms for a given hall number. * If an invalid hall number is given, 0 will be returned. */ static unsigned short transformsCount(unsigned short hallNumber); /** * @return an array of transforms for a given hall number and a vector v. * The vector should be in fractional coordinates. * If an invalid hall number is given, an empty array will be returned. */ static Array getTransforms(unsigned short hallNumber, const Vector3& v); /** * Fill a crystal with atoms by using transforms from a hall number. * Nothing will be done if the molecule does not have a unit cell. * The cartesian tolerance is used to check if an atom is already * present at that location. If there is another atom within that * distance, the new atom will not be placed there. */ static void fillUnitCell(Molecule& mol, unsigned short hallNumber, double cartTol = 1e-5, bool wrapToCell = true, bool allCopies = false); /** * Reduce a cell to its asymmetric unit. * Nothing will be done if the molecule does not have a unit cell. * The cartesian tolerance is used to check if an atom is present * at a location within the tolerance distance. * If an atom is present, the atom gets removed. */ static void reduceToAsymmetricUnit(Molecule& mol, unsigned short hallNumber, double cartTol = 1e-5); private: /** * Get the transforms string stored in the database. */ static const char* transformsString(unsigned short hallNumber); }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_SPACE_GROUPS_H avogadrolibs-1.101.0/avogadro/core/symbolatomtyper.cpp000066400000000000000000000011451506155467400230540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "symbolatomtyper.h" #include "elements.h" namespace Avogadro::Core { SymbolAtomTyper::SymbolAtomTyper(const Molecule* mol) : AtomTyper(mol) { } std::string SymbolAtomTyper::type(const Atom& atom) { return std::string(Elements::symbol(atom.atomicNumber())); } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/symbolatomtyper.h000066400000000000000000000017741506155467400225310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_SYMBOLATOMTYPER_H #define AVOGADRO_CORE_SYMBOLATOMTYPER_H #include "avogadrocoreexport.h" #include #include namespace Avogadro::Core { /** * @class SymbolAtomTyper symbolatomtyper.h * @brief The SymbolAtomTyper class is a simple implementation of AtomTyper that * assigns element symbols to each atom. */ class AVOGADROCORE_EXPORT SymbolAtomTyper : public AtomTyper { public: explicit SymbolAtomTyper(const Molecule* mol = nullptr); ~SymbolAtomTyper() override = default; protected: std::string type(const Atom& atom) override; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_SYMBOLATOMTYPER_H avogadrolibs-1.101.0/avogadro/core/types.h000066400000000000000000000036251506155467400204200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_TYPES_H #define AVOGADRO_CORE_TYPES_H namespace Avogadro { /** Symbolic constants representing various built-in C++ types. */ enum Type { UnknownType = -1, CharType, UCharType, ShortType, UShortType, IntType, UIntType, FloatType, DoubleType }; template class TypeTraits { public: enum { EnumValue = -1 }; static const char* name() { return "Unsupported type."; } }; template <> class TypeTraits { public: enum { EnumValue = CharType }; static const char* name() { return "char"; } }; template <> class TypeTraits { public: enum { EnumValue = UCharType }; static const char* name() { return "unsigned char"; } }; template <> class TypeTraits { public: enum { EnumValue = ShortType }; static const char* name() { return "short"; } }; template <> class TypeTraits { public: enum { EnumValue = UShortType }; static const char* name() { return "unsigned short"; } }; template <> class TypeTraits { public: enum { EnumValue = IntType }; static const char* name() { return "int"; } }; template <> class TypeTraits { public: enum { EnumValue = UIntType }; static const char* name() { return "unsigned int"; } }; template <> class TypeTraits { public: enum { EnumValue = FloatType }; static const char* name() { return "float"; } }; template <> class TypeTraits { public: enum { EnumValue = DoubleType }; static const char* name() { return "double"; } }; } // namespace Avogadro #endif // AVOGADRO_CORE_H avogadrolibs-1.101.0/avogadro/core/unitcell.cpp000066400000000000000000000042051506155467400214210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "unitcell.h" #include namespace Avogadro::Core { bool UnitCell::isRegular(const Matrix3& m) { constexpr double tiny = 1e-6; const auto a = m.col(0).norm(); const auto b = m.col(1).norm(); const auto c = m.col(2).norm(); const auto w = a * b * c; return w > 0.0 && std::fabs(m.determinant()) > tiny * w; } void UnitCell::setCellParameters(Real a_, Real b_, Real c_, Real al, Real be, Real ga) { // Convert parameters to matrix. See "Appendix 2: Coordinate Systems and // Transformations" of the PDB guide (ref v2.2, 4/23/13, // http://www.bmsc.washington.edu/CrystaLinks/man/pdb/guide2.2_frame.html) const Real cosAlpha = std::cos(al); const Real cosBeta = std::cos(be); const Real cosGamma = std::cos(ga); const Real sinGamma = std::sin(ga); m_cellMatrix(0, 0) = a_; m_cellMatrix(1, 0) = static_cast(0.0); m_cellMatrix(2, 0) = static_cast(0.0); m_cellMatrix(0, 1) = b_ * cosGamma; m_cellMatrix(1, 1) = b_ * sinGamma; m_cellMatrix(2, 1) = static_cast(0.0); m_cellMatrix(0, 2) = c_ * cosBeta; m_cellMatrix(1, 2) = c_ * (cosAlpha - cosBeta * cosGamma) / sinGamma; m_cellMatrix(2, 2) = (c_ / sinGamma) * std::sqrt( static_cast(1.0) - ((cosAlpha * cosAlpha) + (cosBeta * cosBeta) + (cosGamma * cosGamma)) + (static_cast(2.0) * cosAlpha * cosBeta * cosGamma)); computeFractionalMatrix(); } Real UnitCell::signedAngleRadians(const Vector3& v1, const Vector3& v2, const Vector3& axis) { const Vector3 crossProduct(v1.cross(v2)); const Real crossProductNorm(crossProduct.norm()); const Real dotProduct(v1.dot(v2)); const Real signDet(crossProduct.dot(axis)); const Real angle(std::atan2(crossProductNorm, dotProduct)); return signDet > 0.f ? angle : -angle; } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/unitcell.h000066400000000000000000000235011506155467400210660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_UNITCELL_H #define AVOGADRO_CORE_UNITCELL_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include "matrix.h" #include "vector.h" namespace Avogadro::Core { /** * @class UnitCell unitcell.h * @brief The UnitCell class provides a representation of a crystal's unit cell. */ class AVOGADROCORE_EXPORT UnitCell { public: UnitCell(); UnitCell(Real a, Real b, Real c, Real alpha, Real beta, Real gamma); UnitCell(const Vector3& a, const Vector3& b, const Vector3& c); explicit UnitCell(const Matrix3& cellMatrix); UnitCell(const UnitCell& other); ~UnitCell() = default; UnitCell& operator=(UnitCell other); friend void swap(UnitCell& lhs, UnitCell& rhs); /** The lattice vector in the unit cell. Units: Angstrom @{ */ Vector3 aVector() const { return m_cellMatrix.col(0); } Vector3 bVector() const { return m_cellMatrix.col(1); } Vector3 cVector() const { return m_cellMatrix.col(2); } void setAVector(const Vector3& v); void setBVector(const Vector3& v); void setCVector(const Vector3& v); /** @} */ /** The length of the lattice vector in the unit cell. Units: Angstrom @{ */ Real a() const { return m_cellMatrix.col(0).norm(); } Real b() const { return m_cellMatrix.col(1).norm(); } Real c() const { return m_cellMatrix.col(2).norm(); } /** @} */ /** The angle (radians) between the 'b' and 'c' lattice vectors. */ Real alpha() const; /** The angle (radians) between the 'c' and 'a' lattice vectors. */ Real beta() const; /** The angle (radians) between the 'a' and 'b' lattice vectors. */ Real gamma() const; /** * Set the cell parameters defining the unit cell. @a a, @a b, and @a c are * in Angstrom, @a alpha, @a beta, and @a gamma are in radians. */ void setCellParameters(Real a, Real b, Real c, Real alpha, Real beta, Real gamma); /** * The volume of the unit cell in cubic Angstroms. */ Real volume() const; /** * @return A vector pointing to the origin of the translational image that is * @a i images in the a() direction, @a j images in the b() direction, and * @a k images in the c() direction. */ Vector3 imageOffset(int i, int j, int k) const; /** * The cell matrix with lattice vectors as columns. Units: Angstrom @{ */ const Matrix3& cellMatrix() const; void setCellMatrix(const Matrix3& m); /** @} */ /** * The matrix used to convert cartesian to fractional coordinates. */ const Matrix3& fractionalMatrix() const; void setFractionalMatrix(const Matrix3& m); /** @} */ /** * Convert the cartesian coordinate @a cart to fractional (lattice) units. @{ */ Vector3 toFractional(const Vector3& cart) const; void toFractional(const Vector3& cart, Vector3& frac) const; /** * Convert the fractional (lattice) coordinate @a frac to cartesian units. @{ */ Vector3 toCartesian(const Vector3& frac) const; void toCartesian(const Vector3& frac, Vector3& cart) const; /** @} */ /** * Adjust the fractional (lattice) coordinate @a frac so that it lies within * the unit cell. @{ */ Vector3 wrapFractional(const Vector3& frac) const; void wrapFractional(const Vector3& frac, Vector3& wrapped) const; /** @} */ /** * Adjust the cartesian coordinate @a cart so that it lies within the unit * cell. @{ */ Vector3 wrapCartesian(const Vector3& cart) const; void wrapCartesian(const Vector3& cart, Vector3& wrapped) const; /** @} */ /** * Find the minimum fractional image of a fractional vector @a v. * A minimum image has all fractional coordinates between -0.5 and 0.5. */ static Vector3 minimumImageFractional(const Vector3& v); /** * Find the minimum image of a Cartesian vector @a v. * A minimum image has all fractional coordinates between -0.5 and 0.5 */ Vector3 minimumImage(const Vector3& v) const; /** * Find the shortest distance between vectors @a v1 and @a v2. */ Real distance(const Vector3& v1, const Vector3& v2) const; /** * @return if @a m is regular, i.e. its determinant is nonzero */ static bool isRegular(const Matrix3& m); bool isRegular() const { return isRegular(m_cellMatrix); } private: static Real signedAngleRadians(const Vector3& v1, const Vector3& v2, const Vector3& axis); void computeCellMatrix() { m_cellMatrix = m_fractionalMatrix.inverse(); } void computeFractionalMatrix() { m_fractionalMatrix = m_cellMatrix.inverse(); } Matrix3 m_cellMatrix; Matrix3 m_fractionalMatrix; }; inline UnitCell::UnitCell() : m_cellMatrix(Matrix3::Identity()), m_fractionalMatrix(Matrix3::Identity()) { } inline UnitCell::UnitCell(Real a_, Real b_, Real c_, Real alpha_, Real beta_, Real gamma_) { setCellParameters(a_, b_, c_, alpha_, beta_, gamma_); } inline UnitCell::UnitCell(const Vector3& a_, const Vector3& b_, const Vector3& c_) { m_cellMatrix.col(0) = a_; m_cellMatrix.col(1) = b_; m_cellMatrix.col(2) = c_; computeFractionalMatrix(); } inline UnitCell::UnitCell(const Matrix3& cellMatrix_) : m_cellMatrix(cellMatrix_) { computeFractionalMatrix(); } inline UnitCell::UnitCell(const UnitCell& other) : m_cellMatrix(other.m_cellMatrix), m_fractionalMatrix(other.m_fractionalMatrix) { } inline UnitCell& UnitCell::operator=(UnitCell other) { using std::swap; swap(*this, other); return *this; } inline void swap(UnitCell& lhs, UnitCell& rhs) { using std::swap; swap(lhs.m_cellMatrix, rhs.m_cellMatrix); swap(lhs.m_fractionalMatrix, rhs.m_fractionalMatrix); } inline void UnitCell::setAVector(const Vector3& v) { m_cellMatrix.col(0) = v; computeFractionalMatrix(); } inline void UnitCell::setBVector(const Vector3& v) { m_cellMatrix.col(1) = v; computeFractionalMatrix(); } inline void UnitCell::setCVector(const Vector3& v) { m_cellMatrix.col(2) = v; computeFractionalMatrix(); } inline Real UnitCell::alpha() const { return signedAngleRadians(bVector(), cVector(), aVector()); } inline Real UnitCell::beta() const { return signedAngleRadians(cVector(), aVector(), bVector()); } inline Real UnitCell::gamma() const { return signedAngleRadians(aVector(), bVector(), cVector()); } inline Real UnitCell::volume() const { return std::fabs(aVector().cross(bVector()).dot(cVector())); } inline Vector3 UnitCell::imageOffset(int i, int j, int k) const { return (static_cast(i) * m_cellMatrix.col(0) + static_cast(j) * m_cellMatrix.col(1) + static_cast(k) * m_cellMatrix.col(2)); } inline const Matrix3& UnitCell::cellMatrix() const { return m_cellMatrix; } inline void UnitCell::setCellMatrix(const Matrix3& m) { m_cellMatrix = m; computeFractionalMatrix(); } inline const Matrix3& UnitCell::fractionalMatrix() const { return m_fractionalMatrix; } inline void UnitCell::setFractionalMatrix(const Matrix3& m) { m_fractionalMatrix = m; computeCellMatrix(); } inline Vector3 UnitCell::toFractional(const Vector3& cart) const { return m_fractionalMatrix * cart; } inline void UnitCell::toFractional(const Vector3& cart, Vector3& frac) const { frac = m_fractionalMatrix * cart; } inline Vector3 UnitCell::toCartesian(const Vector3& f) const { return m_cellMatrix * f; } inline void UnitCell::toCartesian(const Vector3& frac, Vector3& cart) const { cart = m_cellMatrix * frac; } inline Vector3 UnitCell::wrapFractional(const Vector3& f) const { const Real one = static_cast(1.0); Vector3 result(std::fmod(f[0], one), std::fmod(f[1], one), std::fmod(f[2], one)); if (result[0] < static_cast(0.0)) ++result[0]; if (result[1] < static_cast(0.0)) ++result[1]; if (result[2] < static_cast(0.0)) ++result[2]; // set anything at 1.0 to 0.0 if (result[0] >= static_cast(0.999999)) result[0] = static_cast(0.0); if (result[1] >= static_cast(0.999999)) result[1] = static_cast(0.0); if (result[2] >= static_cast(0.999999)) result[2] = static_cast(0.0); return result; } inline void UnitCell::wrapFractional(const Vector3& f, Vector3& wrapped) const { const Real one = static_cast(1.0); wrapped = Vector3(std::fmod(f[0], one), std::fmod(f[1], one), std::fmod(f[2], one)); if (wrapped[0] < static_cast(0.0)) ++wrapped[0]; if (wrapped[1] < static_cast(0.0)) ++wrapped[1]; if (wrapped[2] < static_cast(0.0)) ++wrapped[2]; // set anything at 1.0 to 0.0 if (wrapped[0] >= static_cast(0.999999)) wrapped[0] = static_cast(0.0); if (wrapped[1] >= static_cast(0.999999)) wrapped[1] = static_cast(0.0); if (wrapped[2] >= static_cast(0.999999)) wrapped[2] = static_cast(0.0); } inline Vector3 UnitCell::wrapCartesian(const Vector3& cart) const { Vector3 result = toFractional(cart); wrapFractional(result, result); toCartesian(result, result); return result; } inline void UnitCell::wrapCartesian(const Vector3& cart, Vector3& wrapped) const { toFractional(cart, wrapped); wrapFractional(wrapped, wrapped); toCartesian(wrapped, wrapped); } inline Vector3 UnitCell::minimumImageFractional(const Vector3& v) { Real x = v[0] - rint(v[0]); Real y = v[1] - rint(v[1]); Real z = v[2] - rint(v[2]); return Vector3(x, y, z); } inline Vector3 UnitCell::minimumImage(const Vector3& v) const { return toCartesian(minimumImageFractional(toFractional(v))); } inline Real UnitCell::distance(const Vector3& v1, const Vector3& v2) const { return minimumImage(v1 - v2).norm(); } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_UNITCELL_H avogadrolibs-1.101.0/avogadro/core/utilities.h000066400000000000000000000075041506155467400212670ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_UTILITIES_H #define AVOGADRO_CORE_UTILITIES_H #include #include #include #include namespace Avogadro::Core { /** * @brief Split the supplied @p string by the @p delimiter. * @param string The string to be split up. * @param delimiter The delimiter to split the string by. * @param skipEmpty If true any empty items will be skipped. * @return A vector containing the items. */ inline std::vector split(const std::string& string, char delimiter, bool skipEmpty = true) { std::vector elements; std::stringstream stringStream(string); std::string item; while (std::getline(stringStream, item, delimiter)) { if (skipEmpty && item.empty()) continue; elements.push_back(item); } return elements; } /** * @brief Search the input string for the search string. * @param input String to be examined. * @param search String that will be searched for. * @return True if the string contains search, false otherwise. */ inline bool contains(const std::string& input, const std::string& search, bool caseSensitive = true) { if (caseSensitive) { return input.find(search) != std::string::npos; } else { std::string inputLower = input; std::string searchLower = search; std::transform(inputLower.begin(), inputLower.end(), inputLower.begin(), ::tolower); std::transform(searchLower.begin(), searchLower.end(), searchLower.begin(), ::tolower); return inputLower.find(searchLower) != std::string::npos; } } /** * @brief Efficient method to confirm input starts with the search string. * @param input String to be examined. * @param search String that will be searched for. * @return True if the string starts with search, false otherwise. */ inline bool startsWith(const std::string& input, const std::string& search) { return input.size() >= search.size() && input.compare(0, search.size(), search) == 0; } /** * @brief Efficient method to confirm input ends with the ending string. * @param input String to be examined. * @param ending String that will be searched for. * @return True if the string ends with ending, false otherwise. */ inline bool endsWith(std::string const& value, std::string const& ending) { if (ending.size() > value.size()) return false; return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } /** * @brief Trim a string of whitespace from the left and right. */ inline std::string trimmed(const std::string& input) { size_t start = input.find_first_not_of(" \n\r\t"); size_t end = input.find_last_not_of(" \n\r\t"); if (start == std::string::npos && end == std::string::npos) return ""; return input.substr(start, end - start + 1); } /** * @brief Cast the inputString to the specified type. * @param inputString String to cast to the specified type. */ template T lexicalCast(const std::string& inputString) { T value; std::istringstream(inputString) >> value; return value; } /** * @brief Cast the inputString to the specified type. * @param inputString String to cast to the specified type. * @param ok Set to true on success, and false if the string could not be * converted to the specified type. */ template T lexicalCast(const std::string& inputString, bool& ok) { T value; std::istringstream stream(inputString); stream >> value; ok = !stream.fail(); return value; } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_UTILITIES_H avogadrolibs-1.101.0/avogadro/core/variant-inline.h000066400000000000000000000246141506155467400221750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_VARIANT_INLINE_H #define AVOGADRO_CORE_VARIANT_INLINE_H #include "variant.h" #include #include namespace Avogadro::Core { inline Variant::Variant() : m_type(Null) {} inline Variant::Variant(double x, double y, double z) : m_type(Vector) { Vector3* v = new Vector3(x, y, z); m_value.vector = v; } template inline Variant::Variant(T v) : m_type(Null) { setValue(v); } template <> inline Variant::Variant(const char* v) : m_type(String) { m_value.string = new std::string(v); } template <> inline Variant::Variant(const MatrixXf& v) : m_type(Matrix) { MatrixX* m = new MatrixX(v.rows(), v.cols()); *m = v.cast(); m_value.matrix = m; } template <> inline Variant::Variant(const Vector3& v) : m_type(Vector) { Vector3* _v = new Vector3(v); m_value.vector = _v; } template <> inline Variant::Variant(const Vector3f& v) : m_type(Vector) { Vector3* _v = new Vector3(v.x(), v.y(), v.z()); m_value.vector = _v; } template <> inline Variant::Variant(const std::vector& v) : m_type(Matrix) { MatrixX* m = new MatrixX(v.size(), 1); for (size_t i = 0; i < v.size(); ++i) m->coeffRef(i, 0) = v[i]; m_value.matrix = m; } inline Variant::Variant(const Variant& variant) : m_type(variant.type()) { if (m_type == String) m_value.string = new std::string(variant.toString()); else if (m_type == Matrix) m_value.matrix = new MatrixX(*variant.m_value.matrix); else if (m_type == Vector) m_value.vector = new Vector3(*variant.m_value.vector); else if (m_type != Null) m_value = variant.m_value; } inline Variant::~Variant() { clear(); } inline Variant::Type Variant::type() const { return m_type; } inline bool Variant::isNull() const { return m_type == Null; } inline bool Variant::setValue(double x, double y, double z) { clear(); m_type = Vector; m_value.vector = new Vector3(x, y, z); return true; } inline bool Variant::setValue(const std::vector& v) { clear(); m_type = Matrix; m_value.matrix = new MatrixX(v.size(), 1); for (size_t i = 0; i < v.size(); ++i) m_value.matrix->coeffRef(i, 0) = v[i]; return true; } template inline bool Variant::setValue(T v) { AVO_UNUSED(v); #ifndef NDEBUG #if defined(_MSC_VER) std::cerr << " Variant::setValue() not implemented for " << __FUNCSIG__ << std::endl; #else std::cerr << " Variant::setValue() not implemented for " << __PRETTY_FUNCTION__ << std::endl; #endif #endif clear(); return false; } template <> inline bool Variant::setValue(bool v) { clear(); m_type = Bool; m_value._bool = v; return true; } template <> inline bool Variant::setValue(char v) { clear(); m_type = Int; m_value._int = v; return true; } template <> inline bool Variant::setValue(short v) { clear(); m_type = Int; m_value._int = v; return true; } template <> inline bool Variant::setValue(int v) { clear(); m_type = Int; m_value._int = v; return true; } template <> inline bool Variant::setValue(long v) { clear(); m_type = Long; m_value._long = v; return true; } template <> inline bool Variant::setValue(float v) { clear(); m_type = Float; m_value._float = v; return true; } template <> inline bool Variant::setValue(double v) { clear(); m_type = Double; m_value._double = v; return true; } template <> inline bool Variant::setValue(std::string string) { clear(); m_type = String; m_value.string = new std::string(string); return true; } template <> inline bool Variant::setValue(const char* string) { return setValue(std::string(string)); } template <> inline bool Variant::setValue(void* pointer) { clear(); m_type = Pointer; m_value.pointer = pointer; return true; } template <> inline bool Variant::setValue(MatrixX matrix) { clear(); m_type = Matrix; m_value.matrix = new MatrixX(matrix); return true; } template <> inline bool Variant::setValue(Vector3 vector) { clear(); m_type = Vector; m_value.vector = new Vector3(vector); return true; } template <> inline bool Variant::setValue(Vector3f vector) { clear(); m_type = Vector; m_value.vector = new Vector3(vector.x(), vector.y(), vector.z()); return true; } template inline T Variant::value() const { return 0; } template <> inline bool Variant::value() const { if (m_type == Bool) return m_value._bool; else if (m_type == Int) return m_value._int != 0; return false; } template <> inline char Variant::value() const { if (m_type == Int) return static_cast(m_value._int); else if (m_type == String && !m_value.string->empty()) return m_value.string->at(0); return '\0'; } template <> inline short Variant::value() const { if (m_type == Int) return static_cast(m_value._int); else if (m_type == String) return lexical_cast(*m_value.string); return 0; } template <> inline int Variant::value() const { if (m_type == Int) return m_value._int; else if (m_type == Bool) return static_cast(m_value._bool); else if (m_type == Float) return static_cast(m_value._float); else if (m_type == Double) return static_cast(m_value._double); else if (m_type == String) return lexical_cast(*m_value.string); return 0; } template <> inline long Variant::value() const { if (m_type == Long) return m_value._long; else if (m_type == Int) return static_cast(m_value._int); else if (m_type == String) return lexical_cast(*m_value.string); return 0; } template <> inline float Variant::value() const { if (m_type == Float) return m_value._float; else if (m_type == Double) return static_cast(m_value._double); else if (m_type == Int) return static_cast(m_value._int); else if (m_type == String) return lexical_cast(*m_value.string); return 0; } template <> inline double Variant::value() const { if (m_type == Double) return m_value._double; else if (m_type == Float) return static_cast(m_value._float); else if (m_type == Int) return static_cast(m_value._int); else if (m_type == String) return lexical_cast(*m_value.string); return 0; } template <> inline void* Variant::value() const { if (m_type == Pointer) return m_value.pointer; return 0; } template <> inline std::string Variant::value() const { if (m_type == String) return *m_value.string; std::stringstream string; if (m_type == Int) string << m_value._int; else if (m_type == Float) string << m_value._float; else if (m_type == Double) string << m_value._double; return string.str(); } template <> inline MatrixX Variant::value() const { if (m_type == Matrix) return *m_value.matrix; return MatrixX(); } template <> inline const MatrixX& Variant::value() const { if (m_type == Matrix) return *m_value.matrix; // Use a static null matrix for the reference. static MatrixX nullMatrix(0, 0); return nullMatrix; } template <> inline Vector3 Variant::value() const { if (m_type == Vector) return *m_value.vector; return Vector3(); } template <> inline const Vector3& Variant::value() const { if (m_type == Vector) return *m_value.vector; static Vector3 nullVector(0, 0, 0); return nullVector; } template <> inline std::vector Variant::value() const { if (m_type == Matrix && m_value.matrix->cols() == 1) { std::vector list(m_value.matrix->rows()); for (int i = 0; i < m_value.matrix->rows(); ++i) list[i] = m_value.matrix->coeff(i, 0); return list; } return std::vector(); } inline void Variant::clear() { if (m_type == String) { delete m_value.string; m_value.string = 0; } else if (m_type == Matrix) { delete m_value.matrix; m_value.matrix = 0; } else if (m_type == Vector) { delete m_value.vector; m_value.vector = 0; } m_type = Null; } inline bool Variant::toBool() const { return value(); } inline char Variant::toChar() const { return value(); } inline unsigned char Variant::toUChar() const { return value(); } inline short Variant::toShort() const { return value(); } inline unsigned short Variant::toUShort() const { return value(); } inline int Variant::toInt() const { return value(); } inline unsigned int Variant::toUInt() const { return value(); } inline long Variant::toLong() const { return value(); } inline unsigned long Variant::toULong() const { return value(); } inline float Variant::toFloat() const { return value(); } inline double Variant::toDouble() const { return value(); } inline Real Variant::toReal() const { return value(); } inline void* Variant::toPointer() const { return value(); } inline std::string Variant::toString() const { return value(); } inline MatrixX Variant::toMatrix() const { return value(); } inline const MatrixX& Variant::toMatrixRef() const { return value(); } inline Vector3 Variant::toVector3() const { return value(); } inline std::vector Variant::toList() const { return value>(); } // --- Operators ----------------------------------------------------------- // inline Variant& Variant::operator=(const Variant& variant) { if (this != &variant) { // Clear previous data, clear(); // Set the new type. m_type = variant.m_type; // Set the new value, if (m_type == String) m_value.string = new std::string(variant.toString()); else if (m_type == Matrix) m_value.matrix = new MatrixX(*variant.m_value.matrix); else if (m_type == Vector) m_value.vector = new Vector3(*variant.m_value.vector); else if (m_type != Null) m_value = variant.m_value; } return *this; } // --- Internal Methods ---------------------------------------------------- // template inline T Variant::lexical_cast(const std::string& str) { T value; std::istringstream(str) >> value; return value; } } // namespace Avogadro::Core #endif // AVOGADRO_CORE_VARIANT_INLINE_H avogadrolibs-1.101.0/avogadro/core/variant.h000066400000000000000000000103751506155467400207200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_VARIANT_H #define AVOGADRO_CORE_VARIANT_H #include "avogadrocoreexport.h" #include "avogadrocore.h" #include "matrix.h" #include "vector.h" #include namespace Avogadro::Core { /** * @class Variant variant.h * @brief The Variant class represents a union of data values. * * Variant objects allow for the storage of and conversion between a variety of * different data types. */ class AVOGADROCORE_EXPORT Variant { public: // enumerations enum Type { Null, Bool, Int, Long, Float, Double, Pointer, String, Vector, Matrix }; /** Creates a null variant. */ inline Variant(); /** Creates a variant to store @p value. */ template Variant(T value); /** Creates a new copy of @p variant. */ inline Variant(const Variant& variant); /** Creates a variant to store a 3D vector */ Variant(double x, double y, double z); /** Destroys the variant object. */ inline ~Variant(); /** @return variant's type. */ inline Type type() const; /** @return \c true if the variant is null. */ inline bool isNull() const; /** Sets the value of the variant to @p value. */ template bool setValue(T value); /** Sets the value of the variant to a 3D vector */ bool setValue(double x, double y, double z); /** Sets the value of the variant to a vector */ bool setValue(const std::vector& v); /** @return the value of the variant in the type given by \c T. */ template T value() const; /** Clears the variant's data and sets the variant to null. */ inline void clear(); /** @return the value of the variant as a \c bool. */ inline bool toBool() const; /** @return the value of the variant as a \c char. */ inline char toChar() const; /** @return the value of the variant as an \c unsigned \c char. */ inline unsigned char toUChar() const; /** @return the value of the variant as a \c short. */ inline short toShort() const; /** @return the value of the variant as an \c unsigned \c short. */ inline unsigned short toUShort() const; /** @return the value of the variant as an \c int. */ inline int toInt() const; /** @return the value of the variant as an \c unsigned \c int. */ inline unsigned int toUInt() const; /** @return the value of the variant as a \c long. */ inline long toLong() const; /** @return the value of the variant as an \c unsigned \c long. */ inline unsigned long toULong() const; /** @return the value of the variant as a \c float. */ inline float toFloat() const; /** @return the value of the variant as a \c double. */ inline double toDouble() const; /** @return the value of the variant as a \c Real. */ inline Real toReal() const; /** @return the value of the variant as a pointer. */ inline void* toPointer() const; /** @return the value of the variant as a string. */ inline std::string toString() const; /** @return the value of the variant as a MatrixX. */ inline MatrixX toMatrix() const; /** @return the value of the variant as a Vector3 */ inline Vector3 toVector3() const; /** @return the value as a vector */ inline std::vector toList() const; /** * @return a reference to the value of the variant as a MatrixX. * This method will not perform any casting -- if type() is not exactly * MatrixX, the function will fail and return a reference to an empty MatrixX. */ inline const MatrixX& toMatrixRef() const; // operators inline Variant& operator=(const Variant& variant); private: template static T lexical_cast(const std::string& string); private: Type m_type; union { bool _bool; char _char; int _int; long _long; float _float; double _double; void* pointer; std::string* string; Vector3* vector; MatrixX* matrix; } m_value; }; } // namespace Avogadro::Core #include "variant-inline.h" #endif // AVOGADRO_CORE_VARIANT_H avogadrolibs-1.101.0/avogadro/core/variantmap.cpp000066400000000000000000000030371506155467400217460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "variantmap.h" namespace Avogadro::Core { size_t VariantMap::size() const { return m_map.size(); } bool VariantMap::isEmpty() const { return m_map.empty(); } std::vector VariantMap::names() const { std::vector result; result.reserve(size()); for (auto it = constBegin(), itEnd = constEnd(); it != itEnd; ++it) result.push_back((*it).first); return result; } void VariantMap::setValue(const std::string& name, const Variant& v) { m_map[name] = v; } Variant VariantMap::value(const std::string& name) const { auto iter = m_map.find(name); if (iter == m_map.end()) return Variant(); return iter->second; } bool VariantMap::hasValue(const std::string& name) const { return m_map.find(name) != m_map.end(); } VariantMap::iterator VariantMap::begin() { return m_map.begin(); } VariantMap::const_iterator VariantMap::begin() const { return m_map.begin(); } VariantMap::const_iterator VariantMap::constBegin() const { return m_map.begin(); } VariantMap::iterator VariantMap::end() { return m_map.end(); } VariantMap::const_iterator VariantMap::end() const { return m_map.end(); } VariantMap::const_iterator VariantMap::constEnd() const { return m_map.end(); } } // namespace Avogadro::Core avogadrolibs-1.101.0/avogadro/core/variantmap.h000066400000000000000000000043461506155467400214170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_VARIANTMAP_H #define AVOGADRO_CORE_VARIANTMAP_H #include "avogadrocoreexport.h" #include "variant.h" #include #include #include namespace Avogadro::Core { /** * @class VariantMap variantmap.h * @brief The VariantMap class provides a map between string keys and variant * values. */ class AVOGADROCORE_EXPORT VariantMap { public: using iterator = std::map::iterator; using const_iterator = std::map::const_iterator; /** Creates a new variant map object. */ VariantMap() = default; /** Destroys the variant map. */ ~VariantMap() = default; /** Returns the size of the variant map. */ size_t size() const; /** Returns \c true if the variant map is empty (i.e. size() == \c 0). */ bool isEmpty() const; /** Returns the names of the entries in the map. */ std::vector names() const; /** Sets the value of @p name to @p v. */ void setValue(const std::string& name, const Variant& v); /** * Returns the value for @p name. If @p name is not found a null variant is * returned. */ Variant value(const std::string& name) const; /** * Returns true if the key exists in the map. */ bool hasValue(const std::string& name) const; /** * Clears the map. */ void clear() { m_map.clear(); } /** Return an iterator pointing to the beginning of the map. */ iterator begin(); /** \overload */ const_iterator begin() const; /** Return a const_iterator pointing to the beginning of the map. */ const_iterator constBegin() const; /** Return an iterator pointing to the end of the map. */ iterator end(); /** \overload */ const_iterator end() const; /** Return a const_iterator pointing to the end of the map. */ const_iterator constEnd() const; private: std::map m_map; }; } // namespace Avogadro::Core #endif // AVOGADRO_CORE_VARIANTMAP_H avogadrolibs-1.101.0/avogadro/core/vector.h000066400000000000000000000022741506155467400205550ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_CORE_VECTOR_H #define AVOGADRO_CORE_VECTOR_H #include "avogadrocore.h" #include namespace Avogadro { /** Typedefs for vector types. */ using Vector2 = Eigen::Matrix; using Vector3 = Eigen::Matrix; using Vector4 = Eigen::Matrix; using Vector2f = Eigen::Matrix; using Vector3f = Eigen::Matrix; using Vector4f = Eigen::Matrix; using Vector2i = Eigen::Matrix; using Vector3i = Eigen::Matrix; using Vector4i = Eigen::Matrix; using Vector2ub = Eigen::Matrix; using Vector3ub = Eigen::Matrix; using Vector4ub = Eigen::Matrix; /** A simple struct composed of Vector3f to represent a frustrum. */ struct Frustrum { Vector3f points[8]; Vector3f planes[4]; }; } // namespace Avogadro #endif // AVOGADRO_CORE_VECTOR_H avogadrolibs-1.101.0/avogadro/core/version.cpp000066400000000000000000000006431506155467400212710ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "version.h" namespace Avogadro { const char* version() { return AvogadroLibs_VERSION; } } // namespace Avogadro avogadrolibs-1.101.0/avogadro/io/000077500000000000000000000000001506155467400165545ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/io/CMakeLists.txt000066400000000000000000000024011506155467400213110ustar00rootroot00000000000000add_library(IO) avogadro_headers(IO cjsonformat.h cmlformat.h cmsgpackformat.h dcdformat.h fileformat.h fileformatmanager.h gromacsformat.h mdlformat.h lammpsformat.h pdbformat.h sdfformat.h trrformat.h turbomoleformat.h vaspformat.h xyzformat.h ) target_sources(IO PRIVATE cjsonformat.cpp cmlformat.cpp cmsgpackformat.cpp dcdformat.cpp fileformat.cpp fileformatmanager.cpp gromacsformat.cpp lammpsformat.cpp mdlformat.cpp pdbformat.cpp sdfformat.cpp trrformat.cpp turbomoleformat.cpp vaspformat.cpp xyzformat.cpp ) if(USE_HDF5) find_package(HDF5 REQUIRED COMPONENTS C) target_compile_definitions(IO PRIVATE AVO_USE_HDF5 H5_USE_110_API) avogadro_headers(IO hdf5dataformat.h) target_sources(IO PRIVATE hdf5dataformat.cpp) target_link_libraries(IO PRIVATE hdf5::hdf5) endif() if(USE_MMTF) find_package(MMTF REQUIRED) target_compile_definitions(IO PRIVATE AVO_USE_MMTF) avogadro_headers(IO mmtfformat.h) target_sources(IO PRIVATE mmtfformat.cpp) target_link_libraries(IO PRIVATE mmtf::mmtf) endif() avogadro_add_library(IO) target_link_libraries(IO PUBLIC Avogadro::Core PRIVATE struct pugixml::pugixml nlohmann_json::nlohmann_json) if(WIN32) target_link_libraries(IO PRIVATE ws2_32) endif() avogadrolibs-1.101.0/avogadro/io/cjsonformat.cpp000066400000000000000000001517201506155467400216130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cjsonformat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using json = nlohmann::json; namespace Avogadro::Io { using std::string; using std::vector; using Core::Array; using Core::Atom; using Core::BasisSet; using Core::Bond; using Core::CrystalTools; using Core::Cube; using Core::GaussianSet; using Core::LayerData; using Core::LayerManager; using Core::Molecule; using Core::Residue; using Core::Variant; bool setJsonKey(json& j, Molecule& m, const std::string& key) { if (j.count(key) && j.find(key)->is_string()) { m.setData(key, j.value(key, "undefined")); return true; } return false; } bool isNumericArray(json& j) { if (j.is_array() && j.size() > 0) { for (const auto& v : j) { if (!v.is_number()) { return false; } } return true; } return false; } bool isBooleanArray(json& j) { if (j.is_array() && j.size() > 0) { for (const auto& v : j) { if (!v.is_boolean()) { return false; } } return true; } return false; } json eigenColToJson(const MatrixX& matrix, int column) { json j; j = json::array(); for (Eigen::Index i = 0; i < matrix.rows(); ++i) { j.push_back(matrix(i, column)); } return j; } bool CjsonFormat::read(std::istream& file, Molecule& molecule) { return deserialize(file, molecule, true); } bool CjsonFormat::deserialize(std::istream& file, Molecule& molecule, bool isJson) { json jsonRoot; // could throw parse errors try { if (isJson) jsonRoot = json::parse(file, nullptr, false); else // msgpack jsonRoot = json::from_msgpack(file); } catch (json::parse_error& e) { appendError("Error reading CJSON file: " + string(e.what())); return false; } catch (json::type_error& e) { appendError("Error reading CJSON file: " + string(e.what())); return false; } if (jsonRoot.is_discarded()) { appendError("Error reading CJSON file."); return false; } if (!jsonRoot.is_object()) { appendError("Error: Input is not a JSON object."); return false; } auto jsonValue = jsonRoot.find("chemicalJson"); if (jsonValue == jsonRoot.end()) jsonValue = jsonRoot.find("chemical json"); if (jsonValue == jsonRoot.end()) { appendError("Error: no \"chemical json\" key found."); return false; } if (*jsonValue != 0 && *jsonValue != 1) { appendError("Warning: chemical json version is not 0 or 1."); return false; } // Read some basic key-value pairs (all strings). setJsonKey(jsonRoot, molecule, "name"); setJsonKey(jsonRoot, molecule, "inchi"); setJsonKey(jsonRoot, molecule, "formula"); // Read in the atoms. json atoms = jsonRoot["atoms"]; if (!atoms.is_object()) { appendError("The 'atoms' key does not contain an object."); return false; } if (!atoms.contains("elements")) { appendError("The 'atoms' key does not contain an 'elements' key."); return false; } if (atoms.contains("elements") && !atoms["elements"].is_object()) { appendError("The 'elements' key does not contain an object."); return false; } json elements = atoms["elements"]; if (!elements.contains("number")) { appendError("The 'elements' key does not contain a 'number' key."); return false; } json atomicNumbers = elements["number"]; // This represents our minimal spec for a molecule - atoms that have an // atomic number. if (isNumericArray(atomicNumbers) && atomicNumbers.size() > 0) { for (auto& atomicNumber : atomicNumbers) { if (!atomicNumber.is_number_integer() || atomicNumber < 0 || atomicNumber >= Core::element_count) { appendError("Error: atomic number is invalid."); return false; } molecule.addAtom(atomicNumber); } } else { // we're done, actually - this is an empty file return true; } Index atomCount = molecule.atomCount(); if (atoms.contains("coords") && atoms["coords"].is_object()) { if (atoms["coords"].contains("3d") && atoms["coords"]["3d"].is_array()) { json atomicCoords = atoms["coords"]["3d"]; if (isNumericArray(atomicCoords) && atomicCoords.size() == 3 * atomCount) { for (Index i = 0; i < atomCount; ++i) { auto a = molecule.atom(i); a.setPosition3d(Vector3(atomicCoords[3 * i], atomicCoords[3 * i + 1], atomicCoords[3 * i + 2])); } } } if (atoms["coords"].contains("3dSets")) { // Check for coordinate sets, and read them in if found, e.g. // trajectories. json coordSets = atoms["coords"]["3dSets"]; if (coordSets.is_array() && coordSets.size()) { for (unsigned int i = 0; i < coordSets.size(); ++i) { Array setArray; json set = coordSets[i]; if (isNumericArray(set)) { for (unsigned int j = 0; j < set.size() / 3; ++j) { setArray.push_back( Vector3(set[3 * j], set[3 * j + 1], set[3 * j + 2])); } molecule.setCoordinate3d(setArray, i); } } // Make sure the first step is active once we are done loading the sets. molecule.setCoordinate3d(0); } } } // Array of vector3 for forces if available if (atoms.contains("forces")) { json forces = atoms["forces"]; if (isNumericArray(forces) && forces.size() == 3 * atomCount) { for (Index i = 0; i < atomCount; ++i) { auto a = molecule.atom(i); a.setForceVector( Vector3(forces[3 * i], forces[3 * i + 1], forces[3 * i + 2])); } } } // labels if (atoms.contains("labels")) { json labels = atoms["labels"]; if (labels.is_array() && labels.size() == atomCount) { for (size_t i = 0; i < atomCount; ++i) { molecule.setAtomLabel(i, labels[i]); } } } // formal charges if (atoms.contains("formalCharges")) { json formalCharges = atoms["formalCharges"]; if (formalCharges.is_array() && formalCharges.size() == atomCount) { for (size_t i = 0; i < atomCount; ++i) { molecule.atom(i).setFormalCharge(formalCharges[i]); } } } // Read in colors if they are present. if (atoms.contains("colors")) { json colors = atoms["colors"]; if (colors.is_array() && colors.size() == 3 * atomCount) { for (Index i = 0; i < atomCount; ++i) { Vector3ub color(colors[3 * i], colors[3 * i + 1], colors[3 * i + 2]); molecule.setColor(i, color); } } } if (atoms.contains("properties")) { json atomProperties = atoms["properties"]; if (atomProperties.is_object()) { for (auto& property : atomProperties.items()) { if (property.value().is_array()) { // TODO: handle atom properties } } } } // Selection is optional, but if present should be loaded. if (atoms.contains("selected")) { json selection = atoms["selected"]; if (isBooleanArray(selection) && selection.size() == atomCount) for (Index i = 0; i < atomCount; ++i) molecule.setAtomSelected(i, selection[i]); else if (isNumericArray(selection) && selection.size() == atomCount) for (Index i = 0; i < atomCount; ++i) molecule.setAtomSelected(i, selection[i] != 0); } if (atoms.contains("frozen")) { json frozen = atoms["frozen"]; if (isBooleanArray(frozen) && frozen.size() == atomCount) { for (Index i = 0; i < atomCount; ++i) molecule.setFrozenAtom(i, frozen[i]); } // might also be a 3xN array for per-axis freezing else if (isNumericArray(frozen) && frozen.size() == 3 * atomCount) { for (Index i = 0; i < atomCount; ++i) { molecule.setFrozenAtomAxis(i, 0, frozen[3 * i] != 0); molecule.setFrozenAtomAxis(i, 1, frozen[3 * i + 1] != 0); molecule.setFrozenAtomAxis(i, 2, frozen[3 * i + 2] != 0); } } } if (atoms.find("layer") != atoms.end()) { json layerJson = atoms["layer"]; if (isNumericArray(layerJson)) { auto& layer = LayerManager::getMoleculeInfo(&molecule)->layer; for (Index i = 0; i < atomCount && i < layerJson.size(); ++i) { while (layerJson[i] > layer.maxLayer()) { layer.addLayer(); } layer.addAtom(layerJson[i], i); } } } // Bonds are optional, but if present should be loaded. if (jsonRoot.contains("bonds")) { json bonds = jsonRoot["bonds"]; if (bonds.is_object() && isNumericArray(bonds["connections"]["index"])) { json connections = bonds["connections"]["index"]; for (unsigned int i = 0; i < connections.size() / 2; ++i) { Index atom1 = static_cast(connections[2 * i]); Index atom2 = static_cast(connections[2 * i + 1]); if (atom1 < atomCount && atom2 < atomCount && atom1 != atom2) { // avoid self-bonds molecule.addBond(atom1, atom2, 1); } } if (bonds.contains("order")) { json order = bonds["order"]; if (isNumericArray(order)) { for (unsigned int i = 0; i < molecule.bondCount() && i < order.size(); ++i) { molecule.bond(i).setOrder(static_cast(order[i])); } } } // are there bond labels? if (bonds.contains("labels")) { json bondLabels = bonds["labels"]; if (bondLabels.is_array()) { for (unsigned int i = 0; i < molecule.bondCount() && i < bondLabels.size(); ++i) { molecule.setBondLabel(i, bondLabels[i]); } } } if (bonds.contains("properties")) { json bondProperties = bonds["properties"]; if (bondProperties.is_object()) { for (auto& property : bondProperties.items()) { if (property.value().is_array()) { // TODO: handle bond properties } } } } } } // residues are optional, but should be loaded if (jsonRoot.contains("residues")) { json residues = jsonRoot["residues"]; if (residues.is_array()) { for (auto residue : residues) { if (!residue.is_object()) continue; // malformed auto name = residue["name"].get(); auto id = static_cast(residue["id"]); auto chainId = residue["chainId"].get(); Residue newResidue(name, id, chainId); json hetero = residue["hetero"]; if (hetero == true) newResidue.setHeterogen(true); int secStruct = residue.value("secStruct", -1); if (secStruct != -1) newResidue.setSecondaryStructure( static_cast( secStruct)); json atomsResidue = residue["atoms"]; if (atomsResidue.is_object()) { for (auto& item : atomsResidue.items()) { if (item.value() < molecule.atomCount()) { const Atom& atom = molecule.atom(item.value()); newResidue.addResidueAtom(item.key(), atom); } } } json color = residue["color"]; if (color.is_array() && color.size() == 3) { Vector3ub col = Vector3ub(color[0], color[1], color[2]); newResidue.setColor(col); } molecule.addResidue(newResidue); } } } if (jsonRoot.contains("unitCell") || jsonRoot.contains("unit cell")) { json unitCell = jsonRoot["unitCell"]; if (!unitCell.is_object()) unitCell = jsonRoot["unit cell"]; if (unitCell.is_object()) { Core::UnitCell* unitCellObject = nullptr; // read in cell vectors in preference to a, b, c parameters json cellVectors = unitCell["cellVectors"]; if (cellVectors.is_array() && cellVectors.size() == 9 && isNumericArray(cellVectors)) { Vector3 aVector(cellVectors[0], cellVectors[1], cellVectors[2]); Vector3 bVector(cellVectors[3], cellVectors[4], cellVectors[5]); Vector3 cVector(cellVectors[6], cellVectors[7], cellVectors[8]); unitCellObject = new Core::UnitCell(aVector, bVector, cVector); if (!unitCellObject->isRegular()) { appendError("cellVectors are not linear independent"); delete unitCellObject; return false; } } else if (unitCell["a"].is_number() && unitCell["b"].is_number() && unitCell["c"].is_number() && unitCell["alpha"].is_number() && unitCell["beta"].is_number() && unitCell["gamma"].is_number()) { Real a = static_cast(unitCell["a"]); Real b = static_cast(unitCell["b"]); Real c = static_cast(unitCell["c"]); Real alpha = static_cast(unitCell["alpha"]) * DEG_TO_RAD; Real beta = static_cast(unitCell["beta"]) * DEG_TO_RAD; Real gamma = static_cast(unitCell["gamma"]) * DEG_TO_RAD; unitCellObject = new Core::UnitCell(a, b, c, alpha, beta, gamma); if (!unitCellObject->isRegular()) { appendError( "cell parameters do not give linear-independent lattice vectors"); delete unitCellObject; return false; } } if (unitCellObject != nullptr) molecule.setUnitCell(unitCellObject); // check for Hall number if present if (unitCell["hallNumber"].is_number()) { auto hallNumber = static_cast(unitCell["hallNumber"]); if (hallNumber > 0 && hallNumber < 531) molecule.setHallNumber(hallNumber); } else if (unitCell["spaceGroup"].is_string()) { auto hallNumber = Core::SpaceGroups::hallNumber(unitCell["spaceGroup"]); if (hallNumber != 0) molecule.setHallNumber(hallNumber); } } } if (atoms["coords"].contains("3dFractional") || atoms["coords"].contains("3d fractional")) { json fractional = atoms["coords"]["3dFractional"]; if (!fractional.is_array()) fractional = atoms["coords"]["3d fractional"]; if (fractional.is_array() && fractional.size() == 3 * atomCount && isNumericArray(fractional) && molecule.unitCell()) { Array fcoords; fcoords.reserve(atomCount); for (Index i = 0; i < atomCount; ++i) { fcoords.push_back(Vector3(static_cast(fractional[i * 3 + 0]), static_cast(fractional[i * 3 + 1]), static_cast(fractional[i * 3 + 2]))); } CrystalTools::setFractionalCoordinates(molecule, fcoords); } } // Basis set is optional, if present read it in. if (jsonRoot.contains("basisSet")) { json basisSet = jsonRoot["basisSet"]; if (basisSet.is_object()) { auto* basis = new GaussianSet; basis->setMolecule(&molecule); // Gather the relevant pieces together so that they can be read in. json shellTypes = basisSet["shellTypes"]; json primitivesPerShell = basisSet["primitivesPerShell"]; json shellToAtomMap = basisSet["shellToAtomMap"]; json exponents = basisSet["exponents"]; json coefficients = basisSet["coefficients"]; int nGTO = 0; for (unsigned int i = 0; i < shellTypes.size(); ++i) { GaussianSet::orbital type; switch (static_cast(shellTypes[i])) { case 0: type = GaussianSet::S; break; case 1: type = GaussianSet::P; break; case 2: type = GaussianSet::D; break; case -2: type = GaussianSet::D5; break; case 3: type = GaussianSet::F; break; case -3: type = GaussianSet::F7; break; case 4: type = GaussianSet::G; break; case -4: type = GaussianSet::G9; break; default: // If we encounter GTOs we do not understand, the basis is likely // invalid type = GaussianSet::UU; } if (type != GaussianSet::UU) { int b = basis->addBasis(static_cast(shellToAtomMap[i]), type); for (int j = 0; j < static_cast(primitivesPerShell[i]); ++j) { basis->addGto(b, coefficients[nGTO], exponents[nGTO]); ++nGTO; } } } json orbitals = jsonRoot["orbitals"]; if (orbitals.is_object() && basis->isValid()) { basis->setElectronCount(orbitals["electronCount"]); json occupations = orbitals["occupations"]; if (isNumericArray(occupations)) { std::vector occs; for (auto& occupation : occupations) occs.push_back(static_cast(occupation)); basis->setMolecularOrbitalOccupancy(occupations); } json energies = orbitals["energies"]; if (isNumericArray(energies)) { std::vector energyArray; for (auto& energie : energies) energyArray.push_back(static_cast(energie)); basis->setMolecularOrbitalEnergy(energyArray); } json numbers = orbitals["numbers"]; if (isNumericArray(numbers)) { std::vector numArray; for (auto& number : numbers) numArray.push_back(static_cast(number)); basis->setMolecularOrbitalNumber(numArray); } json symmetryLabels = orbitals["symmetries"]; if (symmetryLabels.is_array()) { std::vector symArray; for (auto& sym : symmetryLabels) symArray.push_back(sym); basis->setSymmetryLabels(symArray); } json moCoefficients = orbitals["moCoefficients"]; json moCoefficientsA = orbitals["alphaCoefficients"]; json moCoefficientsB = orbitals["betaCoefficients"]; bool openShell = false; if (isNumericArray(moCoefficients)) { std::vector coeffs; for (auto& moCoefficient : moCoefficients) coeffs.push_back(static_cast(moCoefficient)); basis->setMolecularOrbitals(coeffs); } else if (isNumericArray(moCoefficientsA) && isNumericArray(moCoefficientsB)) { std::vector coeffsA; for (auto& i : moCoefficientsA) coeffsA.push_back(static_cast(i)); std::vector coeffsB; for (auto& i : moCoefficientsB) coeffsB.push_back(static_cast(i)); basis->setMolecularOrbitals(coeffsA, BasisSet::Alpha); basis->setMolecularOrbitals(coeffsB, BasisSet::Beta); openShell = true; } else { std::cout << "No orbital cofficients found!" << std::endl; } // Check for orbital coefficient sets, these are paired with coordinates // when they exist, but have constant basis set, atom types, etc. if (orbitals["sets"].is_array() && orbitals["sets"].size()) { json orbSets = orbitals["sets"]; for (unsigned int idx = 0; idx < orbSets.size(); ++idx) { moCoefficients = orbSets[idx]["moCoefficients"]; moCoefficientsA = orbSets[idx]["alphaCoefficients"]; moCoefficientsB = orbSets[idx]["betaCoefficients"]; if (isNumericArray(moCoefficients)) { std::vector coeffs; for (auto& moCoefficient : moCoefficients) coeffs.push_back(static_cast(moCoefficient)); basis->setMolecularOrbitals(coeffs, BasisSet::Paired, idx); } else if (isNumericArray(moCoefficientsA) && isNumericArray(moCoefficientsB)) { std::vector coeffsA; for (auto& i : moCoefficientsA) coeffsA.push_back(static_cast(i)); std::vector coeffsB; for (auto& i : moCoefficientsB) coeffsB.push_back(static_cast(i)); basis->setMolecularOrbitals(coeffsA, BasisSet::Alpha, idx); basis->setMolecularOrbitals(coeffsB, BasisSet::Beta, idx); openShell = true; } } // Set the first step as active. basis->setActiveSetStep(0); } if (openShell) { // look for alpha and beta orbital energies json energiesA = orbitals["alphaEnergies"]; json energiesB = orbitals["betaEnergies"]; // check if they are numeric arrays if (isNumericArray(energiesA) && isNumericArray(energiesB)) { std::vector moEnergiesA; for (auto& i : energiesA) moEnergiesA.push_back(static_cast(i)); std::vector moEnergiesB; for (auto& i : energiesB) moEnergiesB.push_back(static_cast(i)); basis->setMolecularOrbitalEnergy(moEnergiesA, BasisSet::Alpha); basis->setMolecularOrbitalEnergy(moEnergiesB, BasisSet::Beta); // look for alpha and beta orbital occupations json occupationsA = orbitals["alphaOccupations"]; json occupationsB = orbitals["betaOccupations"]; // check if they are numeric arrays if (isNumericArray(occupationsA) && isNumericArray(occupationsB)) { std::vector moOccupationsA; for (auto& i : occupationsA) moOccupationsA.push_back(static_cast(i)); std::vector moOccupationsB; for (auto& i : occupationsB) moOccupationsB.push_back(static_cast(i)); basis->setMolecularOrbitalOccupancy(moOccupationsA, BasisSet::Alpha); basis->setMolecularOrbitalOccupancy(moOccupationsB, BasisSet::Beta); } } } } molecule.setBasisSet(basis); } } // See if there is any vibration data, load it if so. json vibrations = jsonRoot["vibrations"]; if (vibrations.is_object()) { json frequencies = vibrations["frequencies"]; if (isNumericArray(frequencies)) { Array freqs; for (auto& frequencie : frequencies) { freqs.push_back(static_cast(frequencie)); } molecule.setVibrationFrequencies(freqs); } json intensities = vibrations["intensities"]; if (isNumericArray(intensities)) { Array intens; for (auto& intensitie : intensities) { intens.push_back(static_cast(intensitie)); } molecule.setVibrationIRIntensities(intens); } json raman = vibrations["ramanIntensities"]; if (isNumericArray(raman)) { Array intens; for (auto& i : raman) { intens.push_back(static_cast(i)); } molecule.setVibrationRamanIntensities(intens); } json displacements = vibrations["eigenVectors"]; if (displacements.is_array()) { Array> disps; for (auto arr : displacements) { if (isNumericArray(arr)) { Array mode; mode.resize(arr.size() / 3); double* ptr = &mode[0][0]; for (auto& j : arr) { *(ptr++) = static_cast(j); } disps.push_back(mode); } } molecule.setVibrationLx(disps); } } // check for spectra data json spectra = jsonRoot["spectra"]; if (spectra.is_object()) { // electronic json electronic = spectra["electronic"]; if (electronic.is_object()) { // check to see "energies" and "intensities" json energies = electronic["energies"]; json intensities = electronic["intensities"]; // make sure they are both numeric arrays if (isNumericArray(energies) && isNumericArray(intensities)) { // make sure they are the same size if (energies.size() == intensities.size()) { // create the matrix MatrixX electronicData(energies.size(), 2); // copy the data for (std::size_t i = 0; i < energies.size(); ++i) { electronicData(i, 0) = energies[i]; electronicData(i, 1) = intensities[i]; } // set the data molecule.setSpectra("Electronic", electronicData); } } // check if there's CD data for "rotation" json rotation = electronic["rotation"]; if (isNumericArray(rotation) && rotation.size() == energies.size()) { MatrixX rotationData(rotation.size(), 2); for (std::size_t i = 0; i < rotation.size(); ++i) { rotationData(i, 0) = energies[i]; rotationData(i, 1) = rotation[i]; } molecule.setSpectra("CircularDichroism", rotationData); } } // nmr json nmr = spectra["nmr"]; if (nmr.is_object()) { // chemical shifts json chemicalShifts = nmr["shifts"]; if (isNumericArray(chemicalShifts)) { MatrixX chemicalShiftData(chemicalShifts.size(), 2); for (std::size_t i = 0; i < chemicalShifts.size(); ++i) { chemicalShiftData(i, 0) = static_cast(chemicalShifts[i]); chemicalShiftData(i, 1) = 1.0; } molecule.setSpectra("NMR", chemicalShiftData); } } } // constraints if (jsonRoot.find("constraints") != jsonRoot.end()) { json constraints = jsonRoot["constraints"]; if (constraints.is_array()) { for (auto& constraint : constraints) { if (isNumericArray(constraint)) { // value, atom1, atom2, atom3, atom4 if (constraint.size() == 3) { // bond molecule.addConstraint(constraint[0], constraint[1], constraint[2]); } else if (constraint.size() == 4) { // angle molecule.addConstraint(constraint[0], constraint[1], constraint[2], constraint[3]); } else if (constraint.size() == 5) { // torsion molecule.addConstraint(constraint[0], constraint[1], constraint[2], constraint[3], constraint[4]); } } } } } // properties if (jsonRoot.find("properties") != jsonRoot.end()) { json properties = jsonRoot["properties"]; if (properties.is_object()) { if (properties.find("totalCharge") != properties.end()) { molecule.setData("totalCharge", static_cast(properties["totalCharge"])); } if (properties.find("totalSpinMultiplicity") != properties.end()) { molecule.setData("totalSpinMultiplicity", static_cast(properties["totalSpinMultiplicity"])); } if (properties.find("dipoleMoment") != properties.end()) { // read the numeric array json dipole = properties["dipoleMoment"]; if (isNumericArray(dipole) && dipole.size() == 3) { Core::Variant dipoleMoment(dipole[0], dipole[1], dipole[2]); molecule.setData("dipoleMoment", dipoleMoment); } } // iterate through everything else for (auto& element : properties.items()) { if (element.key() == "totalCharge" || element.key() == "totalSpinMultiplicity" || element.key() == "dipoleMoment") { continue; } if (element.value().type() == json::value_t::array) { // check if it is a numeric array to go into Eigen::MatrixXd json j = element.value(); // convenience std::size_t rows = j.size(); MatrixX matrix; matrix.resize(rows, 1); // default to 1 columns bool isNumeric = true; for (std::size_t row = 0; row < j.size(); ++row) { const auto& jrow = j.at(row); // check to see if we have a simple vector or a matrix if (jrow.type() == json::value_t::array) { matrix.conservativeResize(rows, jrow.size()); for (std::size_t col = 0; col < jrow.size(); ++col) { const auto& value = jrow.at(col); if (value.type() == json::value_t::number_float || value.type() == json::value_t::number_integer || value.type() == json::value_t::number_unsigned) matrix(row, col) = value.get(); else { isNumeric = false; break; } } } else if (jrow.type() == json::value_t::number_float || jrow.type() == json::value_t::number_integer || jrow.type() == json::value_t::number_unsigned) { // just a row vector matrix(row, 0) = jrow.get(); } else { isNumeric = false; break; } } if (isNumeric) molecule.setData(element.key(), matrix); // TODO: add support for non-numeric arrays // std::cout << " property: " << element.key() << " = " << matrix // << " size " << matrix.rows() << 'x' << matrix.cols() // << std::endl; } else if (element.value().type() == json::value_t::number_float) { molecule.setData(element.key(), element.value().get()); } else if (element.value().type() == json::value_t::number_integer) { molecule.setData(element.key(), element.value().get()); } else if (element.value().type() == json::value_t::boolean) { molecule.setData(element.key(), element.value().get()); } else if (element.value().type() == json::value_t::string) { molecule.setData(element.key(), element.value().get()); } else { std::cout << " cannot store property: " << element.key() << " = " << element.value() << " type " << element.value().type_name() << std::endl; } } } } // inputParameters are calculation metadata if (jsonRoot.find("inputParameters") != jsonRoot.end()) { json inputParameters = jsonRoot["inputParameters"]; // add this as a string to the molecule data molecule.setData("inputParameters", inputParameters.dump()); } // Partial charges are optional, but if present should be loaded. if (atoms.contains("partialCharges")) { json partialCharges = atoms["partialCharges"]; if (partialCharges.is_object()) { // keys are types, values are arrays of charges for (auto& kv : partialCharges.items()) { MatrixX charges(atomCount, 1); if (isNumericArray(kv.value()) && kv.value().size() == atomCount) { for (size_t i = 0; i < kv.value().size(); ++i) { charges(i, 0) = kv.value()[i]; } molecule.setPartialCharges(kv.key(), charges); } } } } if (jsonRoot.find("layer") != jsonRoot.end()) { auto names = LayerManager::getMoleculeInfo(&molecule); json visible = jsonRoot["layer"]["visible"]; if (isBooleanArray(visible)) { for (const auto& v : visible) { names->visible.push_back(v); } } json locked = jsonRoot["layer"]["locked"]; if (isBooleanArray(locked)) { for (const auto& l : locked) { names->locked.push_back(l); } } json enables = jsonRoot["layer"]["enable"]; if (enables.is_object()) { for (const auto& enable : enables.items()) { if (isBooleanArray(enable.value())) { names->enable[enable.key()] = std::vector(); for (const auto& e : enable.value()) { names->enable[enable.key()].push_back(e); } } } } json settings = jsonRoot["layer"]["settings"]; if (settings.is_object()) { for (const auto& setting : settings.items()) { if (isBooleanArray(setting.value())) { names->settings[setting.key()] = Core::Array(); for (const auto& s : setting.value()) { names->settings[setting.key()].push_back(new LayerData(s)); } } } } } return true; } bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) { return serialize(file, molecule, true); } bool CjsonFormat::serialize(std::ostream& file, const Molecule& molecule, bool isJson) { json opts; if (!options().empty()) opts = json::parse(options(), nullptr, false); else opts = json::object(); json root; root["chemicalJson"] = 1; if (opts.value("properties", true)) { if (molecule.data("name").type() == Variant::String) root["name"] = molecule.data("name").toString().c_str(); if (molecule.data("inchi").type() == Variant::String) root["inchi"] = molecule.data("inchi").toString().c_str(); } json properties; // these methods assume neutral singlet if not set // or approximate from formal charges and # of electrons properties["totalCharge"] = molecule.totalCharge(); properties["totalSpinMultiplicity"] = molecule.totalSpinMultiplicity(); // loop through all other properties const auto map = molecule.dataMap(); for (const auto& element : map) { if (element.first == "name" || element.first == "inchi") continue; // check for "inputParameters" and handle it separately if (element.first == "inputParameters") { json inputParameters = json::parse(element.second.toString()); root["inputParameters"] = inputParameters; continue; } // check if the key is atom.* or bond.* and handle it separately if (element.first.find("atom.") == 0 || element.first.find("bond.") == 0 || element.first.find("residue.") == 0) { continue; } if (element.second.type() == Variant::String) properties[element.first] = element.second.toString().c_str(); else if (element.second.type() == Variant::Double) properties[element.first] = element.second.toDouble(); else if (element.second.type() == Variant::Float) properties[element.first] = element.second.toFloat(); else if (element.second.type() == Variant::Int) properties[element.first] = element.second.toInt(); else if (element.second.type() == Variant::Bool) properties[element.first] = element.second.toBool(); else if (element.second.type() == Variant::Vector) { // e.g. dipole moment Vector3 v = element.second.toVector3(); json vector; vector.push_back(v.x()); vector.push_back(v.y()); vector.push_back(v.z()); properties[element.first] = vector; } else if (element.second.type() == Variant::Matrix) { MatrixX m = element.second.toMatrix(); json matrix; for (int i = 0; i < m.rows(); ++i) { json row; for (int j = 0; j < m.cols(); ++j) { row.push_back(m(i, j)); } matrix.push_back(row); } properties[element.first] = matrix; } } root["properties"] = properties; if (molecule.unitCell()) { json unitCell; unitCell["a"] = molecule.unitCell()->a(); unitCell["b"] = molecule.unitCell()->b(); unitCell["c"] = molecule.unitCell()->c(); unitCell["alpha"] = molecule.unitCell()->alpha() * RAD_TO_DEG; unitCell["beta"] = molecule.unitCell()->beta() * RAD_TO_DEG; unitCell["gamma"] = molecule.unitCell()->gamma() * RAD_TO_DEG; json vectors; vectors.push_back(molecule.unitCell()->aVector().x()); vectors.push_back(molecule.unitCell()->aVector().y()); vectors.push_back(molecule.unitCell()->aVector().z()); vectors.push_back(molecule.unitCell()->bVector().x()); vectors.push_back(molecule.unitCell()->bVector().y()); vectors.push_back(molecule.unitCell()->bVector().z()); vectors.push_back(molecule.unitCell()->cVector().x()); vectors.push_back(molecule.unitCell()->cVector().y()); vectors.push_back(molecule.unitCell()->cVector().z()); unitCell["cellVectors"] = vectors; // write the Hall number and space group unitCell["hallNumber"] = molecule.hallNumber(); unitCell["spaceGroup"] = Core::SpaceGroups::international(molecule.hallNumber()); root["unitCell"] = unitCell; } // check for spectra data // vibrations are separate if (molecule.spectraTypes().size() != 0) { json spectra, electronic, nmr; bool hasElectronic = false; for (const auto& type : molecule.spectraTypes()) { if (type == "Electronic") { hasElectronic = true; electronic["energies"] = eigenColToJson(molecule.spectra(type), 0); electronic["intensities"] = eigenColToJson(molecule.spectra(type), 1); } else if (type == "CircularDichroism") { electronic["rotation"] = eigenColToJson(molecule.spectra(type), 1); } else if (type == "NMR") { json data; data["shifts"] = eigenColToJson(molecule.spectra(type), 0); spectra["nmr"] = data; } } if (hasElectronic) { spectra["electronic"] = electronic; } root["spectra"] = spectra; } // Create a basis set/MO matrix we can round trip. if (molecule.basisSet() && dynamic_cast(molecule.basisSet())) { json basis; auto gaussian = dynamic_cast(molecule.basisSet()); // Map the shell types from enumeration to integer values. auto symmetry = gaussian->symmetry(); json shellTypes; for (int i : symmetry) { switch (i) { case GaussianSet::S: shellTypes.push_back(0); break; case GaussianSet::P: shellTypes.push_back(1); break; case GaussianSet::D: shellTypes.push_back(2); break; case GaussianSet::D5: shellTypes.push_back(-2); break; case GaussianSet::F: shellTypes.push_back(3); break; case GaussianSet::F7: shellTypes.push_back(-3); break; case GaussianSet::G: shellTypes.push_back(4); break; case GaussianSet::G9: shellTypes.push_back(-4); break; default: // Something bad, put in a silly number... shellTypes.push_back(426942); } } basis["shellTypes"] = shellTypes; // This bit is slightly tricky, map from our index to primitives per // shell. if (gaussian->gtoIndices().size() && gaussian->atomIndices().size()) { auto gtoIndices = gaussian->gtoIndices(); auto gtoA = gaussian->gtoA(); json primitivesPerShell; for (size_t i = 0; i < gtoIndices.size() - 1; ++i) primitivesPerShell.push_back(gtoIndices[i + 1] - gtoIndices[i]); primitivesPerShell.push_back(gtoA.size() - gtoIndices.back()); basis["primitivesPerShell"] = primitivesPerShell; auto atomIndices = gaussian->atomIndices(); json shellToAtomMap; for (unsigned int& atomIndice : atomIndices) shellToAtomMap.push_back(atomIndice); basis["shellToAtomMap"] = shellToAtomMap; auto gtoC = gaussian->gtoC(); json exponents; json coefficients; for (size_t i = 0; i < gtoA.size(); ++i) { exponents.push_back(gtoA[i]); coefficients.push_back(gtoC[i]); } basis["exponents"] = exponents; basis["coefficients"] = coefficients; // Write out the basis set if a valid one exists. root["basisSet"] = basis; } // Now get the MO matrix, potentially other things. Need to get a handle // on when we have just one (paired), or two (alpha and beta) to write. auto moMatrix = gaussian->moMatrix(); auto betaMatrix = gaussian->moMatrix(BasisSet::Beta); json moCoefficients; for (int j = 0; j < moMatrix.cols(); ++j) for (int i = 0; i < moMatrix.rows(); ++i) moCoefficients.push_back(moMatrix(i, j)); if (betaMatrix.cols() > 0 && betaMatrix.rows() > 0) { json moBeta; for (int j = 0; j < moMatrix.cols(); ++j) for (int i = 0; i < moMatrix.rows(); ++i) moBeta.push_back(moMatrix(i, j)); root["orbitals"]["alphaCoefficients"] = moCoefficients; root["orbitals"]["betaCoefficients"] = moBeta; } else { root["orbitals"]["moCoefficients"] = moCoefficients; } // Some energy, occupation, and number data potentially. auto energies = gaussian->moEnergy(); if (energies.size() > 0) { json energyData; for (double& energie : energies) { energyData.push_back(energie); } auto betaEnergies = gaussian->moEnergy(BasisSet::Beta); if (betaEnergies.size() > 0) { json betaEnergyData; for (double& energie : betaEnergies) { betaEnergyData.push_back(energie); } root["orbitals"]["alphaEnergies"] = energyData; root["orbitals"]["betaEnergies"] = betaEnergyData; } else root["orbitals"]["energies"] = energyData; } auto occ = gaussian->moOccupancy(); if (occ.size() > 0) { json occData; for (unsigned char& it : occ) occData.push_back(static_cast(it)); auto betaOcc = gaussian->moOccupancy(BasisSet::Beta); if (betaOcc.size() > 0) { json betaOccData; for (unsigned char& it : betaOcc) betaOccData.push_back(static_cast(it)); root["orbitals"]["alphaOccupations"] = occData; root["orbitals"]["betaOccupations"] = betaOccData; } else root["orbitals"]["occupations"] = occData; } auto num = gaussian->moNumber(); if (num.size() > 0) { json numData; for (unsigned int& it : num) numData.push_back(it); root["orbitals"]["numbers"] = numData; } root["orbitals"]["electronCount"] = gaussian->electronCount(); } // Write out any cubes that are present in the molecule. if (molecule.cubeCount() > 0) { const Cube* cube = molecule.cube(0); json cubeData; for (float it : *cube->data()) { cubeData.push_back(it); } // Get the origin, max, spacing, and dimensions to place in the object. json cubeObj; json cubeMin; cubeMin.push_back(cube->min().x()); cubeMin.push_back(cube->min().y()); cubeMin.push_back(cube->min().z()); cubeObj["origin"] = cubeMin; json cubeSpacing; cubeSpacing.push_back(cube->spacing().x()); cubeSpacing.push_back(cube->spacing().y()); cubeSpacing.push_back(cube->spacing().z()); cubeObj["spacing"] = cubeSpacing; json cubeDims; cubeDims.push_back(cube->dimensions().x()); cubeDims.push_back(cube->dimensions().y()); cubeDims.push_back(cube->dimensions().z()); cubeObj["dimensions"] = cubeDims; cubeObj["scalars"] = cubeData; root["cube"] = cubeObj; } // Create and populate the atom arrays. if (molecule.atomCount()) { json atoms; json elements; json selected; json colors; Vector3ub color; bool hasCustomColors = molecule.colors().size() == molecule.atomCount(); for (Index i = 0; i < molecule.atomCount(); ++i) { elements.push_back(molecule.atom(i).atomicNumber()); selected.push_back(molecule.atomSelected(i)); color = molecule.color(i); colors.push_back(color.x()); colors.push_back(color.y()); colors.push_back(color.z()); } atoms["elements"]["number"] = elements; if (!molecule.isSelectionEmpty()) atoms["selected"] = selected; if (hasCustomColors) atoms["colors"] = colors; // check for frozen atoms json frozen; // any atoms frozen? bool anyFrozen = false; // any atoms with custom axis? // (i.e., we need to save the matrix) bool axisFrozen = false; Eigen::VectorXd frozenAtomMask = molecule.frozenAtomMask(); for (Index i = 0; i < molecule.atomCount(); ++i) { if (molecule.frozenAtom(i)) { anyFrozen = true; frozen.push_back(1); } else { frozen.push_back(0); } // check for custom axis if (i * 3 + 2 < frozenAtomMask.size()) { Real sum = frozenAtomMask[3 * i] + frozenAtomMask[3 * i + 1] + frozenAtomMask[3 * i + 2]; // check if it's not all frozen or all unfrozen if (sum != 3.0 && sum != 0.0) { axisFrozen = true; } } } if (anyFrozen) { if (axisFrozen) { // iterate through the mask as an array json frozenAtomMaskArray; for (Index i = 0; i < frozenAtomMask.size(); ++i) { frozenAtomMaskArray.push_back(frozenAtomMask[i]); } atoms["frozen"] = frozenAtomMaskArray; } else { atoms["frozen"] = frozen; } } // check for partial charges auto partialCharges = molecule.partialChargeTypes(); if (!partialCharges.empty()) { // add them to the atoms object for (const auto& type : partialCharges) { MatrixX chargesMatrix = molecule.partialCharges(type); json charges; for (Index i = 0; i < molecule.atomCount(); ++i) { charges.push_back(chargesMatrix(i, 0)); } atoms["partialCharges"][type] = charges; } } json coords; // 3d positions: if (molecule.atomPositions3d().size() == molecule.atomCount()) { // everything gets real-space Cartesians json coords3d; for (const auto& it : molecule.atomPositions3d()) { coords3d.push_back(it.x()); coords3d.push_back(it.y()); coords3d.push_back(it.z()); } coords["3d"] = coords3d; // if the unit cell exists, also write fractional coords if (molecule.unitCell()) { json coordsFractional; Array fcoords; CrystalTools::fractionalCoordinates( *molecule.unitCell(), molecule.atomPositions3d(), fcoords); for (auto& fcoord : fcoords) { coordsFractional.push_back(fcoord.x()); coordsFractional.push_back(fcoord.y()); coordsFractional.push_back(fcoord.z()); } coords["3dFractional"] = coordsFractional; } // if the molecule has multiple coordinate sets, write them out if (molecule.coordinate3dCount() > 1) { json coords3dSets; for (Index i = 0; i < molecule.coordinate3dCount(); ++i) { json coordsSet; const auto& positions = molecule.coordinate3d(i); for (const auto& it : positions) { coordsSet.push_back(it.x()); coordsSet.push_back(it.y()); coordsSet.push_back(it.z()); } coords3dSets.push_back(coordsSet); } coords["3dSets"] = coords3dSets; } } // 2d positions: if (molecule.atomPositions2d().size() == molecule.atomCount()) { json coords2d; for (const auto& it : molecule.atomPositions2d()) { coords2d.push_back(it.x()); coords2d.push_back(it.y()); } coords["2d"] = coords2d; } atoms["coords"] = coords; // forces if present const auto forceVectors = molecule.forceVectors(); if (forceVectors.size() == molecule.atomCount()) { json forces; for (const auto& force : forceVectors) { forces.push_back(force.x()); forces.push_back(force.y()); forces.push_back(force.z()); } atoms["forces"] = forces; } // check for atom labels Array atomLabels = molecule.atomLabels(); if (atomLabels.size() == molecule.atomCount()) { json labels; for (Index i = 0; i < molecule.atomCount(); ++i) { labels.push_back(atomLabels[i]); } atoms["labels"] = labels; } // formal charges json formalCharges; bool hasFormalCharges = false; for (size_t i = 0; i < molecule.atomCount(); ++i) { formalCharges.push_back(molecule.formalCharge(i)); if (molecule.formalCharge(i) != 0) hasFormalCharges = true; } if (hasFormalCharges) atoms["formalCharges"] = formalCharges; auto layer = LayerManager::getMoleculeInfo(&molecule)->layer; if (layer.atomCount()) { json atomLayer; for (Index i = 0; i < layer.atomCount(); ++i) { atomLayer.push_back(layer.getLayerID(i)); } atoms["layer"] = atomLayer; } // TODO check for atom properties root["atoms"] = atoms; // end atoms } // Create and populate the bond arrays. if (molecule.bondCount()) { json bonds; json connections; json order; for (Index i = 0; i < molecule.bondCount(); ++i) { Bond bond = molecule.bond(i); connections.push_back(bond.atom1().index()); connections.push_back(bond.atom2().index()); order.push_back(bond.order()); } bonds["connections"]["index"] = connections; bonds["order"] = order; // check if there are bond labels Array bondLabels = molecule.bondLabels(); if (bondLabels.size() == molecule.bondCount()) { json labels; for (Index i = 0; i < molecule.bondCount(); ++i) { labels.push_back(bondLabels[i]); } bonds["labels"] = labels; } // TODO check for bond properties root["bonds"] = bonds; } // Create and populate any residue arrays if (molecule.residues().size() > 0) { json residues; // array of objects for (auto residue : molecule.residues()) { json entry; entry["name"] = residue.residueName(); entry["id"] = residue.residueId(); entry["chainId"] = residue.chainId(); entry["secStruct"] = residue.secondaryStructure(); if (residue.isHeterogen()) entry["hetero"] = true; json color; color.push_back(residue.color()[0]); color.push_back(residue.color()[1]); color.push_back(residue.color()[2]); entry["color"] = color; json atoms; for (const auto& item : residue.atomNameMap()) { // dictionary between names and atom Id atoms[item.first] = item.second.index(); } entry["atoms"] = atoms; residues.push_back(entry); } root["residues"] = residues; // TODO check for residue properties } // any constraints? auto constraintList = molecule.constraints(); if (!constraintList.empty()) { json constraints; for (auto& constraint : constraintList) { json constraintEntry; constraintEntry.push_back(constraint.value()); constraintEntry.push_back(constraint.aIndex()); constraintEntry.push_back(constraint.bIndex()); if (constraint.cIndex() != MaxIndex) { constraintEntry.push_back(constraint.cIndex()); } if (constraint.dIndex() != MaxIndex) { constraintEntry.push_back(constraint.dIndex()); } constraints.push_back(constraintEntry); } root["constraints"] = constraints; } // If there is vibrational data write this out too. if (molecule.vibrationFrequencies().size() > 0 && (molecule.vibrationFrequencies().size() == molecule.vibrationIRIntensities().size())) { json vibrations; json modes; json freqs; json inten; json raman; json eigenVectors; for (size_t i = 0; i < molecule.vibrationFrequencies().size(); ++i) { modes.push_back(static_cast(i) + 1); freqs.push_back(molecule.vibrationFrequencies()[i]); inten.push_back(molecule.vibrationIRIntensities()[i]); if (molecule.vibrationRamanIntensities().size() > i) raman.push_back(molecule.vibrationRamanIntensities()[i]); Core::Array atomDisplacements = molecule.vibrationLx(i); json eigenVector; for (auto pos : atomDisplacements) { eigenVector.push_back(pos[0]); eigenVector.push_back(pos[1]); eigenVector.push_back(pos[2]); } eigenVectors.push_back(eigenVector); } vibrations["modes"] = modes; vibrations["frequencies"] = freqs; vibrations["intensities"] = inten; if (molecule.vibrationRamanIntensities().size() > 0) vibrations["ramanIntensities"] = raman; vibrations["eigenVectors"] = eigenVectors; root["vibrations"] = vibrations; } auto names = LayerManager::getMoleculeInfo(&molecule); json layer; json visible; for (const bool v : names->visible) { visible.push_back(v); } layer["visible"] = visible; json locked; for (const bool l : names->locked) { locked.push_back(l); } layer["locked"] = locked; for (const auto& enables : names->enable) { json enable; for (const bool e : enables.second) { enable.push_back(e); } layer["enable"][enables.first] = enable; } for (const auto& settings : names->settings) { json setting; for (const auto& e : settings.second) { setting.push_back(e->serialize()); } layer["settings"][settings.first] = setting; } root["layer"] = layer; if (isJson) #ifndef NDEBUG // if debugging, pretty print file << std::setw(2) << root; #else // release mode file << root; #endif else { // write msgpack json::to_msgpack(root, file); } return true; } vector CjsonFormat::fileExtensions() const { vector ext; ext.emplace_back("cjson"); return ext; } vector CjsonFormat::mimeTypes() const { vector mime; mime.emplace_back("chemical/x-cjson"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/cjsonformat.h000066400000000000000000000035701506155467400212570ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_CJSONFORMAT_H #define AVOGADRO_IO_CJSONFORMAT_H #include "fileformat.h" namespace Avogadro { namespace Core { class GaussianSet; } namespace Io { /** * @class CjsonFormat cjsonformat.h * @brief Implementation of the Chemical JSON format. */ class AVOGADROIO_EXPORT CjsonFormat : public FileFormat { public: CjsonFormat() = default; ~CjsonFormat() override = default; Operations supportedOperations() const override { return ReadWrite | File | Stream | String; } FileFormat* newInstance() const override { return new CjsonFormat; } std::string identifier() const override { return "Avogadro: CJSON"; } std::string name() const override { return "Chemical JSON"; } std::string description() const override { return "CJSON format is a lightweight intermediate format used to exchange " "information between Avogadro and other data parsing applications"; } std::string specificationUrl() const override { return "https://github.com/openchemistry/chemicaljson"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; // internal - to allow JSON or MsgPack to be written bool deserialize(std::istream& in, Core::Molecule& molecule, bool json); bool serialize(std::ostream& out, const Core::Molecule& molecule, bool json); }; } // namespace Io } // namespace Avogadro #endif // AVOGADRO_IO_CJSONFORMAT_H avogadrolibs-1.101.0/avogadro/io/cmlformat.cpp000066400000000000000000000542471506155467400212600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cmlformat.h" #ifdef AVO_USE_HDF5 #include "hdf5dataformat.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::Io { using std::string; using namespace std::string_literals; using pugi::xml_attribute; using pugi::xml_document; using pugi::xml_node; using namespace Core; namespace { class CmlFormatPrivate { public: CmlFormatPrivate(Molecule* mol, xml_document& document, std::string filename_) : success(false), molecule(mol), moleculeNode(nullptr), filename(filename_) { // Parse the CML document, and create molecules/elements as necessary. moleculeNode = document.child("molecule"); xml_node cmlNode = document.child("cml"); if (cmlNode) moleculeNode = cmlNode.child("molecule"); if (moleculeNode) { // Parse the various components we know about. #ifdef AVO_USE_HDF5 data(); #endif success = properties(); if (success) success = atoms(); if (success) success = bonds(); } else { error += "Error, no molecule node found."; success = false; } } bool properties() { xml_attribute attribute; xml_node node; node = moleculeNode.child("name"); if (node && node.value()) molecule->setData("name", std::string(node.child_value())); node = moleculeNode.child("identifier"); if (node && node.value()) { attribute = node.attribute("convention"); if (attribute && std::string(attribute.value()) == "iupac:inchi") { attribute = node.attribute("value"); if (attribute && std::string(attribute.name()) == "value") molecule->setData("inchi", std::string(attribute.value())); } } // Unit cell: node = moleculeNode.child("crystal"); if (node) { float a(0); float b(0); float c(0); float alpha(0); float beta(0); float gamma(0); enum { CellA = 0, CellB, CellC, CellAlpha, CellBeta, CellGamma }; std::bitset<6> parsedValues; for (pugi::xml_node scalar = node.child("scalar"); scalar; scalar = scalar.next_sibling("scalar")) { pugi::xml_attribute title = scalar.attribute("title"); if (title) { std::string titleStr(title.value()); if (titleStr == "a") { a = scalar.text().as_float(); parsedValues.set(CellA); } else if (titleStr == "b") { b = scalar.text().as_float(); parsedValues.set(CellB); } else if (titleStr == "c") { c = scalar.text().as_float(); parsedValues.set(CellC); } else if (titleStr == "alpha") { alpha = scalar.text().as_float() * DEG_TO_RAD_F; parsedValues.set(CellAlpha); } else if (titleStr == "beta") { beta = scalar.text().as_float() * DEG_TO_RAD_F; parsedValues.set(CellBeta); } else if (titleStr == "gamma") { gamma = scalar.text().as_float() * DEG_TO_RAD_F; parsedValues.set(CellGamma); } } } if (parsedValues.count() != 6) { error += "Incomplete unit cell description."; return false; } // look for space group, e.g. // xml_node symmetry = node.child("symmetry"); unsigned short hall = 0; if (symmetry) { xml_attribute spaceGroup = symmetry.attribute("spaceGroup"); if (spaceGroup) { // look for space group in the space group table hall = Core::SpaceGroups::hallNumber(std::string(spaceGroup.value())); } } auto* cell = new UnitCell; cell->setCellParameters(a, b, c, alpha, beta, gamma); if (!cell->isRegular()) { error += " does not give linear independent lattice vectors"; delete cell; return false; } molecule->setUnitCell(cell); if (hall != 0) molecule->setHallNumber(hall); } return true; } bool atoms() { xml_node atomArray = moleculeNode.child("atomArray"); if (!atomArray) return false; xml_node node = atomArray.child("atom"); Atom atom; while (node) { // Step through all of the atom attributes and store them. { /* Element Attribute Check */ xml_attribute elementAtt = node.attribute("elementType"); if (elementAtt) { unsigned char atomicNumber = Elements::atomicNumberFromSymbol(elementAtt.value()); atom = molecule->addAtom(atomicNumber); } else { // There is no element data, this atom node is corrupt. error += "Warning, corrupt element node found."; return false; } } { /* ID Attribute Check */ xml_attribute idAtt = node.attribute("id"); if (idAtt) atomIds[std::string(idAtt.value())] = atom.index(); else // Atom nodes must have IDs - bail. return false; } // Check for 3D geometry. { /* XYZ3 and Fract Attribute Check */ xml_attribute x3Att = node.attribute("x3"); xml_attribute xFractAtt = node.attribute("xFract"); if (x3Att) { xml_attribute y3 = node.attribute("y3"); xml_attribute z3 = node.attribute("z3"); if (y3 && z3) { // It looks like we have a valid 3D position. Vector3 position(lexicalCast(x3Att.value()), lexicalCast(y3.value()), lexicalCast(z3.value())); atom.setPosition3d(position); } else { // Corrupt 3D position supplied for atom. return false; } } else if (xFractAtt) { if (!molecule->unitCell()) { error += "No unit cell defined. " "Cannot interpret fractional coordinates."; return false; } xml_attribute yF = node.attribute("yFract"); xml_attribute zF = node.attribute("zFract"); if (yF && zF) { Vector3 coord(static_cast(xFractAtt.as_float()), static_cast(yF.as_float()), static_cast(zF.as_float())); molecule->unitCell()->toCartesian(coord, coord); atom.setPosition3d(coord); } else { error += "Missing y or z fractional coordinate on atom."; return false; } } } // Check for 2D geometry. { /* XY2 Attribute Check */ xml_attribute x2Att = node.attribute("x2"); if (x2Att) { xml_attribute y2 = node.attribute("y2"); if (y2) { Vector2 position(lexicalCast(x2Att.value()), lexicalCast(y2.value())); atom.setPosition2d(position); } else { // Corrupt 2D position supplied for atom. return false; } } } // Check formal charge. { /* Formal Charge Attribute Check */ xml_attribute formalChargeAtt = node.attribute("formalCharge"); if (formalChargeAtt) { auto formalCharge = lexicalCast(formalChargeAtt.value()); atom.setFormalCharge(formalCharge); } } // Move on to the next atom node (if there is one). node = node.next_sibling("atom"); } return true; } bool bonds() { xml_node bondArray = moleculeNode.child("bondArray"); if (!bondArray) return true; xml_node node = bondArray.child("bond"); while (node) { xml_attribute attribute = node.attribute("atomRefs2"); Bond bond; if (attribute) { // Should contain two elements separated by a space. std::string refs(attribute.value()); std::vector tokens = split(refs, ' '); if (tokens.size() != 2) // Corrupted file/input we don't understand return false; std::map::const_iterator begin, end; begin = atomIds.find(tokens[0]); end = atomIds.find(tokens[1]); if (begin != atomIds.end() && end != atomIds.end() && begin->second < molecule->atomCount() && end->second < molecule->atomCount()) { bond = molecule->addBond(molecule->atom(begin->second), molecule->atom(end->second)); } else { // Couldn't parse the bond begin and end. return false; } } if (!bond.isValid()) { // Couldn't create the bond. return false; } attribute = node.attribute("order"); if (attribute && strlen(attribute.value()) == 1) { char o = attribute.value()[0]; switch (o) { case '1': case 'S': case 's': bond.setOrder(1); break; case '2': case 'D': case 'd': bond.setOrder(2); break; case '3': case 'T': case 't': bond.setOrder(3); break; case '4': bond.setOrder(4); break; case '5': bond.setOrder(5); break; case '6': bond.setOrder(6); break; default: bond.setOrder(1); } } else { bond.setOrder(1); } // Move on to the next bond node (if there is one). node = node.next_sibling("bond"); } return true; } #ifdef AVO_USE_HDF5 bool data() { xml_node dataNode = moleculeNode.child("dataMap").first_child(); if (!dataNode) return true; Hdf5DataFormat hdf5; hdf5.openFile(filename + ".h5", Hdf5DataFormat::ReadOnly); do { std::string dataNodeName = dataNode.name(); std::string dataName = dataNode.attribute("name").as_string(); std::string dataType = dataNode.attribute("dataType").as_string(); std::stringstream dataStream(dataNode.text().as_string()); Variant variant; // Read data from HDF5? if (dataNodeName == "hdf5data") { if (!hdf5.isOpen()) { error += "CmlFormatPrivate::data: Cannot read data member '" + dataName + "'. Cannot open file " + filename + ".h5."; continue; } if (dataType != "xsd:double") { error += "CmlFormatPrivate::data: Cannot read data member '" + dataName + "'. Data type is not 'double'."; continue; } MatrixX matrix; if (!hdf5.readDataset(dataStream.str(), matrix)) { error += "CmlFormatPrivate::data: Cannot read data member '" + dataName + "': Unable to read data set '" + dataStream.str() + "' from " + filename + ".h5"; continue; } variant.setValue(matrix); } // or read data from CML? else if (dataNodeName == "scalar") { if (dataType == "xsd:boolean") { bool tmp; dataStream >> tmp; variant.setValue(tmp); } else if (dataType == "xsd:int") { int tmp; dataStream >> tmp; variant.setValue(tmp); } else if (dataType == "xsd:long") { long tmp; dataStream >> tmp; variant.setValue(tmp); } else if (dataType == "xsd:float") { float tmp; dataStream >> tmp; variant.setValue(tmp); } else if (dataType == "xsd:double") { double tmp; dataStream >> tmp; variant.setValue(tmp); } else if (dataType == "xsd:string") { string tmp; dataStream >> tmp; variant.setValue(tmp); } else { error += "CmlFormatPrivate::data: handled scalar data type: " + dataType; continue; } } molecule->setData(dataName, variant); } while ((dataNode = dataNode.next_sibling())); hdf5.closeFile(); return true; } #endif bool success; Molecule* molecule; xml_node moleculeNode; std::map atomIds; string filename; string error; }; } // namespace bool CmlFormat::read(std::istream& file, Core::Molecule& mol) { xml_document document; pugi::xml_parse_result result = document.load(file); if (!result) { appendError("Error parsing XML: " + std::string(result.description())); return false; } CmlFormatPrivate parser(&mol, document, fileName()); if (!parser.success) appendError(parser.error); return parser.success; } std::string formatNumber(std::stringstream& s, double n) { s.str(""); // clear it s.precision(6); s << n; return s.str(); } std::string formatNumber(std::stringstream& s, int n) { s.str(""); // clear it s << n; return s.str(); } bool CmlFormat::write(std::ostream& out, const Core::Molecule& mol) { xml_document document; // Imbue the output stream with the C numeric locale // i.e. modify current stream locale with "C" numeric std::locale currentLocale(""); std::locale numLocale(out.getloc(), "C", std::locale::numeric); out.imbue(numLocale); // imbue modified locale // We also need to set the locale temporarily for XML string formatting std::stringstream numberStream; // use this to format floats as C-format strings numberStream.imbue(numLocale); // Add a custom declaration node. xml_node declaration = document.prepend_child(pugi::node_declaration); declaration.append_attribute("version") = "1.0"; declaration.append_attribute("encoding") = "UTF-8"; xml_node moleculeNode = document.append_child("molecule"); // Standard XML namespaces for CML. moleculeNode.append_attribute("xmlns") = "http://www.xml-cml.org/schema"; moleculeNode.append_attribute("xmlns:cml") = "http://www.xml-cml.org/dictionary/cml"; moleculeNode.append_attribute("xmlns:units") = "http://www.xml-cml.org/unit/nonSi"; moleculeNode.append_attribute("xmlns:xsd") = "http://www.w3c.org/2001/XMLSchema"; moleculeNode.append_attribute("xmlns:iupac") = "http://www.iupac.org"; // Save the name if present into the file. if (mol.data("name").type() == Variant::String) { xml_node node = moleculeNode.append_child("name"); node.text() = mol.data("name").toString().c_str(); } // If the InChI is available, embed that in the CML file. if (mol.data("inchi").type() == Variant::String) { xml_node node = moleculeNode.append_child("identifier"); node.append_attribute("convention") = "iupac:inchi"; node.append_attribute("value") = mol.data("inchi").toString().c_str(); } // Cell specification const UnitCell* cell = mol.unitCell(); if (cell) { xml_node crystalNode = moleculeNode.append_child("crystal"); // Add the unit cell parameters. xml_node crystalANode = crystalNode.append_child("scalar"); xml_node crystalBNode = crystalNode.append_child("scalar"); xml_node crystalCNode = crystalNode.append_child("scalar"); xml_node crystalAlphaNode = crystalNode.append_child("scalar"); xml_node crystalBetaNode = crystalNode.append_child("scalar"); xml_node crystalGammaNode = crystalNode.append_child("scalar"); crystalANode.append_attribute("title") = "a"; crystalBNode.append_attribute("title") = "b"; crystalCNode.append_attribute("title") = "c"; crystalAlphaNode.append_attribute("title") = "alpha"; crystalBetaNode.append_attribute("title") = "beta"; crystalGammaNode.append_attribute("title") = "gamma"; crystalANode.append_attribute("units") = "units:angstrom"; crystalBNode.append_attribute("units") = "units:angstrom"; crystalCNode.append_attribute("units") = "units:angstrom"; crystalAlphaNode.append_attribute("units") = "units:degree"; crystalBetaNode.append_attribute("units") = "units:degree"; crystalGammaNode.append_attribute("units") = "units:degree"; crystalANode.text() = formatNumber(numberStream, cell->a()).c_str(); crystalBNode.text() = formatNumber(numberStream, cell->b()).c_str(); crystalCNode.text() = formatNumber(numberStream, cell->c()).c_str(); crystalAlphaNode.text() = formatNumber(numberStream, cell->alpha() * RAD_TO_DEG).c_str(); crystalBetaNode.text() = formatNumber(numberStream, cell->beta() * RAD_TO_DEG).c_str(); crystalGammaNode.text() = formatNumber(numberStream, cell->gamma() * RAD_TO_DEG).c_str(); // add the space group unsigned short hall = mol.hallNumber(); if (hall != 0) { xml_node spaceGroupNode = crystalNode.append_child("symmetry"); spaceGroupNode.append_attribute("spaceGroup") = Core::SpaceGroups::international(hall); } } xml_node atomArrayNode = moleculeNode.append_child("atomArray"); for (Index i = 0; i < mol.atomCount(); ++i) { xml_node atomNode = atomArrayNode.append_child("atom"); std::ostringstream index; index << 'a' << i + 1; atomNode.append_attribute("id") = index.str().c_str(); Atom a = mol.atom(i); atomNode.append_attribute("elementType") = Elements::symbol(a.atomicNumber()); if (cell) { Vector3 fracPos = cell->toFractional(a.position3d()); atomNode.append_attribute("xFract") = formatNumber(numberStream, fracPos.x()).c_str(); atomNode.append_attribute("yFract") = formatNumber(numberStream, fracPos.y()).c_str(); atomNode.append_attribute("zFract") = formatNumber(numberStream, fracPos.z()).c_str(); } else { atomNode.append_attribute("x3") = formatNumber(numberStream, a.position3d().x()).c_str(); atomNode.append_attribute("y3") = formatNumber(numberStream, a.position3d().y()).c_str(); atomNode.append_attribute("z3") = formatNumber(numberStream, a.position3d().z()).c_str(); } if (a.formalCharge() != 0) { atomNode.append_attribute("formalCharge") = formatNumber(numberStream, a.formalCharge()).c_str(); } } xml_node bondArrayNode = moleculeNode.append_child("bondArray"); for (Index i = 0; i < mol.bondCount(); ++i) { xml_node bondNode = bondArrayNode.append_child("bond"); Bond b = mol.bond(i); std::ostringstream index; index << "a" << b.atom1().index() + 1 << " a" << b.atom2().index() + 1; bondNode.append_attribute("atomRefs2") = index.str().c_str(); bondNode.append_attribute("order") = b.order(); } #ifdef AVO_USE_HDF5 Hdf5DataFormat hdf5; bool openFile = true; xml_node dataMapNode = moleculeNode.append_child("dataMap"); VariantMap dataMap = mol.dataMap(); for (VariantMap::const_iterator it = dataMap.constBegin(), itEnd = dataMap.constEnd(); it != itEnd; ++it) { const std::string& name_ = (*it).first; // Skip names that are handled elsewhere: if (name_ == "inchi") continue; const Variant& var = (*it).second; if (var.type() == Variant::Null) { appendError("CmlFormat::writeFile: skipping null dataMap member '" + name_ + "'."); continue; } xml_node dataNode = dataMapNode.append_child(); dataNode.append_attribute("name") = name_.c_str(); switch (var.type()) { case Variant::Null: // Already skipped above break; case Variant::Bool: dataNode.set_name("scalar"); dataNode.append_attribute("dataType") = "xsd:boolean"; dataNode.text() = var.toBool(); break; case Variant::Int: dataNode.set_name("scalar"); dataNode.append_attribute("dataType") = "xsd:int"; dataNode.text() = var.toInt(); break; case Variant::Long: dataNode.set_name("scalar"); dataNode.append_attribute("dataType") = "xsd:long"; dataNode.text() = var.toString().c_str(); break; case Variant::Float: dataNode.set_name("scalar"); dataNode.append_attribute("dataType") = "xsd:float"; dataNode.text() = var.toFloat(); break; case Variant::Double: dataNode.set_name("scalar"); dataNode.append_attribute("dataType") = "xsd:double"; dataNode.text() = var.toDouble(); break; case Variant::Pointer: appendError( "CmlFormat::writeFile: Skipping void* molecule data member '" + name_ + "'"); break; case Variant::String: dataNode.set_name("scalar"); dataNode.append_attribute("dataType") = "xsd:string"; dataNode.text() = var.toString().c_str(); break; case Variant::Matrix: { if (openFile) { if (!hdf5.openFile(fileName() + ".h5", Hdf5DataFormat::ReadWriteAppend)) { appendError( "CmlFormat::writeFile: Cannot open file: " + fileName() + ".h5"); } openFile = false; } dataNode.set_name("hdf5data"); dataNode.append_attribute("dataType") = "xsd:double"; dataNode.append_attribute("ndims") = "2"; const MatrixX& matrix = var.toMatrixRef(); std::stringstream stream; stream << matrix.rows() << " " << matrix.cols(); dataNode.append_attribute("dims") = stream.str().c_str(); std::string h5Path = "molecule/dataMap/"s + name_; dataNode.text() = h5Path.c_str(); hdf5.writeDataset(h5Path, matrix); } break; default: appendError("CmlFormat::writeFile: Unrecognized type for member '" + name_ + "'."); break; } } hdf5.closeFile(); #endif document.save(out, " "); return true; } std::vector CmlFormat::fileExtensions() const { std::vector ext; ext.emplace_back("cml"); return ext; } std::vector CmlFormat::mimeTypes() const { std::vector mime; mime.emplace_back("chemical/x-cml"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/cmlformat.h000066400000000000000000000027711506155467400207200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_CMLFORMAT_H #define AVOGADRO_IO_CMLFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class CmlFormat cmlformat.h * @brief Implementation of the Chemical Markup Language format. * @author Marcus D. Hanwell */ class AVOGADROIO_EXPORT CmlFormat : public FileFormat { public: CmlFormat() = default; ~CmlFormat() override = default; Operations supportedOperations() const override { return ReadWrite | File | Stream | String; } FileFormat* newInstance() const override { return new CmlFormat; } std::string identifier() const override { return "Avogadro: CML"; } std::string name() const override { return "Chemical Markup Language"; } std::string description() const override { return "TODO: Describe the format."; } std::string specificationUrl() const override { return "http://www.xml-cml.org/schema/schema3/"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; }; } // namespace Avogadro::Io #endif // CMLFORMAT_H avogadrolibs-1.101.0/avogadro/io/cmsgpackformat.cpp000066400000000000000000000017031506155467400222620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cmsgpackformat.h" #include namespace Avogadro::Io { std::vector CMsgPackFormat::fileExtensions() const { std::vector ext; ext.emplace_back("cmpk"); return ext; } std::vector CMsgPackFormat::mimeTypes() const { std::vector mime; mime.emplace_back("chemical/x-cmpack"); return mime; } bool CMsgPackFormat::read(std::istream& in, Core::Molecule& molecule) { return CjsonFormat::deserialize(in, molecule, false); } bool CMsgPackFormat::write(std::ostream& out, const Core::Molecule& molecule) { return CjsonFormat::serialize(out, molecule, false); } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/cmsgpackformat.h000066400000000000000000000035441506155467400217340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_CMSGPACKFORMAT_H #define AVOGADRO_IO_CMSGPACKFORMAT_H #include "cjsonformat.h" #include "fileformat.h" namespace Avogadro { namespace Core { class GaussianSet; } namespace Io { /** * @class CMsgPackFormat cmsgpackformat.h * @brief Implementation of the Chemical MessagePack format. */ class AVOGADROIO_EXPORT CMsgPackFormat : public CjsonFormat { public: // Base class constructors are called automatically CMsgPackFormat() = default; ~CMsgPackFormat() override = default; Operations supportedOperations() const override { return ReadWrite | File | Stream | String; } FileFormat* newInstance() const override { return new CMsgPackFormat; } std::string identifier() const override { return "Avogadro: CMsgPack"; } std::string name() const override { return "Chemical MessagePack"; } std::string description() const override { return "CMsgPack format is a lightweight intermediate format used to " "exchange " "information between Avogadro and other data parsing applications"; } std::string specificationUrl() const override { return "https://github.com/openchemistry/chemicaljson"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; // write MessagePack bool m_json = false; }; } // namespace Io } // namespace Avogadro #endif // AVOGADRO_IO_CMSGPACKFORMAT_H avogadrolibs-1.101.0/avogadro/io/dcdformat.cpp000066400000000000000000000302261506155467400212260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "dcdformat.h" #include "struct.h" #include #include #include #include #include #include #include #include #include using std::map; using std::string; using std::to_string; using std::vector; namespace Avogadro::Io { using Core::Array; using Core::Atom; using Core::Molecule; using Core::UnitCell; #ifndef _WIN32 #endif #define DCD_EOF (-1) constexpr int DCD_MAGIC = 84; constexpr int DCD_IS_CHARMM = 0x01; constexpr int DCD_HAS_4DIMS = 0x02; constexpr int DCD_HAS_EXTRA_BLOCK = 0x04; int swap_integer(int inp) { return (((inp << 24) & 0xff000000) | ((inp << 8) & 0x00ff0000) | ((inp >> 8) & 0x0000ff00) | ((inp >> 24) & 0x000000ff)); } char swap_endian(char endian) { if (endian == '>') return '<'; else return '>'; } bool DcdFormat::read(std::istream& inStream, Core::Molecule& mol) { /** Endian type, Buffer and Format char containers for unpacking and storing * data using struct library */ char endian = '>'; char buff[BUFSIZ]; char fmt[BUFSIZ]; /** Variables to store various components from the binary data unpacked using * the struct library */ char raw[84]; char* remarks; double DELTA; int magic; int charmm; int NAMNF; int NTITLE; int lenRemarks; int NATOMS; int blockSize; // Determining size of file inStream.seekg(0, inStream.end); int fileLen = inStream.tellg(); inStream.seekg(0, inStream.beg); // Reading magic number snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &magic); if (magic != DCD_MAGIC) { magic = swap_integer(magic); endian = swap_endian(endian); if (magic != DCD_MAGIC) { appendError("File does not start with magic number 84."); return false; } } // CORD snprintf(fmt, sizeof(fmt), "%c%ds", endian, magic); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, raw); if (raw[0] != 'C' || raw[1] != 'O' || raw[2] != 'R' || raw[3] != 'D') { appendError("Keyword CORD not found."); return false; } // Determining whether the trajectory file is from CHARMM or not if (*(reinterpret_cast(raw + 80)) != 0) { charmm = DCD_IS_CHARMM; if (*(reinterpret_cast(raw + 44)) != 0) charmm |= DCD_HAS_EXTRA_BLOCK; if (*(reinterpret_cast(raw + 48)) == 1) charmm |= DCD_HAS_4DIMS; } else { charmm = 0; } // number of fixed atoms NAMNF = *(reinterpret_cast(raw + 36)); // DELTA (timestep) is stored as a double with X-PLOR but as a float with // CHARMM if (charmm & DCD_IS_CHARMM) { float ftmp; ftmp = *(reinterpret_cast(raw + 40)); DELTA = static_cast(ftmp); } else { (DELTA) = *(reinterpret_cast(raw + 40)); } snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &magic); snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &blockSize); if (((blockSize - 4) % 80) == 0) { // Read NTITLE, the number of 80 character title strings snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &NTITLE); lenRemarks = NTITLE * 80; remarks = reinterpret_cast(malloc(lenRemarks)); snprintf(fmt, sizeof(fmt), "%c%ds", endian, lenRemarks); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, remarks); snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); int endSize; struct_unpack(buff, fmt, &endSize); } else { appendError("Block size must be 4 plus a multiple of 80."); return false; } snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); int fourInput; struct_unpack(buff, fmt, &fourInput); if (fourInput != 4) { // Error } snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &NATOMS); snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &fourInput); if (fourInput != 4) { appendError("Expected token 4. Read token " + to_string(fourInput)); return false; } if (NAMNF != 0) { int** FREEINDEXES = reinterpret_cast(calloc((NATOMS - NAMNF), sizeof(int))); if (*FREEINDEXES == nullptr) { appendError("MALLOC failed."); return false; } /* Read in index array size */ snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); int arrSize; struct_unpack(buff, fmt, &arrSize); if (arrSize != (NATOMS - NAMNF) * 4) { appendError("DCD file contains bad format."); return false; } snprintf(fmt, sizeof(fmt), "%c%di", endian, (NATOMS - NAMNF)); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, *FREEINDEXES); snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &arrSize); if (arrSize != (NATOMS - NAMNF) * 4) { appendError("DCD file contains bad format."); return false; } } // CHARMM trajectories have an extra block to be read, that contains // information about the unit cell if ((charmm & DCD_IS_CHARMM) && (charmm & DCD_HAS_EXTRA_BLOCK)) { snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); int leadingNum; struct_unpack(buff, fmt, &leadingNum); if (leadingNum == 48) { double unitcell[6]; for (double& aa : unitcell) { snprintf(fmt, sizeof(fmt), "%c%dd", endian, 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &aa); } if (unitcell[1] >= -1.0 && unitcell[1] <= 1.0 && unitcell[3] >= -1.0 && unitcell[3] <= 1.0 && unitcell[4] >= -1.0 && unitcell[4] <= 1.0) { // CHARMM and certain NAMD files have the cosines instead of angles // This formulation improves rounding behavior for orthogonal cells // so that the angles end up at precisely 90 degrees, unlike acos() unitcell[4] = M_PI_2 - asin(unitcell[4]); /* cosBC */ unitcell[3] = M_PI_2 - asin(unitcell[3]); /* cosAC */ unitcell[1] = M_PI_2 - asin(unitcell[1]); /* cosAB */ } auto* cell = new UnitCell(unitcell[0], unitcell[2], unitcell[5], unitcell[4], unitcell[3], unitcell[1]); if (!cell->isRegular()) { appendError("cell matrix is singular"); delete cell; return false; } mol.setUnitCell(cell); } else { inStream.read(buff, leadingNum); } inStream.read(buff, sizeof(int)); } // Reading the atom coordinates int formatint[6]; Array cx, cy, cz; cx.reserve(NATOMS); cy.reserve(NATOMS); cz.reserve(NATOMS); snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[0]); for (int i = 0; i < NATOMS; ++i) { // X coordinates snprintf(fmt, sizeof(fmt), "%c%df", endian, 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &cx[i]); /* code */ } snprintf(fmt, sizeof(fmt), "%c2i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[1], &formatint[2]); for (int i = 0; i < NATOMS; ++i) { // Y coordinates snprintf(fmt, sizeof(fmt), "%c%df", endian, 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &cy[i]); } snprintf(fmt, sizeof(fmt), "%c2i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[3], &formatint[4]); for (int i = 0; i < NATOMS; ++i) { // Z coordinates snprintf(fmt, sizeof(fmt), "%c%df", endian, 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &cz[i]); } snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[5]); typedef map AtomTypeMap; AtomTypeMap atomTypes; unsigned char customElementCounter = CustomElementMin; for (int i = 0; i < NATOMS; ++i) { Vector3 pos(cx[i], cy[i], cz[i]); AtomTypeMap::const_iterator it; atomTypes.insert(std::make_pair(to_string(i), customElementCounter++)); it = atomTypes.find(to_string(i)); // if (customElementCounter > CustomElementMax) { // appendError("Custom element type limit exceeded."); // return false; // } Atom newAtom = mol.addAtom(it->second); newAtom.setPosition3d(pos); } mol.setTimeStep(0, 0); // Skipping fourth dimension block if ((charmm & DCD_IS_CHARMM) && (charmm & DCD_HAS_EXTRA_BLOCK)) { snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); int sizeToRead; struct_unpack(buff, fmt, &sizeToRead); inStream.read(buff, sizeToRead); inStream.read(buff, sizeof(int)); } // Set the custom element map if needed if (!atomTypes.empty()) { Molecule::CustomElementMap elementMap; for (const auto& atomType : atomTypes) { elementMap.insert( std::make_pair(atomType.second, "Atom " + atomType.first)); } mol.setCustomElementMap(elementMap); } mol.setCoordinate3d(mol.atomPositions3d(), 0); // Do we have an animation? int coordSet = 1; while ((static_cast(inStream.tellg()) != fileLen) && (static_cast(inStream.tellg()) != DCD_EOF)) { // Reading the atom coordinates Array positions; positions.reserve(NATOMS); snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[0]); for (int i = 0; i < NATOMS; ++i) { // X coordinates snprintf(fmt, sizeof(fmt), "%c%df", endian, 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &cx[i]); } snprintf(fmt, sizeof(fmt), "%c2i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[1], &formatint[2]); for (int i = 0; i < NATOMS; ++i) { // Y coordinates snprintf(fmt, sizeof(fmt), "%c%df", endian, 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &cy[i]); } snprintf(fmt, sizeof(fmt), "%c2i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[3], &formatint[4]); for (int i = 0; i < NATOMS; ++i) { // Z coordinates snprintf(fmt, sizeof(fmt), "%c%df", endian, 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &cz[i]); } snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &formatint[5]); for (int i = 0; i < NATOMS; ++i) { Vector3 pos(cx[i], cy[i], cz[i]); positions.push_back(pos); } mol.setTimeStep(DELTA * coordSet, coordSet); // Skipping fourth dimension block if ((charmm & DCD_IS_CHARMM) && (charmm & DCD_HAS_EXTRA_BLOCK)) { snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); int sizeToRead; struct_unpack(buff, fmt, &sizeToRead); inStream.read(buff, sizeToRead); inStream.read(buff, sizeof(int)); } mol.setCoordinate3d(positions, coordSet++); } return true; } bool DcdFormat::write(std::ostream&, const Core::Molecule&) { return false; } std::vector DcdFormat::fileExtensions() const { std::vector ext; ext.emplace_back("dcd"); return ext; } std::vector DcdFormat::mimeTypes() const { std::vector mime; mime.emplace_back("application/octet-stream"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/dcdformat.h000066400000000000000000000027421506155467400206750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_DCDFORMAT_H #define AVOGADRO_IO_DCDFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class DcdFormat dcdformat.h * @brief Implementation of the generic dcd trajectory format. * @author Adarsh B */ class AVOGADROIO_EXPORT DcdFormat : public FileFormat { public: DcdFormat() = default; ~DcdFormat() override = default; Operations supportedOperations() const override { return ReadWrite | MultiMolecule | File | Stream | String; } FileFormat* newInstance() const override { return new DcdFormat; } std::string identifier() const override { return "Avogadro: DCD"; } std::string name() const override { return "DCD"; } std::string description() const override { return "CHARMM/NAMD/LAMMPS DCD Trajectory format."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& molecule) override; bool write(std::ostream& outStream, const Core::Molecule& molecule) override; }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_DCDFORMAT_H avogadrolibs-1.101.0/avogadro/io/fileformat.cpp000066400000000000000000000106621506155467400214150ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "fileformat.h" #include #include #include #include namespace Avogadro::Io { using std::ifstream; using std::locale; using std::ofstream; FileFormat::FileFormat() : m_mode(None), m_in(nullptr), m_out(nullptr) {} FileFormat::~FileFormat() { delete m_in; delete m_out; } bool FileFormat::validateFileName(const std::string& fileName) { bool valid = !fileName.empty(); if (valid) { // check if the filename contains invalid characters static std::string forbiddenChars(",^@={}[]~!?:&*\"|#%<>$\"'();`'"); valid = fileName.find_first_of(forbiddenChars) == std::string::npos; // check if the filename contains ".." which we should not allow valid = valid && fileName.find("..") == std::string::npos; } // Finally check against Windows names // .. we do this on all platforms because CON.cif, for example // is problematic to send to a Windows user. if (valid) { static std::string forbiddenNames( "CON PRN AUX NUL COM1 COM2 COM3 COM4 COM5 " "COM6 COM7 COM8 COM9 LPT1 LPT2 LPT3 LPT4 " "LPT5 LPT6 LPT7 LPT8 LPT9"); // case insensitive search, since con.txt is also a problem // https://stackoverflow.com/a/19839371/131896 auto it = std::search(fileName.begin(), fileName.end(), forbiddenNames.begin(), forbiddenNames.end(), [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); }); valid = (it == fileName.end()); } return valid; } bool FileFormat::open(const std::string& fileName_, Operation mode_) { close(); m_fileName = fileName_; m_mode = mode_; if (!m_fileName.empty()) { // Imbue the standard C locale. locale cLocale("C"); if (m_mode & Read) { auto* file = new ifstream(m_fileName.c_str(), std::ifstream::binary); m_in = file; if (file->is_open()) { m_in->imbue(cLocale); return true; } else { appendError("Error opening file: " + fileName_); return false; } } else if (m_mode & Write) { auto* file = new ofstream(m_fileName.c_str(), std::ofstream::binary); m_out = file; if (file->is_open()) { m_out->imbue(cLocale); return true; } else { appendError("Error opening file: " + fileName_); return false; } } } return false; } void FileFormat::close() { if (m_in) { delete m_in; m_in = nullptr; } if (m_out) { delete m_out; m_out = nullptr; } m_mode = None; } bool FileFormat::readMolecule(Core::Molecule& molecule) { if (!m_in) return false; return read(*m_in, molecule); } bool FileFormat::writeMolecule(const Core::Molecule& molecule) { if (!m_out) return false; return write(*m_out, molecule); } bool FileFormat::readFile(const std::string& fileName_, Core::Molecule& molecule) { bool result = open(fileName_, Read); if (!result) return false; result = readMolecule(molecule); close(); return result; } bool FileFormat::writeFile(const std::string& fileName_, const Core::Molecule& molecule) { bool result = open(fileName_, Write); if (!result) return false; result = writeMolecule(molecule); close(); return result; } bool FileFormat::readString(const std::string& string, Core::Molecule& molecule) { std::istringstream stream(string, std::istringstream::in); // Imbue the standard C locale. locale cLocale("C"); stream.imbue(cLocale); return read(stream, molecule); } bool FileFormat::writeString(std::string& string, const Core::Molecule& molecule) { std::ostringstream stream(string, std::ostringstream::out); // Imbue the standard C locale. locale cLocale("C"); stream.imbue(cLocale); bool result = write(stream, molecule); string = stream.str(); return result; } void FileFormat::clear() { m_fileName.clear(); m_error.clear(); } void FileFormat::appendError(const std::string& errorString, bool newLine) { m_error += errorString; if (newLine) m_error += "\n"; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/fileformat.h000066400000000000000000000204321506155467400210560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_FILEFORMAT_H #define AVOGADRO_IO_FILEFORMAT_H #include "avogadroioexport.h" #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace Io { /** * @class FileFormat fileformat.h * @brief General API for file formats. * @author Marcus D. Hanwell * * This serves as the common base class for chemical file formats. Classes * deriving from this one override the read and write virtual methods and * operate on the given streams. Several other signatures are available for * convenience. If there is an error reading or writing a file the string * returned by error() will give more details. */ class AVOGADROIO_EXPORT FileFormat { public: FileFormat(); virtual ~FileFormat(); /** * @brief Flags defining supported operations. */ enum Operation { None = 0x0, Read = 0x1, Write = 0x2, ReadWrite = Read | Write, MultiMolecule = 0x4, Stream = 0x10, String = 0x20, File = 0x40, All = ReadWrite | MultiMolecule | Stream | String | File }; using Operations = int; /** * @return Operation flags defining the capabilities of this format. */ virtual Operations supportedOperations() const = 0; /** * @brief Validates the given file name. * * Checks if the filename contains any invalid characters (e.g. ..) * Also checks if the filename contains a restricted name on Windows. * e.g., CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, etc. * * @param fileName The name of the file to be validated. * @return true if the file name is valid, false otherwise. */ static bool validateFileName(const std::string& fileName); /** * @brief Open the specified file in Read or Write mode. * @return True on success, false on failure. */ bool open(const std::string& fileName, Operation mode); /** * @brief The mode the format is currently operating in. * @return The mode the format is in. */ Operation mode() { return m_mode; } /** * @brief Check if the supplied mode(s) is being used. * @param isInMode The mode(s) to test against * @return True if the format is currently in the supplied mode(s). */ bool isMode(Operation isInMode) { return (m_mode & isInMode) != None; } /** * @brief Close any opened file handles. */ void close(); /** * @brief Read in a molecule, if there are no molecules to read molecule will * be empty. This can be used to read in one or more molecules from a given * file using repeated calls for each molecule. * @param molecule The molecule the data will be read into. * @return True on success, false on failure. */ bool readMolecule(Core::Molecule& molecule); /** * @brief Write out a molecule. This can be used to write one or more * molecules to a given file using repeated calls for each molecule. * @param molecule The molecule the data will be written from. * @return True on success, false on failure. */ bool writeMolecule(const Core::Molecule& molecule); /** * @brief Read the given @p in stream and load it into @p molecule. * @param in The input file stream. * @param molecule The molecule the data will be read into. * @return True on success, false on failure. */ virtual bool read(std::istream& in, Core::Molecule& molecule) = 0; /** * @brief Write to the given @p out stream the contents of @p molecule. * @param out The output stream to write the data to. * @param molecule The contents of this molecule will be written to output. * @return True on success, false on failure. */ virtual bool write(std::ostream& out, const Core::Molecule& molecule) = 0; /** * @brief Read the given @p fileName and load it into @p molecule. * @param fileName The full path to the file to be read in. * @param molecule The molecule the data will be read into. * @return True on success, false on failure. */ bool readFile(const std::string& fileName, Core::Molecule& molecule); /** * @brief Write to the given @p fileName the contents of @p molecule. * @param fileName The full path to the file to be written. * @param molecule The contents of this molecule will be written to the file. * @return True on success, false on failure. */ bool writeFile(const std::string& fileName, const Core::Molecule& molecule); /** * @brief Read the given @p string and load it into @p molecule. * @param string The string containing the molecule file contents. * @param molecule The molecule the data will be read into. * @return True on success, false on failure. */ bool readString(const std::string& string, Core::Molecule& molecule); /** * @brief Write to the given @p string the contents of @p molecule. * @param string The string to write the contents of the molecule into. * @param molecule The contents of this molecule will be written to the * string. * @return True on success, false on failure. */ bool writeString(std::string& string, const Core::Molecule& molecule); /** * @brief Get the error string, contains errors/warnings encountered. * @return String containing any errors or warnings encountered. */ std::string error() const { return m_error; } /** * @brief Get the file name (if known). * @return The full path to the file name as supplied, can be empty. */ std::string fileName() const { return m_fileName; } /** * @brief Set options for the file reader. * @param options The options, each reader chooses how to use/interpret them. */ void setOptions(const std::string& options) { m_options = options; } /** * @brief Get the file format options, can be used to change file IO. * @return The options set for the reader (defaults to empty). */ std::string options() const { return m_options; } /** * Clear the format and reset all state. */ virtual void clear(); /** * Create a new instance of the file format class. Ownership passes to the * caller. */ virtual FileFormat* newInstance() const = 0; /** * @brief A unique identifier, used to retrieve formats programmatically. * CML, XYZ, PDB etc. A runtime warning will be generated if the identifier * is not unique. */ virtual std::string identifier() const = 0; /** * @brief The name of the format, should be short such as Chemical Markup * Language, XYZ format, Protein Databank etc. */ virtual std::string name() const = 0; /** * A description of the format, along with any relevant help text for users. */ virtual std::string description() const = 0; /** * The URL of the format specification if available (relevant web page/wiki * otherwise). */ virtual std::string specificationUrl() const = 0; /** * @brief Get the file name extension(s) that the format supports reading. * @return A vector containing a list of extensions (in lower case). */ virtual std::vector fileExtensions() const = 0; /** * @brief Get the MIME type(s) that the format supports reading. * @return A vector containing a list of MIME type(s) (in lower case). */ virtual std::vector mimeTypes() const = 0; protected: /** * @brief Append an error to the error string for the format. * @param errorString The error to be added. * @param newLine Add a new line after the error string? */ void appendError(const std::string& errorString, bool newLine = true); private: std::string m_error; std::string m_fileName; std::string m_options; // Streams for reading/writing data, especially streaming data in/out. Operation m_mode; std::istream* m_in; std::ostream* m_out; }; inline FileFormat::Operation operator|(FileFormat::Operation a, FileFormat::Operation b) { return static_cast(static_cast(a) | static_cast(b)); } } // namespace Io } // namespace Avogadro #endif // AVOGADRO_IO_FILEFORMAT_H avogadrolibs-1.101.0/avogadro/io/fileformatmanager.cpp000066400000000000000000000263551506155467400227560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "fileformatmanager.h" #include "fileformat.h" #include "cjsonformat.h" #include "cmlformat.h" #include "cmsgpackformat.h" #include "dcdformat.h" #include "gromacsformat.h" #include "lammpsformat.h" #include "mdlformat.h" #include "pdbformat.h" #include "sdfformat.h" #include "trrformat.h" #include "turbomoleformat.h" #include "vaspformat.h" #include "xyzformat.h" #ifdef AVO_USE_MMTF #include "mmtfformat.h" #endif #include #include using std::unique_ptr; namespace Avogadro::Io { FileFormatManager& FileFormatManager::instance() { static FileFormatManager instance; return instance; } bool FileFormatManager::readFile(Core::Molecule& molecule, const std::string& fileName, const std::string& fileExtension, const std::string& options) const { FileFormat* format(nullptr); if (fileExtension.empty()) { // We need to guess the file extension. size_t pos = fileName.find_last_of('.'); format = filteredFormatFromFormatMap(fileName.substr(pos + 1), FileFormat::Read | FileFormat::File, m_fileExtensions); } else { format = filteredFormatFromFormatMap( fileExtension, FileFormat::Read | FileFormat::File, m_fileExtensions); } if (!format) return false; unique_ptr formatInstance(format->newInstance()); formatInstance->setOptions(options); return formatInstance->readFile(fileName, molecule); } bool FileFormatManager::writeFile(const Core::Molecule& molecule, const std::string& fileName, const std::string& fileExtension, const std::string& options) const { FileFormat* format(nullptr); if (fileExtension.empty()) { // We need to guess the file extension. size_t pos = fileName.find_last_of('.'); format = filteredFormatFromFormatMap(fileName.substr(pos + 1), FileFormat::Write | FileFormat::File, m_fileExtensions); } else { format = filteredFormatFromFormatMap( fileExtension, FileFormat::Write | FileFormat::File, m_fileExtensions); } if (!format) return false; unique_ptr formatInstance(format->newInstance()); formatInstance->setOptions(options); return formatInstance->writeFile(fileName, molecule); } bool FileFormatManager::readString(Core::Molecule& molecule, const std::string& string, const std::string& fileExtension, const std::string& options) const { FileFormat* format(filteredFormatFromFormatMap( fileExtension, FileFormat::Read | FileFormat::String, m_fileExtensions)); if (!format) return false; unique_ptr formatInstance(format->newInstance()); formatInstance->setOptions(options); return formatInstance->readString(string, molecule); } bool FileFormatManager::writeString(const Core::Molecule& molecule, std::string& string, const std::string& fileExtension, const std::string& options) const { FileFormat* format(filteredFormatFromFormatMap( fileExtension, FileFormat::Write | FileFormat::String, m_fileExtensions)); if (!format) return false; unique_ptr formatInstance(format->newInstance()); formatInstance->setOptions(options); return formatInstance->writeString(string, molecule); } bool FileFormatManager::registerFormat(FileFormat* format) { return instance().addFormat(format); } bool FileFormatManager::unregisterFormat(const std::string& identifier) { return instance().removeFormat(identifier); } bool FileFormatManager::addFormat(FileFormat* format) { if (!format) { appendError("Supplied format was null."); return false; } if (m_identifiers.count(format->identifier()) > 0) { appendError("Format " + format->identifier() + " already loaded."); return false; } for (auto& m_format : m_formats) { if (m_format == format) { appendError("The format object was already loaded."); return false; } } // If we got here then the format is unique enough to be added. size_t index = m_formats.size(); m_formats.push_back(format); m_identifiers[format->identifier()].push_back(index); std::vector mimes = format->mimeTypes(); for (auto& mime : mimes) { m_mimeTypes[mime].push_back(index); } std::vector extensions = format->fileExtensions(); for (auto& extension : extensions) { m_fileExtensions[extension].push_back(index); } return true; } namespace { // Lookup each key from "keys" in "map", and remove "val" from the Map's // data value (which is a vector of ValueType) template void removeFromMap(Map& map, const VectorOfKeys& keys, const ValueType& val) { for (auto key = keys.begin(), keyEnd = keys.end(); key != keyEnd; ++key) { auto mapMatch = map.find(*key); if (mapMatch == map.end()) continue; typename Map::mapped_type& vec = mapMatch->second; if (vec.size() <= 1) { map.erase(*key); } else { auto newEnd = std::remove(vec.begin(), vec.end(), val); vec.resize(newEnd - vec.begin()); } } } } // namespace bool FileFormatManager::removeFormat(const std::string& identifier) { FormatIdVector ids = m_identifiers[identifier]; m_identifiers.erase(identifier); if (ids.empty()) return false; for (size_t id : ids) { FileFormat* fmt = m_formats[id]; if (fmt == nullptr) continue; removeFromMap(m_mimeTypes, fmt->mimeTypes(), id); removeFromMap(m_fileExtensions, fmt->fileExtensions(), id); m_formats[id] = nullptr; delete fmt; } return true; } FileFormat* FileFormatManager::newFormatFromIdentifier( const std::string& id, FileFormat::Operations filter) const { FileFormat* format(filteredFormatFromFormatMap(id, filter, m_identifiers)); return format ? format->newInstance() : nullptr; } FileFormat* FileFormatManager::newFormatFromMimeType( const std::string& mime, FileFormat::Operations filter) const { FileFormat* format(filteredFormatFromFormatMap(mime, filter, m_mimeTypes)); return format ? format->newInstance() : nullptr; } FileFormat* FileFormatManager::newFormatFromFileExtension( const std::string& extension, FileFormat::Operations filter) const { FileFormat* format( filteredFormatFromFormatMap(extension, filter, m_fileExtensions)); return format ? format->newInstance() : nullptr; } std::vector FileFormatManager::identifiers( FileFormat::Operations filter) const { return filteredKeysFromFormatMap(filter, m_identifiers); } std::vector FileFormatManager::mimeTypes( FileFormat::Operations filter) const { return filteredKeysFromFormatMap(filter, m_mimeTypes); } std::vector FileFormatManager::fileExtensions( FileFormat::Operations filter) const { return filteredKeysFromFormatMap(filter, m_fileExtensions); } std::vector FileFormatManager::fileFormats( FileFormat::Operations filter) const { std::vector result; for (auto m_format : m_formats) { if (filter == FileFormat::None || (filter & m_format->supportedOperations()) == filter) { result.push_back(m_format); } } return result; } std::vector FileFormatManager::fileFormatsFromMimeType( const std::string& mimeType, FileFormat::Operations filter) const { std::vector matches = filteredFormatsFromFormatMap(mimeType, filter, m_mimeTypes); return std::vector(matches.begin(), matches.end()); } std::vector FileFormatManager::fileFormatsFromFileExtension( const std::string& extension, FileFormat::Operations filter) const { std::vector matches = filteredFormatsFromFormatMap(extension, filter, m_fileExtensions); return std::vector(matches.begin(), matches.end()); } std::string FileFormatManager::error() const { return m_error; } FileFormatManager::FileFormatManager() { addFormat(new CmlFormat); addFormat(new CjsonFormat); addFormat(new CMsgPackFormat); addFormat(new DcdFormat); addFormat(new GromacsFormat); addFormat(new LammpsTrajectoryFormat); addFormat(new LammpsDataFormat); addFormat(new MdlFormat); addFormat(new OutcarFormat); addFormat(new PdbFormat); addFormat(new PoscarFormat); addFormat(new SdfFormat); addFormat(new TrrFormat); addFormat(new TurbomoleFormat); addFormat(new XyzFormat); #ifdef AVO_USE_MMTF addFormat(new MMTFFormat); #endif } FileFormatManager::~FileFormatManager() { // Delete the file formats that were loaded. for (auto& m_format : m_formats) { delete m_format; } m_formats.clear(); } std::vector FileFormatManager::filteredKeysFromFormatMap( FileFormat::Operations filter, const FileFormatManager::FormatIdMap& fmap) const { std::vector result; for (const auto& it : fmap) { for (auto formatIt = it.second.begin(); formatIt != it.second.end(); ++formatIt) { if (filter == FileFormat::None || (m_formats[*formatIt]->supportedOperations() & filter) == filter) { result.push_back(it.first); break; } } } return result; } std::vector FileFormatManager::filteredFormatsFromFormatMap( const std::string& key, FileFormat::Operations filter, const FileFormatManager::FormatIdMap& fmap) const { std::vector result; auto it = fmap.find(key); if (it != fmap.end()) result = filteredFormatsFromFormatVector(filter, it->second); return result; } FileFormat* FileFormatManager::filteredFormatFromFormatMap( const std::string& key, FileFormat::Operations filter, const FileFormatManager::FormatIdMap& fmap) const { auto it = fmap.find(key); if (it != fmap.end()) return filteredFormatFromFormatVector(filter, it->second); return nullptr; } std::vector FileFormatManager::filteredFormatsFromFormatVector( FileFormat::Operations filter, const FileFormatManager::FormatIdVector& v) const { std::vector result; for (unsigned long it : v) { if (filter == FileFormat::None || (m_formats[it]->supportedOperations() & filter) == filter) { result.push_back(m_formats[it]); } } return result; } FileFormat* FileFormatManager::filteredFormatFromFormatVector( FileFormat::Operations filter, const FileFormatManager::FormatIdVector& v) const { for (unsigned long it : v) { if (filter == FileFormat::None || (m_formats[it]->supportedOperations() & filter) == filter) { return m_formats[it]; } } return nullptr; } void FileFormatManager::appendError(const std::string& errorMessage) { m_error += errorMessage + "\n"; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/fileformatmanager.h000066400000000000000000000316651506155467400224230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_FILEFORMATMANAGER_H #define AVOGADRO_IO_FILEFORMATMANAGER_H #include "avogadroioexport.h" #include "fileformat.h" // For FileFormat::Operation enum. #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace Io { /** * @struct CaseInsensitiveComparator fileformatmanager.h * * @brief Class to handle case-insensitive comparisons of file extensions. * Adapted from https://stackoverflow.com/a/3009806/131896 **/ struct CaseInsensitiveComparator { // case-independent (ci) compare_less binary function struct lowerCaseCompare { bool operator()(const unsigned char& c1, const unsigned char& c2) const { return tolower(c1) < tolower(c2); } }; bool operator()(const std::string& s1, const std::string& s2) const noexcept { return std::lexicographical_compare(s1.begin(), s1.end(), // source range s2.begin(), s2.end(), // dest range lowerCaseCompare()); // comparison } }; /** * @class FileFormatManager fileformatmanager.h * * @brief Class to manage registration, searching and creation of file formats. * @author Marcus D. Hanwell * * The file format manager is a singleton class that handles the runtime * registration, search, creation and eventual destruction of file formats. It * can be used to gain a listing of available formats, register new formats and * retrieve the correct format to facilitate file IO. * * All files IO can take place independent of this manager, but for automated * registration and look up this is the preferred API. It is possible to use * the convenience API without ever dealing directly with a format class. */ class AVOGADROIO_EXPORT FileFormatManager { public: /** * Get the singleton instance of the file format manager. This instance should * not be deleted. */ static FileFormatManager& instance(); /** * Load @p molecule with the @p fileName contents supplied, inferring the * @p fileExtension if it is empty. The @p options can be used to modify * the behavior of the file format. * @return True on success, false on failure. */ bool readFile(Core::Molecule& molecule, const std::string& fileName, const std::string& fileExtension = std::string(), const std::string& options = std::string()) const; /** * Write @p molecule to the @p fileName supplied, inferring the * @p fileExtension if it is empty. The @p options can be used to modify * the behavior of the file format. * @return True on success, false on failure. */ bool writeFile(const Core::Molecule& molecule, const std::string& fileName, const std::string& fileExtension = std::string(), const std::string& options = std::string()) const; /** * Load @p molecule with the contents of @p string, using the supplied * @p fileExtension to determine the format. The @p options can be used to * modify the behavior of the file format. * @return True on success, false on failure. */ bool readString(Core::Molecule& molecule, const std::string& string, const std::string& fileExtension, const std::string& options = std::string()) const; /** * Write @p molecule to the @p string, using the supplied @p fileExtension * to determine the format. The @p options can be used to modify the behavior * of the file format. * @return True on success, false on failure. */ bool writeString(const Core::Molecule& molecule, std::string& string, const std::string& fileExtension, const std::string& options = std::string()) const; /** * @brief Register a new file format with the format manager. * @param format An instance of the format to manage, the manager assumes * ownership of the object passed in. * @return True on success, false on failure. */ static bool registerFormat(FileFormat* format); /** * @brief Unregister a file format from the format manager. * @param identifier The identifier for the format to remove. * @return True on success, false on failure. */ static bool unregisterFormat(const std::string& identifier); /** * Add the supplied @p format to the manager, registering its ID, MIME type, * file extension and other relevant data for later lookup. The manager * assumes ownership of the supplied object. * @return True on success, false on failure. */ bool addFormat(FileFormat* format); /** * Remove the format with the identifier @a identifier from the manager. * @return True on success, false on failure. */ bool removeFormat(const std::string& identifier); /** * New instance of the format for the specified @p identifier. Ownership * is passed to the caller. * @param identifier The unique identifier of the format. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @return Instance of the format, nullptr if not found. Ownership passes to * the * caller. */ FileFormat* newFormatFromIdentifier( const std::string& identifier, FileFormat::Operations filter = FileFormat::None) const; /** * New instance of the format for the specified @p mimeType. Ownership * is passed to the caller. * @param mimeType The MIME type (in lower case). * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @return Instance of the format, nullptr if not found. Ownership passes to * the * caller. */ FileFormat* newFormatFromMimeType( const std::string& mimeType, FileFormat::Operations filter = FileFormat::None) const; /** * New instance of the format for the specified file @p extension. Ownership * is passed to the caller. * @param extension The file extension (in lower case). * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @return Instance of the format, nullptr if not found. Ownership passes to * the * caller. */ FileFormat* newFormatFromFileExtension( const std::string& extension, FileFormat::Operations filter = FileFormat::None) const; /** * Get a list of all loaded identifiers, optionally matching the specified * filter. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. */ std::vector identifiers( FileFormat::Operations filter = FileFormat::None) const; /** * Get a list of all loaded MIME types, optionally matching the specified * filter. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. */ std::vector mimeTypes( FileFormat::Operations filter = FileFormat::None) const; /** * Get a list of the file extensions supported, optionally matching the * specified filter. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. */ std::vector fileExtensions( FileFormat::Operations filter = FileFormat::None) const; /** * Get a list of known FileFormat objects, optionally matching the * specified filter. * @warning The objects in the returned list are owned by the * FileFormatManager and cannot be modified. Use FileFormat::newInstance() * to create mutable copies. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. */ std::vector fileFormats( FileFormat::Operations filter = FileFormat::None) const; /** * Get a list of known FileFormat objects that handle the specified MIME type, * optionally matching a filter. * @warning The objects in the returned list are owned by the * FileFormatManager and cannot be modified. Use FileFormat::newInstance() * to create mutable copies. * @param mimeType MIME type. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. */ std::vector fileFormatsFromMimeType( const std::string& mimeType, FileFormat::Operations filter = FileFormat::None) const; /** * Get a list of known FileFormat objects that handle the specified file * extension, optionally matching a filter. * @warning The objects in the returned list are owned by the * FileFormatManager and cannot be modified. Use FileFormat::newInstance() * to create mutable copies. * @param extension File extension. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. */ std::vector fileFormatsFromFileExtension( const std::string& extension, FileFormat::Operations filter = FileFormat::None) const; /** * Get any errors that have been logged when loading formats. */ std::string error() const; private: using FormatIdVector = std::vector; using FormatIdMap = std::map; FileFormatManager(); ~FileFormatManager(); FileFormatManager(const FileFormatManager&); // Not implemented. FileFormatManager& operator=(const FileFormatManager&); // Not implemented. /** * @brief Return keys from a map that have formats matching the supplied * operation filter. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @param fmap The FormatMap to operate on. */ std::vector filteredKeysFromFormatMap( FileFormat::Operations filter, const FormatIdMap& fmap) const; /** * @brief Return formats from a map that match the supplied key and operation * filter. * @note Ownership of the format filter(s) remains with the FileFormatManager. * Use FileFormat::newInstance to clone each format before use. * @param key The map key. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @param fmap The FormatIdMap to operate on. */ std::vector filteredFormatsFromFormatMap( const std::string& key, FileFormat::Operations filter, const FormatIdMap& fmap) const; /** * @brief Return a format from a map that matches the supplied key and * operation filter. * @note Ownership of the format filter(s) remains with the FileFormatManager. * Use FileFormat::newInstance to clone each format before use. * @param key The map key. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @param fmap The FormatIdMap to operate on. */ FileFormat* filteredFormatFromFormatMap(const std::string& key, FileFormat::Operations filter, const FormatIdMap& fmap) const; /** * @brief Return formats from a vector that match the supplied operation * filter. * @note Ownership of the format filter(s) remains with the FileFormatManager. * Use FileFormat::newInstance to clone each format before use. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @param fvec The FormatIdVector to operate on. */ std::vector filteredFormatsFromFormatVector( FileFormat::Operations filter, const FormatIdVector& fvec) const; /** * @brief Return the first format from a vector that matches the supplied * operation filter. * @note Ownership of the format filter(s) remains with the FileFormatManager. * Use FileFormat::newInstance to clone each format before use. * @param filter Bitwise combination of FileFormat::Operation values that * represents the minimum required capabilities. * @param fmap The FormatIdVector to operate on. */ FileFormat* filteredFormatFromFormatVector(FileFormat::Operations filter, const FormatIdVector& fvec) const; /** * @brief Append warnings/errors to the error message string. * @param errorMessage The error message to append. */ void appendError(const std::string& errorMessage); std::vector m_formats; FormatIdMap m_identifiers; FormatIdMap m_mimeTypes; FormatIdMap m_fileExtensions; std::string m_error; }; } // namespace Io } // namespace Avogadro #endif // AVOGADRO_IO_FILEFORMATMANAGER_H avogadrolibs-1.101.0/avogadro/io/gromacsformat.cpp000066400000000000000000000160431506155467400221300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gromacsformat.h" #include #include #include #include #include #include #include #include #include #include using namespace std::string_literals; namespace Avogadro::Io { using Core::Atom; using Core::Elements; using Core::lexicalCast; using Core::Molecule; using Core::Residue; using Core::split; using Core::trimmed; using Core::UnitCell; std::vector GromacsFormat::fileExtensions() const { return std::vector(1, "gro"s); } std::vector GromacsFormat::mimeTypes() const { return std::vector(1, "chemical/x-gro"s); } bool GromacsFormat::read(std::istream& in, Molecule& molecule) { // Allow ADL ofr string using std::string; string buffer; string value; Residue* r = nullptr; size_t currentResidueId = 0; // Title std::getline(in, buffer); if (!buffer.empty()) molecule.setData("name", trimmed(buffer)); // Atom count std::getline(in, buffer); buffer = trimmed(buffer); bool ok; auto numAtoms = lexicalCast(buffer, ok); if (buffer.empty() || !ok) { appendError("Number of atoms (line 2) invalid."); return false; } // read atom info: using AtomTypeMap = std::map; AtomTypeMap atomTypes; unsigned char customElementCounter = CustomElementMin; Vector3 pos; while (numAtoms-- > 0) { std::getline(in, buffer); // Figure out the distance between decimal points, implement support for // variable precision as specified: // "any number of decimal places, the format will then be n+5 positions with // n decimal places (n+1 for velocities) in stead of 8 with 3 (with 4 for // velocities)". size_t decimal1 = buffer.find(".", 20); size_t decimal2 = string::npos; int decimalSep = 0; if (decimal1 != string::npos) decimal2 = buffer.find(".", decimal1 + 1); if (decimal2 != string::npos) decimalSep = decimal2 - decimal1; if (decimalSep == 0) { appendError("Decimal separation of 0 found in atom positions: " + buffer); return false; } if (buffer.size() < static_cast(20 + 3 * decimalSep)) { appendError("Error reading atom specification -- line too short: " + buffer); return false; } // Format of buffer is: (all indices start at 1, variable dp throws this). // Offset: 0 format: %5i value: Residue number // Offset: 5 format: %-5s value: Residue name // Offset: 10 format: %5s value: Atom name // Offset: 15 format: %5i value: Atom number // Offset: 20 format: %8.3f value: x coordinate (nm) // Offset: 28 format: %8.3f value: y coordinate (nm) // Offset: 36 format: %8.3f value: z coordinate (nm) // Offset: 44 format: %8.4f value: x velocity (nm/ps, a.k.a. km/s) // Offset: 52 format: %8.4f value: y velocity (nm/ps, a.k.a. km/s) // Offset: 60 format: %8.4f value: z velocity (nm/ps, a.k.a. km/s) auto residueId = lexicalCast(buffer.substr(0, 5), ok); if (!ok) { appendError("Failed to parse residue sequence number: " + buffer.substr(0, 5)); return false; } if (residueId != currentResidueId) { currentResidueId = residueId; auto residueName = lexicalCast(buffer.substr(5, 5), ok); if (!ok) { appendError("Failed to parse residue name: " + buffer.substr(5, 5)); return false; } // gro files do not have a chain ID. So we use a makeshift dummy ID char dummyChainId = '0'; r = &molecule.addResidue(residueName, currentResidueId, dummyChainId); } // Atom name: value = trimmed(buffer.substr(10, 5)); Atom atom; int atomicNum = 0; if (r != nullptr) r->getAtomicNumber(value); if (atomicNum) { atom = molecule.addAtom(atomicNum); } else { unsigned char atomicNumFromSymbol = Elements::atomicNumberFromSymbol(value); if (atomicNumFromSymbol != 255) { atom = molecule.addAtom(atomicNumFromSymbol); } else { auto it = atomTypes.find(value); if (it == atomTypes.end()) { atomTypes.insert(std::make_pair(value, customElementCounter++)); it = atomTypes.find(value); if (customElementCounter > CustomElementMax) { appendError("Custom element type limit exceeded."); return false; } } atom = molecule.addAtom(it->second); } } // Coords for (int i = 0; i < 3; ++i) { value = trimmed(buffer.substr(20 + i * decimalSep, decimalSep)); pos[i] = lexicalCast(value, ok); if (!ok || value.empty()) { appendError( "Error reading atom specification -- invalid coordinate: '" + buffer + "' (bad coord: '" + value + "')"); return false; } } atom.setPosition3d(pos * static_cast(10.0)); // nm --> Angstrom if (r) { r->addResidueAtom(value, atom); } } // Set the custom element map if needed: if (!atomTypes.empty()) { Molecule::CustomElementMap elementMap; for (const auto& atomType : atomTypes) { elementMap.insert(std::make_pair(atomType.second, atomType.first)); } molecule.setCustomElementMap(elementMap); } // Box description: // v1(x) v2(y) v3(z) [v1(y) v1(z) v2(x) v2(z) v3(x) v3(y)] // The last six values may be omitted, set all non-specified values to 0. // v1(y) == v1(z) == v2(z) == 0 always. std::getline(in, buffer); std::vector tokens(split(buffer, ' ', true)); if (tokens.size() > 0) { if (tokens.size() != 3 && tokens.size() != 9) { appendError("Invalid box specification -- need either 3 or 9 values: '" + buffer + "'"); return false; } // Index arrays for parsing loop: const int rows[] = { 0, 1, 2, 1, 2, 0, 2, 0, 1 }; const int cols[] = { 0, 1, 2, 0, 0, 1, 1, 2, 2 }; Matrix3 cellMatrix = Matrix3::Zero(); for (size_t i = 0; i < tokens.size(); ++i) { cellMatrix(rows[i], cols[i]) = lexicalCast(tokens[i], ok); if (!ok || tokens[i].empty()) { appendError("Invalid box specification -- bad value: '" + tokens[i] + "'"); return false; } } auto* cell = new UnitCell; cell->setCellMatrix(cellMatrix * static_cast(10)); // nm --> Angstrom if (!cell->isRegular()) { appendError("box vectors are not linear independent"); delete cell; return false; } molecule.setUnitCell(cell); } return true; } bool GromacsFormat::write(std::ostream&, const Core::Molecule&) { return false; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/gromacsformat.h000066400000000000000000000031251506155467400215720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_GROMACSFORMAT_H #define AVOGADRO_IO_GROMACSFORMAT_H #include "avogadroioexport.h" #include "fileformat.h" #include namespace Avogadro::Io { /** * @class GromacsFormat gromacsformat.h * @brief Simple GROMACS .gro file reader. */ class AVOGADROIO_EXPORT GromacsFormat : public FileFormat { public: GromacsFormat() = default; ~GromacsFormat() override = default; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new GromacsFormat; } std::string identifier() const override { return "Avogadro: GROMACS"; } std::string name() const override { return "GROMACS"; } std::string description() const override { return "Read GROMACS .gro files."; } std::string specificationUrl() const override { return "http://www.gromacs.org/Documentation/File_Formats/.gro_File"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; // Unimplemented bool write(std::ostream& out, const Core::Molecule& molecule) override; }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_GROMACSFORMAT_H avogadrolibs-1.101.0/avogadro/io/hdf5dataformat.cpp000066400000000000000000000302311506155467400221500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "hdf5dataformat.h" #include "hdf5.h" #include #include #include namespace Avogadro { namespace Io { // Exclude from Doxygen: /// @cond class Hdf5DataFormat::ResizeContainer { public: virtual ~ResizeContainer() {}; virtual bool resize(const std::vector& dims) = 0; virtual void* dataPointer() = 0; protected: int dimsToNumberOfElements(const std::vector& vec) { if (vec.empty()) return 0; int result = vec.front(); for (size_t i = 1; i < vec.size(); ++i) result *= vec[i]; return result; } }; // Internal storage. Used to keep HDF5 stuff out of the header. class Hdf5DataFormat::Private { public: Private() : fileId(H5I_INVALID_HID), threshold(1024) {} std::string filename; hid_t fileId; size_t threshold; }; namespace { // Build up a list of absolute paths to all datasets in the file. To be used // with H5Ovisit (see Hdf5DataFormat::datasets()). class ListDatasetsVisitor { public: std::vector datasets; static herr_t operation(hid_t /*o_id*/, const char* name, const H5O_info_t* object_info, void* op_data) { // If this object isn't a dataset, continue if (object_info->type != H5O_TYPE_DATASET) return 0; ListDatasetsVisitor* self = reinterpret_cast(op_data); self->datasets.push_back(std::string(name)); return 0; } }; class ResizeMatrixX : public Avogadro::Io::Hdf5DataFormat::ResizeContainer { MatrixX& m_data; public: ResizeMatrixX(MatrixX& data) : m_data(data) {} bool resize(const std::vector& dims) { if (dims.size() != 2) return false; m_data.resize(dims[0], dims[1]); return true; } void* dataPointer() { return m_data.data(); } }; class ResizeVector : public Avogadro::Io::Hdf5DataFormat::ResizeContainer { std::vector& m_data; public: ResizeVector(std::vector& data) : m_data(data) {} bool resize(const std::vector& dims) { m_data.resize(dimsToNumberOfElements(dims)); return true; } void* dataPointer() { return &m_data[0]; } }; class ResizeArray : public Avogadro::Io::Hdf5DataFormat::ResizeContainer { Avogadro::Core::Array& m_data; public: ResizeArray(Avogadro::Core::Array& data) : m_data(data) {} bool resize(const std::vector& dims) { m_data.resize(dimsToNumberOfElements(dims)); return true; } void* dataPointer() { return &m_data[0]; } }; } // end unnamed namespace // end doxygen exclude: /// @endcond Hdf5DataFormat::Hdf5DataFormat() : d(new Private()) {} Hdf5DataFormat::~Hdf5DataFormat() { if (isOpen()) closeFile(); delete d; } bool Hdf5DataFormat::isOpen() const { return d->fileId != H5I_INVALID_HID; } bool Hdf5DataFormat::openFile(const std::string& filename_, Hdf5DataFormat::OpenMode mode) { // File already open? if (isOpen()) return false; switch (mode) { case ReadOnly: // File must exist -- use open d->fileId = H5Fopen(filename_.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); break; case ReadWriteTruncate: // Create new file: d->fileId = H5Fcreate(filename_.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); break; case ReadWriteAppend: // Test if the file exists: if (FILE* handle = fopen(filename_.c_str(), "r")) { // Exists! Use open fclose(handle); d->fileId = H5Fopen(filename_.c_str(), H5F_ACC_RDWR, H5P_DEFAULT); } else { // File doesn't exist yet. Create it. d->fileId = H5Fcreate(filename_.c_str(), H5F_ACC_EXCL, H5P_DEFAULT, H5P_DEFAULT); } break; } // Error opening if file id is negative if (d->fileId < 0) { d->fileId = H5I_INVALID_HID; return false; } d->filename = filename_; return true; } std::string Hdf5DataFormat::filename() const { return d->filename; } bool Hdf5DataFormat::closeFile() { // Is there a file open? if (!isOpen()) return false; herr_t err = H5Fclose(d->fileId); if (err < 0) return false; d->fileId = H5I_INVALID_HID; d->filename.clear(); return true; } void Hdf5DataFormat::setThreshold(size_t bytes) { d->threshold = bytes; } size_t Hdf5DataFormat::threshold() const { return d->threshold; } bool Hdf5DataFormat::exceedsThreshold(size_t bytes) const { return bytes > d->threshold; } bool Hdf5DataFormat::exceedsThreshold(const MatrixX& data) const { return exceedsThreshold(data.rows() * data.cols() * sizeof(double)); } bool Hdf5DataFormat::exceedsThreshold(const std::vector& data) const { return exceedsThreshold(data.size() * sizeof(double)); } bool Hdf5DataFormat::exceedsThreshold(const Core::Array& data) const { return exceedsThreshold(data.size() * sizeof(double)); } bool Hdf5DataFormat::datasetExists(const std::string& path) const { if (!isOpen()) return false; // "/" cannot be a valid dataset, and this function must take an absolute path if (path.size() < 2) return false; // Verify that all paths leading to the target exist, one by one (grr...) size_t slashIndex = 0; do { slashIndex = path.find('/', slashIndex + 1); if (slashIndex != std::string::npos) { htri_t exists = H5Lexists(d->fileId, path.substr(0, slashIndex).c_str(), H5P_DEFAULT); if (exists != 1) return false; } } while (slashIndex != std::string::npos); // Verify that the deepest link exists if (H5Lexists(d->fileId, path.c_str(), H5P_DEFAULT) != 1) return false; // Verify that the deepest link resolves to an object if (H5Oexists_by_name(d->fileId, path.c_str(), H5P_DEFAULT) != 1) return false; // See if the object is a dataset H5O_info_t info; if (H5Oget_info_by_name(d->fileId, path.c_str(), &info, H5P_DEFAULT) < 0) return false; return info.type == H5O_TYPE_DATASET; } bool Hdf5DataFormat::removeDataset(const std::string& path) const { if (!isOpen()) return false; return H5Ldelete(d->fileId, path.c_str(), H5P_DEFAULT) >= 0; } std::vector Hdf5DataFormat::datasetDimensions( const std::string& path) const { std::vector result; if (!isOpen()) return result; if (!datasetExists(path)) return result; // Open dataset hid_t dataset_id = H5Dopen(d->fileId, path.c_str(), H5P_DEFAULT); if (dataset_id < 0) return result; // Lookup dimensions // Get dataspace for dataset hid_t dataspace_id = H5Dget_space(dataset_id); if (dataset_id < 0) { H5Dclose(dataset_id); return result; } // Get number of dimensions. int ndims = H5Sget_simple_extent_ndims(dataspace_id); if (ndims <= 0) { H5Sclose(dataspace_id); H5Dclose(dataset_id); return result; } // Get actual dimensions. hsize_t* hdims = new hsize_t[ndims]; int checkDims = H5Sget_simple_extent_dims(dataspace_id, hdims, nullptr); // Copy dimensions if successful. if (checkDims == ndims) { result.resize(ndims); std::copy(hdims, hdims + ndims, result.begin()); } // Cleanup. delete[] hdims; H5Sclose(dataspace_id); H5Dclose(dataset_id); return result; } bool Hdf5DataFormat::writeRawDataset(const std::string& path, const double data[], int ndims, size_t dims[]) const { if (!isOpen()) return false; // Remove old data set if it exists. if (datasetExists(path)) { if (!removeDataset(path)) return false; } // Get dimensions of data. hsize_t* hdims = new hsize_t[ndims]; for (int i = 0; i < ndims; ++i) { hdims[i] = static_cast(dims[i]); } // Create a dataspace description. hid_t dataspace_id = H5Screate_simple(ndims, hdims, nullptr); delete[] hdims; if (dataspace_id < 0) return false; // Create any intermediate groups if needed: hid_t lcpl_id = H5Pcreate(H5P_LINK_CREATE); if (lcpl_id == -1 || H5Pset_create_intermediate_group(lcpl_id, 1) < 0) { H5Sclose(dataspace_id); return false; } // Create the dataset. hid_t dataset_id = H5Dcreate(d->fileId, path.c_str(), H5T_NATIVE_DOUBLE, dataspace_id, lcpl_id, H5P_DEFAULT, H5P_DEFAULT); if (dataset_id < 0) { H5Sclose(dataspace_id); return false; } // Write the actual data. herr_t err = H5Dwrite(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, dataspace_id, H5P_DEFAULT, data); // Cleanup. H5Dclose(dataset_id); H5Sclose(dataspace_id); if (err < 0) return false; return true; } bool Hdf5DataFormat::writeDataset(const std::string& path, const MatrixX& data) const { size_t dims[2] = { static_cast(data.rows()), static_cast(data.cols()) }; // Transpose data -- Eigen uses column-major ordering. return this->writeRawDataset(path, data.transpose().data(), 2, dims); } bool Hdf5DataFormat::writeDataset(const std::string& path, const std::vector& data, int ndims, size_t* dims) const { size_t size = data.size(); return this->writeRawDataset(path, &(data[0]), ndims, dims ? dims : &size); } bool Hdf5DataFormat::writeDataset(const std::string& path, const Core::Array& data, int ndims, size_t* dims) const { size_t size = data.size(); return this->writeRawDataset(path, &(data[0]), ndims, dims ? dims : &size); } std::vector Hdf5DataFormat::readRawDataset( const std::string& path, ResizeContainer& container) const { std::vector result; if (!isOpen()) return result; if (!datasetExists(path)) return result; // Open dataset hid_t dataset_id = H5Dopen(d->fileId, path.c_str(), H5P_DEFAULT); if (dataset_id < 0) return result; // Lookup dimensions // Get dataspace for dataset hid_t dataspace_id = H5Dget_space(dataset_id); if (dataset_id < 0) { H5Dclose(dataset_id); return result; } // Get number of dimensions. int ndims = H5Sget_simple_extent_ndims(dataspace_id); if (ndims <= 0) { H5Sclose(dataspace_id); H5Dclose(dataset_id); return result; } // Get actual dimensions. hsize_t* hdims = new hsize_t[ndims]; if (H5Sget_simple_extent_dims(dataspace_id, hdims, nullptr) != ndims) { delete[] hdims; H5Sclose(dataspace_id); H5Dclose(dataset_id); return result; } result.reserve(ndims); for (int i = 0; i < ndims; ++i) { result.push_back(static_cast(hdims[i])); } // Allocate and read into data. if (!container.resize(result)) { result.clear(); H5Sclose(dataspace_id); H5Dclose(dataset_id); return result; } if (H5Dread(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, dataspace_id, H5P_DEFAULT, container.dataPointer()) < 0) { result.clear(); H5Sclose(dataspace_id); H5Dclose(dataset_id); return result; } // Cleanup H5Sclose(dataspace_id); H5Dclose(dataset_id); return result; } bool Hdf5DataFormat::readDataset(const std::string& path, MatrixX& data) const { ResizeMatrixX container(data); return !readRawDataset(path, container).empty(); } std::vector Hdf5DataFormat::readDataset(const std::string& path, std::vector& data) const { ResizeVector container(data); return readRawDataset(path, container); } std::vector Hdf5DataFormat::readDataset(const std::string& path, Core::Array& data) const { ResizeArray container(data); return readRawDataset(path, container); } std::vector Hdf5DataFormat::datasets() const { if (!isOpen()) return std::vector(); ListDatasetsVisitor visitor; herr_t code = H5Ovisit(d->fileId, H5_INDEX_NAME, H5_ITER_INC, &visitor.operation, &visitor); if (code < 0) return std::vector(); return visitor.datasets; } } // namespace Io } // namespace Avogadro avogadrolibs-1.101.0/avogadro/io/hdf5dataformat.h000066400000000000000000000330501506155467400216170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_HDF5DATAFORMAT_H #define AVOGADRO_HDF5DATAFORMAT_H #include "avogadroioexport.h" #include // can't forward declare eigen types #include #include #include namespace Avogadro { namespace Core { template class Array; } namespace Io { /** * @class Hdf5DataFormat hdf5dataformat.h * @brief The Hdf5DataFormat class provides access to data stored in HDF5 files. * @author Allison Vacanti * * This class is intended to supplement an existing format reader/writer by * providing the option to write large data to an HDF5 file store. The purpose * is to keep text format files at a manageable size. * * To use this class, open or create an HDF5 file with the openFile method, * using the appropriate OpenMode for the intended operation. Data can be * written to the file using the writeDataset methods and retrieved using the * readDataset methods. When finished, call closeFile to release the file * resources from the HDF5 library. * * A complete set of datasets available in an open file can be retrieved with * the datasets() method, and the existence of a particular dataset can be * tested with datasetExists(). removeDataset() can be used to unlink an * existing dataset from the file, though this will not free any space on disk. * The space occupied by an unlinked dataset may be reclaimed by new write * operations, but only if they occur before the file is closed. * * A convenient thresholding system is implemented to help the accompanying text * format writer determine which data is "large" enough to be stored in HDF5. A * size threshold (in bytes) may be set with the setThreshold() function (the * default is 1KB). A data object may be passed to the exceedsThreshold method * to see if the size of the data in the container exceeds the currently set * threshold. If so, it should be written into the HDF5 file by writeDataset. * If not, it should be serialized into the text file in a suitable format. The * thresholding operations are optional; the threshold size does not affect the * behavior of the read/write methods and are only for user convenience. */ class AVOGADROIO_EXPORT Hdf5DataFormat { public: Hdf5DataFormat(); /** Destructor. Closes any open file before freeing memory. */ ~Hdf5DataFormat(); /** Open modes for use with openFile(). */ enum OpenMode { /** Open an existing file in read-only mode. The file must exist. */ ReadOnly = 0, /** * Create a file in read/write mode, removing any existing file with the * same name. */ ReadWriteTruncate, /** * Open an file in read/write mode. If the file exist, its contents will be * preserved. If it does not, a new file will be created. */ ReadWriteAppend }; /** @return true if a file is open. */ bool isOpen() const; /** * @brief openFile Open a file for use by this reader/writer. * @param filename_ Name of the file to open. * @param mode OpenMode for the file. Default is ReadWriteAppend. * @note Only a single file may be opened at a time. Attempting to open * multiple files without calling closeFile() will fail. * @return True if the file is successfully opened/create by the HDF5 * subsystem, false otherwise. */ bool openFile(const std::string& filename_, OpenMode mode = ReadWriteAppend); /** * @return The name of the open file, or an empty string if no file is open. */ std::string filename() const; /** * @brief closeFile Close the file and reset the reader/writer. Another file * may be opened after calling this function. * @return true if the file is successfully released by the HDF5 subsystem. */ bool closeFile(); /** * @brief setThreshold Set the threshold size in bytes that will be used in * the exceedsThreshold functions. The threshold can be used to determine * which * data is considered "large enough" to be stored in HDF5, rather than an * accompanying format. * @param bytes The size in bytes for the threshold. Default: 1KB. */ void setThreshold(size_t bytes); /** @return The current threshold size in bytes. Default: 1KB. */ size_t threshold() const; /** * @brief exceedsThreshold Test if a data set is "large enough" to be stored * in HDF5 format. If this function returns true, the number of bytes tested * is larger than the threshold and the data should be written into the HDF5 * file. If false, the data should be written into the accompanying format. * @param bytes The size of the dataset in bytes * @return true if the size exceeds the threshold set by setThreshold. */ bool exceedsThreshold(size_t bytes) const; /** * @brief exceedsThreshold Test if a data set is "large enough" to be stored * in HDF5 format. If this function returns true, the size of the data in the * object is larger than the threshold and should be written into the HDF5 * file. If false, the data should be written into the accompanying format. * @param data Data object to test. * @return true if the size of the serializable data in @a data exceeds the * threshold set by setThreshold. */ bool exceedsThreshold(const MatrixX& data) const; /** * @brief exceedsThreshold Test if a data set is "large enough" to be stored * in HDF5 format. If this function returns true, the size of the data in the * object is larger than the threshold and should be written into the HDF5 * file. If false, the data should be written into the accompanying format. * @param data Data object to test. * @return true if the size of the serializable data in @a data exceeds the * threshold set by setThreshold. */ bool exceedsThreshold(const std::vector& data) const; /** * @brief exceedsThreshold Test if a data set is "large enough" to be stored * in HDF5 format. If this function returns true, the size of the data in the * object is larger than the threshold and should be written into the HDF5 * file. If false, the data should be written into the accompanying format. * @param data Data object to test. * @return true if the size of the serializable data in @a data exceeds the * threshold set by setThreshold. */ bool exceedsThreshold(const Core::Array& data) const; /** * @brief datasetExists Test if the currently open file contains a dataset at * the HDF5 absolute path @a path. * @param path An absolute path into the HDF5 data. * @return true if the object at @a path both exists and is a dataset, false * otherwise. */ bool datasetExists(const std::string& path) const; /** * @brief removeDataset Remove a dataset from the currently opened file. * @param path An absolute path into the HDF5 data. * @return true if the dataset exists and has been successfully removed. * \warning Removing datasets can be expensive in terms of filesize, as * deleted space cannot be reclaimed by HDF5 once the file is closed, and the * file will not decrease in size as datasets are removed. For details, see * http://www.hdfgroup.org/HDF5/doc/H5.user/Performance.html#Freespace. */ bool removeDataset(const std::string& path) const; /** * @brief datasetDimensions Find the dimensions of a dataset. * @param path An absolute path into the HDF5 data. * @return A vector containing the dimensionality of the data, major dimension * first. If an error is encountered, an empty vector is returned. */ std::vector datasetDimensions(const std::string& path) const; /** * @brief writeDataset Write the data to the currently opened file at the * specified absolute HDF5 path. * @param path An absolute path into the HDF5 data. * @param data The data container to serialize to HDF5. * @return true if the data is successfully written, false otherwise. */ bool writeDataset(const std::string& path, const MatrixX& data) const; /** * @brief writeDataset Write the data to the currently opened file at the * specified absolute HDF5 path. * @param path An absolute path into the HDF5 data. * @param data The data container to serialize to HDF5. * @param ndims The number of dimensions in the data. Default: 1. * @param dims The dimensionality of the data, major dimension first. Default: * data.size(). * @note Since std::vector is a flat container, the dimensionality data is * only used to set up the dataset metadata in the HDF5 container. Omitting * the dimensionality parameters will write a flat array. * @return true if the data is successfully written, false otherwise. */ bool writeDataset(const std::string& path, const std::vector& data, int ndims = 1, size_t* dims = nullptr) const; /** * @brief writeDataset Write the data to the currently opened file at the * specified absolute HDF5 path. * @param path An absolute path into the HDF5 data. * @param data The data container to serialize to HDF5. * @param ndims The number of dimensions in the data. Default: 1. * @param dims The dimensionality of the data, major dimension first. Default: * data.size(). * @note Since this is a flat container, the dimensionality data is * only used to set up the dataset metadata in the HDF5 container. Omitting * the dimensionality parameters will write a flat array. * @return true if the data is successfully written, false otherwise. */ bool writeDataset(const std::string& path, const Core::Array& data, int ndims = 1, size_t* dims = nullptr) const; /** * @brief readDataset Populate the data container @data with data at from the * specified path in the currently opened HDF5 file. * @param path An absolute path into the HDF5 data. * @param data The data container to into which the HDF5 data shall be * deserialized. @a data will be resized to fit the data. * @return true if the data is successfully read, false otherwise. If the * read fails, the @a data object may be left in an unpredictable state. */ bool readDataset(const std::string& path, MatrixX& data) const; /** * @brief readDataset Populate the data container @data with data at from the * specified path in the currently opened HDF5 file. * @param path An absolute path into the HDF5 data. * @param data The data container to into which the HDF5 data shall be * deserialized. @a data will be resized to fit the data. * @return A vector containing the dimensionality of the dataset, major * dimension first. If an error occurs, an empty vector is returned and *data * will be set to nullptr. */ std::vector readDataset(const std::string& path, std::vector& data) const; /** * @brief readDataset Populate the data container @data with data at from the * specified path in the currently opened HDF5 file. * @param path An absolute path into the HDF5 data. * @param data The data container to into which the HDF5 data shall be * deserialized. @a data will be resized to fit the data. * @return A vector containing the dimensionality of the dataset, major * dimension first. If an error occurs, an empty vector is returned and *data * will be set to nullptr. */ std::vector readDataset(const std::string& path, Core::Array& data) const; /** * @brief datasets Traverse the currently opened file and return a list of all * dataset objects in the file. * @return A list of datasets in the current file. * \warning The list is not cached internal and is recalculated on each call. * This may be expensive on large HDF5 files, so external caching is * recommended if this data is frequently needed. */ std::vector datasets() const; /** Used to abstract details of container resizing. */ class ResizeContainer; private: /** * @brief writeRawDataset Write the data to the currently opened file at the * specified absolute HDF5 path. * @param path An absolute path into the HDF5 data. * @param data The data container to serialize to HDF5. * @param ndims The number of dimensions in the data. * @param dims The data dimensions, major dimension first. * @note Since a double[] is a flat container, the dimensionality data is * only used to set up the dataset metadata in the HDF5 container. The result * of multiplying all values in @a dims must equal the length of the @a data. * @return true if the data is successfully written, false otherwise. */ bool writeRawDataset(const std::string& path, const double data[], int ndims, size_t dims[]) const; /** * @brief readRawDataset Populate the data container @data with data from the * specified path in the currently opened HDF5 file. * @param path An absolute path into the HDF5 data. * @param container A subclass of ResizeContainer with the container to read * data into. * @return A vector containing the dimensionality of the dataset, major * dimension first. If an error occurs, an empty vector is returned. */ std::vector readRawDataset(const std::string& path, ResizeContainer& container) const; class Private; /** Internal storage, used to encapsulate HDF5 data. */ Private* const d; }; } // namespace Io } // namespace Avogadro #endif // AVOGADRO_HDF5DATAFORMAT_H avogadrolibs-1.101.0/avogadro/io/lammpsformat.cpp000066400000000000000000000470731506155467400217750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "lammpsformat.h" #include #include #include #include #include #include #include #include #include #include using std::getline; using std::map; using std::string; using std::to_string; namespace Avogadro::Io { using Core::Array; using Core::Atom; using Core::Bond; using Core::CrystalTools; using Core::Elements; using Core::lexicalCast; using Core::Molecule; using Core::split; using Core::trimmed; using Core::UnitCell; bool LammpsTrajectoryFormat::read(std::istream& inStream, Core::Molecule& mol) { size_t numAtoms = 0, timestep = 0, x_idx = -1, y_idx = -1, z_idx = -1, type_idx = -1, id_idx = -1; double x_min = 0, x_max = 0, y_min = 0, y_max = 0, z_min = 0, z_max = 0, tilt_xy = 0, tilt_xz = 0, tilt_yz = 0, scale_x = 0., scale_y = 0., scale_z = 0.; // This can likely be removed as it is clearly not used anywhere, suppress for // now. AVO_UNUSED(id_idx); string buffer; getline(inStream, buffer); // Finish the first line buffer = trimmed(buffer); if (buffer != "ITEM: TIMESTEP") { appendError("No timestep item found."); return false; } getline(inStream, buffer); if (!buffer.empty()) { timestep = lexicalCast(buffer); mol.setTimeStep(timestep, 0); } getline(inStream, buffer); buffer = trimmed(buffer); if (buffer != "ITEM: NUMBER OF ATOMS") { appendError("No number of atoms item found."); return false; } getline(inStream, buffer); if (!buffer.empty()) numAtoms = lexicalCast(buffer); // If unit cell is triclinic, tilt factors are needed to define the supercell getline(inStream, buffer); if (buffer.find("ITEM: BOX BOUNDS xy xz yz") == 0) { // Read x_min, x_max, tiltfactor_xy getline(inStream, buffer); std::vector box_bounds_x(split(buffer, ' ')); x_min = lexicalCast(box_bounds_x.at(0)); x_max = lexicalCast(box_bounds_x.at(1)); tilt_xy = lexicalCast(box_bounds_x.at(2)); // Read y_min, y_max, tiltfactor_xz getline(inStream, buffer); std::vector box_bounds_y(split(buffer, ' ')); y_min = lexicalCast(box_bounds_y.at(0)); y_max = lexicalCast(box_bounds_y.at(1)); tilt_xz = lexicalCast(box_bounds_y.at(2)); getline(inStream, buffer); // Read z_min, z_max, tiltfactor_yz std::vector box_bounds_z(split(buffer, ' ')); z_min = lexicalCast(box_bounds_z.at(0)); z_max = lexicalCast(box_bounds_z.at(1)); tilt_yz = lexicalCast(box_bounds_z.at(2)); x_min -= std::min(std::min(std::min(tilt_xy, tilt_xz), tilt_xy + tilt_xz), (double)0); x_max -= std::max(std::max(std::max(tilt_xy, tilt_xz), tilt_xy + tilt_xz), (double)0); y_min -= std::min(tilt_yz, (double)0); y_max -= std::max(tilt_yz, (double)0); } // Else if unit cell is orthogonal, tilt factors are zero else if (buffer.find("ITEM: BOX BOUNDS") == 0) { // Read x_min, x_max getline(inStream, buffer); std::vector box_bounds_x(split(buffer, ' ')); x_min = lexicalCast(box_bounds_x.at(0)); x_max = lexicalCast(box_bounds_x.at(1)); // Read y_min, y_max getline(inStream, buffer); std::vector box_bounds_y(split(buffer, ' ')); y_min = lexicalCast(box_bounds_y.at(0)); y_max = lexicalCast(box_bounds_y.at(1)); // Read z_min, z_max getline(inStream, buffer); std::vector box_bounds_z(split(buffer, ' ')); z_min = lexicalCast(box_bounds_z.at(0)); z_max = lexicalCast(box_bounds_z.at(1)); } typedef map AtomTypeMap; AtomTypeMap atomTypes; unsigned char customElementCounter = CustomElementMin; // x,y,z stand for the coordinate axes // s stands for scaled coordinates // u stands for unwrapped coordinates // scale_x = 0. if coordinates are cartesian and 1 if fractional (scaled) getline(inStream, buffer); std::vector labels(split(buffer, ' ')); for (size_t i = 0; i < labels.size(); i++) { if (labels[i] == "x" || labels[i] == "xu") { x_idx = i; scale_x = 0.; } else if (labels[i] == "xs" || labels[i] == "xsu") { x_idx = i; scale_x = 1.; } else if (labels[i] == "y" || labels[i] == "yu") { y_idx = i; scale_y = 0.; } else if (labels[i] == "ys" || labels[i] == "ysu") { y_idx = i; scale_y = 1.; } else if (labels[i] == "z" || labels[i] == "zu") { z_idx = i; scale_z = 0.; } else if (labels[i] == "zs" || labels[i] == "zsu") { z_idx = i; scale_z = 1.; } else if (labels[i] == "type") { type_idx = i; } else if (labels[i] == "id") { id_idx = i; } } // Parse atoms for (size_t i = 0; i < numAtoms; ++i) { getline(inStream, buffer); std::vector tokens(split(buffer, ' ')); if (tokens.size() < labels.size() - 2) { appendError("Not enough tokens in this line: " + buffer); return false; } unsigned char atomicNum(0); atomicNum = lexicalCast(tokens[type_idx - 2]); // If parsed coordinates are fractional, the corresponding unscaling is // done. Else the positions are assigned as parsed. Vector3 pos((1 - scale_x) * lexicalCast(tokens[x_idx - 2]) + scale_x * (x_min + (x_max - x_min) * lexicalCast(tokens[x_idx - 2])), (1 - scale_y) * lexicalCast(tokens[y_idx - 2]) + scale_y * (y_min + (y_max - y_min) * lexicalCast(tokens[y_idx - 2])), (1 - scale_z) * lexicalCast(tokens[z_idx - 2]) + scale_z * (z_min + (z_max - z_min) * lexicalCast(tokens[z_idx - 2]))); auto it = atomTypes.find(to_string(atomicNum)); if (it == atomTypes.end()) { atomTypes.insert( std::make_pair(to_string(atomicNum), customElementCounter++)); it = atomTypes.find(to_string(atomicNum)); if (customElementCounter > CustomElementMax) { appendError("Custom element type limit exceeded."); return false; } } Atom newAtom = mol.addAtom(it->second); newAtom.setPosition3d(pos); } // Set the custom element map if needed: if (!atomTypes.empty()) { Molecule::CustomElementMap elementMap; for (const auto& atomType : atomTypes) { elementMap.insert(std::make_pair(atomType.second, atomType.first)); } mol.setCustomElementMap(elementMap); } // Check that all atoms were handled. if (mol.atomCount() != numAtoms) { std::ostringstream errorStream; errorStream << "Error parsing atom at index " << mol.atomCount() << " (line " << 10 + mol.atomCount() << ").\n" << buffer; appendError(errorStream.str()); return false; } mol.setCoordinate3d(mol.atomPositions3d(), 0); auto* uc = new UnitCell(Vector3(x_max - x_min, 0, 0), Vector3(tilt_xy, y_max - y_min, 0), Vector3(tilt_xz, tilt_yz, z_max - z_min)); if (!uc->isRegular()) { appendError( "'ITEM: BOX BOUNDS' does not give linear-independent lattive vectors"); delete uc; return false; } mol.setUnitCell(uc); // Do we have an animation? size_t numAtoms2; int coordSet = 1; while (getline(inStream, buffer) && trimmed(buffer) == "ITEM: TIMESTEP") { x_idx = -1; y_idx = -1; z_idx = -1; type_idx = -1; id_idx = -1; x_min = 0; x_max = 0; y_min = 0; y_max = 0; z_min = 0; z_max = 0; tilt_xy = 0; tilt_xz = 0; tilt_yz = 0; scale_x = 0.; scale_y = 0.; scale_z = 0.; getline(inStream, buffer); if (!buffer.empty()) { timestep = lexicalCast(buffer); mol.setTimeStep(timestep, coordSet); } getline(inStream, buffer); buffer = trimmed(buffer); if (buffer != "ITEM: NUMBER OF ATOMS") { appendError("No number of atoms item found."); return false; } getline(inStream, buffer); if (!buffer.empty()) numAtoms2 = lexicalCast(buffer); if (numAtoms2 != numAtoms) { appendError("Number of atoms isn't constant in the trajectory."); } // If unit cell is triclinic, tilt factors are needed to define the // supercell getline(inStream, buffer); if (buffer.find("ITEM: BOX BOUNDS xy xz yz") == 0) { // Read x_min, x_max, tiltfactor_xy getline(inStream, buffer); std::vector box_bounds_x(split(buffer, ' ')); x_min = lexicalCast(box_bounds_x.at(0)); x_max = lexicalCast(box_bounds_x.at(1)); tilt_xy = lexicalCast(box_bounds_x.at(2)); // Read y_min, y_max, tiltfactor_xz getline(inStream, buffer); std::vector box_bounds_y(split(buffer, ' ')); y_min = lexicalCast(box_bounds_y.at(0)); y_max = lexicalCast(box_bounds_y.at(1)); tilt_xz = lexicalCast(box_bounds_y.at(2)); getline(inStream, buffer); // Read z_min, z_max, tiltfactor_yz std::vector box_bounds_z(split(buffer, ' ')); z_min = lexicalCast(box_bounds_z.at(0)); z_max = lexicalCast(box_bounds_z.at(1)); tilt_yz = lexicalCast(box_bounds_z.at(2)); x_min -= std::min(std::min(std::min(tilt_xy, tilt_xz), tilt_xy + tilt_xz), (double)0); x_max -= std::max(std::max(std::max(tilt_xy, tilt_xz), tilt_xy + tilt_xz), (double)0); y_min -= std::min(tilt_yz, (double)0); y_max -= std::max(tilt_yz, (double)0); } // Else if unit cell is orthogonal, tilt factors are zero else if (buffer.find("ITEM: BOX BOUNDS") == 0) { // Read x_min, x_max getline(inStream, buffer); std::vector box_bounds_x(split(buffer, ' ')); x_min = lexicalCast(box_bounds_x.at(0)); x_max = lexicalCast(box_bounds_x.at(1)); // Read y_min, y_max getline(inStream, buffer); std::vector box_bounds_y(split(buffer, ' ')); y_min = lexicalCast(box_bounds_y.at(0)); y_max = lexicalCast(box_bounds_y.at(1)); // Read z_min, z_max getline(inStream, buffer); std::vector box_bounds_z(split(buffer, ' ')); z_min = lexicalCast(box_bounds_z.at(0)); z_max = lexicalCast(box_bounds_z.at(1)); } // x,y,z stand for the coordinate axes // s stands for scaled coordinates // u stands for unwrapped coordinates // scale_x = 0. if coordinates are cartesian and 1 if fractional (scaled) getline(inStream, buffer); labels = std::vector(split(buffer, ' ')); for (size_t i = 0; i < labels.size(); ++i) { if (labels[i] == "x" || labels[i] == "xu") { x_idx = i; scale_x = 0.; } else if (labels[i] == "xs" || labels[i] == "xsu") { x_idx = i; scale_x = 1.; } else if (labels[i] == "y" || labels[i] == "yu") { y_idx = i; scale_y = 0.; } else if (labels[i] == "ys" || labels[i] == "ysu") { y_idx = i; scale_y = 1.; } else if (labels[i] == "z" || labels[i] == "zu") { z_idx = i; scale_z = 0.; } else if (labels[i] == "zs" || labels[i] == "zsu") { z_idx = i; scale_z = 1.; } else if (labels[i] == "type") { type_idx = i; } else if (labels[i] == "id") { id_idx = i; } } Array positions; positions.reserve(numAtoms); for (size_t i = 0; i < numAtoms; ++i) { getline(inStream, buffer); std::vector tokens(split(buffer, ' ')); if (tokens.size() < 5) { appendError("Not enough tokens in this line: " + buffer); return false; } // If parsed coordinates are fractional, the corresponding unscaling is // done. Else the positions are assigned as parsed. Vector3 pos( (1 - scale_x) * lexicalCast(tokens[x_idx - 2]) + scale_x * (x_min + (x_max - x_min) * lexicalCast(tokens[x_idx - 2])), (1 - scale_y) * lexicalCast(tokens[y_idx - 2]) + scale_y * (y_min + (y_max - y_min) * lexicalCast(tokens[y_idx - 2])), (1 - scale_z) * lexicalCast(tokens[z_idx - 2]) + scale_z * (z_min + (z_max - z_min) * lexicalCast(tokens[z_idx - 2]))); positions.push_back(pos); } mol.setCoordinate3d(positions, coordSet++); auto* uc = new UnitCell(Vector3(x_max - x_min, 0, 0), Vector3(tilt_xy, y_max - y_min, 0), Vector3(tilt_xz, tilt_yz, z_max - z_min)); if (!uc->isRegular()) { appendError( "'ITEM: BOX BOUNDS' does not give linear-independent lattive vectors"); return false; } mol.setUnitCell(uc); } return true; } bool LammpsTrajectoryFormat::write(std::ostream&, const Core::Molecule&) { return false; } std::vector LammpsTrajectoryFormat::fileExtensions() const { std::vector ext; ext.emplace_back("dump"); return ext; } std::vector LammpsTrajectoryFormat::mimeTypes() const { std::vector mime; mime.emplace_back("text/lammps"); return mime; } bool LammpsDataFormat::read(std::istream&, Core::Molecule&) { return false; } bool LammpsDataFormat::write(std::ostream& outStream, const Core::Molecule& mol) { Core::Molecule mol2(mol); CrystalTools::rotateToStandardOrientation(mol2, CrystalTools::TransformAtoms); // Title if (mol2.data("name").toString().length()) outStream << mol2.data("name").toString() << std::endl; else outStream << "LAMMPS data file generated by Avogadro" << std::endl; std::ostringstream massStream, atomStream, bondStream; double xmin, xmax, ymin, ymax, zmin, zmax; xmin = xmax = ymin = ymax = zmin = zmax = 0.0; size_t numAtoms = mol2.atomCount(); outStream << to_string(numAtoms) << " atoms\n"; size_t numBonds = mol2.bondCount(); outStream << to_string(numBonds) << " bonds\n"; // A map of atomic symbols to their quantity. size_t idx = 1; Array atomicNumbers = mol2.atomicNumbers(); std::map composition; for (unsigned char& atomicNumber : atomicNumbers) { if (composition.find(atomicNumber) == composition.end()) { composition[atomicNumber] = idx++; } } outStream << composition.size() << " atom types\n"; // Masses massStream << "Masses\n\n"; auto iter = composition.begin(); while (iter != composition.end()) { massStream << iter->second << " " << Elements::mass(iter->first) << "\n"; ++iter; } massStream << std::endl << std::endl << std::endl; if (numAtoms) { // Atomic coordinates atomStream << "Atoms\n\n"; for (Index i = 0; i < numAtoms; ++i) { Atom atom = mol2.atom(i); if (!atom.isValid()) { appendError("Internal error: Atom invalid."); return false; } Vector3 coords = atom.position3d(); if (i == 0) { xmin = coords[0]; xmax = coords[0]; ymin = coords[1]; ymax = coords[1]; zmin = coords[2]; zmax = coords[2]; } else { xmin = std::min(coords[0], xmin); xmax = std::max(coords[0], xmax); ymin = std::min(coords[1], ymin); ymax = std::max(coords[1], ymax); zmin = std::min(coords[2], zmin); zmax = std::max(coords[2], zmax); } const unsigned int lineSize = 256; char atomline[lineSize]; snprintf(atomline, lineSize - 1, "%-*d %d %10f %10f %10f\n", static_cast(log(numAtoms)) + 1, static_cast(i + 1), static_cast(composition[atomicNumbers[i]]), coords.x(), coords.y(), coords.z()); atomStream << atomline; } atomStream << std::endl << std::endl; } if (numBonds) { // Bonds std::map, int> bondIds; int bondItr = 1; bondStream << "Bonds\n\n"; const unsigned int lineSize = 256; for (Index i = 0; i < numBonds; ++i) { char bondline[lineSize]; Bond b = mol2.bond(i); if (bondIds.find(std::make_pair(b.atom1().atomicNumber(), b.atom2().atomicNumber())) != bondIds.end()) { snprintf(bondline, lineSize - 1, "%-*d %7d %7d %7d\n", static_cast(log(numAtoms) + 1), static_cast(i + 1), bondIds[std::make_pair(b.atom1().atomicNumber(), b.atom2().atomicNumber())], static_cast(b.atom1().index() + 1), static_cast(b.atom2().index() + 1)); bondStream << bondline; } else if (bondIds.find(std::make_pair(b.atom2().atomicNumber(), b.atom1().atomicNumber())) != bondIds.end()) { snprintf(bondline, lineSize - 1, "%-*d %7d %7d %7d\n", static_cast(log(numAtoms) + 1), static_cast(i + 1), bondIds[std::make_pair(b.atom1().atomicNumber(), b.atom2().atomicNumber())], static_cast(b.atom2().index() + 1), static_cast(b.atom1().index() + 1)); bondStream << bondline; } else { bondIds.insert(std::make_pair( std::make_pair(b.atom1().atomicNumber(), b.atom2().atomicNumber()), bondItr++)); snprintf(bondline, lineSize - 1, "%-*d %7d %7d %7d\n", static_cast(log(numAtoms) + 1), static_cast(i + 1), bondIds[std::make_pair(b.atom1().atomicNumber(), b.atom2().atomicNumber())], static_cast(b.atom1().index() + 1), static_cast(b.atom2().index() + 1)); bondStream << bondline; } } } UnitCell* unitcell = mol2.unitCell(); const unsigned int lineSize = 256; char simBoxBlock[lineSize]; if (unitcell) { const Matrix3& mat = unitcell->cellMatrix().transpose(); snprintf(simBoxBlock, lineSize - 1, "%10f %10f xlo xhi\n%10f %10f ylo yhi\n%10f %10f zlo zhi\n%10f " "%10f %10f xy xz yz", 0.0, mat(0, 0), 0.0, mat(1, 1), 0.0, mat(2, 2), mat(1, 0), mat(2, 0), mat(2, 1)); outStream << simBoxBlock; } else { snprintf(simBoxBlock, lineSize - 1, "%10f %10f xlo xhi\n%10f %10f ylo yhi\n%10f %10f zlo zhi\n%10f " "%10f %10f xy xz yz", xmin - 0.5, xmax - 0.5, ymin - 0.5, ymax - 0.5, zmin - 0.5, zmax - 0.5, 0.0, 0.0, 0.0); outStream << simBoxBlock; } outStream << std::endl << std::endl << std::endl; outStream << massStream.str(); outStream << atomStream.str(); outStream << bondStream.str(); return true; } std::vector LammpsDataFormat::fileExtensions() const { std::vector ext; ext.emplace_back("lmpdat"); return ext; } std::vector LammpsDataFormat::mimeTypes() const { std::vector mime; mime.emplace_back("N/A"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/lammpsformat.h000066400000000000000000000050761506155467400214370ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_LAMMPSFORMAT_H #define AVOGADRO_IO_LAMMPSFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class LammpsTrajectoryFormat lammpsformat.h * @brief Implementation of the generic lammps trajectory format. * @author Adarsh B */ class AVOGADROIO_EXPORT LammpsTrajectoryFormat : public FileFormat { public: LammpsTrajectoryFormat() = default; ~LammpsTrajectoryFormat() override = default; Operations supportedOperations() const override { return ReadWrite | MultiMolecule | File | Stream | String; } FileFormat* newInstance() const override { return new LammpsTrajectoryFormat; } std::string identifier() const override { return "Avogadro: LAMMPS Trajectory dump"; } std::string name() const override { return "LAMMPS"; } std::string description() const override { return "Generic LAMMPS Trajectory format."; } std::string specificationUrl() const override { return "http://lammps.sandia.gov/"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& molecule) override; bool write(std::ostream& outStream, const Core::Molecule& molecule) override; }; class AVOGADROIO_EXPORT LammpsDataFormat : public FileFormat { public: LammpsDataFormat() = default; ~LammpsDataFormat() override = default; Operations supportedOperations() const override { return ReadWrite | MultiMolecule | File | Stream | String; } FileFormat* newInstance() const override { return new LammpsDataFormat; } std::string identifier() const override { return "Avogadro: LAMMPS Data"; } std::string name() const override { return "LAMMPS"; } std::string description() const override { return "Generic LAMMPS Data format."; } std::string specificationUrl() const override { return "http://lammps.sandia.gov/"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& molecule) override; bool write(std::ostream& outStream, const Core::Molecule& molecule) override; }; } // namespace Avogadro::Io // end Avogadro namespace #endif // AVOGADRO_IO_LAMMPSFORMAT_H avogadrolibs-1.101.0/avogadro/io/mdlformat.cpp000066400000000000000000000552671506155467400212640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "mdlformat.h" #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Core::Atom; using Avogadro::Core::Bond; using Avogadro::Core::Elements; using Avogadro::Core::endsWith; using Avogadro::Core::lexicalCast; using Avogadro::Core::split; using Avogadro::Core::startsWith; using Avogadro::Core::trimmed; using std::getline; using std::istringstream; using std::setprecision; using std::setw; using std::string; namespace Avogadro::Io { using chargePair = std::pair; namespace { // Helper function to handle partial charge property blocks // e.g. PUBCHEM_MMFF94_PARTIAL_CHARGES void handlePartialCharges(Core::Molecule& mol, std::string data, std::string name = "MMFF94") { // the string starts with the number of charges // then atom index charge MatrixX charges(mol.atomCount(), 1); std::istringstream iss(data); size_t numCharges; iss >> numCharges; if (numCharges == 0 || numCharges > mol.atomCount()) { return; } for (size_t i = 0; i < numCharges; ++i) { if (!iss.good()) { return; } size_t index = 0; Real charge = 0.0; iss >> index; if (iss.fail() || index == 0 || index > mol.atomCount()) { return; } iss >> charge; if (iss.fail()) { return; } // prints with atom index 1, not zero charges(index - 1, 0) = charge; } // if present, remove atom.dpos and CHARGES from the string if (startsWith(name, "atom.dpos")) { name = name.substr(9, name.size() - 16); } if (endsWith(name, "CHARGES")) { name = name.substr(0, name.size() - 7); } mol.setPartialCharges(name, charges); } } // namespace bool MdlFormat::read(std::istream& in, Core::Molecule& mol) { string buffer; int spinMultiplicity = 1; // check for RAD lines // The first line is the molecule name. getline(in, buffer); buffer = trimmed(buffer); // Check for the record separator in SDF, and skip if found. if (buffer == "$$$$") { getline(in, buffer); buffer = trimmed(buffer); } if (!buffer.empty()) mol.setData("name", buffer); if (!in.good()) { appendError("Error reading molecule name."); return false; } // Skip the next two lines (generator, and comment). getline(in, buffer); getline(in, buffer); if (!in.good()) { appendError("Error reading generator and comment lines."); return false; } // The counts line, and version identifier. getline(in, buffer); // should be long enough, e.g. // 5 4 0 0 0 0 0 0 0 0999 V2000 if (!in.good() || buffer.size() < 39) { appendError("Error reading counts line."); return false; } bool ok(false); int numAtoms(lexicalCast(buffer.substr(0, 3), ok)); if (!ok) { appendError("Error parsing number of atoms."); return false; } int numBonds(lexicalCast(buffer.substr(3, 3), ok)); if (!ok) { appendError("Error parsing number of bonds."); return false; } string mdlVersion(trimmed(buffer.substr(33))); if (mdlVersion == "V3000") return readV3000(in, mol); else if (mdlVersion != "V2000") { appendError("Unsupported MDL version: " + mdlVersion); return false; } // Parse the atom block. std::vector chargeList; for (int i = 0; i < numAtoms; ++i) { Vector3 pos; getline(in, buffer); // 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 if (!in.good() || buffer.size() < 40) { appendError("Error reading atom block."); return false; } pos.x() = lexicalCast(buffer.substr(0, 10), ok); if (!ok) { appendError("Failed to parse x coordinate: " + buffer.substr(0, 10)); return false; } pos.y() = lexicalCast(buffer.substr(10, 10), ok); if (!ok) { appendError("Failed to parse y coordinate: " + buffer.substr(10, 10)); return false; } pos.z() = lexicalCast(buffer.substr(20, 10), ok); if (!ok) { appendError("Failed to parse z coordinate: " + buffer.substr(20, 10)); return false; } string element(trimmed(buffer.substr(31, 3))); auto charge(lexicalCast(trimmed(buffer.substr(36, 3)))); if (!buffer.empty()) { unsigned char atomicNum = Elements::atomicNumberFromSymbol(element); Atom newAtom = mol.addAtom(atomicNum); newAtom.setPosition3d(pos); // In case there's no CHG property if (charge == 4) // doublet radical spinMultiplicity += 1; charge = (charge > 4) ? ((charge <= 7) ? 4 - charge : 0) : ((charge < 4) ? charge : 0); if (charge) chargeList.emplace_back(newAtom.index(), charge); continue; } else { appendError("Error parsing atom block: " + buffer); return false; } } // Parse the bond block. for (int i = 0; i < numBonds; ++i) { // Bond atom indices start at 1, -1 for C++. getline(in, buffer); // 1 2 1 0 0 0 0 if (!in.good() || buffer.size() < 10) { appendError("Error reading bond block."); return false; } int begin(lexicalCast(buffer.substr(0, 3), ok) - 1); if (!ok) { appendError("Error parsing beginning bond index:" + buffer.substr(0, 3)); return false; } int end(lexicalCast(buffer.substr(3, 3), ok) - 1); if (!ok) { appendError("Error parsing end bond index:" + buffer.substr(3, 3)); return false; } int order(lexicalCast(buffer.substr(6, 3), ok)); if (!ok) { appendError("Error parsing bond order:" + buffer.substr(6, 3)); return false; } if (begin < 0 || begin >= numAtoms || end < 0 || end >= numAtoms) { appendError("Bond read in with out of bounds index."); return false; } mol.addBond(mol.atom(begin), mol.atom(end), static_cast(order)); } // Parse the properties block until the end of the file. // Property lines count is not used, as it it now unsupported. bool foundEnd(false); bool foundChgProperty(false); while (getline(in, buffer)) { if (!in.good() || buffer.size() < 6) { break; } #ifndef NDEBUG std::cout << " prefix " << buffer.substr(0, 6) << std::endl; #endif string prefix = buffer.substr(0, 6); if (prefix == "M END") { foundEnd = true; break; } else if (prefix == "M CHG") { if (!foundChgProperty) chargeList.clear(); // Forget old-style charges size_t entryCount(lexicalCast(buffer.substr(6, 3), ok)); if (buffer.length() < 17 + 8 * (entryCount - 1)) { appendError("Error parsing charge block."); std::cout << " " << entryCount << " " << buffer.length() << std::endl; return false; } for (size_t i = 0; i < entryCount; i++) { size_t index(lexicalCast(buffer.substr(10 + 8 * i, 3), ok) - 1); if (!ok) { appendError("Error parsing charged atom index:" + buffer.substr(10 + 8 * i, 3)); return false; } auto charge(lexicalCast(buffer.substr(14 + 8 * i, 3), ok)); if (!ok) { appendError("Error parsing atom charge:" + buffer.substr(14 + 8 * i, 3)); return false; } if (charge) chargeList.emplace_back(index, charge); } } else if (prefix == "M RAD") { // radical center spinMultiplicity = 1; // reset and count size_t entryCount(lexicalCast(buffer.substr(6, 3), ok)); if (buffer.length() < 17 + 8 * (entryCount - 1)) { appendError("Error parsing radical block."); return false; } for (size_t i = 0; i < entryCount; i++) { [[maybe_unused]] size_t index( lexicalCast(buffer.substr(10 + 8 * i, 3), ok) - 1); if (!ok) { appendError("Error parsing radical atom index:" + buffer.substr(10 + 8 * i, 3)); return false; } auto radical(lexicalCast(buffer.substr(14 + 8 * i, 3), ok)); if (!ok) { appendError("Error parsing radical type:" + buffer.substr(14 + 8 * i, 3)); return false; } // we don't set radical centers, just count them // for the total spin multiplicity if (radical == 2) // doublet spinMultiplicity += 1; else if (radical == 3) // triplet spinMultiplicity += 2; } } else if (prefix == "M ISO") { // isotope size_t entryCount(lexicalCast(buffer.substr(6, 3), ok)); if (buffer.length() < 17 + 8 * (entryCount - 1)) { appendError("Error parsing isotope block."); return false; } for (size_t i = 0; i < entryCount; i++) { [[maybe_unused]] size_t index( lexicalCast(buffer.substr(10 + 8 * i, 3), ok) - 1); if (!ok) { appendError("Error parsing isotope atom index:" + buffer.substr(10 + 8 * i, 3)); return false; } [[maybe_unused]] auto isotope( lexicalCast(buffer.substr(14 + 8 * i, 3), ok)); if (!ok) { appendError("Error parsing isotope type:" + buffer.substr(14 + 8 * i, 3)); return false; } // TODO: Implement isotope setting // mol.atom(index).setIsotope(isotope); } } } if (!foundEnd) { appendError("Error, ending tag for file not found."); return false; } // Apply charges. for (auto& i : chargeList) { size_t index = i.first; signed int charge = i.second; mol.setFormalCharge(index, charge); } // Set the total spin multiplicity if (spinMultiplicity > 1) mol.setData("totalSpinMultiplicity", spinMultiplicity); // Check that all atoms were handled. if (mol.atomCount() != static_cast(numAtoms) || mol.bondCount() != static_cast(numBonds)) { std::ostringstream errorStream; errorStream << "Error parsing file, got " << mol.atomCount() << "atoms, expected " << numAtoms << ", got " << mol.bondCount() << ", expected " << numBonds << "."; appendError(errorStream.str()); return false; } // Now parse the data block. bool inValue(false); string dataName; string dataValue; while (getline(in, buffer) && in.good()) { if (trimmed(buffer) == "$$$$") break; if (inValue) { if (buffer.empty() && dataName.length() > 0) { // check for partial charges if (dataName == "PUBCHEM_MMFF94_PARTIAL_CHARGES") handlePartialCharges(mol, dataValue); else if (startsWith(dataName, "atom.dpos") && endsWith(dataName, "CHARGES")) // remove the "CHARGES" from the end of the string handlePartialCharges(mol, dataValue, dataName); else mol.setData(dataName, dataValue); dataName.clear(); dataValue.clear(); inValue = false; } else { if (dataValue.length()) dataValue += "\n"; dataValue += buffer; } } else if (startsWith(buffer, "> ")) { // This is a data header, read the name of the entry, and the value on // the following lines. // e.g., > // dataName will be anything from < to > size_t start = buffer.find('<'); size_t end = buffer.find('>', start); if (start != string::npos && end != string::npos) { dataName = buffer.substr(start + 1, end - start - 1); inValue = true; } } } // handle pKa from QupKake model if (mol.hasData("pka") && mol.hasData("idx")) { // pka can sometimes say "tensor(3.1452)" or "3.1452" // just convert to a string with 2 decimal places std::string pka = mol.data("pka").toString(); if (startsWith(pka, "tensor(")) pka = pka.substr(7, pka.size() - 8); // find the decimal to only keep 2 decimal places size_t decimal = pka.find("."); if (decimal != std::string::npos) pka = pka.substr(0, decimal + 3); mol.setData("pka", pka); // convert the idx to an atom index // and set the label std::string idx = mol.data("idx").toString(); size_t atomIdx = lexicalCast(idx); mol.setAtomLabel(atomIdx, pka); } return true; } bool MdlFormat::readV3000(std::istream& in, Core::Molecule& mol) { string buffer; int spinMultiplicity = 1; // check for RAD lines // we should have M V30 BEGIN CTAB getline(in, buffer); if (trimmed(buffer) != "M V30 BEGIN CTAB") { appendError("Error parsing V3000 file, expected 'M V30 BEGIN CTAB'."); return false; } // now we should get the counts line // e.g. 'M V30 COUNTS 23694 24297 0 0 1' getline(in, buffer); // split by whitespace std::vector counts = split(trimmed(buffer), ' '); if (counts.size() < 5) { appendError("Error parsing V3000 counts line."); return false; } bool ok(false); int numAtoms(lexicalCast(counts[3], ok)); if (!ok) { appendError("Error parsing number of atoms."); return false; } int numBonds(lexicalCast(counts[4], ok)); if (!ok) { appendError("Error parsing number of bonds."); return false; } // Parse the atom block. // 'M V30 BEGIN ATOM' // 'M V30 1 N 171.646 251.874 224.877 0' getline(in, buffer); if (trimmed(buffer) != "M V30 BEGIN ATOM") { appendError("Error parsing V3000 atom block."); return false; } for (int i = 0; i < numAtoms; ++i) { getline(in, buffer); std::vector atomData = split(trimmed(buffer), ' '); if (atomData.size() < 7) { appendError("Error parsing V3000 atom line."); return false; } string element(trimmed(atomData[3])); unsigned char atomicNum = Elements::atomicNumberFromSymbol(element); Atom newAtom = mol.addAtom(atomicNum); Vector3 pos; pos.x() = lexicalCast(atomData[4], ok); if (!ok) { appendError("Failed to parse x coordinate: " + atomData[3]); return false; } pos.y() = lexicalCast(atomData[5], ok); if (!ok) { appendError("Failed to parse y coordinate: " + atomData[4]); return false; } pos.z() = lexicalCast(atomData[6], ok); if (!ok) { appendError("Failed to parse z coordinate: " + atomData[5]); return false; } newAtom.setPosition3d(pos); // check for formal charge in the atom block // CHG=1 for example if (atomData.size() > 8) { // loop through the key=value pairs for (size_t j = 8; j < atomData.size(); ++j) { string key = atomData[j]; if (startsWith(key, "CHG=")) { int charge = lexicalCast(key.substr(4), ok); if (!ok) { appendError("Failed to parse atom charge: " + key); return false; } newAtom.setFormalCharge(charge); } else if (startsWith(key, "RAD=")) { // radical center int radical = lexicalCast(key.substr(4), ok); if (!ok) { appendError("Failed to parse radical type: " + key); return false; } // we don't set radical centers, just count them // for the total spin multiplicity if (radical == 2) // doublet spinMultiplicity += 1; else if (radical == 3) // triplet spinMultiplicity += 2; } else if (startsWith(key, "ISO=")) { // isotope [[maybe_unused]] int isotope = lexicalCast(key.substr(4), ok); if (!ok) { appendError("Failed to parse isotope type: " + key); return false; } // TODO: handle isotopes // mol.atom(i).setIsotope(isotope); } } // end of key-value loop } } // end of atom block getline(in, buffer); // check for END ATOM if (trimmed(buffer) != "M V30 END ATOM") { appendError("Error parsing V3000 atom block."); return false; } // bond block // 'M V30 BEGIN BOND' // 'M V30 1 1 1 2' getline(in, buffer); if (trimmed(buffer) != "M V30 BEGIN BOND") { appendError("Error parsing V3000 bond block."); return false; } for (int i = 0; i < numBonds; ++i) { getline(in, buffer); std::vector bondData = split(trimmed(buffer), ' '); if (bondData.size() < 5) { appendError("Error parsing V3000 bond line."); return false; } int order = lexicalCast(bondData[3], ok); if (!ok) { appendError("Failed to parse bond order: " + bondData[3]); return false; } int atom1 = lexicalCast(bondData[4], ok) - 1; if (!ok) { appendError("Failed to parse bond atom1: " + bondData[4]); return false; } int atom2 = lexicalCast(bondData[5], ok) - 1; if (!ok) { appendError("Failed to parse bond atom2: " + bondData[5]); return false; } mol.addBond(mol.atom(atom1), mol.atom(atom2), static_cast(order)); } // end of bond block // look for M END while (getline(in, buffer)) { if (trimmed(buffer) == "M END") break; } // read in any properties while (getline(in, buffer)) { if (startsWith(buffer, "> <")) { string key = trimmed(buffer.substr(3, buffer.length() - 4)); string value; while (getline(in, buffer)) { if (trimmed(buffer) == "") break; value += buffer + "\n"; } mol.setData(key, value); } } // if we have a spin multiplicity, set it if (spinMultiplicity > 1) mol.setData("totalSpinMultiplicity", spinMultiplicity); return true; } bool MdlFormat::writeV3000(std::ostream& out, const Core::Molecule& mol) { // write the "fake" counts line out << " 0 0 0 0 0 999 V3000\n"; out << "M V30 BEGIN CTAB\n"; out << "M V30 COUNTS " << mol.atomCount() << ' ' << mol.bondCount() << " 0 0 0\n"; // atom block out << "M V30 BEGIN ATOM\n"; for (size_t i = 0; i < mol.atomCount(); ++i) { Atom atom = mol.atom(i); out << "M V30 " << i + 1 << ' ' << Elements::symbol(atom.atomicNumber()) << ' ' << atom.position3d().x() << ' ' << atom.position3d().y() << ' ' << atom.position3d().z() << " 0"; if (atom.formalCharge()) out << " CHG=" << atom.formalCharge(); out << "\n"; } out << "M V30 END ATOM\n"; // bond block out << "M V30 BEGIN BOND\n"; for (size_t i = 0; i < mol.bondCount(); ++i) { Bond bond = mol.bond(i); out << "M V30 " << i + 1 << ' ' << static_cast(bond.order()) << ' ' << (bond.atom1().index() + 1) << ' ' << (bond.atom2().index() + 1) << " \n"; } out << "M V30 END BOND\n"; out << "M V30 END CTAB\n"; out << "M END\n"; // TODO: isotopes, radicals, etc. if (m_writeProperties) { const auto dataMap = mol.dataMap(); for (const auto& key : dataMap.names()) { // skip some keys if (key == "modelView" || key == "projection") continue; out << "> <" << key << ">\n"; out << dataMap.value(key).toString() << "\n"; out << "\n"; // empty line between data blocks } } if (m_writeProperties || isMode(FileFormat::MultiMolecule)) out << "$$$$\n"; return true; } bool MdlFormat::write(std::ostream& out, const Core::Molecule& mol) { // Header lines. out << mol.data("name").toString() << "\n"; // Mol block header - need to indicate 3D coords // e.g. Avogadro08072512293D // name of program MMDDYYHM3D auto now = std::chrono::system_clock::now(); // Convert to time_t for use with ctime functions std::time_t time_t_now = std::chrono::system_clock::to_time_t(now); // Convert to a local time structure (tm) std::tm* local_time = std::localtime(&time_t_now); // Format the time using std::put_time // %m: Month as decimal number (01-12) // %d: Day of the month as decimal number (01-31) // %y: Year without century (00-99) // %H: Hour in 24-hour format (00-23) // %M: Minute as decimal number (00-59) out << " Avogadro" << std::put_time(local_time, "%m%d%y%H%M") << "3D\n\n"; // Counts line. if (mol.atomCount() > 999 || mol.bondCount() > 999) { // we need V3000 support for big molecules return writeV3000(out, mol); } out << setw(3) << std::right << mol.atomCount() << setw(3) << mol.bondCount() << " 0 0 0 0 0 0 0 0999 V2000\n"; // Atom block. std::vector chargeList; for (size_t i = 0; i < mol.atomCount(); ++i) { Atom atom = mol.atom(i); signed int charge = atom.formalCharge(); if (charge) chargeList.emplace_back(atom.index(), charge); unsigned int chargeField = (charge < 0) ? ((charge >= -3) ? 4 - charge : 0) : ((charge <= 3) ? charge : 0); out << setw(10) << std::right << std::fixed << setprecision(4) << atom.position3d().x() << setw(10) << atom.position3d().y() << setw(10) << atom.position3d().z() << ' ' << setw(3) << std::left << Elements::symbol(atom.atomicNumber()) << " 0" << setw(3) << std::right << chargeField /* for compatibility */ << " 0 0 0 0 0 0 0 0 0 0\n"; } // Bond block. for (size_t i = 0; i < mol.bondCount(); ++i) { Bond bond = mol.bond(i); out.unsetf(std::ios::floatfield); out << setw(3) << std::right << bond.atom1().index() + 1 << setw(3) << bond.atom2().index() + 1 << setw(3) << static_cast(bond.order()) << " 0 0 0 0\n"; } // Properties block. for (auto& i : chargeList) { Index atomIndex = i.first; signed int atomCharge = i.second; out << "M CHG 1 " << setw(3) << std::right << atomIndex + 1 << ' ' << setw(3) << atomCharge << "\n"; } // TODO: isotopes, etc. out << "M END\n"; // Data block if (m_writeProperties) { const auto dataMap = mol.dataMap(); for (const auto& key : dataMap.names()) { // skip some keys if (key == "modelView" || key == "projection") continue; out << "> <" << key << ">\n"; out << dataMap.value(key).toString() << "\n"; out << "\n"; // empty line between data blocks } } if (m_writeProperties || isMode(FileFormat::MultiMolecule)) out << "$$$$\n"; return true; } std::vector MdlFormat::fileExtensions() const { std::vector ext; ext.emplace_back("mol"); return ext; } std::vector MdlFormat::mimeTypes() const { std::vector mime; mime.emplace_back("chemical/x-mdl-molfile"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/mdlformat.h000066400000000000000000000037651506155467400207250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_MDLFORMAT_H #define AVOGADRO_IO_MDLFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class MdlFormat mdlformat.h * @brief Implementation of the generic MDL format. * @author Marcus D. Hanwell * * Currently just supports V2000 of the format. */ class AVOGADROIO_EXPORT MdlFormat : public FileFormat { public: MdlFormat() = default; ~MdlFormat() override = default; Operations supportedOperations() const override { return ReadWrite | MultiMolecule | File | Stream | String; } FileFormat* newInstance() const override { return new MdlFormat; } std::string identifier() const override { return "Avogadro: MDL"; } std::string name() const override { return "MDL"; } std::string description() const override { return "Generic format that contains atoms, bonds, positions."; } std::string specificationUrl() const override { return "http://help.accelrysonline.com/ulm/onelab/1.0/content/ulm_pdfs/" "direct/" "reference/ctfileformats2016.pdf"; /* for previous (2011) version, see: https://web.archive.org/web/20180329184712/http://download.accelrys.com/freeware/ctfile-formats/ctfile-formats.zip */ } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool readV3000(std::istream& in, Core::Molecule& molecule); bool write(std::ostream& out, const Core::Molecule& molecule) override; bool writeV3000(std::ostream& out, const Core::Molecule& molecule); protected: bool m_writeProperties = false; }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_MDLFORMAT_H avogadrolibs-1.101.0/avogadro/io/mmtfformat.cpp000066400000000000000000000203511506155467400214350ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "mmtfformat.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::Io { using std::string; using std::vector; using Core::Array; using Core::Elements; using Core::lexicalCast; using Core::Molecule; namespace { // from latest MMTF code, under the MIT license // https://github.com/rcsb/mmtf-cpp/blob/master/include/mmtf/structure_data.hpp #if MMTF_SPEC_VERSION_MAJOR <= 1 && MMTF_SPEC_VERSION_MINOR < 1 bool is_polymer(const unsigned int chain_index, const std::vector& entity_list) { for (const auto& i : entity_list) { if (std::find(i.chainIndexList.begin(), i.chainIndexList.end(), chain_index) != i.chainIndexList.end()) { return (i.type == "polymer" || i.type == "POLYMER"); } } return false; } #endif } // namespace bool MMTFFormat::read(std::istream& file, Molecule& molecule) { mmtf::StructureData structure; try { mmtf::decodeFromStream(structure, file); } catch (...) { // if decoding failed, the file is broken return false; } // This controls which model we load, currently just the first? size_t modelIndex = 0; size_t atomSkip = 0; size_t chainIndex = 0; size_t groupIndex = 0; size_t atomIndex = 0; molecule.setData("name", structure.title); if (structure.unitCell.size() == 6) { Real a = static_cast(structure.unitCell[0]); Real b = static_cast(structure.unitCell[1]); Real c = static_cast(structure.unitCell[2]); Real alpha = static_cast(structure.unitCell[3]) * DEG_TO_RAD; Real beta = static_cast(structure.unitCell[4]) * DEG_TO_RAD; Real gamma = static_cast(structure.unitCell[5]) * DEG_TO_RAD; auto* unitCellObject = new Core::UnitCell(a, b, c, alpha, beta, gamma); if (!unitCellObject->isRegular()) { appendError("cell matrix is singular"); delete unitCellObject; return false; } molecule.setUnitCell(unitCellObject); } // spaceGroup if (structure.spaceGroup.size() > 0) { unsigned short hall = 0; hall = Core::SpaceGroups::hallNumber(structure.spaceGroup); if (hall != 0) { molecule.setHallNumber(hall); } } auto modelChainCount = static_cast(structure.chainsPerModel[modelIndex]); auto entityList = structure.entityList; auto secStructList = structure.secStructList; Array rawToAtomId; Array altAtomIds; Array altAtomCoordSets; Array altAtomLocs; std::set altLocs; Array altAtomPositions; for (Index j = 0; j < modelChainCount; j++) { auto chainGroupCount = static_cast(structure.groupsPerChain[chainIndex]); bool ok; std::string chainid_string = structure.chainIdList[chainIndex]; char chainid = lexicalCast(chainid_string.substr(0, 1), ok); bool isPolymer = is_polymer(chainIndex, entityList); // A group is like a residue or other molecule in a PDB file. for (size_t k = 0; k < chainGroupCount; k++) { auto groupType = static_cast(structure.groupTypeList[groupIndex]); const auto& group = structure.groupList[groupType]; auto groupId = static_cast(structure.groupIdList[groupIndex]); auto resname = group.groupName; auto& residue = molecule.addResidue(resname, groupId, chainid); // Stores if the group / residue is a heterogen // if (!isPolymer || mmtf::is_hetatm(group.chemCompType.c_str())) residue.setHeterogen(true); // Unfortunately, while the spec says secondary structure // is (optionally) in groups, the code doesn't make it available. // group.secStruct is a binary type // https://github.com/rcsb/mmtf/blob/master/spec.md#secstructlist // 0 = pi helix, 1 = bend, 2 = alpha helix, 3 = extended beta, 4 = 3-10 // helix, etc. // residue.setSecondaryStructure(group.secStruct); // // instead, we'll get it from secStructList auto secStructure = structure.secStructList[groupIndex]; residue.setSecondaryStructure( static_cast(secStructure)); // Save the offset before we go changing it Index atomOffset = atomIndex - atomSkip; Index groupSize = group.atomNameList.size(); for (Index l = 0; l < groupSize; l++) { Vector3 pos(static_cast(structure.xCoordList[atomIndex]), static_cast(structure.yCoordList[atomIndex]), static_cast(structure.zCoordList[atomIndex])); if (structure.altLocList[atomIndex] != '\0' && structure.altLocList[atomIndex] != 'A') { rawToAtomId.push_back(-1); altAtomIds.push_back(molecule.atomCount() - 1); altAtomLocs.push_back(structure.altLocList[atomIndex]); altLocs.insert(structure.altLocList[atomIndex]); altAtomPositions.push_back(pos); atomIndex++; continue; } auto atom = molecule.addAtom( Elements::atomicNumberFromSymbol(group.elementList[l])); atom.setFormalCharge(group.formalChargeList[l]); atom.setPosition3d(pos); std::string atomName = group.atomNameList[l]; residue.addResidueAtom(atomName, atom); rawToAtomId.push_back(molecule.atomCount() - 1); atomIndex++; } // Intra-residue bonds for (size_t l = 0; l < group.bondOrderList.size(); l++) { auto atom1 = static_cast( rawToAtomId[atomOffset + group.bondAtomList[l * 2]]); auto atom2 = static_cast( rawToAtomId[atomOffset + group.bondAtomList[l * 2 + 1]]); char bo = static_cast(group.bondOrderList[l]); if (atom1 < molecule.atomCount() && atom2 < molecule.atomCount()) molecule.addBond(atom1, atom2, bo); } // This is the original PDB Chain name // if (!structure_.chainNameList.empty()) { // structure.chainNameList[chainIndex_]; //} groupIndex++; } chainIndex++; } // Use this eventually for multi-model formats modelIndex++; // These are for inter-residue bonds for (size_t i = 0; i < structure.bondAtomList.size() / 2; i++) { auto atom1 = static_cast(rawToAtomId[structure.bondAtomList[i * 2]]); auto atom2 = static_cast(rawToAtomId[structure.bondAtomList[i * 2 + 1]]); /* Code for multiple models // We are below the atoms we care about if (atom1 < atomSkip || atom2 < atomSkip) { continue; } // We are above the atoms we care about if (atom1 > atomIndex || atom2 > atomIndex) { continue; } */ if (atom1 < molecule.atomCount() && atom2 < molecule.atomCount()) molecule.addBond(atom1, atom2, 1); // Always a single bond } for (char l : altLocs) { Array coordinateSet = molecule.atomPositions3d(); bool found = false; for (size_t i = 0; i < altAtomLocs.size(); i++) { if (altAtomLocs[i] == l) { found = true; coordinateSet[altAtomIds[i]] = altAtomPositions[i]; } } if (found) { molecule.setCoordinate3d(coordinateSet, molecule.coordinate3dCount() ? molecule.coordinate3dCount() : 1); } } return true; } bool MMTFFormat::write(std::ostream&, const Core::Molecule&) { return false; } vector MMTFFormat::fileExtensions() const { vector ext; ext.emplace_back("mmtf"); return ext; } vector MMTFFormat::mimeTypes() const { vector mime; mime.emplace_back("chemical/x-mmtf"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/mmtfformat.h000066400000000000000000000031651506155467400211060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_MMTFFORMAT_H #define AVOGADRO_IO_MMTFFORMAT_H #include "fileformat.h" namespace Avogadro { namespace Core { class GaussianSet; } namespace Io { /** * @class MMTFFormat mmtfformat.h * @brief Implementation of the MMTF format. */ class AVOGADROIO_EXPORT MMTFFormat : public FileFormat { public: MMTFFormat() = default; ~MMTFFormat() override = default; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new MMTFFormat; } std::string identifier() const override { return "Avogadro: MMTF"; } std::string name() const override { return "MacroMolecular Transmission Format"; } std::string description() const override { return "MMTF is a format used to express MacroMolecular data in a " "compressed binary format."; } std::string specificationUrl() const override { return "http://mmtf.rcsb.org/"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; // unimplemented bool write(std::ostream& out, const Core::Molecule& molecule) override; }; } // namespace Io } // namespace Avogadro #endif // AVOGADRO_IO_MMTFFORMAT_H avogadrolibs-1.101.0/avogadro/io/pdbformat.cpp000066400000000000000000000253211506155467400212410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "pdbformat.h" #include "avogadro/core/avogadrocore.h" #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Core::Array; using Avogadro::Core::Atom; using Avogadro::Core::Elements; using Avogadro::Core::lexicalCast; using Avogadro::Core::Residue; using Avogadro::Core::SecondaryStructureAssigner; using Avogadro::Core::startsWith; using Avogadro::Core::trimmed; using std::getline; using std::istringstream; using std::string; namespace Avogadro::Io { bool PdbFormat::read(std::istream& in, Core::Molecule& mol) { string buffer; std::vector terList; Residue* r = nullptr; size_t currentResidueId = 0; bool ok(false); int coordSet = 0; Array positions; Array rawToAtomId; Array altAtomIds; Array altAtomCoordSets; Array altAtomLocs; std::set altLocs; Array altAtomPositions; while (getline(in, buffer)) { // Read Each line one by one if (!in.good()) break; if (startsWith(buffer, "ENDMDL")) { if (coordSet == 0) { mol.setCoordinate3d(mol.atomPositions3d(), coordSet++); positions.reserve(mol.atomCount()); } else { mol.setCoordinate3d(positions, coordSet++); positions.clear(); } } // e.g. CRYST1 4.912 4.912 6.696 90.00 90.00 120.00 P1 1 // https://www.wwpdb.org/documentation/file-format-content/format33/sect8.html else if (startsWith(buffer, "CRYST1") && buffer.length() >= 55) { // PDB reports in degrees and Angstroms // Avogadro uses radians internally Real a = lexicalCast(buffer.substr(6, 9), ok); Real b = lexicalCast(buffer.substr(15, 9), ok); Real c = lexicalCast(buffer.substr(24, 9), ok); Real alpha = lexicalCast(buffer.substr(33, 7), ok) * DEG_TO_RAD; Real beta = lexicalCast(buffer.substr(40, 7), ok) * DEG_TO_RAD; Real gamma = lexicalCast(buffer.substr(47, 8), ok) * DEG_TO_RAD; auto* cell = new Core::UnitCell(a, b, c, alpha, beta, gamma); if (!cell->isRegular()) { appendError("CRYST1 does not give linear independent lattice vectors"); delete cell; return false; } mol.setUnitCell(cell); } else if (startsWith(buffer, "ATOM") || startsWith(buffer, "HETATM")) { if (buffer.length() < 54) { appendError("Error reading line."); return false; } // First we initialize the residue instance auto residueId = lexicalCast(buffer.substr(22, 4), ok); if (!ok) { appendError("Failed to parse residue sequence number: " + buffer.substr(22, 4)); return false; } if (residueId != currentResidueId) { currentResidueId = residueId; auto residueName = lexicalCast(buffer.substr(17, 3), ok); if (!ok) { appendError("Failed to parse residue name: " + buffer.substr(17, 3)); return false; } char chainId = lexicalCast(buffer.substr(21, 1), ok); if (!ok) { chainId = 'A'; // it's a non-standard "PDB"-like file } r = &mol.addResidue(residueName, currentResidueId, chainId); if (startsWith(buffer, "HETATM")) r->setHeterogen(true); } auto atomName = lexicalCast(buffer.substr(12, 4), ok); if (!ok) { appendError("Failed to parse atom name: " + buffer.substr(12, 4)); return false; } Vector3 pos; // Coordinates pos.x() = lexicalCast(buffer.substr(30, 8), ok); if (!ok) { appendError("Failed to parse x coordinate: " + buffer.substr(30, 8)); return false; } pos.y() = lexicalCast(buffer.substr(38, 8), ok); if (!ok) { appendError("Failed to parse y coordinate: " + buffer.substr(38, 8)); return false; } pos.z() = lexicalCast(buffer.substr(46, 8), ok); if (!ok) { appendError("Failed to parse z coordinate: " + buffer.substr(46, 8)); return false; } auto altLoc = lexicalCast(buffer.substr(16, 1), ok); string element; // Element symbol, right justified unsigned char atomicNum = 255; if (buffer.size() >= 78) { element = buffer.substr(76, 2); element = trimmed(element); if (element.length() == 2) element[1] = std::tolower(element[1]); atomicNum = Elements::atomicNumberFromSymbol(element); if (atomicNum == 255) appendError("Invalid element"); } if (atomicNum == 255) { // non-standard or old-school PDB file - try to parse the atom name element = trimmed(atomName); // remove any trailing digits while (element.size() && std::isdigit(element.back())) element.pop_back(); if (element == "SE") // For Sulphur element = 'S'; atomicNum = Elements::atomicNumberFromSymbol(element); if (atomicNum == 255) { appendError("Invalid element"); continue; // skip this invalid record } } if (altLoc.compare("") && altLoc.compare("A")) { if (coordSet == 0) { rawToAtomId.push_back(-1); altAtomIds.push_back(mol.atomCount() - 1); } else { altAtomIds.push_back(positions.size() - 1); } altAtomCoordSets.push_back(coordSet); altAtomLocs.push_back(altLoc[0]); altLocs.insert(altLoc[0]); altAtomPositions.push_back(pos); } else if (coordSet == 0) { Atom newAtom = mol.addAtom(atomicNum); newAtom.setPosition3d(pos); if (r != nullptr) { r->addResidueAtom(atomName, newAtom); } rawToAtomId.push_back(mol.atomCount() - 1); } else { positions.push_back(pos); } } else if (startsWith(buffer, "TER") && buffer.length() >= 11) { // This is very important, each TER // record also counts in the serial. // Need to account for that when comparing with CONECT terList.push_back(lexicalCast(buffer.substr(6, 5), ok)); if (!ok) { appendError("Failed to parse TER serial"); return false; } } else if (startsWith(buffer, "CONECT")) { if (buffer.length() < 16) { appendError("Error reading line."); return false; } int a = lexicalCast(buffer.substr(6, 5), ok); if (!ok) { appendError("Failed to parse bond connection a " + buffer.substr(6, 5)); return false; } --a; size_t terCount; for (terCount = 0; terCount < terList.size() && a > terList[terCount]; ++terCount) ; // semicolon is intentional a = a - terCount; a = rawToAtomId[a]; int bCoords[] = { 11, 16, 21, 26 }; for (int i = 0; i < 4; i++) { if (trimmed(buffer.substr(bCoords[i], 5)) == "") break; else { int b = lexicalCast(buffer.substr(bCoords[i], 5), ok) - 1; if (!ok) { appendError("Failed to parse bond connection b" + std::to_string(i) + " " + buffer.substr(bCoords[i], 5)); // return false; continue; // skip this invalid record } for (terCount = 0; terCount < terList.size() && b > terList[terCount]; ++terCount) ; // semicolon is intentional b = b - terCount; b = rawToAtomId[b]; if (a >= 0 && b >= 0) { auto aIndex = static_cast(a); auto bIndex = static_cast(b); if (aIndex < mol.atomCount() && bIndex < mol.atomCount()) { mol.Avogadro::Core::Molecule::addBond(aIndex, bIndex, 1); } else { appendError("Invalid bond connection: " + std::to_string(a) + " - " + std::to_string(b)); } } } } } } // End while loop if (mol.atomCount() == 0) { appendError("No atoms found in this file."); return false; } int count = mol.coordinate3dCount() ? mol.coordinate3dCount() : 1; for (int c = 0; c < count; ++c) { for (char l : altLocs) { Array coordinateSet = c == 0 ? mol.atomPositions3d() : mol.coordinate3d(c); bool found = false; for (size_t i = 0; i < altAtomCoordSets.size(); ++i) { if (altAtomCoordSets[i] == c && altAtomLocs[i] == l) { found = true; coordinateSet[altAtomIds[i]] = altAtomPositions[i]; } } if (found) mol.setCoordinate3d( coordinateSet, mol.coordinate3dCount() ? mol.coordinate3dCount() : 1); } } mol.perceiveBondsSimple(); mol.perceiveBondsFromResidueData(); perceiveSubstitutedCations(mol); // if there are residue data, assign secondary structure if (mol.residueCount() != 0) { SecondaryStructureAssigner ssa; ssa.assign(&mol); } return true; } // End read std::vector PdbFormat::fileExtensions() const { std::vector ext; ext.emplace_back("pdb"); ext.emplace_back("ent"); return ext; } std::vector PdbFormat::mimeTypes() const { std::vector mime; mime.emplace_back("chemical/x-pdb"); return mime; } void PdbFormat::perceiveSubstitutedCations(Core::Molecule& molecule) { for (Index i = 0; i < molecule.atomCount(); i++) { unsigned char requiredBondCount(0); switch (molecule.atomicNumber(i)) { case 7: case 15: case 33: case 51: requiredBondCount = 4; break; case 8: case 16: case 34: case 52: requiredBondCount = 3; } if (!requiredBondCount) continue; unsigned char bondCount(0); Index j = 0; for (const auto& bond : molecule.bonds(i)) { unsigned char otherAtomicNumber(0); otherAtomicNumber = molecule.atomicNumber(bond.getOtherAtom(i).index()); bondCount += bond.order(); if (otherAtomicNumber && otherAtomicNumber != 6) { bondCount = 0; break; } j++; } if (bondCount == requiredBondCount) { molecule.setFormalCharge(i, 1); } } } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/pdbformat.h000066400000000000000000000032761506155467400207130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_PDBFORMAT_H #define AVOGADRO_IO_PDBFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class PdbFormat pdbformat.h * @brief Parser for the PDB format. * @author Tanuj Kumar */ class AVOGADROIO_EXPORT PdbFormat : public FileFormat { public: PdbFormat() = default; ~PdbFormat() override = default; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new PdbFormat; } std::string identifier() const override { return "Avogadro: PDB"; } std::string name() const override { return "PDB"; } std::string description() const override { return "Format that contains atoms, bonds, positions and secondary" "structures of proteins."; } std::string specificationUrl() const override { return "http://www.wwpdb.org/documentation/file-format-content/" "format33/v3.3.html"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Writing a PDB file is not currently supported return false; } void perceiveSubstitutedCations(Core::Molecule& molecule); }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_PDBFORMAT_H avogadrolibs-1.101.0/avogadro/io/sdfformat.cpp000066400000000000000000000017451506155467400212540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "sdfformat.h" namespace Avogadro::Io { SdfFormat::SdfFormat() : MdlFormat() { m_writeProperties = true; } SdfFormat::~SdfFormat() {} bool SdfFormat::read(std::istream& in, Core::Molecule& mol) { return MdlFormat::read(in, mol); } bool SdfFormat::write(std::ostream& out, const Core::Molecule& mol) { return MdlFormat::write(out, mol); } std::vector SdfFormat::fileExtensions() const { std::vector ext; ext.emplace_back("sdf"); ext.emplace_back("sd3"); return ext; } std::vector SdfFormat::mimeTypes() const { std::vector mime; mime.emplace_back("chemical/x-mdl-molfile"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/sdfformat.h000066400000000000000000000036451506155467400207220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_SDFFORMAT_H #define AVOGADRO_IO_SDFFORMAT_H #include "fileformat.h" #include "mdlformat.h" namespace Avogadro { namespace Io { /** * @class SdfFormat sdfformat.h * @brief Implementation of the generic SDF format. * @author Marcus D. Hanwell * * Differs from the MDL / Mol format in that it includes properties * * Currently just supports V2000 of the format. */ class AVOGADROIO_EXPORT SdfFormat : public MdlFormat { public: SdfFormat(); ~SdfFormat() override; Operations supportedOperations() const override { return ReadWrite | MultiMolecule | File | Stream | String; } FileFormat* newInstance() const override { return new SdfFormat; } std::string identifier() const override { return "Avogadro: SDF"; } std::string name() const override { return "SDF"; } std::string description() const override { return "Generic format that contains atoms, bonds, positions."; } std::string specificationUrl() const override { return "http://help.accelrysonline.com/ulm/onelab/1.0/content/ulm_pdfs/" "direct/" "reference/ctfileformats2016.pdf"; /* for previous (2011) version, see: https://web.archive.org/web/20180329184712/http://download.accelrys.com/freeware/ctfile-formats/ctfile-formats.zip */ } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; }; } // namespace Io } // namespace Avogadro #endif // AVOGADRO_IO_MDLFORMAT_H avogadrolibs-1.101.0/avogadro/io/trrformat.cpp000066400000000000000000000366431506155467400213140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "trrformat.h" #include "struct.h" #include #include #include #include #include #include #include #include using std::map; using std::pair; using std::string; using std::to_string; namespace Avogadro::Io { using Core::Array; using Core::Atom; using Core::Molecule; using Core::UnitCell; constexpr int GROMACS_MAGIC = 1993; constexpr int DIM = 3; constexpr float NM_TO_ANGSTROM = 10.0; string TRRVERSION = "GMX_trn_file"; string HEADITEMS[] = { "ir_size", "e_size", "box_size", "vir_size", "pres_size", "top_size", "sym_size", "x_size", "v_size", "f_size", "natoms", "step", "nre", "time", "lambda" }; int swapInteger(int inp) { return (((inp << 24) & 0xff000000) | ((inp << 8) & 0x00ff0000) | ((inp >> 8) & 0x0000ff00) | ((inp >> 24) & 0x000000ff)); } char swapEndian(char endian) { if (endian == '>') return '<'; else return '>'; } /* Checks whether the data stored in the binary file is of float or double type */ int isDouble(map& header) { int SIZE_DOUBLE = struct_calcsize("d"); int size = 0; string headerKeys[] = { "box_size", "x_size", "v_size", "f_size" }; for (auto& headerKey : headerKeys) { if (header[headerKey] != 0) { if (headerKey == "box_size") { size = (int)(header[headerKey] / DIM * DIM); break; } else { size = (int)(header[headerKey] / (header["natoms"] * DIM)); break; } } } return size == SIZE_DOUBLE; } bool TrrFormat::read(std::istream& inStream, Core::Molecule& mol) { bool doubleStatus; char endian = '>', buff[BUFSIZ], fmt[BUFSIZ], raw[1000]; int magic, natoms, slen0, slen1, headval[13]; string subs, keyCheck[] = { "box_size", "vir_size", "pres_size" }, keyCheck2[] = { "x_size", "v_size", "f_size" }; map header; // Determining size of file inStream.seekg(0, inStream.end); int fileLen = inStream.tellg(); inStream.seekg(0, inStream.beg); // Binary file must start with 1993 snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &magic); if (magic != GROMACS_MAGIC) { // Endian conversion magic = swapInteger(magic); endian = swapEndian(endian); if (magic != GROMACS_MAGIC) { appendError("Frame does not start with magic number 1993."); return false; } } snprintf(fmt, sizeof(fmt), "%c2i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &slen0, &slen1); // Reading trajectory version string snprintf(fmt, sizeof(fmt), "%c%ds", endian, slen0 - 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, raw); subs = string(raw).substr(0, 12); if (subs != TRRVERSION) { appendError("Gromacs version string mismatch."); return false; } // "ir_size", "e_size", "box_size", "vir_size", "pres_size", // "top_size", "sym_size", "x_size", "v_size", "f_size", // "natoms", "step", "nre" snprintf(fmt, sizeof(fmt), "%c13i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &headval[0], &headval[1], &headval[2], &headval[3], &headval[4], &headval[5], &headval[6], &headval[7], &headval[8], &headval[9], &headval[10], &headval[11], &headval[12]); for (int i = 0; i < 13; ++i) { header.insert(pair(HEADITEMS[i], headval[i])); } // Reading timestep and lambda doubleStatus = isDouble(header); if (doubleStatus) { double header0, header1; snprintf(fmt, sizeof(fmt), "%c2d", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &header0, &header1); header.insert(pair("time", header0)); header.insert(pair("lambda", header1)); } else { float header0, header1; snprintf(fmt, sizeof(fmt), "%c2f", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &header0, &header1); header.insert(pair("time", header0)); header.insert(pair("lambda", header1)); } // Reading matrices corresponding to "box_size", "vir_size", "pres_size" for (auto& _kid : keyCheck) { if (header[_kid] != 0) { if (doubleStatus) { snprintf(fmt, sizeof(fmt), "%c%dd", endian, DIM * DIM); double mat[DIM][DIM]; inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &mat[0][0], &mat[0][1], &mat[0][2], &mat[1][0], &mat[1][1], &mat[1][2], &mat[2][0], &mat[2][1], &mat[2][2]); if (_kid == "box_size") { auto* uc = new UnitCell( Vector3(mat[0][0] * NM_TO_ANGSTROM, mat[0][1] * NM_TO_ANGSTROM, mat[0][2] * NM_TO_ANGSTROM), Vector3(mat[1][0] * NM_TO_ANGSTROM, mat[1][1] * NM_TO_ANGSTROM, mat[1][2] * NM_TO_ANGSTROM), Vector3(mat[2][0] * NM_TO_ANGSTROM, mat[2][1] * NM_TO_ANGSTROM, mat[2][2] * NM_TO_ANGSTROM)); if (!uc->isRegular()) { appendError("lattice vectors are not linear independent"); delete uc; return false; } mol.setUnitCell(uc); } } else { snprintf(fmt, sizeof(fmt), "%c%df", endian, DIM * DIM); float mat[DIM][DIM]; inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &mat[0][0], &mat[0][1], &mat[0][2], &mat[1][0], &mat[1][1], &mat[1][2], &mat[2][0], &mat[2][1], &mat[2][2]); if (_kid == "box_size") { auto* uc = new UnitCell( Vector3(mat[0][0] * NM_TO_ANGSTROM, mat[0][1] * NM_TO_ANGSTROM, mat[0][2] * NM_TO_ANGSTROM), Vector3(mat[1][0] * NM_TO_ANGSTROM, mat[1][1] * NM_TO_ANGSTROM, mat[1][2] * NM_TO_ANGSTROM), Vector3(mat[2][0] * NM_TO_ANGSTROM, mat[2][1] * NM_TO_ANGSTROM, mat[2][2] * NM_TO_ANGSTROM)); if (!uc->isRegular()) { appendError("lattice vectors are not linear independent"); return false; } mol.setUnitCell(uc); } } } } using AtomTypeMap = map; AtomTypeMap atomTypes; unsigned char customElementCounter = CustomElementMin; // Reading the coordinates of positions, velocities and forces for (auto& _kid : keyCheck2) { natoms = header["natoms"]; double coordsDouble[DIM]; float coordsFloat[DIM]; for (int i = 0; i < natoms; ++i) { if (header[_kid] != 0) { memset(coordsDouble, 0, sizeof(coordsDouble)); memset(coordsFloat, 0, sizeof(coordsFloat)); if (doubleStatus) { snprintf(fmt, sizeof(fmt), "%c%dd", endian, DIM); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &coordsDouble[0], &coordsDouble[1], &coordsDouble[2]); } else { snprintf(fmt, sizeof(fmt), "%c%df", endian, DIM); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &coordsFloat[0], &coordsFloat[1], &coordsFloat[2]); } if (_kid == "x_size") { // If parsed coordinates are fractional, the corresponding unscaling // is done. Else the positions are assigned as parsed. Vector3 pos( coordsDouble[0] * NM_TO_ANGSTROM + coordsFloat[0] * NM_TO_ANGSTROM, coordsDouble[1] * NM_TO_ANGSTROM + coordsFloat[1] * NM_TO_ANGSTROM, coordsDouble[2] * NM_TO_ANGSTROM + coordsFloat[2] * NM_TO_ANGSTROM); AtomTypeMap::const_iterator it; // if (it == atomTypes.end()) { atomTypes.insert( std::make_pair(to_string(i), customElementCounter++)); it = atomTypes.find(to_string(i)); // if (customElementCounter > CustomElementMax) { // appendError("Custom element type limit exceeded."); // return false; // } Atom newAtom = mol.addAtom(it->second); newAtom.setPosition3d(pos); } } } // Set the custom element map if needed if (!atomTypes.empty()) { Molecule::CustomElementMap elementMap; for (const auto& atomType : atomTypes) { elementMap.insert( std::make_pair(atomType.second, "Atom " + atomType.first)); } mol.setCustomElementMap(elementMap); } } mol.setCoordinate3d(mol.atomPositions3d(), 0); // Do we have an animation? // EOF check int coordSet = 1; while (static_cast(inStream.tellg()) != fileLen) { // Binary header must start with 1993 snprintf(fmt, sizeof(fmt), "%c1i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &magic); if (magic != GROMACS_MAGIC) { // Endian conversion magic = swapInteger(magic); endian = swapEndian(endian); if (magic != GROMACS_MAGIC) { appendError("Frame does not start with magic number 1993."); return false; } } snprintf(fmt, sizeof(fmt), "%c2i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &slen0, &slen1); // Reading trajectory version string snprintf(fmt, sizeof(fmt), "%c%ds", endian, slen0 - 1); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, raw); subs = string(raw).substr(0, 12); if (subs != TRRVERSION) { appendError("Gromacs version string mismatch."); return false; } // "ir_size", "e_size", "box_size", "vir_size", "pres_size", // "top_size", "sym_size", "x_size", "v_size", "f_size", // "natoms", "step", "nre" snprintf(fmt, sizeof(fmt), "%c13i", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &headval[0], &headval[1], &headval[2], &headval[3], &headval[4], &headval[5], &headval[6], &headval[7], &headval[8], &headval[9], &headval[10], &headval[11], &headval[12]); for (int i = 0; i < 13; ++i) { header.insert(pair(HEADITEMS[i], headval[i])); } // Reading timestep and lambda doubleStatus = isDouble(header); if (doubleStatus) { double header0, header1; snprintf(fmt, sizeof(fmt), "%c2d", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &header0, &header1); header.insert(pair("time", header0)); header.insert(pair("lambda", header1)); } else { float header0, header1; snprintf(fmt, sizeof(fmt), "%c2f", endian); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &header0, &header1); header.insert(pair("time", header0)); header.insert(pair("lambda", header1)); } // Reading matrices corresponding to "box_size", "vir_size", "pres_size" for (auto& _kid : keyCheck) { if (header[_kid] != 0) { natoms = header["natoms"]; if (doubleStatus) { snprintf(fmt, sizeof(fmt), "%c%dd", endian, DIM * DIM); double mat[DIM][DIM]; inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &mat[0][0], &mat[0][1], &mat[0][2], &mat[1][0], &mat[1][1], &mat[1][2], &mat[2][0], &mat[2][1], &mat[2][2]); if (_kid == "box_size") { auto* uc = new UnitCell( Vector3(mat[0][0] * NM_TO_ANGSTROM, mat[0][1] * NM_TO_ANGSTROM, mat[0][2] * NM_TO_ANGSTROM), Vector3(mat[1][0] * NM_TO_ANGSTROM, mat[1][1] * NM_TO_ANGSTROM, mat[1][2] * NM_TO_ANGSTROM), Vector3(mat[2][0] * NM_TO_ANGSTROM, mat[2][1] * NM_TO_ANGSTROM, mat[2][2] * NM_TO_ANGSTROM)); if (!uc->isRegular()) { appendError("lattice vectors are not linear independent"); return false; } mol.setUnitCell(uc); } } else { snprintf(fmt, sizeof(fmt), "%c%df", endian, DIM * DIM); float mat[DIM][DIM]; inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &mat[0][0], &mat[0][1], &mat[0][2], &mat[1][0], &mat[1][1], &mat[1][2], &mat[2][0], &mat[2][1], &mat[2][2]); if (_kid == "box_size") { auto* uc = new UnitCell( Vector3(mat[0][0] * NM_TO_ANGSTROM, mat[0][1] * NM_TO_ANGSTROM, mat[0][2] * NM_TO_ANGSTROM), Vector3(mat[1][0] * NM_TO_ANGSTROM, mat[1][1] * NM_TO_ANGSTROM, mat[1][2] * NM_TO_ANGSTROM), Vector3(mat[2][0] * NM_TO_ANGSTROM, mat[2][1] * NM_TO_ANGSTROM, mat[2][2] * NM_TO_ANGSTROM)); if (!uc->isRegular()) { appendError("lattice vectors are not linear independent"); return false; } mol.setUnitCell(uc); } } } } natoms = header["natoms"]; Array positions; positions.reserve(natoms); // Reading the coordinates of positions, velocities and forces for (auto& _kid : keyCheck2) { double coordsDouble[DIM]; float coordsFloat[DIM]; for (int i = 0; i < natoms; ++i) { if (header[_kid] != 0) { memset(coordsDouble, 0, sizeof(coordsDouble)); memset(coordsFloat, 0, sizeof(coordsFloat)); if (doubleStatus) { snprintf(fmt, sizeof(fmt), "%c%dd", endian, DIM); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &coordsDouble[0], &coordsDouble[1], &coordsDouble[2]); } else { snprintf(fmt, sizeof(fmt), "%c%df", endian, DIM); inStream.read(buff, struct_calcsize(fmt)); struct_unpack(buff, fmt, &coordsFloat[0], &coordsFloat[1], &coordsFloat[2]); } if (_kid == "x_size") { // If parsed coordinates are fractional, the corresponding unscaling // is done. Else the positions are assigned as parsed. Vector3 pos(coordsDouble[0] * NM_TO_ANGSTROM + coordsFloat[0] * NM_TO_ANGSTROM, coordsDouble[1] * NM_TO_ANGSTROM + coordsFloat[1] * NM_TO_ANGSTROM, coordsDouble[2] * NM_TO_ANGSTROM + coordsFloat[2] * NM_TO_ANGSTROM); positions.push_back(pos); } } } } mol.setCoordinate3d(positions, coordSet++); positions.clear(); } return true; } bool TrrFormat::write(std::ostream&, const Core::Molecule&) { return false; } std::vector TrrFormat::fileExtensions() const { std::vector ext; ext.emplace_back("trr"); return ext; } std::vector TrrFormat::mimeTypes() const { std::vector mime; mime.emplace_back("application/octet-stream"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/trrformat.h000066400000000000000000000030541506155467400207470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_TRRFORMAT_H #define AVOGADRO_IO_TRRFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class TrrFormat trrformat.h * @brief Implementation of the generic trr trajectory format. * @author Adarsh B */ class AVOGADROIO_EXPORT TrrFormat : public FileFormat { public: TrrFormat() = default; ~TrrFormat() override = default; Operations supportedOperations() const override { return ReadWrite | MultiMolecule | File | Stream | String; } FileFormat* newInstance() const override { return new TrrFormat; } std::string identifier() const override { return "Avogadro: GROMACS TRR"; } std::string name() const override { return "TRR"; } std::string description() const override { return "Generic TRR Trajectory format."; } std::string specificationUrl() const override { return "http://manual.gromacs.org/current/online/trr.html"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& molecule) override; // uninplemented bool write(std::ostream& outStream, const Core::Molecule& molecule) override; }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_TRRFORMAT_H avogadrolibs-1.101.0/avogadro/io/turbomoleformat.cpp000066400000000000000000000173651506155467400225150ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "turbomoleformat.h" #include #include #include #include #include #include #include #include #include #include using json = nlohmann::json; using std::getline; using std::string; namespace Avogadro::Io { using Core::Atom; using Core::Elements; using Core::lexicalCast; using Core::split; #ifndef _WIN32 using std::isalpha; #endif bool TurbomoleFormat::read(std::istream& inStream, Core::Molecule& mol) { json opts; if (!options().empty()) opts = json::parse(options(), nullptr, false); else opts = json::object(); bool hasCell = false; bool hasLattice = false; bool fractionalCoords = false; // possible lattice constants Real a, b, c, alpha, beta, gamma; a = b = c = 100.0; alpha = beta = gamma = 90.0; // defaults if periodicity is not 3 Vector3 v1(100.0, 0.0, 0.0); Vector3 v2(0.0, 100.0, 0.0); Vector3 v3(0.0, 0.0, 100.0); // we loop through each line until we hit $end or EOF string buffer; getline(inStream, buffer); while (inStream.good() && !buffer.empty()) { if (buffer.find("$end") != std::string::npos) break; else if (buffer.find("$coord") != std::string::npos) { // check if there's a conversion to be done Real coordConversion = BOHR_TO_ANGSTROM; // default is Bohr if (buffer.find("ang") != std::string::npos) coordConversion = 1.0; // leave as Angstrom else if (buffer.find("frac") != std::string::npos) { fractionalCoords = true; coordConversion = 1.0; // we may not know the lattice constants yet } getline(inStream, buffer); while (buffer.find("$") == std::string::npos) { // parse atoms until we see another '$' section // e.g. 0.0000 0.000000 -0.73578 o std::vector tokens(split(buffer, ' ')); if (tokens.size() < 4) { appendError("Not enough tokens in this line: " + buffer); return false; } unsigned char atomicNum(0); if (isalpha(tokens[3][0])) { tokens[3][0] = toupper(tokens[3][0]); atomicNum = Elements::atomicNumberFromSymbol(tokens[3]); } else atomicNum = static_cast(lexicalCast(tokens[3])); Vector3 pos(lexicalCast(tokens[0]), lexicalCast(tokens[1]), lexicalCast(tokens[2])); Atom newAtom = mol.addAtom(atomicNum); newAtom.setPosition3d(pos * coordConversion); // next line getline(inStream, buffer); } } else if (buffer.find("$cell") != std::string::npos) { hasCell = true; Real cellConversion = BOHR_TO_ANGSTROM; if (buffer.find("ang") != std::string::npos) cellConversion = 1.0; // leave as Angstrom getline(inStream, buffer); std::vector tokens(split(buffer, ' ')); if (tokens.size() < 6) { appendError("Not enough tokens in this line: " + buffer); return false; } a = lexicalCast(tokens[0]) * cellConversion; b = lexicalCast(tokens[1]) * cellConversion; c = lexicalCast(tokens[2]) * cellConversion; alpha = lexicalCast(tokens[3]) * DEG_TO_RAD; beta = lexicalCast(tokens[4]) * DEG_TO_RAD; gamma = lexicalCast(tokens[5]) * DEG_TO_RAD; } else if (buffer.find("$lattice") != std::string::npos) { hasLattice = true; Real latticeConversion = BOHR_TO_ANGSTROM; // default if (buffer.find("ang") != std::string::npos) latticeConversion = 1.0; // leave as Angstrom for (int line = 0; line < 3; ++line) { getline(inStream, buffer); std::vector tokens(split(buffer, ' ')); if (tokens.size() < 3) break; if (line == 0) { v1.x() = lexicalCast(tokens[0]) * latticeConversion; v1.y() = lexicalCast(tokens[1]) * latticeConversion; v1.z() = lexicalCast(tokens[2]) * latticeConversion; } else if (line == 1) { v2.x() = lexicalCast(tokens[0]) * latticeConversion; v2.y() = lexicalCast(tokens[1]) * latticeConversion; v2.z() = lexicalCast(tokens[2]) * latticeConversion; } else if (line == 2) { v3.x() = lexicalCast(tokens[0]) * latticeConversion; v3.y() = lexicalCast(tokens[1]) * latticeConversion; v3.z() = lexicalCast(tokens[2]) * latticeConversion; } } } getline(inStream, buffer); } // done reading the file Core::UnitCell* cell = nullptr; std::string tmp; if (hasLattice) { cell = new Core::UnitCell(v1, v2, v3); tmp = "$lattice"; } else if (hasCell) { cell = new Core::UnitCell(a, b, c, alpha, beta, gamma); tmp = "$cell"; } if (cell) { if (!cell->isRegular()) { appendError(tmp + " does not give linear independent lattice vectors"); delete cell; return false; } mol.setUnitCell(cell); } // if we have fractional coordinates, we need to convert them to cartesian if (fractionalCoords) { auto* cell = mol.unitCell(); for (Index i = 0; i < mol.atomCount(); ++i) { mol.setAtomPosition3d(i, cell->toCartesian(mol.atomPosition3d(i))); } } // This format has no connectivity information, so perceive basics at least. if (opts.value("perceiveBonds", true)) { mol.perceiveBondsSimple(); mol.perceiveBondOrders(); } return true; } bool TurbomoleFormat::write(std::ostream& outStream, const Core::Molecule& mol) { size_t numAtoms = mol.atomCount(); outStream << "$coord angs\n"; for (size_t i = 0; i < numAtoms; ++i) { Atom atom = mol.atom(i); if (!atom.isValid()) { appendError("Internal error: Atom invalid."); return false; } std::string symbol = Elements::symbol(atom.atomicNumber()); symbol[0] = tolower(symbol[0]); outStream << " " << std::setw(18) << std::right << std::fixed << std::setprecision(10) << atom.position3d().x() << " " << std::setw(18) << std::right << std::fixed << std::setprecision(10) << atom.position3d().y() << " " << std::setw(18) << std::right << std::fixed << std::setprecision(10) << atom.position3d().z() << " " << std::setw(5) << std::right << symbol << "\n"; } if (mol.unitCell()) { outStream << "$periodic 3\n"; outStream << "$lattice angs\n"; outStream << mol.unitCell()->aVector().x() << ' '; outStream << mol.unitCell()->aVector().y() << ' '; outStream << mol.unitCell()->aVector().z() << '\n'; outStream << mol.unitCell()->bVector().x() << ' '; outStream << mol.unitCell()->bVector().y() << ' '; outStream << mol.unitCell()->bVector().z() << '\n'; outStream << mol.unitCell()->cVector().x() << ' '; outStream << mol.unitCell()->cVector().y() << ' '; outStream << mol.unitCell()->cVector().z() << '\n'; } outStream << "$end\n"; return true; } std::vector TurbomoleFormat::fileExtensions() const { std::vector ext; ext.emplace_back("coord"); ext.emplace_back("tmol"); return ext; } std::vector TurbomoleFormat::mimeTypes() const { std::vector mime; mime.emplace_back("chemical/x-turbomole"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/turbomoleformat.h000066400000000000000000000032501506155467400221460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_TURBOMOLEFORMAT_H #define AVOGADRO_IO_TURBOMOLEFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class TurbomoleFormat turbomoleformat.h * @brief Implementation of the Turbomole coord format. * @author Geoffrey Hutchison */ class AVOGADROIO_EXPORT TurbomoleFormat : public FileFormat { public: TurbomoleFormat() = default; ~TurbomoleFormat() override = default; Operations supportedOperations() const override { return ReadWrite | File | Stream | String; } FileFormat* newInstance() const override { return new TurbomoleFormat; } std::string identifier() const override { return "Avogadro: Turbomole Coord"; } std::string name() const override { return "Turbomole"; } std::string description() const override { return "Generic format that tabulates atomic symbols and 3D positions."; } std::string specificationUrl() const override { return "https://xtb-docs.readthedocs.io/en/latest/" "geometry.html#turbomole-coordinate-input"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& molecule) override; bool write(std::ostream& outStream, const Core::Molecule& molecule) override; }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_TURBOMOLEFORMAT_H avogadrolibs-1.101.0/avogadro/io/vaspformat.cpp000066400000000000000000000356331506155467400214540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vaspformat.h" #include // for atomicNumberFromSymbol() #include // for matrix3 #include #include #include // for split(), trimmed(), lexicalCast() #include // for Vector3 #include // for std::count() #include #include namespace Avogadro::Io { using std::getline; using std::map; using std::string; using Core::Array; using Core::Atom; using Core::Elements; using Core::lexicalCast; using Core::Molecule; using Core::split; using Core::trimmed; using Core::UnitCell; bool PoscarFormat::read(std::istream& inStream, Core::Molecule& mol) { size_t numLines = std::count(std::istreambuf_iterator(inStream), std::istreambuf_iterator(), '\n'); // There must be at least 7 "\n"'s to have a minimum crystal (including 1 // atom) if (numLines < 7) { appendError("Error: POSCAR file is 7 or fewer lines long"); return false; } // We have to go back to the beginning if we are going to read again inStream.clear(); inStream.seekg(0, std::ios::beg); // We'll use these throughout bool ok; string line; std::vector stringSplit; // First line is comment line getline(inStream, line); line = trimmed(line); string title = " "; if (!line.empty()) title = line; // Next line is scaling factor getline(inStream, line); auto scalingFactor = lexicalCast(line, ok); if (!ok) { appendError("Error: Could not convert scaling factor to double in POSCAR"); return false; } Matrix3 cellMat; // Next comes the matrix for (size_t i = 0; i < 3; ++i) { getline(inStream, line); stringSplit = split(line, ' '); // If this is not three, then there is some kind of error in the line if (stringSplit.size() != 3) { appendError("Error reading lattice vectors in POSCAR"); return false; } // UnitCell expects a matrix of this form cellMat(0, i) = lexicalCast(stringSplit.at(0)) * scalingFactor; cellMat(1, i) = lexicalCast(stringSplit.at(1)) * scalingFactor; cellMat(2, i) = lexicalCast(stringSplit.at(2)) * scalingFactor; } // Sometimes, atomic symbols go here. getline(inStream, line); stringSplit = split(line, ' '); if (stringSplit.empty()) { appendError("Error reading numbers of atom types in POSCAR"); return false; } // Try a lexical cast here. If it fails, assume we have an atomic symbols list lexicalCast(trimmed(stringSplit.at(0)), ok); std::vector symbolsList; std::vector atomicNumbers; if (!ok) { // Assume atomic symbols are here and store them symbolsList = split(line, ' '); // Store atomic nums for (auto& i : symbolsList) atomicNumbers.push_back(Elements::atomicNumberFromSymbol(i)); // This next one should be atom types getline(inStream, line); } // If the atomic symbols aren't here, try to find them in the title // In Vasp 4.x, symbols are in the title like so: " O4H2 " else { stringSplit = split(title, ' '); if (stringSplit.size() != 0) { string trimmedFormula = trimmed(stringSplit.at(0)); // Let's replace all numbers with spaces for (char& i : trimmedFormula) { if (isdigit(i)) i = ' '; } // Now get the symbols with a simple space split symbolsList = split(trimmedFormula, ' '); for (auto& i : symbolsList) atomicNumbers.push_back(Elements::atomicNumberFromSymbol(i)); } } stringSplit = split(line, ' '); std::vector atomCounts; for (auto& i : stringSplit) { auto atomCount = lexicalCast(i); atomCounts.push_back(atomCount); } // If we never filled up the atomic numbers, fill them up // now with "1, 2, 3…" if (atomicNumbers.size() == 0) for (size_t i = 1; i <= atomCounts.size(); ++i) atomicNumbers.push_back(i); if (atomicNumbers.size() != atomCounts.size()) { appendError("Error: numSymbols and numTypes are not equal in POSCAR!"); return false; } // Starts with either [Ss]elective dynamics, [KkCc]artesian, or // other for fractional coords. getline(inStream, line); line = trimmed(line); // If selective dynamics, get the next line if (line.empty() || line.at(0) == 'S' || line.at(0) == 's') getline(inStream, line); line = trimmed(line); if (line.empty()) { appendError("Error determining Direct or Cartesian in POSCAR"); return false; } bool cart; // Check if we're using cartesian or fractional coordinates: if (line.at(0) == 'K' || line.at(0) == 'k' || line.at(0) == 'C' || line.at(0) == 'c') { cart = true; } // Assume direct if one of these was not found else { cart = false; } std::vector atoms; for (unsigned int atomCount : atomCounts) { for (size_t j = 0; j < atomCount; ++j) { getline(inStream, line); stringSplit = split(line, ' '); // This may be greater than 3 with selective dynamics if (stringSplit.size() < 3) { appendError("Error reading atomic coordinates in POSCAR"); return false; } Vector3 tmpAtom(lexicalCast(stringSplit.at(0)), lexicalCast(stringSplit.at(1)), lexicalCast(stringSplit.at(2))); atoms.push_back(tmpAtom); } } // Let's make a unit cell auto* cell = new UnitCell(cellMat); if (!cell->isRegular()) { appendError("cell vectors are not linear independent"); delete cell; return false; } // If our atomic coordinates are fractional, convert them to Cartesian if (!cart) { for (auto& atom : atoms) atom = cell->toCartesian(atom); } // If they're cartesian, we just need to apply the scaling factor else { for (auto& atom : atoms) atom *= scalingFactor; } // If we made it this far, the read was a success! // Delete the current molecule. Add the new title and unit cell mol.clearAtoms(); mol.setData("name", title); mol.setUnitCell(cell); // Now add the atoms size_t k = 0; for (size_t i = 0; i < atomCounts.size(); ++i) { unsigned char atomicNum = atomicNumbers.at(i); for (size_t j = 0; j < atomCounts.at(i); ++j) { Atom newAtom = mol.addAtom(atomicNum); newAtom.setPosition3d(atoms.at(k)); ++k; } } return true; } bool PoscarFormat::write(std::ostream& outStream, const Core::Molecule& mol) { // Title if (mol.data("name").toString().length()) outStream << mol.data("name").toString() << std::endl; else outStream << "POSCAR" << std::endl; // Scaling factor outStream << " 1.00000000" << std::endl; // 3x3 matrix. Transpose is needed to orient the matrix correctly. const Matrix3& mat = mol.unitCell()->cellMatrix().transpose(); for (size_t i = 0; i < 3; ++i) { for (size_t j = 0; j < 3; ++j) { outStream << " " << std::setw(10) << std::right << std::fixed << std::setprecision(8) << mat(i, j); } outStream << std::endl; } // Adapted from chemkit: // A map of atomic symbols to their quantity. Array atomicNumbers = mol.atomicNumbers(); std::map composition; for (unsigned char& atomicNumber : atomicNumbers) { composition[atomicNumber]++; } // Atom symbols auto iter = composition.begin(); while (iter != composition.end()) { outStream << " " << Elements::symbol(iter->first); ++iter; } outStream << std::endl; // Numbers of each type iter = composition.begin(); while (iter != composition.end()) { outStream << " " << iter->second; ++iter; } outStream << std::endl; // Direct or cartesian? outStream << "Direct" << std::endl; // Final section is atomic coordinates size_t numAtoms = mol.atomCount(); // We need to make sure we that group the atomic numbers together. // The outer loop is for grouping them. iter = composition.begin(); while (iter != composition.end()) { unsigned char currentAtomicNum = iter->first; for (size_t i = 0; i < numAtoms; ++i) { // We need to group atomic numbers together. If this one is not // the current atomic number, skip over it. if (atomicNumbers.at(i) != currentAtomicNum) continue; Atom atom = mol.atom(i); if (!atom.isValid()) { appendError("Internal error: Atom invalid."); return false; } Vector3 fracCoords = mol.unitCell()->toFractional(atom.position3d()); outStream << " " << std::setw(10) << std::right << std::fixed << std::setprecision(8) << fracCoords.x() << " " << std::setw(10) << std::right << std::fixed << std::setprecision(8) << fracCoords.y() << " " << std::setw(10) << std::right << std::fixed << std::setprecision(8) << fracCoords.z() << "\n"; } ++iter; } return true; } std::vector PoscarFormat::fileExtensions() const { std::vector ext; ext.emplace_back("POSCAR"); ext.emplace_back("CONTCAR"); ext.emplace_back("vasp"); return ext; } std::vector PoscarFormat::mimeTypes() const { std::vector mime; mime.emplace_back("N/A"); return mime; } bool OutcarFormat::read(std::istream& inStream, Core::Molecule& mol) { std::string buffer, dashedStr, positionStr, latticeStr; positionStr = " POSITION"; latticeStr = " Lattice vectors:"; dashedStr = " -----------"; std::vector stringSplit; int coordSet = 0, natoms = 0; Array positions; Vector3 ax1, ax2, ax3; bool ax1Set = false, ax2Set = false, ax3Set = false; typedef map AtomTypeMap; AtomTypeMap atomTypes; unsigned char customElementCounter = CustomElementMin; while (getline(inStream, buffer)) { // Checks whether the buffer object contains the lattice vectors keyword if (buffer.substr(0, latticeStr.size()) == latticeStr) { // Checks whether lattice vectors have been already set. Reason being that // only the first occurrence denotes the true lattice vectors, and the // ones following these are vectors of the primitive cell. if (!(ax1Set && ax2Set && ax3Set)) { getline(inStream, buffer); for (int i = 0; i < 3; ++i) { getline(inStream, buffer); stringSplit = split(buffer, ' '); if (stringSplit[0] == "A1") { ax1 = Vector3(lexicalCast(stringSplit.at(3).substr( 0, stringSplit.at(3).size() - 1)), lexicalCast(stringSplit.at(4).substr( 0, stringSplit.at(4).size() - 1)), lexicalCast(stringSplit.at(5).substr( 0, stringSplit.at(5).size() - 1))); ax1Set = true; } else if (stringSplit[0] == "A2") { ax2 = Vector3(lexicalCast(stringSplit.at(3).substr( 0, stringSplit.at(3).size() - 1)), lexicalCast(stringSplit.at(4).substr( 0, stringSplit.at(4).size() - 1)), lexicalCast(stringSplit.at(5).substr( 0, stringSplit.at(5).size() - 1))); ax2Set = true; } else if (stringSplit[0] == "A3") { ax3 = Vector3(lexicalCast(stringSplit.at(3).substr( 0, stringSplit.at(3).size() - 1)), lexicalCast(stringSplit.at(4).substr( 0, stringSplit.at(4).size() - 1)), lexicalCast(stringSplit.at(5).substr( 0, stringSplit.at(5).size() - 1))); ax3Set = true; } } // Checks whether all the three axis vectors have been read if (ax1Set && ax2Set && ax3Set) { auto* cell = new UnitCell(ax1, ax2, ax3); if (!cell->isRegular()) { appendError("cell vectors are not linear independent"); return false; } mol.setUnitCell(cell); } } } // Checks whether the buffer object contains the POSITION keyword else if (buffer.substr(0, positionStr.size()) == positionStr) { getline(inStream, buffer); // Double checks whether the succeeding line is a sequence of dashes if (buffer.substr(0, dashedStr.size()) == dashedStr) { // natoms is not known, so the loop proceeds till the bottom dashed line // is encountered while (true) { getline(inStream, buffer); // Condition for encountering dashed line if (buffer.substr(0, dashedStr.size()) == dashedStr) { if (coordSet == 0) { mol.setCoordinate3d(mol.atomPositions3d(), coordSet++); positions.reserve(natoms); } else { mol.setCoordinate3d(positions, coordSet++); positions.clear(); } break; } // Parsing the coordinates stringSplit = split(buffer, ' '); Vector3 tmpAtom(lexicalCast(stringSplit.at(0)), lexicalCast(stringSplit.at(1)), lexicalCast(stringSplit.at(2))); if (coordSet == 0) { AtomTypeMap::const_iterator it; atomTypes.insert( std::make_pair(std::to_string(natoms), customElementCounter++)); it = atomTypes.find(std::to_string(natoms)); // if (customElementCounter > CustomElementMax) { // appendError("Custom element type limit exceeded."); // return false; // } Atom newAtom = mol.addAtom(it->second); newAtom.setPosition3d(tmpAtom); natoms++; } else { positions.push_back(tmpAtom); } } } } } // Set the custom element map if needed: if (!atomTypes.empty()) { Molecule::CustomElementMap elementMap; for (const auto& atomType : atomTypes) { elementMap.insert( std::make_pair(atomType.second, "Atom " + atomType.first)); } mol.setCustomElementMap(elementMap); } return true; } bool OutcarFormat::write(std::ostream&, const Core::Molecule&) { return false; } std::vector OutcarFormat::fileExtensions() const { std::vector ext; ext.emplace_back("OUTCAR"); return ext; } std::vector OutcarFormat::mimeTypes() const { std::vector mime; mime.emplace_back("N/A"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/vaspformat.h000066400000000000000000000050261506155467400211120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_VASPFORMAT_H #define AVOGADRO_IO_VASPFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class PoscarFormat vaspformat.h * @brief Implementation of the generic POSCAR format. * @author Patrick S. Avery */ class AVOGADROIO_EXPORT PoscarFormat : public FileFormat { public: PoscarFormat() = default; ~PoscarFormat() override = default; Operations supportedOperations() const override { return ReadWrite | File | Stream | String; } FileFormat* newInstance() const override { return new PoscarFormat; } std::string identifier() const override { return "Avogadro: POSCAR"; } std::string name() const override { return "POSCAR"; } std::string description() const override { return "Format used by VASP that contains crystal cell and atom info."; } std::string specificationUrl() const override { return "http://cms.mpi.univie.ac.at/vasp/guide/node59.html"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& mol) override; bool write(std::ostream& outStream, const Core::Molecule& mol) override; }; class AVOGADROIO_EXPORT OutcarFormat : public FileFormat { public: OutcarFormat() = default; ~OutcarFormat() override = default; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new OutcarFormat; } std::string identifier() const override { return "Avogadro: OUTCAR"; } std::string name() const override { return "OUTCAR"; } std::string description() const override { return "Format used by VASP that contains trajectory output of a DFT/MD " "calculation."; } std::string specificationUrl() const override { return "https://cms.mpi.univie.ac.at/wiki/index.php/OUTCAR"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& mol) override; // unimplemented bool write(std::ostream& outStream, const Core::Molecule& mol) override; }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_VASPFORMAT_H avogadrolibs-1.101.0/avogadro/io/xyzformat.cpp000066400000000000000000000276311506155467400213340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "xyzformat.h" #include #include #include #include #include #include #include #include #include #include using json = nlohmann::json; using std::endl; using std::getline; using std::string; namespace Avogadro::Io { using Core::Array; using Core::Atom; using Core::Elements; using Core::lexicalCast; using Core::split; using Core::trimmed; #ifndef _WIN32 using std::isalpha; #endif bool findEnergy(const std::string& buffer, double& energyValue) { // Check for energy in the comment line // orca uses E -680.044112849966 (with spaces) // xtb uses energy: -680.044112849966 // Open Babel uses Energy: -680.044112849966 std::size_t energyStart = buffer.find("energy:"); std::size_t offset = 7; if (energyStart == std::string::npos) { energyStart = buffer.find("Energy:"); } if (energyStart == std::string::npos) { energyStart = buffer.find(" E "); offset = 3; } if (energyStart != std::string::npos) { // find the next whitespace or end of the string std::size_t energyEnd = buffer.find_first_of(" \t", energyStart + offset); if (energyEnd == std::string::npos) energyEnd = buffer.size(); std::string energy = buffer.substr(energyStart + offset, energyEnd); energyValue = lexicalCast(energy); return true; } return false; } bool XyzFormat::read(std::istream& inStream, Core::Molecule& mol) { json opts; if (!options().empty()) opts = json::parse(options(), nullptr, false); else opts = json::object(); size_t numAtoms = 0; if (!(inStream >> numAtoms)) { appendError("Error parsing number of atoms."); return false; } string buffer; getline(inStream, buffer); // Finish the first line if (!inStream.good()) { appendError("Error reading first line."); return false; } getline(inStream, buffer); // comment or name or energy if (!buffer.empty()) mol.setData("name", trimmed(buffer)); double energy = 0.0; std::vector energies; if (findEnergy(buffer, energy)) { mol.setData("totalEnergy", energy); energies.push_back(energy); } // check for Lattice= in an extended XYZ from ASE and company // e.g. Lattice="H11 H21 H31 H12 H22 H32 H13 H23 H33" // https://atomsk.univ-lille.fr/doc/en/format_xyz.html // https://gitlab.com/ase/ase/-/merge_requests/62 std::size_t start = buffer.find("Lattice="); if (start != std::string::npos) { // step through bit by bit until we hit the next quote character start = start + 8; // skip over the first quote if (buffer[start] == '\"') { start++; } std::size_t end = buffer.find('\"', start); std::string lattice = buffer.substr(start, (end - start)); std::vector tokens(split(lattice, ' ')); // check for size std::cout << "Lattice size: " << tokens.size() << std::endl; if (tokens.size() >= 9) { Vector3 v1(lexicalCast(tokens[0]), lexicalCast(tokens[1]), lexicalCast(tokens[2])); Vector3 v2(lexicalCast(tokens[3]), lexicalCast(tokens[4]), lexicalCast(tokens[5])); Vector3 v3(lexicalCast(tokens[6]), lexicalCast(tokens[7]), lexicalCast(tokens[8])); auto* cell = new Core::UnitCell(v1, v2, v3); std::cout << " Lattice: " << cell->aVector() << " " << cell->bVector() << " " << cell->cVector() << std::endl; if (!cell->isRegular()) { appendError("Lattice vectors are not linear independent"); delete cell; } else { mol.setUnitCell(cell); } } } // check to see if there's an extended XYZ Properties= line // e.g. Properties=species:S:1:pos:R:3 // https://gitlab.com/ase/ase/-/merge_requests/62 start = buffer.find("Properties="); unsigned int chargeColumn = 0; unsigned int forceColumn = 0; std::vector charges; if (start != std::string::npos) { start = start + 11; // skip over "Properties=" unsigned int stop = buffer.find(' ', start); unsigned int length = stop - start; // we want to track columns after the position // (esp. charge, spin, force, velocity, etc.) std::string properties = buffer.substr(start, length); std::vector tokens(split(properties, ':')); unsigned int column = 0; for (size_t i = 0; i < tokens.size(); i += 3) { // we can safely assume species and pos are present if (tokens[i] == "charge") { chargeColumn = column; } else if (tokens[i] == "force" || tokens[i] == "forces") { forceColumn = column; } // TODO other properties (velocity, spin, selection, etc.) // increment column based on the count of the property if (i + 2 < tokens.size()) { column += lexicalCast(tokens[i + 2]); } } } if (!inStream.good()) { appendError("Error reading comment line."); return false; } // Parse atoms for (size_t i = 0; i < numAtoms; ++i) { getline(inStream, buffer); if (!inStream.good()) { appendError("Error reading atom at index " + std::to_string(i) + "."); return false; } std::vector tokens; // check for tabs PR#1512 if (buffer.find('\t') != std::string::npos) tokens = split(buffer, '\t'); else tokens = split(buffer, ' '); if (tokens.size() < 4) { appendError("Not enough tokens in this line: " + buffer); return false; } unsigned char atomicNum(0); if (isalpha(tokens[0][0])) atomicNum = Elements::atomicNumberFromSymbol(tokens[0]); else atomicNum = static_cast(lexicalCast(tokens[0])); Vector3 pos(lexicalCast(tokens[1]), lexicalCast(tokens[2]), lexicalCast(tokens[3])); Atom newAtom = mol.addAtom(atomicNum); newAtom.setPosition3d(pos); // check for charge and force columns if (chargeColumn > 0 && chargeColumn < tokens.size()) { charges.push_back(lexicalCast(tokens[chargeColumn])); // we set the charges after all atoms are added } if (forceColumn > 0 && forceColumn < tokens.size()) { Vector3 force(lexicalCast(tokens[forceColumn]), lexicalCast(tokens[forceColumn + 1]), lexicalCast(tokens[forceColumn + 2])); newAtom.setForceVector(force); } } // Check that all atoms were handled. if (mol.atomCount() != numAtoms) { std::ostringstream errorStream; errorStream << "Error parsing atom at index " << mol.atomCount() << " (line " << 3 + mol.atomCount() << ").\n" << buffer; appendError(errorStream.str()); return false; } // Do we have an animation? size_t numAtoms2; // check if the next frame has the same number of atoms getline(inStream, buffer); // should be the number of atoms if (buffer.size() == 0 || buffer[0] == '>') { getline(inStream, buffer); // Orca 6 prints ">" separators } if ((numAtoms2 = lexicalCast(buffer)) && numAtoms == numAtoms2) { getline(inStream, buffer); // comment line // check for properties in the comment line if (findEnergy(buffer, energy)) { energies.push_back(energy); } mol.setCoordinate3d(mol.atomPositions3d(), 0); int coordSet = 1; bool done = false; while (numAtoms == numAtoms2) { Array positions; positions.reserve(numAtoms); for (size_t i = 0; i < numAtoms; ++i) { getline(inStream, buffer); if (inStream.eof()) { numAtoms2 = 0; done = true; break; // break this inner loop } std::vector tokens(split(buffer, ' ')); if (tokens.size() < 4) { appendError("Not enough tokens in this line: " + buffer); return false; } Vector3 pos(lexicalCast(tokens[1]), lexicalCast(tokens[2]), lexicalCast(tokens[3])); positions.push_back(pos); } if (!done) mol.setCoordinate3d(positions, coordSet++); if (getline(inStream, buffer)) { if (inStream.eof()) { numAtoms2 = 0; break; // break this inner loop } if (buffer.size() == 0 || buffer[0] == '>') getline(inStream, buffer); // Orca 6 prints ">" separators if (inStream.eof()) { numAtoms2 = 0; break; // break this inner loop } numAtoms2 = lexicalCast(buffer); if (numAtoms != numAtoms2) break; } std::getline(inStream, buffer); // Skip the blank // check for energies if (findEnergy(buffer, energy)) { energies.push_back(energy); } positions.clear(); } } // This format has no connectivity information, so perceive basics at least. if (opts.value("perceiveBonds", true)) { mol.perceiveBondsSimple(); mol.perceiveBondOrders(); } // have to set the charges after creating bonds // (since modifying bonds invalidates the partial charges) if (!charges.empty()) { MatrixX chargesMatrix = MatrixX::Zero(mol.atomCount(), 1); for (size_t i = 0; i < charges.size(); ++i) { chargesMatrix(i, 0) = charges[i]; } mol.setPartialCharges("From File", chargesMatrix); } if (energies.size() > 1) mol.setData("energies", energies); return true; } bool XyzFormat::write(std::ostream& outStream, const Core::Molecule& mol) { size_t numAtoms = mol.atomCount(); outStream << numAtoms << std::endl; if (mol.unitCell()) { // default to including Lattice for extended XYZ if present // https://atomsk.univ-lille.fr/doc/en/format_xyz.html // https://gitlab.com/ase/ase/-/merge_requests/62 outStream << "Lattice=\""; outStream << mol.unitCell()->aVector().x() << ' '; outStream << mol.unitCell()->aVector().y() << ' '; outStream << mol.unitCell()->aVector().z() << ' '; outStream << mol.unitCell()->bVector().x() << ' '; outStream << mol.unitCell()->bVector().y() << ' '; outStream << mol.unitCell()->bVector().z() << ' '; outStream << mol.unitCell()->cVector().x() << ' '; outStream << mol.unitCell()->cVector().y() << ' '; outStream << mol.unitCell()->cVector().z(); outStream << "\" Properties=species:S:1:pos:R:3" << endl; } else { if (mol.data("name").toString().length()) outStream << mol.data("name").toString() << endl; else outStream << "XYZ file generated by Avogadro.\n"; } for (size_t i = 0; i < numAtoms; ++i) { Atom atom = mol.atom(i); if (!atom.isValid()) { appendError("Internal error: Atom invalid."); return false; } outStream << std::setw(3) << std::left << Elements::symbol(atom.atomicNumber()) << " " << std::setw(15) << std::right << std::fixed << std::setprecision(10) << atom.position3d().x() << " " << std::setw(15) << std::right << std::fixed << std::setprecision(10) << atom.position3d().y() << " " << std::setw(15) << std::right << std::fixed << std::setprecision(10) << atom.position3d().z() << "\n"; } return true; } std::vector XyzFormat::fileExtensions() const { std::vector ext; ext.emplace_back("xyz"); ext.emplace_back("exyz"); ext.emplace_back("extxyz"); ext.emplace_back("allxyz"); return ext; } std::vector XyzFormat::mimeTypes() const { std::vector mime; mime.emplace_back("chemical/x-xyz"); return mime; } } // namespace Avogadro::Io avogadrolibs-1.101.0/avogadro/io/xyzformat.h000066400000000000000000000030301506155467400207640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_IO_XYZFORMAT_H #define AVOGADRO_IO_XYZFORMAT_H #include "fileformat.h" namespace Avogadro::Io { /** * @class XyzFormat xyzformat.h * @brief Implementation of the generic xyz format. * @author Allison Vacanti */ class AVOGADROIO_EXPORT XyzFormat : public FileFormat { public: XyzFormat() = default; ~XyzFormat() override = default; Operations supportedOperations() const override { return ReadWrite | MultiMolecule | File | Stream | String; } FileFormat* newInstance() const override { return new XyzFormat; } std::string identifier() const override { return "Avogadro: XYZ"; } std::string name() const override { return "XYZ"; } std::string description() const override { return "Generic format that tabulates atomic symbols and 3D positions."; } std::string specificationUrl() const override { return "http://openbabel.org/wiki/XYZ"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& inStream, Core::Molecule& molecule) override; bool write(std::ostream& outStream, const Core::Molecule& molecule) override; }; } // namespace Avogadro::Io #endif // AVOGADRO_IO_XYZFORMAT_H avogadrolibs-1.101.0/avogadro/molequeue/000077500000000000000000000000001506155467400201465ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/molequeue/CMakeLists.txt000066400000000000000000000020231506155467400227030ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 COMPONENTS Widgets Network REQUIRED) else() find_package(Qt5 COMPONENTS Widgets Network REQUIRED) endif() add_library(MoleQueue) avogadro_headers(MoleQueue batchjob.h inputgenerator.h inputgeneratordialog.h inputgeneratorwidget.h molequeuedialog.h molequeuemanager.h molequeuequeuelistmodel.h molequeuewidget.h ) target_sources(MoleQueue PRIVATE batchjob.cpp inputgenerator.cpp inputgeneratordialog.cpp inputgeneratorwidget.cpp molequeuedialog.cpp molequeuemanager.cpp molequeuequeuelistmodel.cpp molequeuewidget.cpp client/client.cpp client/jobobject.cpp client/jsonrpcclient.cpp ) set(UIS inputgeneratordialog.ui inputgeneratorwidget.ui molequeuedialog.ui molequeuewidget.ui ) qt_wrap_ui(UI_SOURCES ${UIS}) target_sources(MoleQueue PRIVATE ${UI_SOURCES}) avogadro_add_library(MoleQueue ${HEADERS} ${SOURCES}) set_target_properties(MoleQueue PROPERTIES AUTOMOC TRUE) target_link_libraries(MoleQueue PUBLIC Avogadro::QtGui Qt::Widgets Qt::Network) avogadrolibs-1.101.0/avogadro/molequeue/batchjob.cpp000066400000000000000000000154031506155467400224310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "batchjob.h" #include "molequeuemanager.h" #include #include namespace Avogadro::MoleQueue { // initialize statics const BatchJob::BatchId BatchJob::InvalidBatchId = -1; const BatchJob::RequestId BatchJob::InvalidRequestId = -1; const BatchJob::ServerId BatchJob::InvalidServerId = std::numeric_limits::max(); BatchJob::BatchJob(QObject* par) : QObject(par) { setup(); } BatchJob::BatchJob(const QString& scriptFilePath, QObject* par) : QObject(par), m_inputGenerator(scriptFilePath) { setup(); } BatchJob::BatchId BatchJob::submitNextJob(const Core::Molecule& mol) { // Is everything configured? if (!m_inputGenerator.isValid() || m_inputGeneratorOptions.empty() || m_moleQueueOptions.empty()) { return InvalidBatchId; } // Verify that molequeue is running: MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) return InvalidBatchId; // Generate the input: if (!m_inputGenerator.generateInput(m_inputGeneratorOptions, mol)) { if (!m_inputGenerator.errorList().isEmpty()) { qWarning() << "BatchJob::submitNextJob() error:\n\t" << m_inputGenerator.errorList().join("\n\t"); } return InvalidBatchId; } // Warnings are non-fatal -- just print them for now: if (!m_inputGenerator.warningList().isEmpty()) { qWarning() << "BatchJob::submitNextJob() warning:\n\t" << m_inputGenerator.warningList().join("\n\t"); } BatchId bId = m_jobObjects.size(); // Create the job object: JobObject job; job.fromJson(m_moleQueueOptions); job.setDescription( tr("Batch Job #%L1 (%2)").arg(bId + 1).arg(job.description())); // Main input file: const QString mainFileName = m_inputGenerator.mainFileName(); job.setInputFile(mainFileName, m_inputGenerator.fileContents(mainFileName)); // Any additional input files: QStringList fileNames = m_inputGenerator.fileNames(); fileNames.removeOne(mainFileName); foreach (const QString& fn, fileNames) job.appendAdditionalInputFile(fn, m_inputGenerator.fileContents(fn)); // Submit the job RequestId rId = mqManager.client().submitJob(job); // Was submission successful? if (rId < 0) return InvalidBatchId; // Register the job and assign the ID. m_jobObjects.push_back(job); m_states.push_back(None); m_requests.insert(rId, Request(Request::SubmitJob, bId)); return bId; } bool BatchJob::lookupJob(BatchId bId) { ServerId sId = serverId(static_cast(bId)); if (sId == InvalidServerId) return false; // Verify that molequeue is running: MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) return false; Client& client = mqManager.client(); RequestId rId = client.lookupJob(sId); m_requests.insert(rId, Request(Request::LookupJob, bId)); return true; } void BatchJob::handleSubmissionReply(int rId, unsigned int sId) { Request req = m_requests.value(rId); if (req.isValid()) { m_requests.remove(rId); if (req.batchId >= m_jobObjects.size()) { qWarning() << "BatchJob::handleSubmissionReply(): batchID out of range."; return; } m_jobObjects[req.batchId].setValue("moleQueueId", QVariant(static_cast(sId))); m_serverIds.insert(sId, req.batchId); // Request full job details: lookupJob(req.batchId); } } void BatchJob::handleJobStateChange(unsigned int sId, const QString&, const QString&) { BatchId bId = m_serverIds.value(static_cast(sId), InvalidBatchId); if (bId == InvalidBatchId) return; // Update full job details: lookupJob(bId); } void BatchJob::handleLookupJobReply(int rId, const QJsonObject& jobInfo) { Request req = m_requests.value(rId); if (req.isValid()) { m_requests.remove(rId); if (req.batchId >= m_jobObjects.size()) { qWarning() << "BatchJob::handleSubmissionReply(): batchID out of range."; return; } JobObject& job(m_jobObjects[req.batchId]); job.fromJson(jobInfo); JobState oldState = m_states[req.batchId]; JobState newState = stringToState(job.value("jobState").toString()); m_states[req.batchId] = newState; emit jobUpdated(req.batchId, true); if (!isTerminal(oldState) && isTerminal(newState)) emit jobCompleted(req.batchId, newState); } } void BatchJob::handleErrorResponse(int requestId, int errorCode, const QString& errorMessage, const QJsonValue& errorData) { qDebug() << "Error rcv'd: {" << "requestId:" << requestId << "errorCode:" << errorCode << "errorMessage:" << errorMessage << "errorData:" << errorData << "}"; Request req = m_requests.value(requestId); if (!req.isValid()) return; m_requests.remove(requestId); if (req.batchId < m_jobObjects.size()) return; switch (req.type) { case Request::SubmitJob: // The job was rejected: qDebug() << "Batch job" << req.batchId << "was rejected by MoleQueue."; m_states[req.batchId] = Rejected; m_jobObjects[req.batchId].fromJson(QJsonObject()); break; case Request::LookupJob: qDebug() << "Batch job" << req.batchId << "failed to update."; emit jobUpdated(req.batchId, false); break; default: case Request::InvalidType: break; } } void BatchJob::setup() { static bool metaTypesRegistered = false; if (!metaTypesRegistered) { qRegisterMetaType("Avogadro::QtGui::BatchJob::BatchId"); qRegisterMetaType("BatchId"); qRegisterMetaType("Avogadro::QtGui::BatchJob::ServerId"); qRegisterMetaType("ServerId"); qRegisterMetaType("Avogadro::QtGui::BatchJob::RequestId"); qRegisterMetaType("RequestId"); metaTypesRegistered = true; } MoleQueueManager& mqManager = MoleQueueManager::instance(); Client& client = mqManager.client(); connect(&client, SIGNAL(submitJobResponse(int, uint)), SLOT(handleSubmissionReply(int, uint))); connect(&client, SIGNAL(lookupJobResponse(int, QJsonObject)), SLOT(handleLookupJobReply(int, QJsonObject))); connect(&client, SIGNAL(jobStateChanged(uint, QString, QString)), SLOT(handleJobStateChange(uint, QString, QString))); connect(&client, SIGNAL(errorReceived(int, int, QString, QJsonValue)), SLOT(handleErrorResponse(int, int, QString, QJsonValue))); } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/batchjob.h000066400000000000000000000237721506155467400221060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_BATCHJOB_H #define AVOGADRO_QTGUI_BATCHJOB_H #include #include "inputgenerator.h" #include #include "client/jobobject.h" #include #include #include namespace Avogadro { namespace Core { class Molecule; } // end namespace Core namespace MoleQueue { /** * @brief The BatchJob class manages a collection of jobs that are configured * using the same InputGenerator and MoleQueue options. For use with * InputGeneratorDialog::configureBatchJob(BatchJob&). */ class AVOGADROMOLEQUEUE_EXPORT BatchJob : public QObject { Q_OBJECT public: /** * Job status. Same as those defined in molequeueglobal.h. The 'Rejected' * state is added to identify jobs that rejected by molequeue prior to having * a MoleQueue id (ServerId) set. */ enum JobState { Rejected = -2, Unknown = -1, None = 0, Accepted, QueuedLocal, Submitted, QueuedRemote, RunningLocal, RunningRemote, Finished, Canceled, Error }; /** * Type used to identify a job within this batch. Unique to this object. */ using BatchId = int; static const BatchId InvalidBatchId; /** * Type used to identify requests sent to the MoleQueue server. */ using RequestId = int; static const RequestId InvalidRequestId; /** * Type used by MoleQueue to identify jobs. Unique across the connected * MoleQueue server. */ using ServerId = unsigned int; static const ServerId InvalidServerId; /** * Construct a new BatchJob object. If provided, ese scriptFilePath to setup * the input generator. */ explicit BatchJob(QObject* parent = nullptr); explicit BatchJob(const QString& scriptFilePath, QObject* parent = nullptr); ~BatchJob() override = default; /** * Options for the input generator. * @{ */ void setInputGeneratorOptions(const QJsonObject& opts); QJsonObject inputGeneratorOptions() const; /**@}*/ /** * Options for MoleQueue. * @{ */ void setMoleQueueOptions(const QJsonObject& opts); QJsonObject moleQueueOptions() const; JobObject moleQueueJobTemplate() const; /**@}*/ /** * The internal InputGenerator. * @{ */ const InputGenerator& inputGenerator() const; InputGenerator& inputGenerator(); /**@}*/ /** * A string that will be used in the MoleQueue interface to identify this * batch job. Taken from the InputGenerator configured title. */ QString description() const; /** * @return The state of the job with the batch id @a batchId. */ JobState jobState(BatchId batchId) const; /** * @return The server id of the job with the batch id @a batchId. */ ServerId serverId(BatchId batchId) const; /** * @return The most recent JobObject for the job with the batch id @a batchId. * These are updated for each change in job state. */ JobObject jobObject(BatchId batchId) const; /** * @return True if @a state corresponds to a job that is finished running. */ static bool isTerminal(JobState state); /** * @return True if there are still running jobs. */ bool hasUnfinishedJobs() const; /** * @return The number of jobs that are running. */ int unfinishedJobCount() const; /** * @return The number of completed jobs. */ int finishedJobCount() const; /** * @return The total number of jobs in the batch. */ int jobCount() const; public slots: /** * Submit a job using the current configuration for @a mol. * @return The BatchId of the job, or InvalidBatchId if there was an error. */ virtual BatchId submitNextJob(const Core::Molecule& mol); /** * Request updated job details from the MoleQueue server for the job with * the batch id @a batchId. * * jobUpdated is emitted when the request is complete. * * @return True if the request is sent. */ bool lookupJob(BatchId batchId); signals: /** * Emitted when the reply from lookupJob is received. @a success will be false * if MoleQueue sends an error response (likely because the job was removed * from the job manager). */ void jobUpdated(Avogadro::MoleQueue::BatchJob::BatchId batchId, bool success); /** * Emitted when the job associated with @a batchId completes. @a status * is the final state of the jobs and can be used to determine whether or not * the job finished successfully. */ void jobCompleted(Avogadro::MoleQueue::BatchJob::BatchId batchId, Avogadro::MoleQueue::BatchJob::JobState status); private slots: void handleSubmissionReply(int requestId, unsigned int serverId); void handleJobStateChange(unsigned int serverId, const QString& oldState, const QString& newState); void handleLookupJobReply(int requestId, const QJsonObject& jobInfo); void handleErrorResponse(int requestId, int errorCode, const QString& errorMessage, const QJsonValue& errorData); private: // structs /** * Internal struct for tracking request metadata. */ struct Request { enum Type { InvalidType, SubmitJob, LookupJob }; explicit Request(Type t = InvalidType, BatchId b = InvalidBatchId); bool isValid() const { return type != InvalidType; } Type type; BatchId batchId; }; private: // methods void setup(); static JobState stringToState(const QString& string); static QString stateToString(JobState state); private: // variables InputGenerator m_inputGenerator; QJsonObject m_inputGeneratorOptions; QJsonObject m_moleQueueOptions; /// Cached job states. QList m_jobObjects; /// Lookup batch ids from server ids. QMap m_serverIds; /// Job states. For fast lookups without string conversions. QVector m_states; /// Pending requests. QMap m_requests; }; inline BatchJob::Request::Request(Type t, BatchId b) : type(t), batchId(b) {} inline void BatchJob::setInputGeneratorOptions(const QJsonObject& opts) { m_inputGeneratorOptions = opts; } inline QJsonObject BatchJob::inputGeneratorOptions() const { return m_inputGeneratorOptions; } inline void BatchJob::setMoleQueueOptions(const QJsonObject& opts) { m_moleQueueOptions = opts; } inline QJsonObject BatchJob::moleQueueOptions() const { return m_moleQueueOptions; } inline JobObject BatchJob::moleQueueJobTemplate() const { JobObject result; result.fromJson(m_moleQueueOptions); return result; } inline const InputGenerator& BatchJob::inputGenerator() const { return m_inputGenerator; } inline InputGenerator& BatchJob::inputGenerator() { return m_inputGenerator; } inline QString BatchJob::description() const { return moleQueueJobTemplate().description(); } inline BatchJob::JobState BatchJob::jobState(BatchJob::BatchId id) const { return id < m_states.size() ? m_states[id] : Unknown; } inline BatchJob::ServerId BatchJob::serverId(BatchJob::BatchId id) const { return id < m_jobObjects.size() ? m_jobObjects[id] .value("moleQueueId", InvalidServerId) .value() : InvalidServerId; } inline JobObject BatchJob::jobObject(BatchJob::BatchId id) const { return id < m_jobObjects.size() ? m_jobObjects[id] : JobObject(); } inline bool BatchJob::isTerminal(BatchJob::JobState state) { switch (state) { case Rejected: case Finished: case Canceled: case Error: return true; default: return false; } } inline bool BatchJob::hasUnfinishedJobs() const { for (auto m_state : m_states) { if (!isTerminal(m_state)) return true; } return false; } inline int BatchJob::unfinishedJobCount() const { int result = 0; for (auto m_state : m_states) { if (!isTerminal(m_state)) ++result; } return result; } inline int BatchJob::finishedJobCount() const { int result = 0; for (auto m_state : m_states) { if (isTerminal(m_state)) ++result; } return result; } inline int BatchJob::jobCount() const { return m_serverIds.size(); } inline BatchJob::JobState BatchJob::stringToState(const QString& str) { if (str == QLatin1String("None")) return None; else if (str == QLatin1String("Rejected")) return Rejected; else if (str == QLatin1String("Accepted")) return Accepted; else if (str == QLatin1String("QueuedLocal")) return QueuedLocal; else if (str == QLatin1String("Submitted")) return Submitted; else if (str == QLatin1String("QueuedRemote")) return QueuedRemote; else if (str == QLatin1String("RunningLocal")) return RunningLocal; else if (str == QLatin1String("RunningRemote")) return RunningRemote; else if (str == QLatin1String("Finished")) return Finished; else if (str == QLatin1String("Canceled")) return Canceled; else if (str == QLatin1String("Error")) return Error; else return Unknown; } inline QString BatchJob::stateToString(BatchJob::JobState state) { switch (state) { case None: return QString("None"); case Accepted: return QString("Accepted"); case Rejected: return QString("Rejected"); case QueuedLocal: return QString("QueuedLocal"); case Submitted: return QString("Submitted"); case QueuedRemote: return QString("QueuedRemote"); case RunningLocal: return QString("RunningLocal"); case RunningRemote: return QString("RunningRemote"); case Finished: return QString("Finished"); case Canceled: return QString("Canceled"); case Error: return QString("Error"); default: case Unknown: return QString("Unknown"); } } } // namespace MoleQueue } // namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_BATCHJOB_H avogadrolibs-1.101.0/avogadro/molequeue/client/000077500000000000000000000000001506155467400214245ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/molequeue/client/client.cpp000066400000000000000000000177631506155467400234240ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "client.h" #include "jobobject.h" #include "jsonrpcclient.h" #include namespace Avogadro::MoleQueue { Client::Client(QObject* parent_) : QObject(parent_), m_jsonRpcClient(nullptr) {} Client::~Client() {} bool Client::isConnected() const { if (!m_jsonRpcClient) return false; else return m_jsonRpcClient->isConnected(); } bool Client::connectToServer(const QString& serverName) { if (!m_jsonRpcClient) { m_jsonRpcClient = new JsonRpcClient(this); connect(m_jsonRpcClient, SIGNAL(resultReceived(QJsonObject)), SLOT(processResult(QJsonObject))); connect(m_jsonRpcClient, SIGNAL(notificationReceived(QJsonObject)), SLOT(processNotification(QJsonObject))); connect(m_jsonRpcClient, SIGNAL(errorReceived(QJsonObject)), SLOT(processError(QJsonObject))); connect(m_jsonRpcClient, SIGNAL(connectionStateChanged()), SIGNAL(connectionStateChanged())); } return m_jsonRpcClient->connectToServer(serverName); } int Client::requestQueueList() { if (!m_jsonRpcClient) return -1; QJsonObject packet = m_jsonRpcClient->emptyRequest(); packet["method"] = QLatin1String("listQueues"); if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = ListQueues; return localId; } int Client::submitJob(const JobObject& job) { if (!m_jsonRpcClient) return -1; QJsonObject packet = m_jsonRpcClient->emptyRequest(); packet["method"] = QLatin1String("submitJob"); packet["params"] = job.json(); if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = SubmitJob; return localId; } int Client::lookupJob(unsigned int moleQueueId) { if (!m_jsonRpcClient) return -1; QJsonObject packet = m_jsonRpcClient->emptyRequest(); packet["method"] = QLatin1String("lookupJob"); QJsonObject params; params["moleQueueId"] = static_cast(moleQueueId); packet["params"] = params; if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = LookupJob; return localId; } int Client::cancelJob(unsigned int moleQueueId) { if (!m_jsonRpcClient) return -1; QJsonObject packet = m_jsonRpcClient->emptyRequest(); packet["method"] = QLatin1String("cancelJob"); QJsonObject params; params["moleQueueId"] = static_cast(moleQueueId); packet["params"] = params; if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = CancelJob; return localId; } int Client::registerOpenWith(const QString& name, const QString& executable, const QList& filePatterns) { if (!m_jsonRpcClient) return -1; QJsonObject method; method["executable"] = executable; QJsonObject packet(buildRegisterOpenWithRequest(name, filePatterns, method)); if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = RegisterOpenWith; return localId; } int Client::registerOpenWith(const QString& name, const QString& rpcServer, const QString& rpcMethod, const QList& filePatterns) { if (!m_jsonRpcClient) return -1; QJsonObject method; method["rpcServer"] = rpcServer; method["rpcMethod"] = rpcMethod; QJsonObject packet(buildRegisterOpenWithRequest(name, filePatterns, method)); if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = RegisterOpenWith; return localId; } int Client::listOpenWithNames() { if (!m_jsonRpcClient) return -1; QJsonObject packet = m_jsonRpcClient->emptyRequest(); packet["method"] = QLatin1String("listOpenWithNames"); if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = ListOpenWithNames; return localId; } int Client::unregisterOpenWith(const QString& handlerName) { if (!m_jsonRpcClient) return -1; QJsonObject packet = m_jsonRpcClient->emptyRequest(); packet["method"] = QLatin1String("unregisterOpenWith"); QJsonObject params; params["name"] = handlerName; packet["params"] = params; if (!m_jsonRpcClient->sendRequest(packet)) return -1; int localId = static_cast(packet["id"].toDouble()); m_requests[localId] = UnregisterOpenWith; return localId; } void Client::flush() { if (m_jsonRpcClient) m_jsonRpcClient->flush(); } void Client::processResult(const QJsonObject& response) { if (response["id"] != QJsonValue::Null && m_requests.contains(static_cast(response["id"].toDouble()))) { int localId = static_cast(response["id"].toDouble()); switch (m_requests[localId]) { case ListQueues: emit queueListReceived(response["result"].toObject()); break; case SubmitJob: emit submitJobResponse( localId, static_cast( response["result"].toObject()["moleQueueId"].toDouble())); break; case LookupJob: emit lookupJobResponse(localId, response["result"].toObject()); break; case CancelJob: emit cancelJobResponse(static_cast( response["result"].toObject()["moleQueueId"].toDouble())); break; case RegisterOpenWith: emit registerOpenWithResponse(localId); break; case ListOpenWithNames: emit listOpenWithNamesResponse(localId, response["result"].toArray()); break; case UnregisterOpenWith: emit unregisterOpenWithResponse(localId); break; default: break; } } } void Client::processNotification(const QJsonObject& notification) { if (notification["method"].toString() == "jobStateChanged") { QJsonObject params = notification["params"].toObject(); emit jobStateChanged( static_cast(params["moleQueueId"].toDouble()), params["oldState"].toString(), params["newState"].toString()); } } void Client::processError(const QJsonObject& error) { int localId = static_cast(error["id"].toDouble()); int errorCode = -1; QString errorMessage = tr("No message specified."); QJsonValue errorData; const QJsonValue& errorValue = error.value(QLatin1String("error")); if (errorValue.isObject()) { const QJsonObject errorObject = errorValue.toObject(); if (errorObject.value("code").isDouble()) errorCode = static_cast(errorObject.value("code").toDouble()); if (errorObject.value("message").isString()) errorMessage = errorObject.value("message").toString(); if (errorObject.contains("data")) errorData = errorObject.value("data"); } emit errorReceived(localId, errorCode, errorMessage, errorData); } QJsonObject Client::buildRegisterOpenWithRequest( const QString& name, const QList& filePatterns, const QJsonObject& handlerMethod) { QJsonArray patterns; foreach (const QRegularExpression& regex, filePatterns) { QJsonObject pattern; pattern["regex"] = regex.pattern(); pattern["caseSensitive"] = regex.patternOptions().testFlag( QRegularExpression::CaseInsensitiveOption); patterns.append(pattern); } QJsonObject params; params["name"] = name; params["method"] = handlerMethod; params["patterns"] = patterns; QJsonObject packet = m_jsonRpcClient->emptyRequest(); packet["method"] = QLatin1String("registerOpenWith"); packet["params"] = params; return packet; } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/client/client.h000066400000000000000000000174431506155467400230640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_CLIENT_H #define AVOGADRO_MOLEQUEUE_CLIENT_H #include "avogadromolequeueexport.h" #include #include #include #include #include namespace Avogadro { namespace MoleQueue { class JsonRpcClient; class JobObject; /** * @class Client client.h * @brief The Client class is used by clients to submit jobs to a running * MoleQueue server. * @author Marcus D. Hanwell * * Provides a simple Qt C++ API to use the MoleQueue JSON-RPC calls to submit * and query the state of submitted jobs. */ class AVOGADROMOLEQUEUE_EXPORT Client : public QObject { Q_OBJECT public: explicit Client(QObject* parent_ = nullptr); ~Client() override; /** * Query if the client is connected to a server. * @return True if connected, false if not. */ bool isConnected() const; public slots: /** * Connect to the server. * @param serverName Name of the socket to connect to, the default of * "MoleQueue" is usually correct when connecting to the running MoleQueue. */ bool connectToServer(const QString& serverName = "MoleQueue"); /** * Request the list of queues and programs from the server. The signal * queueListReceived() will be emitted once this has been received. * @return The local ID of the job submission request. */ int requestQueueList(); /** * Submit a job to MoleQueue. If the returned local ID is retained the signal * for a job submission will provide the MoleQueue ID along with the local ID. * @param job The job specification to be submitted to MoleQueue. * @return The local ID of the job submission request. */ int submitJob(const JobObject& job); /** * Request information about a job. You should supply the MoleQueue ID that * was received in response to a job submission. * @param moleQueueId The MoleQueue ID for the job. * @return The local ID of the job submission request. */ int lookupJob(unsigned int moleQueueId); /** * Cancel a job that was submitted. * @param moleQueueId The MoleQueue ID for the job. * @return The local ID of the job submission request. */ int cancelJob(unsigned int moleQueueId); /** * Register an executable file handler with MoleQueue. * @param name GUI name of the file handler. * @param executable Executable to call with the filename as the first * argument. If the full path to the exectuble is not specified, it must be * in the user's $PATH. * @param filePatterns A list of QRegularExpression objects that the handler * can open. The QRegularExpression objects may only use the new * QRegularExpression pattern syntax. * @return The local ID of the request. * @note The executable is expected to use the following calling convention * to open files: ~~~ executable /absolute/path/to/selected/fileName ~~~ */ int registerOpenWith(const QString& name, const QString& executable, const QList& filePatterns); /** * Register a JSON-RPC 2.0 local socket file handler with MoleQueue. * @param name GUI name of the file handler. * @param rpcServer Name of the local socket that the server is listening on. * @param rpcMethod JSON-RPC 2.0 request method to use. * @param filePatterns A list of QRegularExpression objects that the handler * can open. The QRegularExpression objects may only use the new * QRegularExpression pattern syntax. * @return The local ID of the request. * @note The following JSON-RPC 2.0 request is sent to the server when the * handler is activated: ~~~ { "jsonrpc": "2.0", "method": "", "params": { "fileName": "/absolute/path/to/selected/fileName" } }, "id": "XXX" } ~~~ * where is replaced by the @a rpcMethod argument. */ int registerOpenWith(const QString& name, const QString& rpcServer, const QString& rpcMethod, const QList& filePatterns); /** * @brief Request a list of all file handler names. * @return The local ID of the request. */ int listOpenWithNames(); /** * @brief Unregister the indicated file handler from the molequeue server. * @param handlerName Name of the file handler to remove. * @return The local ID of the request. * @sa listOpenWithNames */ int unregisterOpenWith(const QString& handlerName); /** * @brief flush Flush all pending messages to the server. * @warning This should not need to be called if used in an event loop, as Qt * will start writing to the socket as soon as control returns to the event * loop. */ void flush(); signals: /** * Emitted when the connection state changes. */ void connectionStateChanged(); /** * Emitted when the remote queue list is received. This gives a list of lists, * the primary key is the queue name, and that contains a list of available * programs for each queue. * @param queues A JSON object containing the names of the queues and the * programs each queue have available. */ void queueListReceived(QJsonObject queues); /** * Emitted when the job request response is received. * @param localId The local ID the job submission response is in reply to. * @param moleQueueId The remote MoleQueue ID for the job submission (can be * used to perform further actions on the job). */ void submitJobResponse(int localId, unsigned int moleQueueId); /** * Emitted when a job lookup response is received. * @param localId The local ID the job submission response is in reply to. * @param jobInfo A Json object containing all available job information. */ void lookupJobResponse(int localId, QJsonObject jobInfo); /** * Emitted when a job is successfully cancelled. */ void cancelJobResponse(unsigned int moleQueueId); /** * Emitted when the job state changes. */ void jobStateChanged(unsigned int moleQueueId, QString oldState, QString newState); /** * Emitted when a successful registerOpenWith response is received. */ void registerOpenWithResponse(int localId); /** * Emitted when a successful listOpenWithNames response is received. */ void listOpenWithNamesResponse(int localId, QJsonArray handlerNames); /** * Emitted when a successful unregisterOpenWith response is received. */ void unregisterOpenWithResponse(int localId); /** * Emitted when an error response is received. */ void errorReceived(int localId, int errorCode, QString errorMessage, QJsonValue errorData); protected slots: /** Parse the response object and emit the appropriate signal(s). */ void processResult(const QJsonObject& response); /** Parse a notification object and emit the appropriate signal(s). */ void processNotification(const QJsonObject& notification); /** Parse an error object and emit the appropriate signal(s). */ void processError(const QJsonObject& notification); protected: enum MessageType { Invalid = -1, ListQueues, SubmitJob, CancelJob, LookupJob, RegisterOpenWith, ListOpenWithNames, UnregisterOpenWith }; JsonRpcClient* m_jsonRpcClient; QHash m_requests; private: QJsonObject buildRegisterOpenWithRequest( const QString& name, const QList& filePatterns, const QJsonObject& handlerMethod); }; } // End namespace MoleQueue } // End namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_CLIENT_H avogadrolibs-1.101.0/avogadro/molequeue/client/jobobject.cpp000066400000000000000000000060231506155467400240720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "jobobject.h" #include namespace Avogadro::MoleQueue { JobObject::JobObject() {} JobObject::~JobObject() {} void JobObject::setValue(const QString& key, const QVariant& value_) { m_value[key] = QJsonValue::fromVariant(value_); } QVariant JobObject::value(const QString& key, const QVariant& defaultValue) const { return m_value.contains(key) ? m_value[key].toVariant() : defaultValue; } void JobObject::setQueue(const QString& queueName) { m_value["queue"] = queueName; } QString JobObject::queue() const { return m_value["queue"].toString(); } void JobObject::setProgram(const QString& programName) { m_value["program"] = programName; } QString JobObject::program() const { return m_value["program"].toString(); } void JobObject::setDescription(const QString& descriptionText) { m_value["description"] = descriptionText; } QString JobObject::description() const { return m_value["description"].toString(); } void JobObject::setInputFile(const QString& fileName, const QString& contents) { m_value["inputFile"] = fileSpec(fileName, contents); } void JobObject::setInputFile(const QString& path) { m_value["inputFile"] = fileSpec(path); } void JobObject::setInputFile(const QJsonObject& file) { m_value["inputFile"] = file; } QJsonObject JobObject::inputFile() const { return m_value["inputFile"].toObject(); } void JobObject::appendAdditionalInputFile(const QString& fileName, const QString& contents) { QJsonArray extraInputFiles; if (m_value["additionalInputFiles"].isArray()) extraInputFiles = m_value["additionalInputFiles"].toArray(); extraInputFiles.append(fileSpec(fileName, contents)); m_value["additionalInputFiles"] = extraInputFiles; } void JobObject::appendAdditionalInputFile(const QString& path) { QJsonArray extraInputFiles; if (m_value["additionalInputFiles"].isArray()) extraInputFiles = m_value["additionalInputFiles"].toArray(); extraInputFiles.append(fileSpec(path)); m_value["additionalInputFiles"] = extraInputFiles; } void JobObject::setAdditionalInputFiles(const QJsonArray& files) { m_value["additionalInputFiles"] = files; } void JobObject::clearAdditionalInputFiles() { m_value.remove("additionalInputFiles"); } QJsonArray JobObject::additionalInputFiles() const { return m_value["additionalInputFiles"].toArray(); } QJsonObject JobObject::fileSpec(const QString& fileName, const QString& contents) { QJsonObject result; result["filename"] = fileName; result["contents"] = contents; return result; } QJsonObject JobObject::fileSpec(const QString& path) { QJsonObject result; result["path"] = path; return result; } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/client/jobobject.h000066400000000000000000000120241506155467400235350ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_JOBOBJECT_H #define AVOGADRO_MOLEQUEUE_JOBOBJECT_H #include "avogadromolequeueexport.h" #include #include #include namespace Avogadro { namespace MoleQueue { /** * @class JobObject jobobject.h * @brief Simple client-side representation for a MoleQueue job. * @author Marcus D. Hanwell * * The Job class provides a simple interface to the client side representation * of a job to be submitted to MoleQueue. Any fields that are not set/present * will be omitted entirely, or set to default values by MoleQueue. The internal * representation of a job (and the transport used) is JSON. * * The Job class and data structure is very lightweight, and designed to be * easily copied, modified and passed around. */ class AVOGADROMOLEQUEUE_EXPORT JobObject { public: JobObject(); ~JobObject(); /** Set the @p value of the specified @p key. */ void setValue(const QString& key, const QVariant& value); /** Get the value of the specified @p key. If the key is not set, return * @p defaultValue. */ QVariant value(const QString& key, const QVariant& defaultValue = QVariant()) const; /** * Set the job up using the supplied JSON object. This replaces all previous * settings that may have been applied. */ void fromJson(const QJsonObject& jsonObject) { m_value = jsonObject; } /** Get the JSON object with the current job settings in it. */ QJsonObject json() const { return m_value; } /** * Set the queue that the job should be submitted to. This must be a valid * queue name discovered using the client API. */ void setQueue(const QString& queueName); /** * Get the name of the queue that the job will be submitted to. An empty * string means that no queue has been set. */ QString queue() const; /** * Set the program that the job should be submitted to. This must be a valid * program in a valid queue as discovered using the client API. */ void setProgram(const QString& programName); /** * Get the name of the program that the job will be submitted to. An empty * string means that no program has been set. */ QString program() const; /** * Set the description of the job, this is free text. */ void setDescription(const QString& descriptionText); /** * Get the description of the job. */ QString description() const; /** * @brief Set the input file for the job. * @param fileName The file name as it will appear in the working directory. * @param contents The contents of the file specified. */ void setInputFile(const QString& fileName, const QString& contents); /** * Set the input file for the job, the file will be copied and the file name * used in the working directory of the job submission. * \param path The full path to the input file. */ void setInputFile(const QString& path); /** * Set the input file using a JSON object. This must conform to the file * specification. * @param file A JSON object employing file specification to specify input. */ void setInputFile(const QJsonObject& file); /** * Get the input file for the job. This is a JSON object using the file spec. */ QJsonObject inputFile() const; /** * Append an additional input file for the job. * @param fileName The file name as it will appear in the working directory. * @param contents The contents of the file specified. */ void appendAdditionalInputFile(const QString& fileName, const QString& contents); /** * Append an additional input file for the job, the file will be copied and * the file name used in the working directory of the job submission. * @param path The full path to the input file. */ void appendAdditionalInputFile(const QString& path); /** * Set the additional input file using a JSON object. This must conform to the * file specification. * @param files A JSON array employing file specification to specify input. */ void setAdditionalInputFiles(const QJsonArray& files); /** Clear additional input files. */ void clearAdditionalInputFiles(); /** * Get the additional input files for the job. This is a JSON object using the * file spec. */ QJsonArray additionalInputFiles() const; protected: QJsonObject m_value; /** * Generate a filespec JSON object form the supplied file name and contents. */ QJsonObject fileSpec(const QString& fileName, const QString& contents); /** * Generate a filespec JSON object form the supplied file path (must exist). */ QJsonObject fileSpec(const QString& path); }; } // End namespace MoleQueue } // End namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_JOBOBJECT_H avogadrolibs-1.101.0/avogadro/molequeue/client/jsonrpcclient.cpp000066400000000000000000000066121506155467400250120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "jsonrpcclient.h" #include #include #include #include namespace Avogadro::MoleQueue { JsonRpcClient::JsonRpcClient(QObject* parent_) : QObject(parent_), m_packetCounter(0), m_socket(nullptr) { connect(this, SIGNAL(newPacket(QByteArray)), SLOT(readPacket(QByteArray)), Qt::QueuedConnection); } JsonRpcClient::~JsonRpcClient() { flush(); } bool JsonRpcClient::isConnected() const { if (!m_socket) return false; else return m_socket->isOpen(); } bool JsonRpcClient::connectToServer(const QString& serverName_) { if (m_socket && m_socket->isOpen()) { if (m_socket->serverName() == serverName_) { return false; } else { m_socket->close(); delete m_socket; m_socket = nullptr; } } // New connection. if (m_socket == nullptr) { m_socket = new QLocalSocket(this); connect(m_socket, SIGNAL(readyRead()), this, SLOT(readSocket())); } if (serverName_.isEmpty()) { return false; } else { m_socket->connectToServer(serverName_); return isConnected(); } } QString JsonRpcClient::serverName() const { if (m_socket) return m_socket->serverName(); else return QString(); } void JsonRpcClient::flush() { if (m_socket) m_socket->flush(); } QJsonObject JsonRpcClient::emptyRequest() { QJsonObject request; request["jsonrpc"] = QLatin1String("2.0"); request["id"] = static_cast(m_packetCounter++); return request; } bool JsonRpcClient::sendRequest(const QJsonObject& request) { if (!m_socket) return false; QJsonDocument document(request); QDataStream stream(m_socket); stream.setVersion(QDataStream::Qt_4_8); stream << document.toJson(); return true; } void JsonRpcClient::readPacket(const QByteArray message) { // Read packet into a Json value QJsonParseError error; QJsonDocument reader = QJsonDocument::fromJson(message, &error); if (error.error != QJsonParseError::NoError) { emit badPacketReceived("Unparsable message received\n:" + error.errorString() + "\nContent: " + message); return; } else if (!reader.isObject()) { // We need a valid object, something bad happened. emit badPacketReceived("Packet did not contain a valid JSON object."); return; } else { QJsonObject root = reader.object(); if (root["method"] != QJsonValue::Null) { if (root["id"] != QJsonValue::Null) emit badPacketReceived("Received a request packet for the client."); else emit notificationReceived(root); } if (root["result"] != QJsonValue::Null) { // This is a result packet, and should emit a signal. emit resultReceived(root); } else if (root["error"] != QJsonValue::Null) { emit errorReceived(root); } } } void JsonRpcClient::readSocket() { if (m_socket->bytesAvailable() > 0) { QDataStream stream(m_socket); QByteArray json; stream >> json; emit newPacket(json); if (m_socket->bytesAvailable() > 0) QTimer::singleShot(0, this, SLOT(readSocket())); } } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/client/jsonrpcclient.h000066400000000000000000000074341506155467400244620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_JSONRPCCLIENT_H #define AVOGADRO_MOLEQUEUE_JSONRPCCLIENT_H #include "avogadromolequeueexport.h" #include #include class QLocalSocket; namespace Avogadro { namespace MoleQueue { /** * @class JsonRpcClient jsonrpcclient.h * @brief The JsonRpcClient class is used by clients to submit calls to an RPC * server using JSON-RPC 2.0. * @author Marcus D. Hanwell * * Provides a simple Qt C++ API to make JSON-RPC 2.0 calls to an RPC server. To * create a client connection and call a method the following should be done: * @code #include MoleQueue::JsonRpcClient *client = new MoleQueue::JsonRpcClient(this); client->connectToServer("MyRpcServer"); QJsonObject request(client->emptyRequest()); request["method"] = QLatin1String("listQueues"); client->sendRequest(request); @endcode * * You should connect to the appropriate signals in order to act on results, * notifications and errors received in response to requests set using the * client connection. */ class AVOGADROMOLEQUEUE_EXPORT JsonRpcClient : public QObject { Q_OBJECT public: explicit JsonRpcClient(QObject* parent_ = nullptr); ~JsonRpcClient() override; /** * Query if the client is connected to a server. * @return True if connected, false if not. */ bool isConnected() const; /** * @return The server name that the client is connected to. */ QString serverName() const; public slots: /** * Connect to the server. * @param serverName Name of the socket to connect to. */ bool connectToServer(const QString& serverName); /** * @brief flush Flush all pending messages to the server. * @warning This should not need to be called if used in an event loop, as Qt * will start writing to the socket as soon as control returns to the event * loop. */ void flush(); /** * Use this function to construct an empty JSON-RPC 2.0 request, with a valid * request id, JSON-RPC 2.0 key etc. * @return a standard empty JSON-RPC 2.0 packet, the method etc is empty. */ QJsonObject emptyRequest(); /** * Send the Json request to the RPC server. * @param request The JSON-RPC 2.0 request object. * @return True on success, false on failure. */ bool sendRequest(const QJsonObject& request); protected slots: /** * Read incoming packets of data from the server. */ void readPacket(const QByteArray message); /** * Read incoming data, interpret JSON stream. */ void readSocket(); signals: /** * Emitted when the connection state changes. */ void connectionStateChanged(); /** * Emitted when a result is received. */ void resultReceived(QJsonObject message); /** * Emitted when a notification is received. */ void notificationReceived(QJsonObject message); /** * Emitted when an error response is received. */ void errorReceived(QJsonObject message); /** * Emitted when a bad packet was received that the client could not parse. */ void badPacketReceived(QString error); /** * Emitted when a new packet of data is received. This is handled internally, * other classes should listen to resultReceived, notificationReceived, * errorReceived, and badPacketReceived. */ void newPacket(const QByteArray& packet); protected: unsigned int m_packetCounter; QLocalSocket* m_socket; }; } // End namespace MoleQueue } // End namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_JSONRPCCLIENT_H avogadrolibs-1.101.0/avogadro/molequeue/inputgenerator.cpp000066400000000000000000000533131506155467400237250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "inputgenerator.h" #include #include #include #include #include #include #include #include #include #include namespace Avogadro::MoleQueue { using QtGui::GenericHighlighter; using QtGui::PythonScript; InputGenerator::InputGenerator(const QString& scriptFilePath_, QObject* parent_) : QObject(parent_), m_interpreter(new PythonScript(scriptFilePath_, this)), m_moleculeExtension("Unknown") { } InputGenerator::InputGenerator(QObject* parent_) : QObject(parent_), m_interpreter(new PythonScript(this)), m_moleculeExtension("Unknown") { } bool InputGenerator::debug() const { return m_interpreter->debug(); } QJsonObject InputGenerator::options() const { m_errors.clear(); if (m_options.isEmpty()) { qDeleteAll(m_highlightStyles.values()); m_highlightStyles.clear(); // Retrieve/set options QByteArray json = m_interpreter->execute(QStringList() << "--print-options"); if (m_interpreter->hasErrors()) { m_errors << m_interpreter->errorList(); return m_options; } QJsonDocument doc; if (!parseJson(json, doc)) return m_options; if (!doc.isObject()) { m_errors << tr("script --print-options output must be an JSON object " "at top level. Received:\n%1") .arg(json.constData()); return m_options; } m_options = doc.object(); // Check if the generator needs to read a molecule. m_moleculeExtension = "None"; if (m_options.contains("inputMoleculeFormat") && m_options["inputMoleculeFormat"].isString()) { m_moleculeExtension = m_options["inputMoleculeFormat"].toString(); } if (m_options.contains("highlightStyles") && m_options.value("highlightStyles").isArray()) { if (!parseHighlightStyles(m_options.value("highlightStyles").toArray())) { qDebug() << "Failed to parse highlighting styles."; } } } return m_options; } QString InputGenerator::displayName() const { m_errors.clear(); if (m_displayName.isEmpty()) { m_displayName = QString(m_interpreter->execute(QStringList() << "--display-name")); m_errors << m_interpreter->errorList(); m_displayName = m_displayName.trimmed(); } return m_displayName; } QString InputGenerator::scriptFilePath() const { return m_interpreter->scriptFilePath(); } void InputGenerator::setScriptFilePath(const QString& scriptFile) { reset(); m_interpreter->setScriptFilePath(scriptFile); } void InputGenerator::reset() { m_interpreter->setDefaultPythonInterpreter(); m_interpreter->setScriptFilePath(QString()); m_moleculeExtension = "Unknown"; m_displayName = QString(); m_options = QJsonObject(); m_warnings.clear(); m_errors.clear(); m_filenames.clear(); m_mainFileName.clear(); m_files.clear(); m_fileHighlighters.clear(); m_highlightStyles.clear(); } bool InputGenerator::generateInput(const QJsonObject& options_, const Core::Molecule& mol) { m_errors.clear(); m_warnings.clear(); m_filenames.clear(); qDeleteAll(m_fileHighlighters.values()); m_fileHighlighters.clear(); m_mainFileName.clear(); m_files.clear(); // Add the molecule file to the options QJsonObject allOptions(options_); if (!insertMolecule(allOptions, mol)) return false; QByteArray json(m_interpreter->execute(QStringList() << "--generate-input", QJsonDocument(allOptions).toJson())); if (m_interpreter->hasErrors()) { m_errors << m_interpreter->errorList(); return false; } QJsonDocument doc; if (!parseJson(json, doc)) return false; // Update cache bool result = true; if (doc.isObject()) { QJsonObject obj = doc.object(); // Check for any warnings: if (obj.contains("warnings")) { if (obj["warnings"].isArray()) { foreach (const QJsonValue& warning, obj["warnings"].toArray()) { if (warning.isString()) m_warnings << warning.toString(); else m_errors << tr("Non-string warning returned."); } } else { m_errors << tr("'warnings' member is not an array."); } } // Extract input file text: if (obj.contains("files")) { if (obj["files"].isArray()) { foreach (const QJsonValue& file, obj["files"].toArray()) { if (file.isObject()) { QJsonObject fileObj = file.toObject(); if (fileObj["filename"].isString()) { QString fileName = fileObj["filename"].toString(); QString contents; if (fileObj["contents"].isString()) { contents = fileObj["contents"].toString(); } else if (fileObj["filePath"].isString()) { QFile refFile(fileObj["filePath"].toString()); if (refFile.exists() && refFile.open(QFile::ReadOnly)) { contents = QString(refFile.readAll()); } else { contents = tr("Reference file '%1' does not exist.") .arg(refFile.fileName()); m_warnings << tr("Error populating file %1: %2") .arg(fileName, contents); } } else { m_errors << tr("File '%1' poorly formed. Missing string " "'contents' or 'filePath' members.") .arg(fileName); contents = m_errors.back(); result = false; } replaceKeywords(contents, mol); m_filenames << fileName; m_files.insert(fileObj["filename"].toString(), contents); // Concatenate the requested styles for this input file. if (fileObj["highlightStyles"].isArray()) { auto* highlighter(new GenericHighlighter(this)); foreach (const QJsonValue& styleVal, fileObj["highlightStyles"].toArray()) { if (styleVal.isString()) { QString styleName(styleVal.toString()); if (m_highlightStyles.contains(styleName)) { *highlighter += *m_highlightStyles[styleName]; } else { qDebug() << "Cannot find highlight style '" << styleName << "' for file '" << fileName << "'"; } } } if (highlighter->ruleCount() > 0) m_fileHighlighters[fileName] = highlighter; else highlighter->deleteLater(); } } else { result = false; m_errors << tr("Malformed file entry: filename/contents missing" " or not strings:\n%1") .arg(QString(QJsonDocument(fileObj).toJson())); } // end if/else filename and contents are strings } else { result = false; m_errors << tr("Malformed file entry at index %1: Not an object.") .arg(m_filenames.size()); } // end if/else file is JSON object } // end foreach file } else { result = false; m_errors << tr("'files' member not an array."); } // end if obj["files"] is JSON array } else { result = false; m_errors << tr("'files' member missing."); } // end if obj contains "files" // Extract main input filename: if (obj.contains("mainFile")) { if (obj["mainFile"].isString()) { QString mainFile = obj["mainFile"].toString(); if (m_filenames.contains(mainFile)) { m_mainFileName = mainFile; } else { result = false; m_errors << tr("'mainFile' member does not refer to an entry in " "'files'."); } // end if/else mainFile is known } else { result = false; m_errors << tr("'mainFile' member must be a string."); } // end if/else mainFile is string } else { // If no mainFile is specified and there is only one file, use it as the // main file. Otherwise, don't set a main input file -- all files will // be treated as supplemental input files if (m_filenames.size() == 1) m_mainFileName = m_filenames.first(); } // end if/else object contains mainFile } else { result = false; m_errors << tr("Response must be a JSON object at top-level."); } if (!result) m_errors << tr("Script output:\n%1").arg(QString(json)); return result; } int InputGenerator::numberOfInputFiles() const { return m_filenames.size(); } QStringList InputGenerator::fileNames() const { return m_filenames; } QString InputGenerator::mainFileName() const { return m_mainFileName; } QString InputGenerator::fileContents(const QString& fileName) const { return m_files.value(fileName, QString()); } GenericHighlighter* InputGenerator::createFileHighlighter( const QString& fileName) const { GenericHighlighter* toClone(m_fileHighlighters.value(fileName, nullptr)); return toClone ? new GenericHighlighter(*toClone) : toClone; } void InputGenerator::setDebug(bool d) { m_interpreter->setDebug(d); } bool InputGenerator::parseJson(const QByteArray& json, QJsonDocument& doc) const { QJsonParseError error; doc = QJsonDocument::fromJson(json, &error); if (error.error != QJsonParseError::NoError) { m_errors << tr("Parse error at offset %L1: '%2'\nRaw JSON:\n\n%3") .arg(error.offset) .arg(error.errorString()) .arg(QString(json)); return false; } return true; } bool InputGenerator::insertMolecule(QJsonObject& json, const Core::Molecule& mol) const { // Update the cached options if the format is not set if (m_moleculeExtension == "Unknown") options(); if (m_moleculeExtension == "None") return true; Io::FileFormatManager& formats = Io::FileFormatManager::instance(); QScopedPointer format( formats.newFormatFromFileExtension(m_moleculeExtension.toStdString())); if (format.isNull()) { m_errors << tr("Error saving molecule representation to string: " "Unrecognized file format: %1") .arg(m_moleculeExtension); return false; } std::string str; if (!format->writeString(str, mol)) { m_errors << tr("Error saving molecule representation to string: %1", "%1 = error message") .arg(QString::fromStdString(format->error())); return false; } if (m_moleculeExtension != "cjson") { json.insert(m_moleculeExtension, QJsonValue(QString::fromStdString(str))); } else { // If cjson was requested, embed the actual JSON, rather than the string. QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(str.c_str(), &error); if (error.error != QJsonParseError::NoError) { m_errors << tr("Error generating cjson object: Parse error at offset %1: " "%2\nRaw JSON:\n\n%3") .arg(error.offset) .arg(error.errorString()) .arg(QString::fromStdString(str)); return false; } if (!doc.isObject()) { m_errors << tr("Error generator cjson object: Parsed JSON is not an " "object:\n%1") .arg(QString::fromStdString(str)); return false; } json.insert(m_moleculeExtension, doc.object()); } return true; } QString InputGenerator::generateCoordinateBlock(const QString& spec, const Core::Molecule& mol) const { Core::CoordinateBlockGenerator gen; gen.setMolecule(&mol); gen.setSpecification(spec.toStdString()); std::string tmp(gen.generateCoordinateBlock()); if (!tmp.empty()) tmp.resize(tmp.size() - 1); // Pop off the trailing newline return QString::fromStdString(tmp); } void InputGenerator::replaceKeywords(QString& str, const Core::Molecule& mol) const { // Simple keywords: str.replace("$$atomCount$$", QString::number(mol.atomCount())); str.replace("$$bondCount$$", QString::number(mol.bondCount())); // Find each coordinate block keyword in the file, then generate and replace // it with the appropriate values. QRegularExpression coordParser(R"(\$\$coords:([^\$]*)\$\$)"); QRegularExpressionMatch match; int ind = 0; // Not sure while this needs to be a while statement since we replace all in // one go? We never iterate ind... while ((match = coordParser.match(str, ind)).hasMatch()) { // Extract spec and prepare the replacement const QString keyword = match.captured(0); const QString spec = match.captured(1); // Replace all blocks with this signature str.replace(keyword, generateCoordinateBlock(spec, mol)); } // end for coordinate block } bool InputGenerator::parseHighlightStyles(const QJsonArray& json) const { bool result(true); foreach (QJsonValue styleVal, json) { if (!styleVal.isObject()) { qDebug() << "Non-object in highlightStyles array."; result = false; continue; } QJsonObject styleObj(styleVal.toObject()); if (!styleObj.contains("style")) { qDebug() << "Style object missing 'style' member."; result = false; continue; } if (!styleObj.value("style").isString()) { qDebug() << "Style object contains non-string 'style' member."; result = false; continue; } QString styleName(styleObj.value("style").toString()); if (m_highlightStyles.contains(styleName)) { qDebug() << "Duplicate highlight style: " << styleName; result = false; continue; } if (!styleObj.contains("rules")) { qDebug() << "Style object" << styleName << "missing 'rules' member."; result = false; continue; } if (!styleObj.value("rules").isArray()) { qDebug() << "Style object" << styleName << "contains non-array 'rules' member."; result = false; continue; } QJsonArray rulesArray(styleObj.value("rules").toArray()); auto* highlighter( new GenericHighlighter(const_cast(this))); if (!parseRules(rulesArray, *highlighter)) { qDebug() << "Error parsing style" << styleName << '\n' << QString(QJsonDocument(styleObj).toJson()); highlighter->deleteLater(); result = false; continue; } m_highlightStyles.insert(styleName, highlighter); } return result; } bool InputGenerator::parseRules(const QJsonArray& json, GenericHighlighter& highligher) const { bool result(true); foreach (QJsonValue ruleVal, json) { if (!ruleVal.isObject()) { qDebug() << "Rule is not an object."; result = false; continue; } QJsonObject ruleObj(ruleVal.toObject()); if (!ruleObj.contains("patterns")) { qDebug() << "Rule missing 'patterns' array:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } if (!ruleObj.value("patterns").isArray()) { qDebug() << "Rule 'patterns' member is not an array:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } QJsonArray patternsArray(ruleObj.value("patterns").toArray()); if (!ruleObj.contains("format")) { qDebug() << "Rule missing 'format' object:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } if (!ruleObj.value("format").isObject()) { qDebug() << "Rule 'format' member is not an object:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } QJsonObject formatObj(ruleObj.value("format").toObject()); GenericHighlighter::Rule& rule = highligher.addRule(); foreach (QJsonValue patternVal, patternsArray) { QRegularExpression pattern; if (!parsePattern(patternVal, pattern)) { qDebug() << "Error while parsing pattern:" << '\n' << QString(QJsonDocument(patternVal.toObject()).toJson()); result = false; continue; } rule.addPattern(pattern); } QTextCharFormat format; if (!parseFormat(formatObj, format)) { qDebug() << "Error while parsing format:" << '\n' << QString(QJsonDocument(formatObj).toJson()); result = false; } rule.setFormat(format); } return result; } bool InputGenerator::parseFormat(const QJsonObject& json, QTextCharFormat& format) const { // Check for presets first: if (json.contains("preset")) { if (!json["preset"].isString()) { qDebug() << "Preset is not a string."; return false; } QString preset(json["preset"].toString()); /// @todo Store presets in a singleton that can be configured in the GUI, /// rather than hardcoding them. if (preset == "title") { format.setFontFamily("serif"); format.setForeground(Qt::darkGreen); format.setFontWeight(QFont::Bold); } else if (preset == "keyword") { format.setFontFamily("mono"); format.setForeground(Qt::darkBlue); } else if (preset == "property") { format.setFontFamily("mono"); format.setForeground(Qt::darkRed); } else if (preset == "literal") { format.setFontFamily("mono"); format.setForeground(Qt::darkMagenta); } else if (preset == "comment") { format.setFontFamily("serif"); format.setForeground(Qt::darkGreen); format.setFontItalic(true); } else { qDebug() << "Invalid style preset: " << preset; return false; } return true; } // Extract an RGB tuple from 'array' as a QBrush: struct { QBrush operator()(const QJsonArray& array, bool* ok) { *ok = false; QBrush result; if (array.size() != 3) return result; int rgb[3]; for (int i = 0; i < 3; ++i) { if (!array.at(i).isDouble()) return result; rgb[i] = static_cast(array.at(i).toDouble()); if (rgb[i] < 0 || rgb[i] > 255) { qDebug() << "Warning: Color component value invalid: " << rgb[i] << " (Valid range is 0-255)."; } } result.setColor(QColor(rgb[0], rgb[1], rgb[2])); result.setStyle(Qt::SolidPattern); *ok = true; return result; } } colorParser; if (json.contains("foreground") && json.value("foreground").isArray()) { QJsonArray foregroundArray(json.value("foreground").toArray()); bool ok; format.setForeground(colorParser(foregroundArray, &ok)); if (!ok) return false; } if (json.contains("background") && json.value("background").isArray()) { QJsonArray backgroundArray(json.value("background").toArray()); bool ok; format.setBackground(colorParser(backgroundArray, &ok)); if (!ok) return false; } if (json.contains("attributes") && json.value("attributes").isArray()) { QJsonArray attributesArray(json.value("attributes").toArray()); format.setFontWeight(attributesArray.contains(QLatin1String("bold")) ? QFont::Bold : QFont::Normal); format.setFontItalic(attributesArray.contains(QLatin1String("italic"))); format.setFontUnderline( attributesArray.contains(QLatin1String("underline"))); } if (json.contains("family") && json.value("family").isString()) { format.setFontFamily(json.value("family").toString()); } return true; } bool InputGenerator::parsePattern(const QJsonValue& json, QRegularExpression& pattern) const { if (!json.isObject()) return false; QJsonObject patternObj(json.toObject()); QString regexPattern; QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption; if (patternObj.contains(QStringLiteral("regexp")) && patternObj.value(QStringLiteral("regexp")).isString()) { // Use the provided regular expression as-is regexPattern = patternObj.value(QStringLiteral("regexp")).toString(); } else if (patternObj.contains(QStringLiteral("wildcard")) && patternObj.value(QStringLiteral("wildcard")).isString()) { // Convert wildcard pattern (* -> .* and ? -> .) QString wildcard = patternObj.value(QStringLiteral("wildcard")).toString(); regexPattern = QRegularExpression::escape(wildcard) .replace("\\*", ".*") .replace("\\?", "."); } else if (patternObj.contains(QStringLiteral("string")) && patternObj.value(QStringLiteral("string")).isString()) { // Escape the string so it is treated literally in the regex regexPattern = QRegularExpression::escape( patternObj.value(QStringLiteral("string")).toString()); } else { return false; } // Set case sensitivity if specified if (patternObj.contains(QStringLiteral("caseSensitive"))) { bool caseSensitive = patternObj.value(QStringLiteral("caseSensitive")).toBool(true); if (!caseSensitive) { patternOptions |= QRegularExpression::CaseInsensitiveOption; } } // Set the final pattern with options pattern = QRegularExpression(regexPattern, patternOptions); return pattern.isValid(); } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/inputgenerator.h000066400000000000000000000521671506155467400234000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_INPUTGENERATOR_H #define AVOGADRO_MOLEQUEUE_INPUTGENERATOR_H #include "avogadromolequeueexport.h" #include #include #include #include #include #include class QJsonDocument; class QProcess; class QTextCharFormat; namespace Avogadro { namespace Core { class Molecule; } namespace QtGui { class GenericHighlighter; class PythonScript; } // namespace QtGui namespace MoleQueue { /** * @class InputGenerator inputgenerator.h * @brief The InputGenerator class provides an interface to input generator * scripts. * @sa InputGeneratorWidget * * The QuantumInput extension provides a scriptable method for users to add * custom input generators to Avogadro. By writing an executable that implements * the interface defined below, new input generators can be created faster and * easier than writing full Avogadro extensions. * * Script Entry Points * =================== * * The script must handle the following command-line arguments: * - `--debug` Enable extra debugging output. Used with other commands. * It is not required that the script support extra debugging, but it should * not crash when this option is passed. * - `--print-options` Print the available options supported by the * script, e.g. simulation parameters, etc. See below for more details. * - `--generate-input` Read an option block from stdin and print * input files to stdout. See below for more details. * - `--display-name` Print a user-friendly name for the input generator. * This is used in the GUI for menu entries, window titles, etc. * * Specifying parameters with `--print-options` * ============================================ * * The format of the `--print-options` output must be a JSON object of * the following form: ~~~{.js} { "userOptions": { ... }, "highlightStyles": [ { "style": "Descriptive name", "rules": [ { "patterns": [ ... ], "format": { ... } }, ... ], }, ... ], "inputMoleculeFormat": "cjson" } ~~~ * The `userOptions` block contains a JSON object keyed with option names * (e.g. "First option name"), which are used in the GUI to label simulation * parameter settings. Various parameter types are supported: * * Fixed Mutually-Exclusive Parameter Lists * ---------------------------------------- * * Parameters that have a fixed number of mutually-exclusive string values will * be presented using a QComboBox. Such a parameter can be specified in the * `userOptions` block as: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "stringList", "values": ["Option 1", "Option 2", "Option 3"], "default": 0 } } } ~~~ * Here, `Parameter Name` is the label that will be displayed in the GUI as a * label next to the combo box. * The array of strings in `values` will be used as the available entries in * the combo box in the order they are written. * `default` is a zero-based index into the `values` array and indicates * which value should be initially selected by default. * * Short Free-Form Text Parameters * ------------------------------- * * A short text string can be requested (e.g. for the "title" of an * optimization) via: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "string", "default": "blah blah blah" } } } ~~~ * This will add a QLineEdit to the GUI, initialized with the text specified by * `default`. * * Existing files * -------------- * * An input generator can ask for the absolute path to an existing file using * the following option block: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "filePath", "default": "/path/to/some/file" } } } ~~~ * This will add an Avogadro::QtGui::FileBrowseWidget to the GUI, initialized to * the file pointed to by default. * * Clamped Integer Values * ---------------------- * * Scripts may request integer values from a specified range by adding a * user-option of the following form: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "integer", "minimum": -5, "maximum": 5, "default": 0, "prefix": "some text ", "suffix": " units" } } } ~~~ * This block will result in a QSpinBox, configured as follows: * - `minimum` and `maximum` indicate the valid range of integers for the * parameter. * - `default` is the integer value that will be shown initially. * - (optional) `prefix` and `suffix` are used to insert text before or * after the integer value in the spin box. * This is handy for specifying units. * Note that any prefix or suffix will be stripped out of the corresponding * entry in the call to `--generate-input`, and just the raw integer value * will be sent. * * Boolean Parameters * ------------------ * * If a simple on/off value is needed, a boolean type option can be requested: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "boolean", "default": true, } } } ~~~ * This will result in a QCheckBox in the dynamically generated GUI, with * the initial check state shown in `default`. * * Special Parameters * ------------------ * * Some parameters are common to most calculation codes. * If the following parameter names are found, they will be handled specially * while creating the GUI. * It is recommended to use the names below for these options to provide a * consistent interface and ensure that MoleQueue job staging uses correct * values where appropriate. * * | Option name | type | description | * | :----------------: | :--------: | :------------------------------------------------------------------ | * | "Title" | string | Input file title comment, MoleQueue job description. | * | "Filename Base" | string | Input file base name, e.g. "job" in "job.inp". | * | "Processor Cores" | integer | Number of cores to use. Will be passed to MoleQueue. | * | "Calculation Type" | stringList | Type of calculation, e.g. "Single Point" or "Equilibrium Geometry". | * | "Theory" | stringList | Levels of QM theory, e.g. "RHF", "B3LYP", "MP2", "CCSD", etc. | * | "Basis" | stringList | Available basis sets, e.g. "STO-3G", "6-31G**", etc. | * | "Charge" | integer | Charge on the system. | * | "Multiplicity" | integer | Spin multiplicity of the system. | * * Syntax Highlighting * ------------------- * * Rules for syntax highlighting can be specified as a collection of regular * expressions or wildcard patterns and text format specifications in the * "highlightRules" array. The `highlightRules` format is: ~~~{.js} "highlightStyles": [ { "style": "Style 1", "rules": [ (list of highlight rules, see below) ], }, { "style": "Style 2", "rules": [ (list of highlight rules, see below) ], }, ... ], ~~~ * The `style` name is unique to the style object, and used to associate a * set of highlighting rules with particular output files. See the * `--generate-input` documentation for more details. * * The general form of a highlight rule is: ~~~{.js} { "patterns": [ { "regexp": "^Some regexp?$" }, { "wildcard": "A * wildcard expression" }, { "string": "An exact string to match.", "caseSensitive": false }, ... ], "format": { "preset": "" } } ~~~ * * or, * ~~~{.js} { "patterns": [ ... ], "format": { "foreground": [ 255, 128, 64 ], "background": [ 0, 128, 128 ], "attributes": ["bold", "italic", "underline"], "family": "serif" } } ~~~ * * The `patterns` array contains a collection of fixed strings, wildcard * expressions, and regular expressions (using the QRegularExpression syntax * flavor, see the QRegularExpression documentation) that are used to identify * strings that should be formatted. * There must be one of the following members present in each pattern object: * - `regexp` A QRegularExpresion-style regular expression. If no capture groups * ("(...)") are defined, the entire match is formatted. If one or more * capture groups, only the captured texts will be marked. * - `wildcard` A wildcard expression * - `string` An exact string to match. * * Any pattern object may also set a boolean `caseSensitive` member to indicate * whether the match should consider character case. If omitted, a * case-sensitive match is assumed. * * The preferred form of the `format` member is simply a specification of a * preset format. * This allows for consistent color schemes across input generators. * The recognized presets are: * - `"title"`: A human readable title string. * - `"keyword"`: directives defined by the target input format specification * to have special meaning, such as tags indicating where coordinates are * to be found. * - `"property"`: A property of the simulation, such as level of theory, basis * set, minimization method, etc. * - `"literal"`: A numeric literal (i.e. a raw number, such as a coordinate). * - `"comment"`: Sections of the input that are ignored by the simulation code. * * If advanced formatting is desired, the second form of the `format` member * allows fine-tuning of the font properties: * - `foreground` color as an RGB tuple, ranged 0-255 * - `background` color as an RGB tuple, ranged 0-255 * - `attributes` array of font attributes, valid strings are `"bold"`, * `"italic"`, or `"underline"` * - `family` of font. Valid values are `"serif"`, `"sans"`, or `"mono"` * * Any of the font property members may be omitted and default QTextCharFormat * settings will be substituted. * * The input generator extension will apply the entries in the `highlightRules` * object to the text in the order they appear. Thus, later rules will * override the formatting of earlier rules should a conflict arise. * * Requesting Full Structure of Current Molecule * --------------------------------------------- * * The `inputMoleculeFormat` is optional, and can be used to request a * representation of the current molecule's geometry when * `--generate-input` is called. The corresponding value * indicates the format of the molecule that the script expects. If this value * is omitted, no representation of the structure will be provided. * * @note Currently valid options for inputMoleculeFormat are "cjson" for * Chemical JSON or "cml" for Chemical Markup Language. * * Handling User Selections: `--generate-input` * ============================================ * * When `--generate-input` is passed, the information needed to generate * the input file will be written to the script's standard input * channel as JSON string of the following form: ~~~{.js} { "cjson": {...}, "options": { "First option name": "Value 2", "Second option name": "Value 1", ... } } ~~~ * The `cjson` entry will contain a Chemical JSON representation * of the molecule if `inputMoleculeFormat` is set to "cjson" in the * `--print-options` output. * Similarly, a `cml` entry and CML string will exist if a Chemical Markup * Language representation was requested. * It will be omitted entirely if `inputMoleculeFormat` is not set. * The `options` block contains key/value * pairs for each of the options specified in the `userOptions` block of the * `--print-options` output. * * If the script is called with `--generate-input`, it must write a JSON * string to standard output with the following format: ~~~{.js} { "files": [ { "filename": "file1.ext", "contents": "...", "highlightStyles": [ ... ] }, { "filename": "file2.ext", "filePath": "/path/to/file/on/local/filesystem" }, ... ], "warnings": ["First warning.", "Second warning.", ... ], "mainFile": "file2.ext" } ~~~ * The `files` block is an array of objects, which define the actual input * files. The `filename` member provides the name of the file, and * either `contents` or `filePath` provide the text that goes into the file. * The `contents` string will be used as the file contents, and `filePath` * should contain an absolute path to a file on the filesystem to read and use * as the input file contents. * The optional `highlightStyles` member is an array of strings describing any * highlight styles to apply to the file (see `--print-options` documentation). * Each string in this array must match a `style` description in a highlighting * rule in the `--print-options` output. * Zero or more highlighting styles may be applied to any file. * The order of the files in the * GUI will match the order of the files in the array, and the first file will * be displayed first. * * The `warnings` member provides an array of strings that describe non-fatal * warnings to be shown to the users. This is useful for describing * the resolution of conflicting options, e.g. "Ignoring basis set for * semi-empirical calculation.". This member is optional and should be omitted * if no warnings are present. * * The `mainFile` member points to the primary input file for a calculation. * This is the file that will be used as a command line argument when executing * the simulation code (if applicable), and used by MoleQueue to set the * `$$inputFileName$$` and `$$inputFileBaseName$$` input template keywords. * This is optional; if present, the filename must exist in the `files` array. * If absent and only one file is specified in `files`, the single input file * will be used. Otherwise, the main file will be left unspecified. * * Automatic Generation of Geometry * ================================ * * The generation of molecular geometry descriptions may be skipped in the * script and deferred to the InputGenerator class by use of a special keyword. * The "contents" string may contain a keyword of the form ~~~ $$coords:[coordSpec]$$ ~~~ * where `[coordSpec]` is a sequence of characters. * The characters in `[coordSpec]` indicate the information needed about each * atom in the coordinate block. * See the CoordinateBlockGenerator documentation for a list of recognized * characters. * * Other keywords that can be used in the input files are: * - `$$atomCount$$`: Number of atoms in the molecule. * - `$$bondCount$$`: Number of bonds in the molecule. * * Error Handling * ============== * * In general, these scripts should be written robustly so that they will not * fail under normal circumstances. However, if for some reason an error * occurs that must be reported to the user, simply write the error message to * standard output as plain text (i.e. not JSON), and it will be shown to the * user. * * Debugging * ========= * * Debugging may be enabled by defining AVO_QM_INPUT_DEBUG in the process's * environment. This will cause the --debug option to be passed in * all calls to generator scripts, and will print extra information to the * qDebug() stream from within avogadro. The script is free to handle the * debug flag as the author wishes. */ class AVOGADROMOLEQUEUE_EXPORT InputGenerator : public QObject { Q_OBJECT public: /** * Constructor * @param scriptFilePath_ Absolute path to generator script. */ explicit InputGenerator(const QString& scriptFilePath_, QObject* parent_ = nullptr); explicit InputGenerator(QObject* parent_ = nullptr); ~InputGenerator() override = default; /** * @return True if debugging is enabled. */ bool debug() const; /** * @return True if the generator is configured with a valid script. */ bool isValid() const; /** * Query the script for the available options (--generate-options) * and return the output as a JSON object. * @note The results will be cached the first time this function is called * and reused in subsequent calls. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ QJsonObject options() const; /** * Query the script for a user-friendly name (--display-name). * @note The results will be cached the first time this function is called * and reused in subsequent calls. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ QString displayName() const; /** * @return The path to the generator file. */ QString scriptFilePath() const; /** * Set the path to the input generator script file. This will reset any * cached data held by this class. */ void setScriptFilePath(const QString& scriptFile); /** * Clear any cached data and return to an uninitialized state. */ void reset(); /** * Request input files from the script using the supplied options object and * molecule. See the class documentation for details on the @p options_ * object format. * * If the files are generated successfully, use the functions * numberOfInputFiles(), fileNames(), and fileContents() to retrieve them. * * @return true on success and false on failure. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ bool generateInput(const QJsonObject& options_, const Core::Molecule& mol); /** * @return The number of input files stored by generateInput(). * @note This function is only valid after a successful call to * generateInput(). */ int numberOfInputFiles() const; /** * @return A list of filenames created by generateInput(). * @note This function is only valid after a successful call to * generateInput(). */ QStringList fileNames() const; /** * @return The "main" input file of the collection. This is the input file * used by MoleQueue to determine the $$inputFileName$$ and * $$inputFileBaseName$$ keywords. * @note This function is only valid after a successful call to * generateInput(). */ QString mainFileName() const; /** * @return A file contents corresponding to @p fileName. Must call * generateInput() first. * @sa fileNames */ QString fileContents(const QString& fileName) const; /** * @return A syntax highlighter for the file @a fileName. Must call * generateInput() first. The caller takes ownership of the returned object. * If no syntax highlighter is defined, this function returns nullptr. * @sa fileNames */ QtGui::GenericHighlighter* createFileHighlighter( const QString& fileName) const; /** * @return True if an error is set. */ bool hasErrors() const { return !m_errors.isEmpty(); } /** * Reset the error counter. */ void clearErrors() { m_errors.clear(); } /** * @return A QStringList containing all errors that occurred in the last call * to the input generator script. */ QStringList errorList() const { return m_errors; } /** * @return A QStringList containing all warnings returned by the input * generator script in the last call to generateInput. These are * script-specific warnings. */ QStringList warningList() const { return m_warnings; } public slots: /** * Enable/disable debugging. */ void setDebug(bool d); private: QtGui::PythonScript* m_interpreter; void setDefaultPythonInterpreter(); QByteArray execute(const QStringList& args, const QByteArray& scriptStdin = QByteArray()) const; bool parseJson(const QByteArray& json, QJsonDocument& doc) const; QString processErrorString(const QProcess& proc) const; bool insertMolecule(QJsonObject& json, const Core::Molecule& mol) const; QString generateCoordinateBlock(const QString& spec, const Core::Molecule& mol) const; void replaceKeywords(QString& str, const Core::Molecule& mol) const; bool parseHighlightStyles(const QJsonArray& json) const; bool parseRules(const QJsonArray& json, QtGui::GenericHighlighter& highligher) const; bool parseFormat(const QJsonObject& json, QTextCharFormat& format) const; bool parsePattern(const QJsonValue& json, QRegularExpression& pattern) const; // File extension of requested molecule format mutable QString m_moleculeExtension; mutable QString m_displayName; mutable QJsonObject m_options; mutable QStringList m_warnings; mutable QStringList m_errors; QStringList m_filenames; QString m_mainFileName; QMap m_files; QMap m_fileHighlighters; mutable QMap m_highlightStyles; }; inline bool InputGenerator::isValid() const { displayName(); return !hasErrors(); } } // namespace MoleQueue } // namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_INPUTGENERATOR_H avogadrolibs-1.101.0/avogadro/molequeue/inputgeneratordialog.cpp000066400000000000000000000036731506155467400251110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "inputgeneratordialog.h" #include "ui_inputgeneratordialog.h" #include "inputgeneratorwidget.h" namespace Avogadro::MoleQueue { using QtGui::Molecule; InputGeneratorDialog::InputGeneratorDialog(QWidget* parent_) : QDialog(parent_), ui(new Ui::InputGeneratorDialog) { ui->setupUi(this); connect(ui->widget, SIGNAL(closeClicked()), SLOT(accept())); } InputGeneratorDialog::InputGeneratorDialog(const QString& scriptFileName, QWidget* parent_) : QDialog(parent_), ui(new Ui::InputGeneratorDialog) { ui->setupUi(this); connect(ui->widget, SIGNAL(closeClicked()), SLOT(accept())); this->setInputGeneratorScript(scriptFileName); } InputGeneratorDialog::~InputGeneratorDialog() { delete ui; } void InputGeneratorDialog::setInputGeneratorScript(const QString& scriptFile) { ui->widget->setInputGeneratorScript(scriptFile); QString displayName(ui->widget->inputGenerator().displayName()); if (ui->widget->inputGenerator().hasErrors()) setWindowTitle(tr("Input Generator (error)").arg(displayName)); else setWindowTitle(tr("%1 Input Generator").arg(displayName)); } InputGeneratorWidget& InputGeneratorDialog::widget() { return *ui->widget; } const InputGeneratorWidget& InputGeneratorDialog::widget() const { return *ui->widget; } bool InputGeneratorDialog::configureBatchJob(BatchJob& batch) { ui->widget->setBatchMode(true); auto reply = static_cast(exec()); if (reply != Accepted) return false; return ui->widget->configureBatchJob(batch); } void InputGeneratorDialog::setMolecule(Molecule* mol) { ui->widget->setMolecule(mol); } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/inputgeneratordialog.h000066400000000000000000000055031506155467400245500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_INPUTGENERATORDIALOG_H #define AVOGADRO_MOLEQUEUE_INPUTGENERATORDIALOG_H #include "avogadromolequeueexport.h" #include "inputgeneratorwidget.h" #include #include namespace Avogadro { namespace QtGui { class Molecule; } namespace MoleQueue { class BatchJob; namespace Ui { class InputGeneratorDialog; } class InputGeneratorWidget; /** * @class InputGeneratorDialog inputgeneratordialog.h * * @brief The InputGeneratorDialog class provides a thin wrapper around * InputGeneratorWidget for standalone use. * @sa InputGenerator InputGeneratorWidget */ class AVOGADROMOLEQUEUE_EXPORT InputGeneratorDialog : public QDialog { Q_OBJECT public: explicit InputGeneratorDialog(QWidget* parent_ = nullptr); explicit InputGeneratorDialog(const QString& scriptFileName, QWidget* parent_ = nullptr); ~InputGeneratorDialog() override; /** * Use the input generator script pointed to by scriptFilePath. * @param scriptFilePath Absolute path to generator script. */ void setInputGeneratorScript(const QString& scriptFilePath); /** * @return A reference to the internal InputGeneratorWidget. * @{ */ InputGeneratorWidget& widget(); const InputGeneratorWidget& widget() const; /** @} */ /** * Used to configure batch jobs. * * When performing the same calculation on a number of molecules, this method * will ask the user to configure a calculation using the current molecule and * input generator settings. After the calculation settings are accepted, a * MoleQueueDialog is used to set job options. Both calculation and job * options are stored in the supplied BatchJob object. * * Errors are handled internally. User cancellation is indicated by this * method returning false. * * To submit jobs using the configured options, call BatchJob::submitNextJob * for each molecule. * * Typical usage: ~~~ BatchJob *batch = ...; InputGeneratorDialog dlg(scriptFilePath, windowParent); dlg.setMolecule(&refMol); // Representative molecule as placeholder in GUI. dlg.configureBatchJob(*batch); foreach(mol) batch->submitNextJob(mol); ~~~ */ bool configureBatchJob(BatchJob& batch); public slots: /** * Set the molecule used in the simulation. */ void setMolecule(QtGui::Molecule* mol); private: Ui::InputGeneratorDialog* ui; }; } // namespace MoleQueue } // namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_INPUTGENERATORDIALOG_H avogadrolibs-1.101.0/avogadro/molequeue/inputgeneratordialog.ui000066400000000000000000000015771506155467400247450ustar00rootroot00000000000000 Avogadro::MoleQueue::InputGeneratorDialog 0 0 750 650 Dialog Avogadro::MoleQueue::InputGeneratorWidget QWidget
avogadro/molequeue/inputgeneratorwidget.h
1
avogadrolibs-1.101.0/avogadro/molequeue/inputgeneratorwidget.cpp000066400000000000000000000463751506155467400251430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "inputgeneratorwidget.h" #include "batchjob.h" #include "molequeuedialog.h" #include "molequeuemanager.h" #include "ui_inputgeneratorwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::MoleQueue { InputGeneratorWidget::InputGeneratorWidget(QWidget* parent_) : QtGui::JsonWidget(parent_), m_ui(new Ui::InputGeneratorWidget), m_updatePending(false), m_inputGenerator(QString()) { m_ui->setupUi(this); m_ui->warningTextButton->setIcon(QIcon::fromTheme("dialog-warning")); connectButtons(); } InputGeneratorWidget::~InputGeneratorWidget() { delete m_ui; } void InputGeneratorWidget::setInputGeneratorScript(const QString& scriptFile) { m_inputGenerator.setScriptFilePath(scriptFile); m_ui->debugCheckBox->setChecked(m_inputGenerator.debug()); updateOptions(); resetWarningDisplay(); } void InputGeneratorWidget::setMolecule(QtGui::Molecule* mol) { if (mol == m_molecule) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (mol) { // make sure to call the base class method QtGui::JsonWidget::setMolecule(mol); connect(mol, SIGNAL(changed(unsigned int)), SLOT(updatePreviewText())); connect(mol, SIGNAL(changed(unsigned int)), SLOT(updateTitlePlaceholder())); } updateTitlePlaceholder(); updatePreviewTextImmediately(); } bool InputGeneratorWidget::configureBatchJob(BatchJob& batch) const { if (!m_batchMode) return false; QJsonObject mqOpts = promptForBatchJobOptions(); if (mqOpts.empty()) return false; JobObject job; job.fromJson(mqOpts); QJsonObject calcOpts; calcOpts[QLatin1String("options")] = collectOptions(); // Set job description from title: QString description; if (!optionString("Title", description) || description.isEmpty()) description = generateJobTitle(); job.setDescription(description); mqOpts = job.json(); batch.setInputGeneratorOptions(calcOpts); batch.setMoleQueueOptions(mqOpts); return true; } void InputGeneratorWidget::setBatchMode(bool m) { if (m_batchMode != m) { m_batchMode = m; foreach (QTextEdit* edit, m_textEdits) edit->setReadOnly(m_batchMode); m_ui->computeButton->setVisible(!m_batchMode); m_ui->generateButton->setVisible(!m_batchMode); m_ui->closeButton->setText(m_batchMode ? tr("Continue") : tr("Close")); updateTitlePlaceholder(); } } void InputGeneratorWidget::showEvent(QShowEvent* e) { QWidget::showEvent(e); if (m_molecule != nullptr) { int charge = static_cast(m_molecule->totalCharge()); int multiplicity = static_cast(m_molecule->totalSpinMultiplicity()); setOption("Charge", charge); setOption("Multiplicity", multiplicity); } // Update the preview text if an update was requested while hidden. Use a // single shot to allow the dialog to show before popping up any warnings. if (m_updatePending) QTimer::singleShot(0, this, SLOT(updatePreviewTextImmediately())); } void InputGeneratorWidget::updatePreviewText() { if (m_updatePending) return; m_updatePending = true; QTimer::singleShot(250, this, SLOT(updatePreviewTextImmediately())); } void InputGeneratorWidget::updatePreviewTextImmediately() { // If the dialog is not shown, delay the update in case we need to prompt the // user to overwrite changes. Set the m_updatePending flag to true so we'll // know to update in the show event. if (!isVisible()) { m_updatePending = true; return; } // Reset the update throttling m_updatePending = false; // Have any buffers been modified? if (!m_dirtyTextEdits.isEmpty()) { QStringList buffers; foreach (QTextEdit* edit, m_dirtyTextEdits) buffers << m_textEdits.key(edit, tr("Unknown")); QString message = tr("The following file(s) have been modified:\n\n%1\n\n" "Would you like to overwrite your changes to reflect " "the new geometry or job options?", "", buffers.size()) .arg(buffers.join("\n")); int response = QMessageBox::question( this, tr("Overwrite modified input files?"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (static_cast(response) != QMessageBox::Yes) { // Prevent updates while restoring the option cache: bool oldUpdatePending = m_updatePending; m_updatePending = true; // Restore cached options. applyOptions(m_optionCache); m_updatePending = oldUpdatePending; return; } } if (!m_molecule) return; // Generate the input files QJsonObject inputOptions; inputOptions["options"] = collectOptions(); bool success = m_inputGenerator.generateInput(inputOptions, *m_molecule); if (!m_inputGenerator.warningList().isEmpty()) { QString warningHtml; warningHtml += ""; warningHtml += "

" + tr("Problems occurred during input generation:") + "

"; warningHtml += "
    "; foreach (const QString& warning, m_inputGenerator.warningList()) warningHtml += QString("
  • %1
  • ").arg(warning); warningHtml += "
"; setWarning(warningHtml); } else { resetWarningDisplay(); } if (!success) { showError(m_inputGenerator.errorList().join("\n\n")); m_inputGenerator.clearErrors(); return; } // Store the currently displayed tab QPointer currentWidget(m_ui->tabWidget->currentWidget()); // Ensure that the correct tabs are shown: QStringList fileNames = m_inputGenerator.fileNames(); // Remove unneeded tabs foreach (const QString& tabName, m_textEdits.keys()) { if (!fileNames.contains(tabName)) { QTextEdit* edit = m_textEdits.value(tabName); int index = m_ui->tabWidget->indexOf(edit); m_ui->tabWidget->removeTab(index); m_textEdits.remove(tabName); delete edit; } } // Add new tabs foreach (const QString& fileName, fileNames) { if (m_textEdits.contains(fileName)) continue; auto* edit = new QTextEdit(this); edit->setObjectName(fileName); edit->setFontFamily("monospace"); connect(edit, SIGNAL(textChanged()), this, SLOT(textEditModified())); m_ui->tabWidget->addTab(edit, fileName); m_textEdits.insert(fileName, edit); } // Sort and update int index = 0; foreach (const QString& fileName, fileNames) { QTextEdit* edit = m_textEdits.value(fileName); int tabIndex = m_ui->tabWidget->indexOf(edit); if (tabIndex != index) { m_ui->tabWidget->removeTab(tabIndex); m_ui->tabWidget->insertTab(index, edit, fileName); } QtGui::GenericHighlighter* highlighter = m_inputGenerator.createFileHighlighter(fileName); if (highlighter) { highlighter->setParent(this); highlighter->setDocument(edit->document()); } edit->setText(m_inputGenerator.fileContents(fileName)); edit->document()->setModified(false); ++index; } // Reset dirty buffer list and cached option list m_dirtyTextEdits.clear(); m_optionCache = collectOptions(); // Restore current tab if (!currentWidget.isNull()) m_ui->tabWidget->setCurrentWidget(currentWidget); } void InputGeneratorWidget::defaultsClicked() { setOptionDefaults(); updatePreviewTextImmediately(); } void InputGeneratorWidget::generateClicked() { if (m_textEdits.size() == 1) saveSingleFile(m_textEdits.keys().first()); else if (m_textEdits.size() > 1) saveDirectory(); else showError(tr("No input files to save!")); } void InputGeneratorWidget::computeClicked() { // Verify that molequeue is running: MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) { QMessageBox::information(this, tr("Cannot connect to MoleQueue"), tr("Cannot connect to MoleQueue server. Please " "ensure that it is running and try again.")); return; } // Collect info for the MoleQueueDialog: const QString mainFileName = m_inputGenerator.mainFileName(); QString description; if (!optionString("Title", description) || description.isEmpty()) description = generateJobTitle(); QString coresString; int numCores = optionString("Processor Cores", coresString) ? coresString.toInt() : 1; JobObject job; job.setProgram(m_inputGenerator.displayName()); job.setDescription(description); job.setValue("numberOfCores", numCores); for (QMap::const_iterator it = m_textEdits.constBegin(), itEnd = m_textEdits.constEnd(); it != itEnd; ++it) { const QString& fileName = it.key(); if (fileName != mainFileName) job.appendAdditionalInputFile(fileName, it.value()->toPlainText()); else job.setInputFile(fileName, it.value()->toPlainText()); } MoleQueueDialog::SubmitStatus result = MoleQueueDialog::submitJob( this, tr("Submit %1 Calculation").arg(m_inputGenerator.displayName()), job, MoleQueueDialog::WaitForSubmissionResponse | MoleQueueDialog::SelectProgramFromTemplate); switch (result) { default: case MoleQueueDialog::SubmissionSuccessful: case MoleQueueDialog::SubmissionFailed: case MoleQueueDialog::SubmissionAttempted: case MoleQueueDialog::SubmissionAborted: // The dialog handles these cases adequately, we don't need to do // anything. break; case MoleQueueDialog::JobFailed: // Inform the user: QMessageBox::information(this, tr("Job Failed"), tr("The job did not complete successfully."), QMessageBox::Ok); break; case MoleQueueDialog::JobFinished: // Let the world know that the job is ready to open. job has been // overwritten with the final job details. emit openJobOutput(job); // Hide the parent if it's a dialog: if (auto* dlg = qobject_cast(parent())) dlg->hide(); break; } } void InputGeneratorWidget::setWarning(const QString& warn) { qWarning() << tr("Script returns warnings:\n") << warn; m_ui->warningText->setText(warn); m_ui->warningBox->show(); } void InputGeneratorWidget::toggleWarningText() { if (m_ui->warningText->isVisible()) hideWarningText(); else showWarningText(); } void InputGeneratorWidget::showWarningText() { m_ui->warningText->show(); m_ui->warningTextButton->setText(tr("Hide &Warnings")); } void InputGeneratorWidget::hideWarningText() { m_ui->warningText->hide(); m_ui->warningTextButton->setText(tr("Show &Warnings")); } void InputGeneratorWidget::resetWarningDisplay() { m_ui->warningBox->hide(); showWarningText(); } void InputGeneratorWidget::showError(const QString& err) { qWarning() << err; QWidget* theParent = this->isVisible() ? this : qobject_cast(parent()); QDialog dlg(theParent); auto* vbox = new QVBoxLayout(); auto* label = new QLabel(tr("An error has occurred:")); vbox->addWidget(label); auto* textBrowser = new QTextBrowser(); // adjust the size of the text browser to ~80 char wide, ~20 lines high QSize theSize = textBrowser->sizeHint(); QFontMetrics metrics(textBrowser->currentFont()); int charWidth = metrics.horizontalAdvance("i7OPlmWn9/") / 10; int charHeight = metrics.lineSpacing(); theSize.setWidth(80 * charWidth); theSize.setHeight(20 * charHeight); textBrowser->setMinimumSize(theSize); textBrowser->setText(err); vbox->addWidget(textBrowser); dlg.setLayout(vbox); dlg.exec(); } void InputGeneratorWidget::textEditModified() { if (auto* edit = qobject_cast(sender())) { if (edit->document()->isModified()) { if (!m_dirtyTextEdits.contains(edit)) m_dirtyTextEdits << edit; } else { m_dirtyTextEdits.removeOne(edit); } } } void InputGeneratorWidget::updateTitlePlaceholder() { if (auto* titleEdit = qobject_cast(m_widgets.value("Title", nullptr))) { titleEdit->setPlaceholderText(generateJobTitle()); } } QString InputGeneratorWidget::settingsKey(const QString& identifier) const { return QString("quantumInput/%1/%2") .arg(m_inputGenerator.displayName(), identifier); } void InputGeneratorWidget::saveSingleFile(const QString& fileName) { QSettings settings; QString filePath = settings.value(settingsKey("outputDirectory")).toString(); if (filePath.isEmpty()) filePath = QDir::homePath(); filePath = QFileDialog::getSaveFileName(this, tr("Select output filename"), filePath + "/" + fileName); // User cancel: if (filePath.isNull()) return; settings.setValue(settingsKey("outputDirectory"), QFileInfo(filePath).absoluteDir().absolutePath()); QTextEdit* edit = m_textEdits.value(fileName, nullptr); if (!edit) { showError(tr("Internal error: could not find text widget for filename '%1'") .arg(fileName)); return; } QFile file(filePath); bool success = false; if (file.open(QFile::WriteOnly | QFile::Text)) { if (file.write(edit->toPlainText().toLocal8Bit()) > 0) { success = true; } file.close(); } if (!success) { QMessageBox::critical( this, tr("Output Error"), tr("Failed to write to file %1.").arg(file.fileName())); } } void InputGeneratorWidget::saveDirectory() { QSettings settings; QString directory = settings.value(settingsKey("outputDirectory")).toString(); if (directory.isEmpty()) directory = QDir::homePath(); directory = QFileDialog::getExistingDirectory( this, tr("Select output directory"), directory); // User cancel: if (directory.isNull()) return; settings.setValue(settingsKey("outputDirectory"), directory); QDir dir(directory); QStringList fileNames = m_textEdits.keys(); // Check for problems: QStringList errors; bool fatalError = false; do { // Do/while to break on fatal errors if (!dir.exists()) { errors << tr("%1: Directory does not exist!").arg(dir.absolutePath()); fatalError = true; break; } if (!dir.isReadable()) { errors << tr("%1: Directory cannot be read!").arg(dir.absolutePath()); fatalError = true; break; } foreach (const QString& fileName, fileNames) { QFileInfo info(dir.absoluteFilePath(fileName)); if (info.exists()) { errors << tr("%1: File will be overwritten.").arg(info.absoluteFilePath()); } // Attempt to open the file for writing if (!QFile(info.absoluteFilePath()).open(QFile::WriteOnly)) { errors << tr("%1: File is not writable.").arg(info.absoluteFilePath()); fatalError = true; break; } } } while (false); // only run once // Handle fatal errors: if (fatalError) { QString formattedError; switch (errors.size()) { case 0: formattedError = tr("The input files cannot be written due to an unknown error."); break; case 1: formattedError = tr("The input files cannot be written:\n\n%1").arg(errors.first()); break; default: { // If a fatal error occurred, it will be last one in the list. Pop it // off and tell the user that it was the reason we had to stop. QString fatal = errors.last(); QStringList tmp(errors); tmp.pop_back(); formattedError = tr("The input files cannot be written:\n\n%1\n\nWarnings:\n\n%2") .arg(fatal, tmp.join("\n")); break; } } showError(formattedError); return; } // Non-fatal errors: if (!errors.isEmpty()) { QString formattedError = tr("Warning:\n\n%1\n\nWould you like to continue?") .arg(errors.join("\n")); QMessageBox::StandardButton reply = QMessageBox::warning(this, tr("Write input files"), formattedError, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (reply != QMessageBox::Yes) return; } foreach (const QString& fileName, fileNames) { QTextEdit* edit = m_textEdits.value(fileName); QFile file(dir.absoluteFilePath(fileName)); bool success = false; if (file.open(QFile::WriteOnly | QFile::Text)) { if (file.write(edit->toPlainText().toLocal8Bit()) > 0) { success = true; } file.close(); } if (!success) { QMessageBox::critical( this, tr("Output Error"), tr("Failed to write to file %1.").arg(file.fileName())); } } } QJsonObject InputGeneratorWidget::promptForBatchJobOptions() const { // Verify that molequeue is running: MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) { QMessageBox::information(this->parentWidget(), tr("Cannot connect to MoleQueue"), tr("Cannot connect to MoleQueue server. Please " "ensure that it is running and try again.")); return QJsonObject(); } QString coresString; int numCores = optionString("Processor Cores", coresString) ? coresString.toInt() : 1; JobObject job; job.setProgram(m_inputGenerator.displayName()); job.setValue("numberOfCores", numCores); if (!MoleQueueDialog::promptForJobOptions(this->parentWidget(), tr("Configure Job"), job)) { return QJsonObject(); } return job.json(); } void InputGeneratorWidget::connectButtons() { connect(m_ui->debugCheckBox, SIGNAL(toggled(bool)), &m_inputGenerator, SLOT(setDebug(bool))); connect(m_ui->debugCheckBox, SIGNAL(toggled(bool)), SLOT(updatePreviewText())); connect(m_ui->defaultsButton, SIGNAL(clicked()), SLOT(defaultsClicked())); connect(m_ui->generateButton, SIGNAL(clicked()), SLOT(generateClicked())); connect(m_ui->closeButton, SIGNAL(clicked()), SIGNAL(closeClicked())); connect(m_ui->warningTextButton, SIGNAL(clicked()), SLOT(toggleWarningText())); // disable the compute button if Molequeue is not running MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) { m_ui->computeButton->setEnabled(false); } else { connect(m_ui->computeButton, SIGNAL(clicked()), SLOT(computeClicked())); } } void InputGeneratorWidget::updateOptions() { m_options = m_inputGenerator.options(); if (m_inputGenerator.hasErrors()) { showError(m_inputGenerator.errorList().join("\n\n")); m_inputGenerator.clearErrors(); } m_centralWidget = m_ui->optionsWidget; // Create the widgets, etc for the gui buildOptionGui(); setOptionDefaults(); } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/inputgeneratorwidget.h000066400000000000000000000135121506155467400245730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_INPUTGENERATORWIDGET_H #define AVOGADRO_MOLEQUEUE_INPUTGENERATORWIDGET_H #include #include #include #include "inputgenerator.h" class QJsonValue; class QTextEdit; class QWidget; class QFormLayout; namespace Avogadro { namespace QtGui { class Molecule; } namespace MoleQueue { class JobObject; namespace Ui { class InputGeneratorWidget; } class BatchJob; /** * @class InputGeneratorWidget inputgeneratorwidget.h * * @brief The InputGeneratorWidget class provides a user interface for * configuring, saving, editing, and running input files produced by * InputGenerator scripts. * @sa InputGenerator InputGeneratorDialog * * The InputGeneratorWidget creates a GUI to represent the options given by an * input generator script, and has some utilities for job submission through * MoleQueue. * * By default, the widget will configure input files for a single molecule, * which can be either written to disk or submitted for processing with * MoleQueue. * * By enabling batch mode (setBatchMode()), the current molecule is used to * configure a calculation for submission to MoleQueue, and the parameters are * saved. These may be used to configure and submit jobs for other molecules. */ class AVOGADROMOLEQUEUE_EXPORT InputGeneratorWidget : public QtGui::JsonWidget { Q_OBJECT public: /** * Construct a widget that dynamically generates a GUI to configure the * InputGenerator script specified by scriptFilePath. */ explicit InputGeneratorWidget(QWidget* parent_ = nullptr); ~InputGeneratorWidget() override; /** * Use the input generator script pointed to by scriptFilePath. * @param scriptFilePath Absolute path to generator script. */ void setInputGeneratorScript(const QString& scriptFilePath); /** * Access to the underlying input generator object. @{ */ const InputGenerator& inputGenerator() const { return m_inputGenerator; } /** * @return True if the widget is in batch mode. See the class documentation * for details. Default is false. */ bool batchMode() const { return m_batchMode; } /** * Collect the current calculation parameters and prompt for MoleQueue * options. Both option sets are stored in @a batch. */ bool configureBatchJob(BatchJob& batch) const; void setMolecule(QtGui::Molecule* mol) override; public slots: /** * Enable/disable 'template mode'. See the class documentation for details. * Default is off. */ void setBatchMode(bool m); signals: /** * @brief closeClicked is emitted when the close button is clicked. */ void closeClicked(); /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ void openJobOutput(const JobObject& job); protected: /** * Reimplemented to update preview text. Hidden dialogs will wait until they * are reshown to update the text to prevent overwriting any modified buffers. */ void showEvent(QShowEvent* e) override; void updateOptions() override; private slots: /** * Update the input files. This method is throttled, and will only call the * generator script once every 250 milliseconds. */ void updatePreviewText(); /** * Immediately update the input files, bypassing (and resetting) the throttle * mechanism. */ void updatePreviewTextImmediately(); /** * Triggered when the user resets the default values. */ void defaultsClicked(); /** * Triggered when the user requests that the files are written to disk. */ void generateClicked(); /** * Triggered when the user requests that the simulation is submitted to * MoleQueue. */ void computeClicked(); /** * Show the user an warning. These are messages returned by the input * generator script. */ void setWarning(const QString& warn); /** * Toggle the visibility of the warning text. */ void toggleWarningText(); /** * Show the warning text. */ void showWarningText(); /** * Hide the warning text. */ void hideWarningText(); /** * Hide the warning widget. */ void resetWarningDisplay(); /** * Show the user an error message. These are errors that have occurred * in this extension, not necessarily in the input generator script. */ void showError(const QString& err); /** * Triggered when an input file's text edit is modified. */ void textEditModified(); /** * Generate a job title automatically. */ void updateTitlePlaceholder(); private: /** * Generate a QSettings key with the given identifier that is unique to this * input generator's display name. * @param identifier Setting key, e.g. "outputPath" * @return Script-specific key, e.g. "quantumInput/GAMESS/outputPath" * @todo Display names are not necessarily unique, but paths are too long. * Maybe add a namespace qualifier to the script display names? */ QString settingsKey(const QString& identifier) const; /** * Write the input file(s) to disk. Prompts user for target location. * @{ */ void saveSingleFile(const QString& fileName); void saveDirectory(); /**@}*/ /** Get batch job options from MoleQueueDialog. */ QJsonObject promptForBatchJobOptions() const; /** * Make signal/slot connections. */ void connectButtons(); Ui::InputGeneratorWidget* m_ui; bool m_updatePending; QList m_dirtyTextEdits; InputGenerator m_inputGenerator; }; } // namespace MoleQueue } // namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_INPUTGENERATORWIDGET_H avogadrolibs-1.101.0/avogadro/molequeue/inputgeneratorwidget.ui000066400000000000000000000072171506155467400247660ustar00rootroot00000000000000 Avogadro::MoleQueue::InputGeneratorWidget 0 0 813 650 Form 0 0 Calculation Settings 0 0 Qt::Horizontal 40 20 Placeholder text… true Reset Debug Script… Qt::Horizontal QSizePolicy::MinimumExpanding 13 20 Submit Calculation… Save Input… Close avogadrolibs-1.101.0/avogadro/molequeue/molequeuedialog.cpp000066400000000000000000000147601506155467400240430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molequeuedialog.h" #include "molequeuewidget.h" #include "ui_molequeuedialog.h" #include #include #include #include namespace Avogadro::MoleQueue { MoleQueueDialog::MoleQueueDialog(QWidget* parent_) : QDialog(parent_), m_ui(new Ui::MoleQueueDialog) { m_ui->setupUi(this); } MoleQueueDialog::~MoleQueueDialog() { delete m_ui; } MoleQueueDialog::SubmitStatus MoleQueueDialog::submitJob( QWidget* parent_, const QString& caption, JobObject& jobTemplate, SubmitOptions options, unsigned int* moleQueueId, int* submissionRequestId) { // initialize return args if (moleQueueId) *moleQueueId = MoleQueueWidget::InvalidMoleQueueId; if (submissionRequestId) *submissionRequestId = -1; MoleQueueDialog dlg(parent_); dlg.setWindowTitle(caption); dlg.widget().setJobTemplate(jobTemplate); if (options & SelectProgramFromTemplate) dlg.widget().showAndSelectProgram(jobTemplate.program()); for (;;) { int dlgResult = dlg.exec(); if (dlgResult != QDialog::Accepted) return SubmissionAborted; int requestId = dlg.widget().submitJobRequest(); if (options & WaitForSubmissionResponse || dlg.widget().openOutput()) { QProgressDialog progress; progress.setCancelButton(nullptr); progress.setLabelText(tr("Submitting job to MoleQueue…")); progress.setRange(0, 0); progress.setValue(0); progress.show(); QList submittedSignal; submittedSignal << MetaMethod(&dlg.widget(), SIGNAL(jobSubmitted(bool))); if (!dlg.waitForSignal(submittedSignal)) { progress.hide(); QMessageBox::information(&dlg, tr("Job Submission Timeout"), tr("Avogadro timed out waiting for a reply " "from MoleQueue.")); continue; } if (dlg.widget().submissionSuccess()) { if (submissionRequestId != nullptr) *submissionRequestId = dlg.widget().requestId(); if (moleQueueId != nullptr) *moleQueueId = dlg.widget().moleQueueId(); // Do we need to wait for the job to finish? if (!dlg.widget().openOutput()) return SubmissionSuccessful; // Update progress dialog progress.setLabelText(tr("Waiting for job %1 “%2” to finish…", "%1 = job ID, %2 = description") .arg(dlg.widget().moleQueueId()) .arg(jobTemplate.description())); progress.setCancelButtonText(tr("Stop waiting")); // Wait for job completion or progress bar cancellation. QList completionSignals; completionSignals << MetaMethod(&dlg.widget(), SIGNAL(jobFinished(bool))) << MetaMethod(&progress, SIGNAL(canceled())); dlg.waitForSignal(completionSignals, -1); // Did the user cancel? if (progress.wasCanceled()) return SubmissionSuccessful; // Error occurred: if (!dlg.widget().jobSuccess()) return JobFailed; // Update progress bar: progress.setLabelText(tr("Fetching completed job information…")); progress.setCancelButton(nullptr); // Job completed -- overwrite job template with updated job details. connect(&dlg.widget(), SIGNAL(jobUpdated(MoleQueue::JobObject)), &dlg.widget(), SLOT(setJobTemplate(MoleQueue::JobObject))); QList lookupSignal; lookupSignal << MetaMethod(&dlg.widget(), SIGNAL(jobUpdated(MoleQueue::JobObject))); dlg.widget().requestJobLookup(); if (!dlg.waitForSignal(lookupSignal)) { progress.hide(); QMessageBox::information(&dlg, tr("Job Retrieval Timeout"), tr("Avogadro timed out waiting for the " "finished job details from MoleQueue.")); return JobFailed; } jobTemplate = dlg.widget().jobTemplate(); return JobFinished; } else { progress.hide(); QMessageBox::warning( &dlg, tr("Error Submitting Job"), tr("The job has been rejected by MoleQueue: %1", "%1 = error") .arg(dlg.widget().submissionError())); continue; } } else { if (requestId >= 0) { if (submissionRequestId != nullptr) *submissionRequestId = requestId; return SubmissionAttempted; } else { return SubmissionFailed; } } } } bool MoleQueueDialog::promptForJobOptions(QWidget* windowParent, const QString& caption, MoleQueue::JobObject& jobTemplate) { MoleQueueDialog dlg(windowParent); dlg.setWindowTitle(caption); dlg.widget().setBatchMode(true); dlg.widget().setJobTemplate(jobTemplate); if (!jobTemplate.program().isEmpty()) dlg.widget().showAndSelectProgram(jobTemplate.program()); if (static_cast(dlg.exec()) != Accepted) return false; jobTemplate = dlg.widget().configuredJob(); return true; } MoleQueueWidget& MoleQueueDialog::widget() { return *m_ui->widget; } const MoleQueueWidget& MoleQueueDialog::widget() const { return *m_ui->widget; } bool MoleQueueDialog::waitForSignal(const QList& signalList, int msTimeout) const { QEventLoop waiter; foreach (const MetaMethod& sig, signalList) connect(sig.first, sig.second, &waiter, SLOT(quit())); QTimer limiter; if (msTimeout >= 0) { limiter.setSingleShot(true); connect(&limiter, SIGNAL(timeout()), &waiter, SLOT(quit())); limiter.start(msTimeout); } waiter.exec(); return limiter.isActive(); } void MoleQueueDialog::done(int r) { if (r == QDialog::Accepted && !widget().programSelected()) { QMessageBox::information(parentWidget(), tr("No program selected."), tr("Please select the target program from the " "\"Queue and Program\" list.")); } else { QDialog::done(r); } } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/molequeuedialog.h000066400000000000000000000134101506155467400234770ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_MOLEQUEUEDIALOG_H #define AVOGADRO_MOLEQUEUE_MOLEQUEUEDIALOG_H #include "avogadromolequeueexport.h" #include #include namespace Avogadro::MoleQueue { class JobObject; class MoleQueueWidget; namespace Ui { class MoleQueueDialog; } /** * @class MoleQueueDialog molequeuedialog.h * * @brief The MoleQueueDialog class provides a thin wrapper around * MoleQueueWidget for standalone use. * @sa MoleQueueWidget MoleQueueManager */ class AVOGADROMOLEQUEUE_EXPORT MoleQueueDialog : public QDialog { Q_OBJECT public: explicit MoleQueueDialog(QWidget* parent_ = nullptr); ~MoleQueueDialog() override; /** * @brief Options controlling job submission behavior in the submitJob method. */ enum SubmitOption { /** * Keep the dialog open until MoleQueue replies to the submission request. * If a submission error occurs, the user will have to opportunity to fix * it. */ WaitForSubmissionResponse = 0x1, /** * Use the program in the template job to initialize the queue/program view. * All queues containing a matching program will be expanded, and the first * match will be selected. * To match, an existing program must contain the template program string, * and comparisons are case insensitive. */ SelectProgramFromTemplate = 0x2 }; Q_DECLARE_FLAGS(SubmitOptions, SubmitOption) /** * @brief Return values from submitJob indicating result. */ enum SubmitStatus { /** * The job was accepted by MoleQueue. * This can only be returned when WaitForSubmissionResponse IS set as an * option. */ SubmissionSuccessful = 0, /** * The job was not submitted to MoleQueue, likely due to a disconnected * server. * This can only be returned when WaitForSubmissionResponse IS NOT * set as an option. */ SubmissionFailed, /** * The job was submitted to MoleQueue. This can only be returned when * WaitForSubmissionResponse is NOT set as an option. */ SubmissionAttempted, /** * The user canceled the submission. */ SubmissionAborted, /** * The user requested that the job output be opened when finished, but * the job did not finish successfully (the job was either canceled or * failed). */ JobFailed, /** * The user requested that the job output be opened when finished, and * the job completed without error. The jobTemplate argument of * submitJob will be overwritten with the current job details, fetched * from the server after the job enters the "Finished" state. */ JobFinished }; /** * Show a job configuration dialog and let the user submit the job to * MoleQueue. * @param parent_ The parent widget for parenting/layout purposes. * @param caption The dialog title. * @param jobTemplate A template job, used to initialize GUI options. If * the user requests that the job output is opened and the job finishes * successfully, this will be overwritten with the current job details, and * JobFinished is returned. * @param options Bitwise combination of flags that control dialog behavior. * @param moleQueueId If not nullptr, the variable referenced by this pointer * will be overwritten by the MoleQueue Id of the submitted job when the * option WaitForSubmissionResponse is set. * If an error occurs or the required option is not set, this value will be * set to MoleQueueWidget::InvalidMoleQueueId. * @param submissionRequestId If not nullptr, the variable referenced by this * pointer will be overwritten by the submitJob JSON-RPC 2.0 request id. * If an error occurs, this value will be set to -1. * @return A SubmitStatus enum value indicating the result of the submission. */ static SubmitStatus submitJob(QWidget* parent_, const QString& caption, JobObject& jobTemplate, SubmitOptions options, unsigned int* moleQueueId = nullptr, int* submissionRequestId = nullptr); /** * Show a job configuration dialog and collect the user's selected options. * @param windowParent The parent of the dialog window. * @param caption Title of the dialog window. * @param jobTemplate JobObject with initial options. Will be overwritten * with the configured job options. * @return True on success, false otherwise. */ static bool promptForJobOptions(QWidget* windowParent, const QString& caption, JobObject& jobTemplate); /** * @return A reference to the internal MoleQueueWidget instance. * @{ */ MoleQueueWidget& widget(); const MoleQueueWidget& widget() const; /** @} */ public slots: void done(int r) override; private: using MetaMethod = QPair; /** * Wait @a timeout milliseconds for @a source to emit @a signal. * @param signalList List of QObject* and const char* (signals) to listen for. * @param msTimeout Timeout in milliseconds. A negative value will wait * forever. * @return True if a signal in @a signalList is received, false on timeout. */ bool waitForSignal(const QList& signalList, int msTimeout = 5000) const; Ui::MoleQueueDialog* m_ui; }; Q_DECLARE_OPERATORS_FOR_FLAGS(MoleQueueDialog::SubmitOptions) } // namespace Avogadro::MoleQueue #endif // AVOGADRO_MOLEQUEUE_MOLEQUEUEDIALOG_H avogadrolibs-1.101.0/avogadro/molequeue/molequeuedialog.ui000066400000000000000000000035401506155467400236700ustar00rootroot00000000000000 Avogadro::MoleQueue::MoleQueueDialog 0 0 400 300 Dialog Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Avogadro::MoleQueue::MoleQueueWidget QWidget
avogadro/molequeue/molequeuewidget.h
1
buttonBox accepted() Avogadro::MoleQueue::MoleQueueDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::MoleQueue::MoleQueueDialog reject() 316 260 286 274
avogadrolibs-1.101.0/avogadro/molequeue/molequeuemanager.cpp000066400000000000000000000033471506155467400242150ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molequeuemanager.h" namespace Avogadro::MoleQueue { MoleQueueManager* MoleQueueManager::m_instance = nullptr; MoleQueueManager::MoleQueueManager(QObject* parent_) : QObject(parent_), m_client(this), m_queueModel(this) { connect(&m_client, SIGNAL(queueListReceived(QJsonObject)), SLOT(updateQueueModel(QJsonObject))); } MoleQueueManager& MoleQueueManager::instance() { return m_instance ? *m_instance : *(m_instance = new MoleQueueManager()); } bool MoleQueueManager::connectIfNeeded() { return m_client.isConnected() || m_client.connectToServer(); } Client& MoleQueueManager::client() { return m_client; } const Client& MoleQueueManager::client() const { return m_client; } MoleQueueQueueListModel& MoleQueueManager::queueListModel() { return m_queueModel; } bool MoleQueueManager::requestQueueList() { return m_client.isConnected() && m_client.requestQueueList() >= 0; } void MoleQueueManager::updateQueueModel(const QJsonObject& json) { QList queueList; QList programList; foreach (const QString& queue, json.keys()) { queueList.append(queue); programList.append(QStringList()); QStringList& progs = programList.back(); foreach (const QJsonValue& program, json.value(queue).toArray()) { if (program.isString()) progs << program.toString(); } } m_queueModel.setQueueList(queueList, programList); emit queueListUpdated(); } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/molequeuemanager.h000066400000000000000000000045501506155467400236570ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_MOLEQUEUEMANAGER_H #define AVOGADRO_MOLEQUEUE_MOLEQUEUEMANAGER_H #include #include "molequeuequeuelistmodel.h" #include #include "client/client.h" namespace Avogadro::MoleQueue { /** * @class MoleQueueManager molequeuemanager.h * * @brief The MoleQueueManager class provides access to a MoleQueue server. * * This singleton class provides access to a single MoleQueue::Client instance * that can be used to communicate with the server. The available queues and * programs are cached in a MoleQueueQueueListModel (queueListModel()). The * connectIfNeeded convenience function can be used to ensure that the client * is connected before use. */ class AVOGADROMOLEQUEUE_EXPORT MoleQueueManager : public QObject { Q_OBJECT public: explicit MoleQueueManager(QObject* parent_ = nullptr); ~MoleQueueManager() override = default; /** * @return The singleton instance. */ static MoleQueueManager& instance(); /** * Test if the client is connected, and if not, attempt a connection. * @return True if the client is already connected or a new connection has * been successfully created. False if the new connection failed. */ bool connectIfNeeded(); /** * @return A reference to the managed Client instance. * @{ */ Client& client(); const Client& client() const; /** @} */ /** * @return A QAbstractItemModel subclass representing the queue/program tree. */ MoleQueueQueueListModel& queueListModel(); public slots: /** * Request that the cached queue list is updated. * @return True if the request is send successfully. */ bool requestQueueList(); signals: /** * Emitted when the internal queue list is updated. */ void queueListUpdated(); private slots: void updateQueueModel(const QJsonObject& queueList); private: static MoleQueueManager* m_instance; Client m_client; MoleQueueQueueListModel m_queueModel; }; } // namespace Avogadro::MoleQueue #endif // AVOGADRO_MOLEQUEUE_MOLEQUEUEMANAGER_H avogadrolibs-1.101.0/avogadro/molequeue/molequeuequeuelistmodel.cpp000066400000000000000000000246341506155467400256460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molequeuequeuelistmodel.h" #include #include namespace Avogadro::MoleQueue { namespace { // Internal id used for queue model indices static const quint32 QueueInternalId(std::numeric_limits::max()); // Internal id used for invalid indices static const quint32 InvalidInternalId(std::numeric_limits::max() - 1); // Maximum assignable internal id. Must be last: static const quint32 MaxInternalId(std::numeric_limits::max() - 2); } // namespace MoleQueueQueueListModel::MoleQueueQueueListModel(QObject* parent_) : QAbstractItemModel(parent_), m_uidCounter(0) { } void MoleQueueQueueListModel::setQueueList(QList queueList, QList programList) { const int numQueues = queueList.size(); if (numQueues != programList.size()) { qWarning() << "Error setting molequeue queuelist data in model: " "number of queues does not match size of program table."; return; } // Sync our data structures with the arguments: int newInd = 0; int oldInd = 0; while (newInd < queueList.size() && oldInd < m_queueList.size()) { const QString& newQueue = queueList[newInd]; const QString& oldQueue = m_queueList[oldInd]; if (newQueue < oldQueue) { const QStringList& newProgs = programList[newInd]; insertQueue(oldInd, newQueue, newProgs); ++oldInd; ++newInd; } else if (oldQueue < newQueue) { removeQueue(oldInd); } else { // newQueue == oldQueue const QStringList& newProgs = programList[newInd]; mergeQueue(oldInd, newProgs); ++oldInd; ++newInd; } } // Add any remaining new queues for (; newInd < queueList.size(); ++newInd, ++oldInd) insertQueue(m_queueList.size(), queueList[newInd], programList[newInd]); // or remove any stale old queues. while (oldInd < m_queueList.size()) removeQueue(oldInd); } QStringList MoleQueueQueueListModel::queues() const { return m_queueList; } QStringList MoleQueueQueueListModel::programs(const QString& queue) const { int ind = m_queueList.indexOf(queue); return ind >= 0 ? m_programList[ind] : QStringList(); } QModelIndexList MoleQueueQueueListModel::findQueueIndices( const QString& filter) const { return match(index(0, 0), Qt::DisplayRole, filter, -1, Qt::MatchContains); } QModelIndexList MoleQueueQueueListModel::findProgramIndices( const QString& programFilter, const QString& queueFilter) const { QModelIndexList result; foreach (const QModelIndex& idx, findQueueIndices(queueFilter)) { result << match(index(0, 0, idx), Qt::DisplayRole, programFilter, -1, Qt::MatchContains); } return result; } bool MoleQueueQueueListModel::lookupProgram(const QModelIndex& idx, QString& queueName, QString& programName) const { QVariant resultVariant = data(idx, QueueProgramRole); if (resultVariant.type() == QVariant::StringList) { QStringList resultList(resultVariant.toStringList()); if (resultList.size() == 2) { queueName = resultList[0]; programName = resultList[1]; return true; } } queueName.clear(); programName.clear(); return false; } QVariant MoleQueueQueueListModel::data(const QModelIndex& idx, int role) const { if (!idx.isValid() || (role != Qt::DisplayRole && role != QueueProgramRole)) { return QVariant(); } if (isQueueIndex(idx)) { if (role == Qt::DisplayRole) { return m_queueList[idx.row()]; } } else { const int queueIndex(idx.parent().row()); if (queueIndex < m_queueList.size()) { const QStringList& progs(m_programList[queueIndex]); if (idx.row() < progs.size()) { const QString& prog(progs[idx.row()]); switch (role) { case Qt::DisplayRole: return prog; case QueueProgramRole: { QStringList result = m_uidLookup.value( static_cast(idx.internalId()), QStringList()); if (result.size() == 2) return result; break; } default: break; } } } } return QVariant(); } Qt::ItemFlags MoleQueueQueueListModel::flags(const QModelIndex& idx) const { if (!idx.isValid()) return Qt::NoItemFlags; if (isQueueIndex(idx)) return Qt::ItemIsEnabled; else return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } QVariant MoleQueueQueueListModel::headerData(int, Qt::Orientation, int) const { return QVariant(); } QModelIndex MoleQueueQueueListModel::index(int row, int column, const QModelIndex& parent_) const { if (!hasIndex(row, column, parent_)) return QModelIndex(); // Queue Index -- parent is invalid. if (!parent_.isValid() && row < m_queueList.size() && column == 0) { return createIndex(row, column, QueueInternalId); } // Program index else if (isQueueIndex(parent_)) { const QStringList& progs(m_programList[parent_.row()]); if (row < progs.size() && column == 0) { const QString& queue(m_queueList[parent_.row()]); const QString& prog(progs[row]); QStringList val = QStringList() << queue << prog; quint32 key = m_uidLookup.key(val, InvalidInternalId); if (key != InvalidInternalId) return createIndex(row, column, key); } } // fail. return QModelIndex(); } QModelIndex MoleQueueQueueListModel::parent(const QModelIndex& child) const { if (child.isValid()) { const auto childId = static_cast(child.internalId()); // Child is queue -- return invalid parent. if (childId == QueueInternalId) return QModelIndex(); // Child is program -- lookup and return queue index. const int queueRow = programUidToQueueRow(childId); if (queueRow >= 0) return createIndex(queueRow, 0, QueueInternalId); } return QModelIndex(); } int MoleQueueQueueListModel::rowCount(const QModelIndex& parent_) const { // Queue count: if (!parent_.isValid()) return m_queueList.size(); else if (isQueueIndex(parent_)) return m_programList[parent_.row()].size(); return 0; } int MoleQueueQueueListModel::columnCount(const QModelIndex& parent_) const { return (!parent_.isValid() || isQueueIndex(parent_)) ? 1 : 0; } void MoleQueueQueueListModel::insertQueue(int row, const QString& queue, const QStringList& progs) { beginInsertRows(QModelIndex(), row, row); m_queueList.insert(row, queue); m_programList.insert(row, QStringList()); endInsertRows(); beginInsertRows(createIndex(row, 0, QueueInternalId), 0, progs.size() - 1); m_programList[row] = progs; foreach (const QString& progName, progs) m_uidLookup.insert(nextUid(), QStringList() << queue << progName); endInsertRows(); } void MoleQueueQueueListModel::removeQueue(int row) { const QString queue(m_queueList[row]); QStringList& progs = m_programList[row]; beginRemoveRows(createIndex(row, 0, QueueInternalId), 0, progs.size() - 1); foreach (const QString& prog, progs) m_uidLookup.remove(lookupUid(queue, prog)); progs.clear(); endRemoveRows(); beginRemoveRows(QModelIndex(), row, row); m_queueList.removeAt(row); m_programList.removeAt(row); endRemoveRows(); } void MoleQueueQueueListModel::mergeQueue(int row, const QStringList& newProgs) { QStringList& oldProgs(m_programList[row]); int oldInd = 0; int newInd = 0; while (oldInd < oldProgs.size() && newInd < newProgs.size()) { const QString& oldProg(oldProgs[oldInd]); const QString& newProg(newProgs[newInd]); if (newProg < oldProg) { insertProgram(row, oldInd, newProg); ++newInd; ++oldInd; } else if (oldProg < newProg) { removeProgram(row, oldInd); } else { // Program exists ++newInd; ++oldInd; } } // Add any remaining new programs for (; newInd < newProgs.size(); ++newInd, ++oldInd) insertProgram(row, m_programList[row].size(), newProgs[newInd]); // Or remove any old programs. while (oldInd < m_programList[row].size()) removeProgram(row, oldInd); } void MoleQueueQueueListModel::insertProgram(int queueRow, int progRow, const QString& progName) { beginInsertRows(createIndex(queueRow, 0, QueueInternalId), progRow, progRow); m_programList[queueRow].insert(progRow, progName); m_uidLookup.insert(nextUid(), QStringList() << m_queueList[queueRow] << progName); endInsertRows(); } void MoleQueueQueueListModel::removeProgram(int queueRow, int progRow) { beginRemoveRows(createIndex(queueRow, 0, QueueInternalId), progRow, progRow); m_uidLookup.remove(lookupUid(queueRow, progRow)); m_programList[queueRow].removeAt(progRow); endRemoveRows(); } bool MoleQueueQueueListModel::isQueueIndex(const QModelIndex& i) const { return i.isValid() && static_cast(i.internalId()) == QueueInternalId && i.row() < m_queueList.size() && i.column() == 0; } bool MoleQueueQueueListModel::isProgramIndex(const QModelIndex& i) const { return i.isValid() && m_uidLookup.contains(static_cast(i.internalId())); } quint32 MoleQueueQueueListModel::lookupUid(const QString& queue, const QString& prog) { return m_uidLookup.key(QStringList() << queue << prog, InvalidInternalId); } quint32 MoleQueueQueueListModel::lookupUid(const int queueRow, const int progRow) { if (queueRow < m_queueList.size()) { QStringList& progs = m_programList[queueRow]; if (progRow < progs.size()) return lookupUid(m_queueList[queueRow], progs[progRow]); } return InvalidInternalId; } int MoleQueueQueueListModel::programUidToQueueRow(quint32 uid) const { const QStringList val(m_uidLookup.value(uid, QStringList())); if (val.size() == 2) { const QString& queue = val[0]; const int queueRow = m_queueList.indexOf(queue); if (queueRow >= 0) return queueRow; } return -1; } quint32 MoleQueueQueueListModel::nextUid() { if (m_uidCounter++ >= MaxInternalId) m_uidCounter = 0; return m_uidCounter; } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/molequeuequeuelistmodel.h000066400000000000000000000121101506155467400252750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_MOLEQUEUEQUEUELISTMODEL_H #define AVOGADRO_MOLEQUEUE_MOLEQUEUEQUEUELISTMODEL_H #include "avogadromolequeueexport.h" #include #include // for gtest unit testing access class MoleQueueQueueListModelTestBridge; namespace Avogadro::MoleQueue { class MoleQueueManager; /** * @class MoleQueueQueueListModel molequeuequeuelistmodel.h * * @brief The MoleQueueQueueListModel class is Qt item model representing the * tree of available queues and programs in a running MoleQueue process. * * This class provides access to the available MoleQueue queues and programs in * a convenient tree item model. Resources can be queried directly using the * queues() and programs() methods, or this item model may be used with a Qt * model view class, such a QTreeView. * * QModelIndex objects that match a queue or program node may be found using the * findQueueIndices() and findProgramIndices() methods. A program model index * may be translated into queue and program strings using the lookupProgram() * method. * * An instance of this class is obtained by calling * MoleQueueManager::instance().queueListModel(), and can be updated by calling * MoleQueueManager::instance().requestQueueList() and waiting for the * MoleQueueManager::queueListUpdated() signal. */ class AVOGADROMOLEQUEUE_EXPORT MoleQueueQueueListModel : public QAbstractItemModel { Q_OBJECT public: ~MoleQueueQueueListModel() override = default; /** * @return A list of the available queues. */ QStringList queues() const; /** * @return A list of programs belonging to @a queue. */ QStringList programs(const QString& queue) const; /** * @return A QModelIndexList containing indices for queues that contain the * string @a filter. Matches are case-insensitive. */ QModelIndexList findQueueIndices(const QString& filter = QString()) const; /** * @return A QModelIndexList containing indices for programs that contain the * string @a programFilter and belong to queues that contain @a queueFilter. * Matches are case-insensitive. */ QModelIndexList findProgramIndices( const QString& programFilter = QString(), const QString& queueFilter = QString()) const; /** * Translate a QModelIndex for a program node into queue and program strings. * @param idx The model index. * @param queueName String reference to be overwritten with the queue name. * @param programName String reference to be overwritten with the queue name. * @return True if the index matched a program node, false otherwise. */ bool lookupProgram(const QModelIndex& idx, QString& queueName, QString& programName) const; // QAbstractItemModel virtuals QVariant data(const QModelIndex& idx, int role) const override; Qt::ItemFlags flags(const QModelIndex& idx = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QModelIndex index(int row, int column, const QModelIndex& parent_ = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent_ = QModelIndex()) const override; int columnCount(const QModelIndex& parent_ = QModelIndex()) const override; protected: friend class MoleQueueManager; friend class ::MoleQueueQueueListModelTestBridge; /** * Protected constructor. Keeps objects isolated to MoleQueueManager ivars. */ explicit MoleQueueQueueListModel(QObject* parent_ = nullptr); /** * Used to retrieve a QStringList with [queueName, programName] from data() * given a program model index. */ enum { QueueProgramRole = Qt::UserRole }; /** * Merge the queue and program lists with the existing model. */ void setQueueList(QList queueList, QList programList); private: void insertQueue(int row, const QString& queue, const QStringList& progs); void removeQueue(int row); void mergeQueue(int row, const QStringList& newProgs); void insertProgram(int queueRow, int progRow, const QString& progName); void removeProgram(int queueRow, int progRow); bool isQueueIndex(const QModelIndex& i) const; bool isProgramIndex(const QModelIndex& i) const; quint32 lookupUid(const QString& queue, const QString& prog); quint32 lookupUid(const int queueRow, const int progRow); int programUidToQueueRow(quint32 uid) const; quint32 nextUid(); QList m_queueList; QList m_programList; // maps program index internal id to [queueName, programName] QStringList QMap m_uidLookup; quint32 m_uidCounter; }; } // namespace Avogadro::MoleQueue #endif // AVOGADRO_MOLEQUEUE_MOLEQUEUEQUEUELISTMODEL_H avogadrolibs-1.101.0/avogadro/molequeue/molequeuewidget.cpp000066400000000000000000000216741506155467400240710ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molequeuewidget.h" #include "ui_molequeuewidget.h" #include "molequeuemanager.h" #include #include #include namespace Avogadro::MoleQueue { const unsigned int MoleQueueWidget::InvalidMoleQueueId( std::numeric_limits::max()); MoleQueueWidget::MoleQueueWidget(QWidget* parent_) : QWidget(parent_), m_ui(new Ui::MoleQueueWidget), m_jobState("Unknown"), m_requestId(-1), m_moleQueueId(InvalidMoleQueueId) { m_ui->setupUi(this); connect(m_ui->refreshProgramsButton, SIGNAL(clicked()), SLOT(refreshPrograms())); MoleQueueManager& mqManager = MoleQueueManager::instance(); m_ui->queueListView->setModel(&mqManager.queueListModel()); if (mqManager.connectIfNeeded()) mqManager.requestQueueList(); } MoleQueueWidget::~MoleQueueWidget() { delete m_ui; } JobObject& MoleQueueWidget::jobTemplate() { return m_jobTemplate; } const JobObject& MoleQueueWidget::jobTemplate() const { return m_jobTemplate; } void MoleQueueWidget::setJobTemplate(const JobObject& job) { m_jobTemplate = job; m_ui->numberOfCores->setValue(job.value("numberOfCores", 1).toInt()); m_ui->cleanRemoteFiles->setChecked( job.value("cleanRemoteFiles", false).toBool()); m_ui->hideFromGui->setChecked(job.value("hideFromGui", false).toBool()); m_ui->popupOnStateChange->setChecked( job.value("popupOnStateChange", false).toBool()); } void MoleQueueWidget::refreshPrograms() { MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) { QMessageBox::information(this, tr("Cannot connect to MoleQueue"), tr("Cannot connect to MoleQueue server. Please " "ensure that it is running and try again.")); return; } mqManager.requestQueueList(); } int MoleQueueWidget::submitJobRequest() { m_submissionError.clear(); m_jobState = "Unknown"; m_requestId = -1; m_moleQueueId = InvalidMoleQueueId; MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) return -1; MoleQueue::JobObject job(configuredJob()); if (job.queue().isEmpty()) // if the queue is not set, the job config failed. return -1; m_requestId = mqManager.client().submitJob(job); if (m_requestId >= 0) { listenForJobSubmitReply(); listenForJobStateChange(); } else { m_submissionError = tr("Client failed to submit job to MoleQueue."); // Single shot ensures that this signal is emitted after control returns // to the event loop QTimer::singleShot(0, this, SIGNAL(jobSubmitted(false))); } return m_requestId; } void MoleQueueWidget::showAndSelectProgram(const QString& programName) { MoleQueueManager& mqManager = MoleQueueManager::instance(); setProperty("selectProgramName", programName); connect(&mqManager, SIGNAL(queueListUpdated()), SLOT(showAndSelectProgramHandler())); if (mqManager.connectIfNeeded()) mqManager.requestQueueList(); } bool MoleQueueWidget::openOutput() const { return m_ui->openOutput->isChecked(); } bool MoleQueueWidget::requestJobLookup() { MoleQueueManager& mqManager = MoleQueueManager::instance(); if (m_moleQueueId != InvalidMoleQueueId && mqManager.connectIfNeeded()) { listenForLookupJobReply(); int reqId = mqManager.client().lookupJob(m_moleQueueId); setProperty("lookupJobRequestId", reqId); return true; } return false; } MoleQueue::JobObject MoleQueueWidget::configuredJob() const { MoleQueueManager& mqManager = MoleQueueManager::instance(); // get queue/program QModelIndexList sel(m_ui->queueListView->selectionModel()->selectedIndexes()); if (sel.size() < 1) { QMessageBox::information(parentWidget(), tr("No program selected."), tr("Please select the target program from the " "\"Queue and Program\" list.")); return MoleQueue::JobObject(); } QString queue; QString program; if (!mqManager.queueListModel().lookupProgram(sel.first(), queue, program)) { QMessageBox::critical(parentWidget(), tr("Internal error."), tr("Unable to resolve program selection. This is " "a bug.")); return MoleQueue::JobObject(); } MoleQueue::JobObject job(m_jobTemplate); job.setQueue(queue); job.setProgram(program); job.setValue("numberOfCores", m_ui->numberOfCores->value()); job.setValue("cleanRemoteFiles", m_ui->cleanRemoteFiles->isChecked()); job.setValue("hideFromGui", m_ui->hideFromGui->isChecked()); job.setValue("popupOnStateChange", m_ui->popupOnStateChange->isChecked()); return job; } void MoleQueueWidget::setBatchMode(bool batch) { m_ui->openOutput->setHidden(batch); m_ui->openOutputLabel->setHidden(batch); } bool MoleQueueWidget::batchMode() const { return m_ui->openOutput->isHidden(); } void MoleQueueWidget::showAndSelectProgramHandler() { MoleQueueManager& mqManager = MoleQueueManager::instance(); const QString program(property("selectProgramName").toString()); setProperty("selectProgramName", QVariant()); disconnect(&mqManager, SIGNAL(queueListUpdated()), this, SLOT(showAndSelectProgramHandler())); // Get all program nodes that match the name QModelIndexList list(mqManager.queueListModel().findProgramIndices(program)); // Expand the corresponding queues foreach (const QModelIndex& mi, list) m_ui->queueListView->expand(mi.parent()); // Select the first entry if (!list.isEmpty()) { m_ui->queueListView->selectionModel()->select( list.first(), QItemSelectionModel::ClearAndSelect); m_ui->queueListView->scrollTo(list.first()); } } void MoleQueueWidget::onLookupJobReply(int reqId, const QJsonObject& result) { QVariant reqIdVariant(property("lookupJobRequestId")); bool ok; int myReqId = reqIdVariant.toInt(&ok); if (ok && reqId == myReqId) { setProperty("lookupJobRequestId", QVariant()); listenForLookupJobReply(false); MoleQueue::JobObject job; job.fromJson(result); emit jobUpdated(job); } } void MoleQueueWidget::onSubmissionSuccess(int localId, unsigned int mqId) { if (localId != m_requestId) return; listenForJobSubmitReply(false); m_moleQueueId = mqId; emit jobSubmitted(true); } void MoleQueueWidget::onSubmissionFailure(int localId, unsigned int, const QString& error) { if (localId != m_requestId) return; listenForJobSubmitReply(false); m_submissionError = error; emit jobSubmitted(false); } void MoleQueueWidget::onJobStateChange(unsigned int mqId, const QString&, const QString& newState) { if (mqId != m_moleQueueId) return; m_jobState = newState; if (m_jobState == QLatin1String("Finished")) { listenForJobStateChange(false); emit jobFinished(true); } else if (m_jobState == QLatin1String("Error") || m_jobState == QLatin1String("Canceled")) { listenForJobStateChange(false); emit jobFinished(false); } } void MoleQueueWidget::listenForLookupJobReply(bool listen) { Client& mqClient(MoleQueueManager::instance().client()); if (listen) { connect(&mqClient, SIGNAL(lookupJobResponse(int, QJsonObject)), this, SLOT(onLookupJobReply(int, QJsonObject))); } else { disconnect(&mqClient, SIGNAL(lookupJobResponse(int, QJsonObject)), this, SLOT(onLookupJobReply(int, QJsonObject))); } } void MoleQueueWidget::listenForJobSubmitReply(bool listen) { MoleQueue::Client& mqClient(MoleQueueManager::instance().client()); if (listen) { connect(&mqClient, SIGNAL(submitJobResponse(int, uint)), this, SLOT(onSubmissionSuccess(int, uint))); connect(&mqClient, SIGNAL(errorReceived(int, uint, QString)), this, SLOT(onSubmissionFailure(int, uint, QString))); } else { disconnect(&mqClient, SIGNAL(submitJobResponse(int, uint)), this, SLOT(onSubmissionSuccess(int, uint))); disconnect(&mqClient, SIGNAL(errorReceived(int, uint, QString)), this, SLOT(onSubmissionFailure(int, uint, QString))); } } void MoleQueueWidget::listenForJobStateChange(bool listen) { MoleQueue::Client& mqClient(MoleQueueManager::instance().client()); if (listen) { connect(&mqClient, SIGNAL(jobStateChanged(uint, QString, QString)), this, SLOT(onJobStateChange(uint, QString, QString))); } else { disconnect(&mqClient, SIGNAL(jobStateChanged(uint, QString, QString)), this, SLOT(onJobStateChange(uint, QString, QString))); } } bool MoleQueueWidget::programSelected() { QModelIndexList sel(m_ui->queueListView->selectionModel()->selectedIndexes()); return sel.size() > 0; } } // namespace Avogadro::MoleQueue avogadrolibs-1.101.0/avogadro/molequeue/molequeuewidget.h000066400000000000000000000135121506155467400235260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_MOLEQUEUE_MOLEQUEUEWIDGET_H #define AVOGADRO_MOLEQUEUE_MOLEQUEUEWIDGET_H #include "avogadromolequeueexport.h" #include #include #include "client/jobobject.h" namespace Avogadro::MoleQueue { namespace Ui { class MoleQueueWidget; } /** * @class MoleQueueWidget molequeuewidget.h * * @brief The MoleQueueWidget class provides a widget for configuring and * submitting a MoleQueue::JobObject. */ class AVOGADROMOLEQUEUE_EXPORT MoleQueueWidget : public QWidget { Q_OBJECT public: explicit MoleQueueWidget(QWidget* parent_ = nullptr); ~MoleQueueWidget() override; /** * A "template" MoleQueue::JobObject that is used to initialize the GUI. * Should be fully configured to submit, as this is used to initialize job * that will be submitted by submitJobRequest. * @{ */ JobObject& jobTemplate(); const JobObject& jobTemplate() const; public slots: void setJobTemplate(const JobObject& job); public: /** @} */ /** * Expand all queue nodes that contain a program that matches @a programName * and select the first matching program node. * Matches are case insensitive. */ void showAndSelectProgram(const QString& programName); /** * @return True if the last submission was successful. Only valid after * jobSubmitted has been emitted. */ bool submissionSuccess() const { return m_moleQueueId != InvalidMoleQueueId; } /** * @return True if the last submission was successful. Only valid after * jobSubmitted has been emitted. */ QString jobState() const { return m_jobState; } /** * @return True if the job has finished running. */ bool jobCompleted() const { return (m_jobState == QLatin1String("Finished") || m_jobState == QLatin1String("Error") || m_jobState == QLatin1String("Canceled")); } /** * @return true if the job completed without error. */ bool jobSuccess() const { return m_jobState == QLatin1String("Finished"); } /** * @return The request id associated with the last call to submitJobRequest. * -1 if there was a submission error. */ int requestId() const { return m_requestId; } /** * Indicates an invalid MoleQueue ID in the moleQueueId() result. */ static const unsigned int InvalidMoleQueueId; /** * @return The MoleQueue ID associated with the last submitJobRequest() call. * Only valid after jobSubmitted has been emitted. * @note If an error occurs, InvalidMoleQueueId will be returned. */ unsigned int moleQueueId() const { return m_moleQueueId; } /** * @return A string describing the submission error when submissionSuccess() * return false. */ QString submissionError() const { return m_submissionError; } /** * @return True if the user has requested that the output file be opened when * the calculation completes. */ bool openOutput() const; /** * @brief Request the current state of the job identified by moleQueueId() * from the server. The result will be emitted in the jobUpdated() signal. * @return True if moleQueueId() is valid and the server is connected, false * if the request cannot be sent. */ bool requestJobLookup(); /** * If the widget is in 'batch mode', options that don't make sense are hidden * (such as 'open output when finished'). */ void setBatchMode(bool batch); bool batchMode() const; /**@}*/ /** * @return True if the user has selected a program, false otherwise. */ bool programSelected(); /** * @return A JobObject with the GUI options. Any settings in jobTemplate that * are not handled by the GUI are passed through untouched to the new object. */ JobObject configuredJob() const; public slots: /** * Query the MoleQueue server (if available) for the list of available queues * and programs. */ void refreshPrograms(); /** * Submit the job returned by configuredJob() to MoleQueue. * @return The request id associated with the submission, or -1 on error. * @note The result of the submission request can be checked by monitoring * jobSubmitted, which will always be emitted after this slot is called. */ int submitJobRequest(); signals: /** * Emitted after a call to submitJobRequest * @param success True if the job has been accepted by MoleQueue. */ void jobSubmitted(bool success); /** * Emitted after jobSubmitted is emitted and the job completes. * @param success True if the job enters the "Finished" state. False if the * job enters the "Canceled" or "Error" states. */ void jobFinished(bool success); /** * Emitted after a successful call to requestJobLookup(). * @param job The result of the lookupJob() RPC query. */ void jobUpdated(const JobObject& job); private slots: void showAndSelectProgramHandler(); void onLookupJobReply(int reqId, const QJsonObject& result); void onSubmissionSuccess(int localId, unsigned int moleQueueId); void onSubmissionFailure(int localId, unsigned int, const QString& error); void onJobStateChange(unsigned int mqId, const QString& oldState, const QString& newState); private: void listenForLookupJobReply(bool listen = true); void listenForJobSubmitReply(bool listen = true); void listenForJobStateChange(bool listen = true); Ui::MoleQueueWidget* m_ui; JobObject m_jobTemplate; QString m_jobState; QString m_submissionError; int m_requestId; unsigned int m_moleQueueId; }; } // namespace Avogadro::MoleQueue #endif // AVOGADRO_MOLEQUEUE_MOLEQUEUEWIDGET_H avogadrolibs-1.101.0/avogadro/molequeue/molequeuewidget.ui000066400000000000000000000150541506155467400237170ustar00rootroot00000000000000 Avogadro::MoleQueue::MoleQueueWidget 0 0 618 300 Form 0 0 Queue and Program: Qt::Horizontal 40 20 Refresh 0 0 QFormLayout::AllNonFixedFieldsGrow Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 <html><head/><body><p>Number of processor cores to reserve for this job.</p></body></html> Processor cores: numberOfCores 1 65536 Qt::Horizontal 40 20 <html><head/><body><p>Delete remote working files upon job completion. Results will still be copied to the local MoleQueue job cache first.</p></body></html> Delete remote files when finished: cleanRemoteFiles <html><head/><body><p>Check to prevent this job from showing up in the MoleQueue GUI by default.</p></body></html> Hide job in MoleQueue: hideFromGui <html><head/><body><p>Show a system popup notification when the job's status changes.</p></body></html> Show progress notifications: popupOnStateChange Open output when finished: openOutput refreshProgramsButton queueListView numberOfCores cleanRemoteFiles hideFromGui popupOnStateChange openOutput avogadrolibs-1.101.0/avogadro/qtgui/000077500000000000000000000000001506155467400172765ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtgui/CMakeLists.txt000066400000000000000000000055401506155467400220420ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 COMPONENTS Widgets Concurrent REQUIRED) else() find_package(Qt5 COMPONENTS Widgets Concurrent REQUIRED) endif() # Provide some simple API to find the plugins, scripts, etc. if(APPLE) # It is a special case, the app bundle logic breaks the relative pathing. add_definitions(-DAvogadroLibs_LIB_DIR=\"lib\") add_definitions(-DAvogadroLibs_DATA_DIR=\"share\") else() add_definitions(-DAvogadroLibs_LIB_DIR=\"${INSTALL_LIBRARY_DIR}\") add_definitions(-DAvogadroLibs_DATA_DIR=\"${INSTALL_DATA_DIR}\") endif() if(USE_SPGLIB) add_definitions(-DUSE_SPGLIB) endif() # Find python for input generator scripts: find_package(Python3 COMPONENTS Interpreter) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/avogadropython.h.in" "namespace Avogadro { static const char *pythonInterpreterPath = \"${Python3_EXECUTABLE}\"; } ") configure_file("${CMAKE_CURRENT_BINARY_DIR}/avogadropython.h.in" "${CMAKE_CURRENT_BINARY_DIR}/avogadropython.h") add_library(QtGui) avogadro_headers(QtGui backgroundfileformat.h colorbutton.h containerwidget.h customelementdialog.h elementtranslator.h extensionplugin.h filebrowsewidget.h fileformatdialog.h gaussiansetconcurrent.h generichighlighter.h hydrogentools.h insertfragmentdialog.h interfacescript.h interfacewidget.h jsonwidget.h layermodel.h rwlayermanager.h meshgenerator.h molecule.h moleculemodel.h multiviewwidget.h periodictableview.h persistentatom.h persistentbond.h pluginlayermanager.h pythonscript.h richtextdelegate.h rwmolecule.h sceneplugin.h scenepluginmodel.h scriptloader.h slatersetconcurrent.h sortfiltertreeproxymodel.h toolplugin.h utilities.h viewfactory.h ) target_sources(QtGui PRIVATE backgroundfileformat.cpp colorbutton.cpp containerwidget.cpp customelementdialog.cpp elementdetail_p.cpp elementitem_p.cpp elementtranslator.cpp extensionplugin.cpp filebrowsewidget.cpp fileformatdialog.cpp gaussiansetconcurrent.cpp generichighlighter.cpp hydrogentools.cpp insertfragmentdialog.cpp interfacescript.cpp interfacewidget.cpp jsonwidget.cpp layermodel.cpp richtextdelegate.cpp rwlayermanager.cpp meshgenerator.cpp molecule.cpp moleculemodel.cpp multiviewwidget.cpp periodictablescene_p.cpp periodictableview.cpp pluginlayermanager.cpp pythonscript.cpp rwmolecule.cpp sceneplugin.cpp scenepluginmodel.cpp scriptloader.cpp slatersetconcurrent.cpp sortfiltertreeproxymodel.cpp toolplugin.cpp utilities.cpp viewfactory.cpp ) set(UIS customelementdialog.ui insertfragmentdialog.ui ) qt_wrap_ui(UI_SOURCES ${UIS}) target_sources(QtGui PRIVATE ${UI_SOURCES}) set(RCS qtgui.qrc ) qt_add_resources(RC_SOURCES ${RCS}) target_sources(QtGui PRIVATE ${RC_SOURCES}) avogadro_add_library(QtGui) target_link_libraries(QtGui PUBLIC Avogadro::IO Qt::Widgets Qt::Concurrent) avogadrolibs-1.101.0/avogadro/qtgui/backgroundfileformat.cpp000066400000000000000000000034401506155467400241730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "backgroundfileformat.h" #include namespace Avogadro::QtGui { BackgroundFileFormat::BackgroundFileFormat(Io::FileFormat* format, QObject* parent) : QObject(parent), m_format(format), m_molecule(nullptr), m_success(false) { } BackgroundFileFormat::~BackgroundFileFormat() { delete m_format; } void BackgroundFileFormat::read() { m_success = false; m_error.clear(); if (!m_molecule) m_error = tr("No molecule set in BackgroundFileFormat!"); if (!m_format) m_error = tr("No Io::FileFormat set in BackgroundFileFormat!"); if (m_fileName.isEmpty()) m_error = tr("No file name set in BackgroundFileFormat!"); if (m_error.isEmpty()) { m_success = m_format->readFile(m_fileName.toLocal8Bit().data(), *m_molecule); if (!m_success) m_error = QString::fromStdString(m_format->error()); } emit finished(); } void BackgroundFileFormat::write() { m_success = false; m_error.clear(); if (!m_molecule) m_error = tr("No molecule set in BackgroundFileFormat!"); if (!m_format) m_error = tr("No Io::FileFormat set in BackgroundFileFormat!"); if (m_fileName.isEmpty()) m_error = tr("No file name set in BackgroundFileFormat!"); if (m_error.isEmpty()) { m_success = m_format->writeFile(m_fileName.toLocal8Bit().data(), *m_molecule); if (!m_success) m_error = QString::fromStdString(m_format->error()); } emit finished(); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/backgroundfileformat.h000066400000000000000000000043261506155467400236440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_BACKGROUNDFILEFORMAT_H #define AVOGADRO_QTGUI_BACKGROUNDFILEFORMAT_H #include "avogadroqtguiexport.h" #include #include namespace Avogadro { namespace Core { class Molecule; } namespace Io { class FileFormat; } namespace QtGui { /** * @brief The BackgroundFileFormat class provides a thin QObject wrapper around * an instance of Io::FileFormat. */ class AVOGADROQTGUI_EXPORT BackgroundFileFormat : public QObject { Q_OBJECT public: /** * This class takes ownership of @a format and will delete it when destructed. */ explicit BackgroundFileFormat(Io::FileFormat* format, QObject* parent = nullptr); ~BackgroundFileFormat() override; /** * The molecule instance to read/write. * @{ */ void setMolecule(Core::Molecule* mol) { m_molecule = mol; } Core::Molecule* molecule() const { return m_molecule; } /**@}*/ /** * The name of the file to read/write. * @{ */ void setFileName(const QString& filename) { m_fileName = filename; } QString fileName() const { return m_fileName; } /**@}*/ /** * The Io::FileFormat to use. */ Io::FileFormat* fileFormat() const { return m_format; } /** * @return True if the operation was successful. */ bool success() const { return m_success; } /** * @return An error string, set if success() is false. */ QString error() const { return m_error; } signals: /** * Emitted when a call to read or write is called. */ void finished(); public slots: /** * Use the fileFormat() to read fileName() into molecule(). */ void read(); /** * Use the fileFormat() to write fileName() from molecule(). */ void write(); private: Io::FileFormat* m_format; Core::Molecule* m_molecule; QString m_fileName; QString m_error; bool m_success; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_BACKGROUNDFILEFORMAT_H avogadrolibs-1.101.0/avogadro/qtgui/colorbutton.cpp000066400000000000000000000037631506155467400223650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ // Adapted from Avogadro 1.0 by Geoffrey Hutchison // Contributed to Avogadro 2.0 by Geoffrey Hutchison #include "colorbutton.h" #include #include namespace Avogadro::QtGui { ColorButton::ColorButton(QWidget* parent) : QAbstractButton(parent), m_color(Qt::white), m_title("") { setMinimumSize(35, 20); connect(this, SIGNAL(clicked()), this, SLOT(changeColor())); } ColorButton::ColorButton(const QColor& initial, QWidget* parent) : QAbstractButton(parent), m_color(initial) { setMinimumSize(35, 20); connect(this, SIGNAL(clicked()), this, SLOT(changeColor())); } void ColorButton::changeColor() { // This could be an ifdef for KColorDialog if KDE is present if (m_title == "") m_color = QColorDialog::getColor(m_color, this); else m_color = QColorDialog::getColor(m_color, this, m_title); update(); emit colorChanged(m_color); } void ColorButton::setColor(const QColor& color) { m_color = color; update(); emit colorChanged(m_color); } void ColorButton::setDialogTitle(const QString title) { m_title = title; } QColor ColorButton::color() const { return m_color; } void ColorButton::paintEvent(QPaintEvent*) { // TODO: If we go to RGBA colors, we should really show two pieces // e.g. ----------- // | /| // | non / | // | alpha/ | // | / | // | /alpha // | / | // ----------- QPainter painter(this); // outer border painter.drawRect(0, 0, width(), height()); // inner color painter.setBrush(m_color); painter.drawRect(4, 4, width() - 8, height() - 8); } bool ColorButton::event(QEvent* e) { return QAbstractButton::event(e); } } // namespace Avogadro::QtGuiavogadrolibs-1.101.0/avogadro/qtgui/colorbutton.h000066400000000000000000000041111506155467400220160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ // Adapted from Avogadro 1.0 by Geoffrey Hutchison // Contributed to Avogadro 2.0 by Geoffrey Hutchison #ifndef AVOGADRO_QTGUI_COLORBUTTON_H #define AVOGADRO_QTGUI_COLORBUTTON_H #include "avogadroqtguiexport.h" #include #include namespace Avogadro { namespace QtGui { /** * @class ColorButton colorbutton.h * @author Geoffrey Hutchison * @brief A button to show the current color and bring up the QColorDialog. * * This class implements a QAbstractButton to display a colored rectangle. * When clicked by the user, it brings up a color picker to select a new * color. * * The widget has a default minimum size of 35x20 pixels. */ class AVOGADROQTGUI_EXPORT ColorButton : public QAbstractButton { Q_OBJECT public: ColorButton(QWidget* parent = 0); explicit ColorButton(const QColor& initial, QWidget* parent = 0); /** * Redraw the widget (i.e., refresh the colored rectangle) */ void paintEvent(QPaintEvent*) override; /** * @param color the new color to be used */ void setColor(const QColor& color); /** * @param custom title for color choice dialog */ void setDialogTitle(const QString title = ""); /** * @return the current color */ QColor color() const; Q_SIGNALS: /** * emit any time the color is changed, either by a user or by setColor() */ void colorChanged(const QColor&); public Q_SLOTS: /** * Call for a change in the current color */ void changeColor(); protected: /** * Generic event handler, currently defaults to calling parent class * (included for future compatibility) */ bool event(QEvent* e) override; QColor m_color; //!< The current color QString m_title; //!< The current dialog title }; } // namespace QtGui } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtgui/containerwidget.cpp000066400000000000000000000033571506155467400232000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "containerwidget.h" #include #include #include #include namespace Avogadro::QtGui { ContainerWidget::ContainerWidget(QWidget* p, Qt::WindowFlags f) : QWidget(p, f), m_viewWidget(nullptr), m_label(new QLabel(QStringLiteral(" "), this)), m_active(false) { auto* h = new QHBoxLayout; h->setContentsMargins(0, 0, 0, 0); auto* v = new QVBoxLayout; v->setContentsMargins(0, 0, 0, 0); v->setSpacing(0); h->addWidget(m_label); h->addStretch(); auto* button = new QPushButton(tr("Split Horizontal"), this); connect(button, SIGNAL(clicked()), SIGNAL(splitHorizontal())); h->addWidget(button); button = new QPushButton(tr("Split Vertical"), this); connect(button, SIGNAL(clicked()), SIGNAL(splitVertical())); h->addWidget(button); button = new QPushButton(tr("Close"), this); connect(button, SIGNAL(clicked()), SIGNAL(closeView())); h->addWidget(button); v->addLayout(h); setLayout(v); } ContainerWidget::~ContainerWidget() {} void ContainerWidget::setViewWidget(QWidget* widget) { if (m_viewWidget) m_viewWidget->deleteLater(); m_viewWidget = widget; layout()->addWidget(widget); } QWidget* ContainerWidget::viewWidget() { return m_viewWidget; } void ContainerWidget::setActive(bool active) { if (m_active != active) { m_active = active; m_label->setText(active ? " * " : " "); } } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/containerwidget.h000066400000000000000000000025121506155467400226350ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_CONTAINERWIDGET_H #define AVOGADRO_QTGUI_CONTAINERWIDGET_H #include "avogadroqtguiexport.h" #include class QLabel; namespace Avogadro { namespace QtGui { /** * @class ContainerWidget containerwidget.h * @brief A widget that contains a single view widget, along with standard * buttons for splitting, maximizing, closing. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT ContainerWidget : public QWidget { Q_OBJECT public: explicit ContainerWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~ContainerWidget() override; void setViewWidget(QWidget* widget); QWidget* viewWidget(); void setActive(bool active); bool isActive() const { return m_active; } signals: void splitVertical(); void splitHorizontal(); void closeView(); private: QWidget* m_viewWidget; QLabel* m_label; bool m_active; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_CONTAINERWIDGET_H avogadrolibs-1.101.0/avogadro/qtgui/customelementdialog.cpp000066400000000000000000000106201506155467400240450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "customelementdialog.h" #include "ui_customelementdialog.h" #include "elementtranslator.h" #include "molecule.h" #include #include #include using Avogadro::Core::Elements; namespace Avogadro::QtGui { CustomElementDialog::CustomElementDialog(Molecule& mol, QWidget* p) : QDialog(p), m_ui(new Ui::CustomElementDialog), m_molecule(mol) { m_ui->setupUi(this); prepareElements(); prepareForm(); } CustomElementDialog::~CustomElementDialog() { delete m_ui; } void CustomElementDialog::resolve(QWidget* p, Molecule& mol) { CustomElementDialog dlg(mol, p); int reply = dlg.exec(); if (static_cast(reply) == Accepted) dlg.apply(); } namespace { struct RemapAtomicNumbers { typedef std::map MapType; const MapType& map; RemapAtomicNumbers(const MapType& m) : map(m) {} RemapAtomicNumbers(const RemapAtomicNumbers& o) : map(o.map) {} unsigned char operator()(unsigned char old) const { if (Core::isCustomElement(old)) { auto it = map.find(old); return it == map.end() ? old : it->second; } return old; } }; } // namespace void CustomElementDialog::apply() { RemapAtomicNumbers::MapType oldToNew; Molecule::CustomElementMap newMap; const Molecule::CustomElementMap& oldMap = m_molecule.customElementMap(); unsigned char newIdGenerator = CustomElementMin; foreach (QComboBox* combo, findChildren()) { unsigned char oldId = static_cast(combo->property("id").toUInt()); int currentIndex = combo->currentIndex(); if (currentIndex == 0) { // Reuse old name: unsigned char newId = newIdGenerator++; auto it = oldMap.find(oldId); newMap.insert(std::make_pair(newId, it->second)); oldToNew.insert(std::make_pair(oldId, newId)); } else { // New element assigned: auto newId = static_cast(currentIndex); oldToNew.insert(std::make_pair(oldId, newId)); } } if (newMap.size() != oldMap.size()) { Core::Array atomicNumbers = m_molecule.atomicNumbers(); std::transform(atomicNumbers.begin(), atomicNumbers.end(), atomicNumbers.begin(), RemapAtomicNumbers(oldToNew)); m_molecule.setAtomicNumbers(atomicNumbers); m_molecule.setCustomElementMap(newMap); m_molecule.emitChanged(Molecule::Atoms | Molecule::Modified); } } void CustomElementDialog::prepareElements() { int maxNumber = ElementTranslator::numberOfElements(); m_elements.reserve(maxNumber); for (int i = 1; i <= maxNumber; ++i) m_elements.append(ElementTranslator::name(i)); } namespace { struct CustomElementFilter { std::set customElements; void operator()(unsigned char atomicNumber) { if (Core::isCustomElement(atomicNumber)) customElements.insert(atomicNumber); } operator std::set() const { return customElements; } }; } // namespace void CustomElementDialog::prepareForm() { const Molecule::CustomElementMap& map = m_molecule.customElementMap(); const Core::Array& atomicNumbers = m_molecule.atomicNumbers(); std::set customElements = std::for_each( atomicNumbers.begin(), atomicNumbers.end(), CustomElementFilter()); Molecule::CustomElementMap::const_iterator match; for (unsigned char customElement : customElements) { if ((match = map.find(customElement)) != map.end()) addRow(customElement, QString::fromStdString(match->second)); else addRow(customElement, QString::fromStdString(Elements::name(customElement))); } } void CustomElementDialog::addRow(unsigned char elementId, const QString& name) { auto* combo = new QComboBox(this); combo->setProperty("id", static_cast(elementId)); combo->addItem(name); combo->addItems(m_elements); unsigned int atomicNumber = Elements::guessAtomicNumber(name.toStdString()); if (atomicNumber != InvalidElement) combo->setCurrentIndex(static_cast(atomicNumber)); else combo->setCurrentIndex(0); m_ui->form->addRow(name + ":", combo); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/customelementdialog.h000066400000000000000000000030001506155467400235040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_CUSTOMELEMENTDIALOG_H #define AVOGADRO_QTGUI_CUSTOMELEMENTDIALOG_H #include "avogadroqtguiexport.h" #include #include namespace Avogadro { namespace QtGui { class Molecule; namespace Ui { class CustomElementDialog; } /** * @class CustomElementDialog customelementdialog.h * * @brief Dialog window for mapping custom elements into elemental types. */ class AVOGADROQTGUI_EXPORT CustomElementDialog : public QDialog { Q_OBJECT public: explicit CustomElementDialog(Molecule& mol, QWidget* parent = nullptr); ~CustomElementDialog() override; /** * Static entry point for using this dialog. @a parent is the parent of the * dialog, @a mol is the molecule to operate on. */ static void resolve(QWidget* parent, Molecule& mol); public slots: /** Apply the changes to the molecule. */ void apply(); private: Ui::CustomElementDialog* m_ui; Molecule& m_molecule; QStringList m_elements; void prepareElements(); void prepareForm(); void addRow(unsigned char customElementId, const QString& name); }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_CUSTOMELEMENTDIALOG_H avogadrolibs-1.101.0/avogadro/qtgui/customelementdialog.ui000066400000000000000000000051421506155467400237030ustar00rootroot00000000000000 Avogadro::QtGui::CustomElementDialog 0 0 250 200 Rename Elements true 0 0 230 130 QFormLayout::AllNonFixedFieldsGrow Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() Avogadro::QtGui::CustomElementDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::QtGui::CustomElementDialog reject() 316 260 286 274 avogadrolibs-1.101.0/avogadro/qtgui/elementdetail_p.cpp000066400000000000000000000101771506155467400231430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "elementdetail_p.h" #include "elementtranslator.h" #include #include #include #include #include #include namespace Avogadro::QtGui { using Core::Elements; ElementDetail::ElementDetail(int elementNumber) : m_width(100), m_height(70), m_element(elementNumber) { } QRectF ElementDetail::boundingRect() const { return QRectF(-m_width / 2, -m_height / 2, m_width, m_height); } QPainterPath ElementDetail::shape() const { QPainterPath path; path.addRect(-m_width / 2, -m_height / 2, m_width, m_height); return path; } void ElementDetail::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { // Set up a font object and get its height QFont font(QStringLiteral("sans-serif")); font.setPixelSize(12); painter->setFont(font); QFontMetrics fm(font); int pixelHeight = fm.height(); QString symbol = Elements::symbol(static_cast(m_element)); QString name(ElementTranslator::name(m_element)); QString mass = QStringLiteral("%L1").arg( Elements::mass(static_cast(m_element)), 0, 'f', 3); const unsigned char* colorTmp = Elements::color(static_cast(m_element)); QColor color(Qt::white); if (colorTmp) { color.setRgb(static_cast(colorTmp[0]), static_cast(colorTmp[1]), static_cast(colorTmp[2])); } // Draw the element detail border and fill with the element colour painter->setBrush(color); painter->setPen(Qt::black); QRectF rect(-m_width / 2, -m_height / 2, m_width, m_height); painter->drawRect(rect); // Draw the element symbol bigger than everything else font.setPixelSize(24); QFontMetrics fm2(font); pixelHeight = fm2.height(); int pixelWidth = fm2.horizontalAdvance(symbol); painter->setFont(font); QRectF symbolRect(-10, -m_height / 2 + 8, pixelWidth, pixelHeight); painter->drawText(symbolRect, Qt::AlignCenter, symbol); // Reduce the font size to draw the other parts font.setPixelSize(12); int pixelHeight2 = fm.height(); painter->setFont(font); // I don't seem to be able to get a nice, cross platform layout working here // I would really like to figure out how to make this more portable - ideas? #ifdef Q_OS_MAC // Draw the proton number QRectF protonNumberRect(-m_width / 2 - 10, -m_height / 2 + 8, m_width / 2, pixelHeight2); painter->drawText(protonNumberRect, Qt::AlignRight, QString::number(m_element)); // Draw the mass QRectF massNumberRect(-m_width / 2, -m_height / 2 + 8 + pixelHeight * 1.1, m_width, pixelHeight2); painter->drawText(massNumberRect, Qt::AlignCenter, mass); // Finally the full element name QRectF nameRect(-m_width / 2, -m_height / 2 + 4 + pixelHeight * 1.1 + pixelHeight2, m_width, pixelHeight); painter->drawText(nameRect, Qt::AlignCenter, name); #else // Draw the proton number QRectF protonNumberRect(-m_width / 2 - 10, -m_height / 2 + 16, m_width / 2, pixelHeight2); painter->drawText(protonNumberRect, Qt::AlignRight, QString::number(m_element)); // Draw the mass QRectF massNumberRect(-m_width / 2, -m_height / 2 + 4 + pixelHeight, m_width, pixelHeight2); painter->drawText(massNumberRect, Qt::AlignCenter, mass); // Finally the full element name QRectF nameRect(-m_width / 2, -m_height / 2 + pixelHeight + 0.8 * pixelHeight2, m_width, pixelHeight); painter->drawText(nameRect, Qt::AlignCenter, name); #endif } void ElementDetail::setElement(int element) { if (m_element != element) { m_element = element; update(boundingRect()); } } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/elementdetail_p.h000066400000000000000000000035211506155467400226030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_ELEMENTDETAIL_P_H #define AVOGADRO_QTGUI_ELEMENTDETAIL_P_H #include namespace Avogadro { namespace QtGui { /** * @class ElementDetail * @internal * @author Marcus D. Hanwell * @brief An item box displaying more detailed information on the element. * * This class implements a QGraphicsItem for displaying a larger box that * gives greater detail about the selected element such as its full name, * proton number and average atomic mass. */ class ElementDetail : public QGraphicsItem { public: /** * Constructor. Should be called with the element number for this item. */ explicit ElementDetail(int elementNumber = 0); /** * @return the bounding rectangle of the element item. */ QRectF boundingRect() const override; /** * @return the painter path which is also a rectangle in this case. */ QPainterPath shape() const override; /** * This is where most of the action takes place. The element box is drawn * along with its symbol, proton number, mass and full name. */ void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; /** * Change the element displayed in the detail object. */ void setElement(int element); private: /** * Width and height of the item. */ int m_width, m_height; /** * The proton number of the item - all other attributes are derived from this. */ int m_element; }; } // End namespace QtGui } // End namespace Avogadro #endif // AVOGADRO_QTGUI_ELEMENTDETAIL_P_H avogadrolibs-1.101.0/avogadro/qtgui/elementitem_p.cpp000066400000000000000000000045001506155467400226300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "elementitem_p.h" #include "elementtranslator.h" #include #include #include #include #include #include namespace Avogadro::QtGui { using Core::Elements; ElementItem::ElementItem(int elementNumber) : m_valid(false), m_color(Qt::white), m_width(26), m_height(26), m_element(elementNumber) { // Want these items to be selectable setFlags(QGraphicsItem::ItemIsSelectable); m_symbol = Elements::symbol(static_cast(m_element)); if (!m_symbol.isEmpty()) m_valid = true; const unsigned char* color = Elements::color(static_cast(m_element)); if (color) { m_color.setRgb(static_cast(color[0]), static_cast(color[1]), static_cast(color[2])); } // Set some custom data to make it easy to figure out which element we are setData(0, m_element); } ElementItem::~ElementItem() {} QRectF ElementItem::boundingRect() const { return QRectF(-m_width / 2, -m_height / 2, m_width, m_height); } QPainterPath ElementItem::shape() const { QPainterPath path; path.addRect(-m_width / 2, -m_height / 2, m_width, m_height); return path; } void ElementItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { if (!m_valid) return; // Fill the rectangle with the element colour QColor bgColor; QPen pen; if (isSelected()) { bgColor = QColor(m_color).lighter(150); pen.setColor(QColor(m_color).darker(150)); pen.setWidth(4); } else { bgColor = QColor(m_color); } painter->setPen(pen); painter->setBrush(bgColor); QRectF rect(-m_width / 2, -m_height / 2, m_width, m_height); painter->drawRect(rect); // Handle the case where the item is selected if (bgColor.value() < 150) pen.setColor(Qt::white); else pen.setColor(Qt::black); painter->setPen(pen); painter->drawText(rect, Qt::AlignCenter, m_symbol); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/elementitem_p.h000066400000000000000000000042151506155467400223000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_ELEMENTITEM_P_H #define AVOGADRO_QTGUI_ELEMENTITEM_P_H #include namespace Avogadro { namespace QtGui { /** * @class ElementItem * @internal * @author Marcus D. Hanwell * @brief An element item, intended to display a single element. * * This class implements a QGraphicsItem for displaying single elements in a * periodic table. It currently allows the setting of the proton number and * gets all other information from OpenBabel. */ class ElementItem : public QGraphicsItem { public: /** * Constructor. Should be called with the element number for this item. The * constructor uses setData to set the element number using the key 0. This * is then used by PeriodicTable to figure out which element was clicked on. */ ElementItem(int elementNumber = 0); ~ElementItem() override; /** * @return the bounding rectangle of the element item. */ QRectF boundingRect() const override; /** * @return the painter path which is also a rectangle in this case. */ QPainterPath shape() const override; /** * This is where most of the action takes place. The element box is drawn * along with its symbol. */ void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; private: /** Indicates if element is well-formed (e.g., has non-empty symbol). */ bool m_valid; /** The element numbers symbol. */ QString m_symbol; /** * The color of the element which will also be used as the background color * for the item box. */ QColor m_color; /** * Width and height of the elements. */ int m_width, m_height; /** * The proton number of the item - all other attributes are derived from this. */ int m_element; }; } // End namespace QtGui } // End namespace Avogadro #endif // AVOGADRO_QTGUI_ELEMENTITEM_P_H avogadrolibs-1.101.0/avogadro/qtgui/elementtranslator.cpp000066400000000000000000000163111506155467400235470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "elementtranslator.h" namespace Avogadro::QtGui { ElementTranslator::ElementTranslator() : QObject() {} QString ElementTranslator::name(int element) { QString result; switch (element) { case 1: result = tr("Hydrogen"); break; case 2: result = tr("Helium"); break; case 3: result = tr("Lithium"); break; case 4: result = tr("Beryllium"); break; case 5: result = tr("Boron"); break; case 6: result = tr("Carbon"); break; case 7: result = tr("Nitrogen"); break; case 8: result = tr("Oxygen"); break; case 9: result = tr("Fluorine"); break; case 10: result = tr("Neon"); break; case 11: result = tr("Sodium"); break; case 12: result = tr("Magnesium"); break; case 13: result = tr("Aluminum"); break; case 14: result = tr("Silicon"); break; case 15: result = tr("Phosphorus"); break; case 16: result = tr("Sulfur"); break; case 17: result = tr("Chlorine"); break; case 18: result = tr("Argon"); break; case 19: result = tr("Potassium"); break; case 20: result = tr("Calcium"); break; case 21: result = tr("Scandium"); break; case 22: result = tr("Titanium"); break; case 23: result = tr("Vanadium"); break; case 24: result = tr("Chromium"); break; case 25: result = tr("Manganese"); break; case 26: result = tr("Iron"); break; case 27: result = tr("Cobalt"); break; case 28: result = tr("Nickel"); break; case 29: result = tr("Copper"); break; case 30: result = tr("Zinc"); break; case 31: result = tr("Gallium"); break; case 32: result = tr("Germanium"); break; case 33: result = tr("Arsenic"); break; case 34: result = tr("Selenium"); break; case 35: result = tr("Bromine"); break; case 36: result = tr("Krypton"); break; case 37: result = tr("Rubidium"); break; case 38: result = tr("Strontium"); break; case 39: result = tr("Yttrium"); break; case 40: result = tr("Zirconium"); break; case 41: result = tr("Niobium"); break; case 42: result = tr("Molybdenum"); break; case 43: result = tr("Technetium"); break; case 44: result = tr("Ruthenium"); break; case 45: result = tr("Rhodium"); break; case 46: result = tr("Palladium"); break; case 47: result = tr("Silver"); break; case 48: result = tr("Cadmium"); break; case 49: result = tr("Indium"); break; case 50: result = tr("Tin"); break; case 51: result = tr("Antimony"); break; case 52: result = tr("Tellurium"); break; case 53: result = tr("Iodine"); break; case 54: result = tr("Xenon"); break; case 55: result = tr("Cesium"); break; case 56: result = tr("Barium"); break; case 57: result = tr("Lanthanum"); break; case 58: result = tr("Cerium"); break; case 59: result = tr("Praseodymium"); break; case 60: result = tr("Neodymium"); break; case 61: result = tr("Promethium"); break; case 62: result = tr("Samarium"); break; case 63: result = tr("Europium"); break; case 64: result = tr("Gadolinium"); break; case 65: result = tr("Terbium"); break; case 66: result = tr("Dysprosium"); break; case 67: result = tr("Holmium"); break; case 68: result = tr("Erbium"); break; case 69: result = tr("Thulium"); break; case 70: result = tr("Ytterbium"); break; case 71: result = tr("Lutetium"); break; case 72: result = tr("Hafnium"); break; case 73: result = tr("Tantalum"); break; case 74: result = tr("Tungsten"); break; case 75: result = tr("Rhenium"); break; case 76: result = tr("Osmium"); break; case 77: result = tr("Iridium"); break; case 78: result = tr("Platinum"); break; case 79: result = tr("Gold"); break; case 80: result = tr("Mercury"); break; case 81: result = tr("Thallium"); break; case 82: result = tr("Lead"); break; case 83: result = tr("Bismuth"); break; case 84: result = tr("Polonium"); break; case 85: result = tr("Astatine"); break; case 86: result = tr("Radon"); break; case 87: result = tr("Francium"); break; case 88: result = tr("Radium"); break; case 89: result = tr("Actinium"); break; case 90: result = tr("Thorium"); break; case 91: result = tr("Protactinium"); break; case 92: result = tr("Uranium"); break; case 93: result = tr("Neptunium"); break; case 94: result = tr("Plutonium"); break; case 95: result = tr("Americium"); break; case 96: result = tr("Curium"); break; case 97: result = tr("Berkelium"); break; case 98: result = tr("Californium"); break; case 99: result = tr("Einsteinium"); break; case 100: result = tr("Fermium"); break; case 101: result = tr("Mendelevium"); break; case 102: result = tr("Nobelium"); break; case 103: result = tr("Lawrencium"); break; case 104: result = tr("Rutherfordium"); break; case 105: result = tr("Dubnium"); break; case 106: result = tr("Seaborgium"); break; case 107: result = tr("Bohrium"); break; case 108: result = tr("Hassium"); break; case 109: result = tr("Meitnerium"); break; case 110: result = tr("Darmstadtium"); break; case 111: result = tr("Roentgenium"); break; case 112: result = tr("Copernicium"); break; case 113: result = tr("Nihonium"); break; case 114: result = tr("Flerovium"); break; case 115: result = tr("Moscovium"); break; case 116: result = tr("Livermorium"); break; case 117: result = tr("Tennessine"); break; case 118: result = tr("Oganesson"); break; default: result = tr("Unknown"); } return result; } int ElementTranslator::numberOfElements() { return 118; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/elementtranslator.h000066400000000000000000000026111506155467400232120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_ELEMENTTRANSLATOR_H #define AVOGADRO_QTGUI_ELEMENTTRANSLATOR_H #include "avogadroqtguiexport.h" #include namespace Avogadro { namespace QtGui { /** * @class ElementTranslator elementtranslator.h * * @brief Internationalization of element names. * @author Geoff Hutchison * * This class provides for translation of element names: e.g., tr("Carbon") * To use the global translator: * @code * #include * ... * Avogadro::elementTranslator.name(6); // Return "carbon" in English * @endcode */ class AVOGADROQTGUI_EXPORT ElementTranslator : public QObject { Q_OBJECT public: ElementTranslator(); /** * Translate element names. * @param element The atomic number of the element to be translated. * @return a QString with the appropriate translation for the current locale. */ static QString name(int element); /** * @return The highest atomic number supported by this class. */ static int numberOfElements(); }; } // End namespace QtGui } // End namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtgui/extensionplugin.cpp000066400000000000000000000021641506155467400232400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "extensionplugin.h" #include namespace Avogadro::QtGui { ExtensionPlugin::ExtensionPlugin(QObject* parent_) : QObject(parent_) {} ExtensionPlugin::~ExtensionPlugin() {} QList ExtensionPlugin::fileFormats() const { return QList(); } ExtensionPluginFactory::~ExtensionPluginFactory() {} bool ExtensionPlugin::readMolecule(Molecule&) { return false; } void ExtensionPlugin::setScene(Rendering::Scene*) {} void ExtensionPlugin::setCamera([[maybe_unused]] Rendering::Camera* camera) {} void ExtensionPlugin::setActiveWidget(QWidget* widget) { setParent(widget); } bool ExtensionPlugin::handleCommand(const QString& command, const QVariantMap& options) { Q_UNUSED(command); Q_UNUSED(options); return false; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/extensionplugin.h000066400000000000000000000123501506155467400227030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_EXTENSIONPLUGIN_H #define AVOGADRO_QTGUI_EXTENSIONPLUGIN_H #include "avogadroqtguiexport.h" #include #include class QAction; namespace Avogadro { namespace QtGui { class Molecule; } namespace Rendering { class Camera; class Scene; } // namespace Rendering namespace Io { class FileFormat; } namespace QtGui { /** * @class ExtensionPlugin extensionplugin.h * @brief The base class for extension plugin factories in Avogadro. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT ExtensionPlugin : public QObject { Q_OBJECT public: explicit ExtensionPlugin(QObject* parent = nullptr); ~ExtensionPlugin() override; /** * The name of the extension plugin, will be displayed in the user interface. */ virtual QString name() const = 0; /** * A description of the extension plugin, may be displayed in the user * interface. */ virtual QString description() const = 0; /** * @return The QActions for this extension (should be at least one). */ virtual QList actions() const = 0; /** * @return The menu path of the supplied action. This can be empty if the * action was not recognized, or contain two or more strings (top level, plus * name, e.g. File, &Open). */ virtual QStringList menuPath(QAction* action = nullptr) const = 0; /** * @return A list of file format readers/writers. * * The caller takes ownership of the objects in the returned list. */ virtual QList fileFormats() const; /** * If the extension plugin has script commands, this method * should be implemented to emit the registerCommand signals. */ virtual void registerCommands() {} public slots: /** * Called when the current molecule changes. */ virtual void setMolecule(QtGui::Molecule* mol) = 0; /** * Call this slot when the moleculeReady signal indicated that a molecule is * ready to be read. The @p molecule will have the data read into it. The slot * will @return true if a molecule was successfully read in. */ virtual bool readMolecule(QtGui::Molecule& mol); /** * Called when the current scene changes. */ virtual void setScene(Rendering::Scene* scene); /** * Called when the active camera for the view changes. */ virtual void setCamera(Rendering::Camera* camera); /** * Called when the active widget changes. This must be used carefully, and * should check that the widget has been cast if used. */ virtual void setActiveWidget(QWidget* widget); /** * Called by the app to handle a command registered by the extension. * (e.g., "renderMovie" or "generateSurface", etc.) * * The app will turn the command into a string and pass it to the extension. * and any options will go from a JSON dictionary to a QVariantMap. * * @return true if the command was handled, false otherwise. */ virtual bool handleCommand(const QString& command, const QVariantMap& options); signals: /** * Signal that the extension has a new molecule that is ready to be loaded. * The application must call readMolecule in order to actually read the * molecule(s). */ void moleculeReady(int numberOfMolecules); /** * Signal that the extension has file formats that are ready to be registered. * If emitted the application should call fileFormats to get the formats the * extension is registering. */ void fileFormatsReady(); /** * Signal to request a particular tool is set as the active tool. This is * useful when loading a structure outside of the normal file API and setting * "Navigator" to active instead of "Editor" (the default). */ void requestActiveTool(QString toolName); /** * Request a specific display type (or types) are made active. * This can be useful when loading a specific type of data that * would be most readily viewed with a specialized view. */ void requestActiveDisplayTypes(QStringList displayTypes); /** * Register a new command with the application. The command will be available * through scripting (e.g., "renderMovie" or "generateSurface", etc.) * * @param command The name of the command to register. * @param description A description of the command. * * @sa handleCommand */ void registerCommand(QString command, QString description); }; /** * @class ExtensionPluginFactory extensionplugin.h * * @brief The base class for extension plugin factories in Avogadro. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT ExtensionPluginFactory : public QtPlugins::PluginFactory { public: ~ExtensionPluginFactory() override; }; } // namespace QtGui } // namespace Avogadro Q_DECLARE_INTERFACE(Avogadro::QtGui::ExtensionPluginFactory, "org.openchemistry.avogadro.ExtensionPluginFactory") #endif // AVOGADRO_QTGUI_EXTENSIONPLUGIN_H avogadrolibs-1.101.0/avogadro/qtgui/filebrowsewidget.cpp000066400000000000000000000131011506155467400233430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "filebrowsewidget.h" #include #include #include #include #include #include #include #include namespace Avogadro::QtGui { FileBrowseWidget::FileBrowseWidget(QWidget* theParent) : QWidget(theParent), m_mode(), // use the setter to initialize filters. m_valid(false), m_fileSystemModel(new QFileSystemModel(this)), m_button(new QPushButton(tr("Browse"))), m_edit(new QLineEdit) { auto* hbox = new QHBoxLayout; hbox->addWidget(m_edit); hbox->addWidget(m_button); setLayout(hbox); // Focus config setFocusPolicy(Qt::StrongFocus); setFocusProxy(m_edit); setTabOrder(m_edit, m_button); // Setup completion m_fileSystemModel->setRootPath(QDir::rootPath()); auto* fsCompleter = new QCompleter(m_fileSystemModel, this); m_edit->setCompleter(fsCompleter); // Connections: connect(m_button, SIGNAL(clicked()), SLOT(browse())); connect(m_edit, SIGNAL(textChanged(QString)), SLOT(testFileName())); connect(m_edit, SIGNAL(textChanged(QString)), SIGNAL(fileNameChanged(QString))); setMode(ExistingFile); } FileBrowseWidget::~FileBrowseWidget() {} QString FileBrowseWidget::fileName() const { return m_edit->text(); } QPushButton* FileBrowseWidget::browseButton() const { return m_button; } QLineEdit* FileBrowseWidget::lineEdit() const { return m_edit; } void FileBrowseWidget::setFileName(const QString& fname) { m_edit->setText(fname); } void FileBrowseWidget::browse() { QString fname(fileName()); QFileInfo info(fname); QString initialFilePath; if (info.isAbsolute()) { initialFilePath = info.absolutePath(); } else if (m_mode == ExecutableFile) { initialFilePath = searchSystemPathForFile(fname); if (!initialFilePath.isEmpty()) initialFilePath = QFileInfo(initialFilePath).absolutePath(); } if (initialFilePath.isEmpty()) initialFilePath = QDir::homePath(); initialFilePath += "/" + info.fileName(); info = QFileInfo(initialFilePath); QFileDialog dlg(this); switch (m_mode) { default: case ExistingFile: dlg.setWindowTitle(tr("Select file:")); break; case ExecutableFile: dlg.setWindowTitle(tr("Select executable:")); dlg.setFilter(QDir::Executable); break; } dlg.setFileMode(QFileDialog::ExistingFile); dlg.setDirectory(info.absolutePath()); dlg.selectFile(info.fileName()); if (static_cast(dlg.exec()) == QFileDialog::Accepted && !dlg.selectedFiles().isEmpty()) setFileName(dlg.selectedFiles().first()); } void FileBrowseWidget::testFileName() { QFileInfo info(fileName()); if (info.isAbsolute()) { if (info.exists()) { if (m_mode != ExecutableFile || info.isExecutable()) { fileNameMatch(); return; } } } else if (m_mode == ExecutableFile) { // for non-absolute executables, search PATH QString absoluteFilePath = searchSystemPathForFile(fileName()); if (!absoluteFilePath.isNull()) { fileNameMatch(); return; } } fileNameNoMatch(); } void FileBrowseWidget::fileNameMatch() { QPalette pal; pal.setColor(QPalette::Text, Qt::black); m_edit->setPalette(pal); m_valid = true; } void FileBrowseWidget::fileNameNoMatch() { QPalette pal; pal.setColor(QPalette::Text, Qt::red); m_edit->setPalette(pal); m_valid = false; } QStringList FileBrowseWidget::searchSystemPathForFiles(const QStringList& execs) { QStringList result; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); if (!env.contains(QStringLiteral("PATH"))) return result; static QString pathSplitter = #ifdef Q_OS_WIN32 ";" #else // WIN32 ":" #endif // WIN32 ; QStringList paths = env.value(QStringLiteral("PATH")).split(pathSplitter, Qt::SkipEmptyParts); foreach (const QString& exec, execs) { foreach (const QString& path, paths) { QFileInfo info(path + "/" + exec); if (!info.exists() || !info.isFile()) { continue; } result << info.absoluteFilePath(); break; } } return result; } QString FileBrowseWidget::searchSystemPathForFile(const QString& exec) { QString result; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); if (!env.contains(QStringLiteral("PATH"))) return result; static QString pathSplitter = #ifdef Q_OS_WIN32 ";" #else // WIN32 ":" #endif // WIN32 ; QStringList paths = env.value(QStringLiteral("PATH")).split(pathSplitter, Qt::SkipEmptyParts); foreach (const QString& path, paths) { QFileInfo info(path + "/" + exec); if (!info.exists() || !info.isFile()) { continue; } result = info.absoluteFilePath(); break; } return result; } void FileBrowseWidget::setMode(FileBrowseWidget::Mode m) { m_mode = m; QDir::Filters modelFilters = QDir::Files | QDir::AllDirs | QDir::NoDot | QDir::Drives; // This should go here, but unfortunately this also filters out a ton of // directories as well... // if (m_mode == ExecutableFile) // modelFilters |= QDir::Executable; m_fileSystemModel->setFilter(modelFilters); testFileName(); } FileBrowseWidget::Mode FileBrowseWidget::mode() const { return m_mode; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/filebrowsewidget.h000066400000000000000000000037561506155467400230270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_FILEBROWSEWIDGET_H #define AVOGADRO_QTGUI_FILEBROWSEWIDGET_H #include "avogadroqtguiexport.h" #include class QFileSystemModel; class QLineEdit; class QPushButton; namespace Avogadro { namespace QtGui { class AVOGADROQTGUI_EXPORT FileBrowseWidget : public QWidget { Q_OBJECT public: enum Mode { ExistingFile = 0, ExecutableFile }; explicit FileBrowseWidget(QWidget* theParent = nullptr); ~FileBrowseWidget() override; QString fileName() const; bool validFileName() const { return m_valid; } QPushButton* browseButton() const; QLineEdit* lineEdit() const; void setMode(Mode m); Mode mode() const; /** * @brief Search the environment variable PATH for a file with the specified * name. * @param exec The name of the file. * @return The absolute path to the file on the system, or a null QString if * not found. */ static QString searchSystemPathForFile(const QString& exec); /** * @brief Search the environment variable PATH for files with the specified * names. * @param execs The names of the files. * @return The absolute paths to the files on the system, or an empty list if * not found. */ static QStringList searchSystemPathForFiles(const QStringList& execs); signals: void fileNameChanged(const QString& filename); public slots: void setFileName(const QString& fname); private slots: void browse(); void testFileName(); void fileNameMatch(); void fileNameNoMatch(); private: Mode m_mode; bool m_valid; QFileSystemModel* m_fileSystemModel; QPushButton* m_button; QLineEdit* m_edit; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_FILEBROWSEWIDGET_H avogadrolibs-1.101.0/avogadro/qtgui/fileformatdialog.cpp000066400000000000000000000271521506155467400233210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "fileformatdialog.h" #include #include #include #include #include #include #include using Avogadro::Io::FileFormat; using Avogadro::Io::FileFormatManager; using std::vector; namespace Avogadro::QtGui { FileFormatDialog::FileFormatDialog(QWidget* parentW) : QFileDialog(parentW) {} FileFormatDialog::~FileFormatDialog() {} FileFormatDialog::FormatFilePair FileFormatDialog::fileToRead( QWidget* parent, const QString& caption, const QString& dir, const QString& filter) { FormatFilePair result(nullptr, QString()); // Use the default read filter if none specified: const QString realFilter = filter.isEmpty() ? readFileFilter() : filter; bool done = false; do { // jump point for continue statements on retry QString fileName = QFileDialog::getOpenFileName(parent, caption, dir, realFilter); if (fileName.isEmpty()) // user cancel return result; const Io::FileFormat* format = findFileFormat( parent, caption, fileName, FileFormat::File | FileFormat::Read); // If none found, give user the option to retry. if (!format) { QMessageBox::StandardButton reply = QMessageBox::question( parent, caption, tr("Unable to find a suitable file reader for " "the selected file."), QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Retry); switch (reply) { default: case QMessageBox::Retry: continue; case QMessageBox::Abort: return result; } } result.first = format; result.second = fileName; done = true; } while (!done); return result; } FileFormatDialog::FormatFilePair FileFormatDialog::fileToWrite( QWidget* parentWidget, const QString& caption, const QString& dir, const QString& filter) { FormatFilePair result(nullptr, QString()); // Use the default read filter if none specified: const QString realFilter = filter.isEmpty() ? writeFileFilter() : filter; QString fileName; do { // jump point for continue statements on retry fileName = QFileDialog::getSaveFileName(parentWidget, caption, dir, realFilter); if (fileName.isEmpty()) // user cancel return result; const Io::FileFormat* format = findFileFormat( parentWidget, caption, fileName, FileFormat::File | FileFormat::Write); // If none found, give user the option to retry. if (!format) { QString extension = QFileInfo(fileName).suffix().toLower(); if (extension.isEmpty()) { QMessageBox::StandardButton reply = QMessageBox::question( parentWidget, caption, tr( "The file extension is missing, so the format cannot be determined." " Do you want to add it?"), QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Retry); switch (reply) { default: case QMessageBox::Retry: continue; case QMessageBox::Abort: return result; } } QMessageBox::StandardButton reply = QMessageBox::question( parentWidget, caption, tr("Unable to find a suitable file writer for " "the selected format."), QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Retry); switch (reply) { default: case QMessageBox::Retry: continue; case QMessageBox::Abort: return result; } } result.first = format; result.second = fileName; } while (fileName.isEmpty()); return result; } const Io::FileFormat* FileFormatDialog::findFileFormat( QWidget* parentWidget, const QString& caption, const QString& fileName, const FileFormat::Operations formatFlags, const QString& formatPrefix) { if (fileName.isEmpty()) return nullptr; // Extract extension from filename. QFileInfo fileInfo(fileName); QString extension = fileInfo.suffix(); if (extension.isEmpty()) extension = fileInfo.fileName(); // Lookup matching file formats. vector matches( FileFormatManager::instance().fileFormatsFromFileExtension( extension.toStdString(), formatFlags)); // Prepare the strings for selectFileFormat: QString noun; QString verb; QString key; // TODO: This does not work for translation... // particularly since len(matches) is known if ((formatFlags & FileFormat::Read && formatFlags & FileFormat::Write) || ((formatFlags & FileFormat::Read) == 0 && (formatFlags & FileFormat::Write) == 0)) { // Both or neither read/write noun = tr("handlers", "File handlers"); verb = tr("handle", "e.g. file handlers that can 'handle' this file."); key = QLatin1String("fileToWrite"); // Just use the write settings } else if (formatFlags & FileFormat::Read) { // Read noun = tr("readers", "File readers"); verb = tr("read", "e.g. file readers that can 'read' this file."); key = QLatin1String("fileToRead"); } else if (formatFlags & FileFormat::Write) { // Write noun = tr("writers", "File writers"); verb = tr("write", "e.g. file writers that can 'write' this file."); key = QLatin1String("fileToWrite"); } return selectFileFormat(parentWidget, matches, caption, tr("Multiple %1 found that can %2 this format. " "Which should be used?") .arg(noun, verb), QString("FileFormatDialog/%1/%2" "/lastUsed") .arg(key, extension), formatPrefix); } QString FileFormatDialog::readFileFilter() { static QString readFilter; if (readFilter.isEmpty()) { vector formats = FileFormatManager::instance().fileFormats(FileFormat::Read | FileFormat::File); readFilter = generateFilterString(formats, AllFiles | AllFormats); } return readFilter; } QString FileFormatDialog::writeFileFilter() { static QString writeFilter; if (writeFilter.isEmpty()) { vector formats = FileFormatManager::instance().fileFormats(FileFormat::Write | FileFormat::File); writeFilter = generateFilterString(formats, WriteFormats | AllFiles); } return writeFilter; } QString FileFormatDialog::generateFilterString( const std::vector& ffs, FileFormatDialog::FilterStringOptions options) { QString filterString; // Create a map that groups the file extensions by name: QMultiMap formatMap; for (auto ff : ffs) { QString name(QString::fromStdString(ff->name())); std::vector exts = ff->fileExtensions(); for (auto& eit : exts) { QString ext(QString::fromStdString(eit)); if (!formatMap.values(name).contains(ext)) { formatMap.insertMulti(name, ext); } } } // This is a list of "extensions" returned by OB that are not actually // file extensions, but rather the full filename of the file. These // will be used as-is in the filter string, while others will be prepended // with "*.". QStringList nonExtensions; nonExtensions << QStringLiteral("POSCAR") // VASP input geometry << QStringLiteral("CONTCAR") // VASP output geometry << QStringLiteral("HISTORY") // DL-POLY history file << QStringLiteral("CONFIG") // DL-POLY config file ; // This holds all known extensions: QStringList allExtensions; foreach (const QString& desc, formatMap.uniqueKeys()) { QStringList extensions; QStringList formatExtensions = formatMap.values(desc); // When writing formats, only list one common extension // .. to ensure the OS appends the extension to the filename if (options & WriteFormats) { if (formatExtensions.contains(QStringLiteral("cml"))) formatExtensions = QStringList("cml"); else if (formatExtensions.contains(QStringLiteral("mol2"))) formatExtensions = QStringList("mol2"); else if (formatExtensions.contains(QStringLiteral("pdb"))) formatExtensions = QStringList("pdb"); else if (formatExtensions.contains(QStringLiteral("sdf"))) formatExtensions = QStringList("sdf"); else if (formatExtensions.contains(QStringLiteral("xyz"))) formatExtensions = QStringList("xyz"); } foreach (QString extension, formatExtensions) { if (!nonExtensions.contains(extension)) extension.prepend("*."); extensions << extension; } if (options & AllFormats) allExtensions << extensions; filterString += QStringLiteral("%1 (%2);;") .arg(desc, extensions.join(QStringLiteral(" "))); } if (options & AllFiles) filterString.prepend(tr("All files") + " (*);;"); if (options & AllFormats) { filterString.prepend((tr("All supported formats") + " (%1);;") .arg(allExtensions.join(QStringLiteral(" ")))); } return filterString; } const Io::FileFormat* FileFormatDialog::selectFileFormat( QWidget* parentWidget, const std::vector& ffs, const QString& caption, const QString& prompt, const QString& settingsKey, const QString& formatPrefix) { if (ffs.empty()) return nullptr; else if (ffs.size() == 1) return ffs[0]; // If more than one format found, prompt user to select one. QStringList idents; for (auto ff : ffs) { idents << QString::fromStdString(ff->identifier()); } // If there is a format prefix, see if that can reduce the results down. QStringList preferred; foreach (const QString& id, idents) if (id.startsWith(formatPrefix)) preferred << id; if (preferred.size() == 1) return ffs[idents.indexOf(preferred.first())]; // We will only show the dialog if the user is pressing // the shift, control or meta keys. if (!(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::MetaModifier))) { // use a script format if set (i.e., these override internal) // otherwise use internal over Open Babel for (int i = 0; i < static_cast(ffs.size()); ++i) { if (idents[i].startsWith("User")) { return ffs[i]; } else if (idents[i].startsWith("Avogadro")) { return ffs[i]; } else if (idents[i].startsWith("OpenBabel")) { return ffs[i]; } } // if we get here, we should show the user the dialog // .. but it should never happen } // See if they used one before: QString lastIdent = settingsKey.isNull() ? QString() : QSettings().value(settingsKey).toString(); int lastIdentIndex = idents.indexOf(lastIdent); // we're going to show the dialog - if there wasn't a choice // the default should be the first one if (lastIdentIndex < 0) lastIdentIndex = 0; bool ok; QString item = QInputDialog::getItem(parentWidget, caption, prompt, idents, lastIdentIndex, false, &ok); int index = idents.indexOf(item); // user cancel if (!ok || index < 0 || index + 1 > static_cast(ffs.size())) return nullptr; // Store chosen reader for next time if (!settingsKey.isNull()) QSettings().setValue(settingsKey, item); return ffs[index]; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/fileformatdialog.h000066400000000000000000000151061506155467400227620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_FILEFORMATDIALOG_H #define AVOGADRO_QTGUI_FILEFORMATDIALOG_H #include "avogadroqtguiexport.h" #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace QtGui { /** * @class FileFormatDialog fileformatdialog.h * * * Allow users to select save/open filenames that can be handled by registered * FileFormats. */ class AVOGADROQTGUI_EXPORT FileFormatDialog : private QFileDialog { Q_OBJECT public: /** * Container for a filename and a compatible file format, used as return * values in static functions. * @note The FileFormat will be set to nullptr to indicate an error. * @note The FileFormat points to the reference instance held by the * FileFormatManager. Use FileFormat::newInstance() to create a usable copy. */ typedef QPair FormatFilePair; /** * @brief Show a QFileDialog to prompt the user for a file to open and resolve * any file format conflicts. This method returns the selected file and * FileFormat reader. * @param parent The parent of the dialog windows. * @param caption The dialog window titles. * @param dir The initial directory shown to the user. * @param filter A list of filters for limiting the files shown to the user. * See the QFileDialog documentation for format. If the string is empty, a * default list of all suitable registered formats will be used. * @return A FormatFilePair object containing the absolute file path and a * compatible file reader. If an error occurs, the format pointer will be * nullptr. */ static FormatFilePair fileToRead(QWidget* parent, const QString& caption = QString(), const QString& dir = QString(), const QString& filter = QString()); /** * @brief Show a QFileDialog to prompt the user for a file to save and resolve * any file format conflicts. This method returns the selected file and * FileFormat writer. * @param parent The parent of the dialog windows. * @param caption The dialog window titles. * @param dir The initial directory shown to the user. * @param filter A list of filters for limiting the files shown to the user. * See the QFileDialog documentation for format. If the string is empty, a * default list of all suitable registered formats will be used. * @return A FormatFilePair object containing the absolute file path and a * compatible file writer. If an error occurs, the format pointer will be * nullptr. */ static FormatFilePair fileToWrite(QWidget* parent, const QString& caption = QString(), const QString& dir = QString(), const QString& filter = QString()); /** * Given a filename and a set of Io::FileFormat::Operation flags, find a * suitable file format from the FileFormatManager. If multiple readers are * found, ask the user to select one. If no suitable format is found, return * nullptr. * @param parentWidget Parent for any dialog windows that will appear. * @param caption Window title for any dialog windows. * @param fileName Filename to use when searching for a format. Formats are * chosen based on the file extension. * @param formatFlags Operations that the format must support. Most likely * (Io::FileFormat::)Read | File or Write | File. * @param formatPrefix Filter on the supplied prefix (default to none). * @return The selected matching reader, or nullptr if no reader is found. */ static const Io::FileFormat* findFileFormat( QWidget* parentWidget, const QString& caption, const QString& fileName, const Io::FileFormat::Operations formatFlags, const QString& formatPrefix = QString()); private: /** * Constructor is private for now to force use of static methods. This * may be made public at some point if additional API is needed or more * complex use cases arise. */ explicit FileFormatDialog(QWidget* parent = nullptr); ~FileFormatDialog() override; /** * @return A filter string for use with a QFileDialog, containing entries * for all file extensions registered with FileFormatManager, as well as a * "catch-all" entry with all known formats, and an "All files (*)" entry. * Only formats registered with (Read | File) will be used. */ static QString readFileFilter(); /** * @return A filter string for use with a QFileDialog, containing entries * for all file extensions registered with FileFormatManager. An * "All files (*)" entry is added as well. * Only formats registered with (Write | File) will be used. */ static QString writeFileFilter(); /** * Used internally by readFileFilter() and writeFileFilter(). * @{ */ public: // Must be public for operator declarations enum FilterStringOption { NoFilterStringOption = 0x0, AllFormats = 0x1, AllFiles = 0x2, WriteFormats = 0x4, }; Q_DECLARE_FLAGS(FilterStringOptions, FilterStringOption) private: static QString generateFilterString( const std::vector& ffs, FilterStringOptions options); /** @} */ /** * Show a dialog to resolve file format conflicts. This allows the user to * select between multiple instances of FileFormat that can handle their * chosen file extension. * @param parentWidget Widget to use as dialog parent. * @param ffs Conflicting FileFormats that the user should choose from. * @param caption The title of the dialog window. * @param prompt The text in the dialog window. * @param settingsKey An optional QSettings key that will be used to * store the user's choice for initializing the dialog next time. * @return The selected FileFormat instance. */ static const Io::FileFormat* selectFileFormat( QWidget* parentWidget, const std::vector& ffs, const QString& caption, const QString& prompt, const QString& settingsKey = QString(), const QString& formatPrefix = QString()); }; } // namespace QtGui } // namespace Avogadro Q_DECLARE_OPERATORS_FOR_FLAGS( Avogadro::QtGui::FileFormatDialog::FilterStringOptions) #endif // AVOGADRO_QTGUI_FILEFORMATDIALOG_H avogadrolibs-1.101.0/avogadro/qtgui/gaussiansetconcurrent.cpp000066400000000000000000000105241506155467400244350ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gaussiansetconcurrent.h" #include #include #include #include #include #include namespace Avogadro::QtGui { using Core::BasisSet; using Core::Cube; using Core::GaussianSet; using Core::GaussianSetTools; using Core::Molecule; template class BasisSetConcurrent { void setMolecule(Molecule* mol) { static_cast(this)->setMolecule(mol); } }; struct GaussianShell { GaussianSetTools* tools; // A pointer to the tools, can't write to member vars Cube* tCube; // The target cube, used to initialise temp cubes too unsigned int pos; // The index of the point to calculate the MO for unsigned int state; // The MO number to calculate }; GaussianSetConcurrent::GaussianSetConcurrent(QObject* p) : QObject(p), m_gaussianShells(nullptr), m_set(nullptr), m_tools(nullptr) { // Watch for the future connect(&m_watcher, SIGNAL(finished()), this, SLOT(calculationComplete())); } GaussianSetConcurrent::~GaussianSetConcurrent() { delete m_gaussianShells; } void GaussianSetConcurrent::setMolecule(Core::Molecule* mol) { if (!mol) return; m_set = dynamic_cast(mol->basisSet()); delete m_tools; m_tools = new GaussianSetTools(mol); } bool GaussianSetConcurrent::calculateMolecularOrbital(Core::Cube* cube, unsigned int state, bool beta) { // We can do some initial set up of the tools here to set electron type. if (!beta) m_tools->setElectronType(BasisSet::Alpha); else m_tools->setElectronType(BasisSet::Beta); return setUpCalculation(cube, state, GaussianSetConcurrent::processOrbital); } bool GaussianSetConcurrent::calculateElectronDensity(Core::Cube* cube) { const MatrixX& matrix = m_set->densityMatrix(); if (matrix.rows() == 0 || matrix.cols() == 0) { // we don't have a density matrix, so calculate one m_set->generateDensityMatrix(); } return setUpCalculation(cube, 0, GaussianSetConcurrent::processDensity); } bool GaussianSetConcurrent::calculateSpinDensity(Core::Cube* cube) { return setUpCalculation(cube, 0, GaussianSetConcurrent::processSpinDensity); } void GaussianSetConcurrent::calculationComplete() { (*m_gaussianShells)[0].tCube->lock()->unlock(); delete m_gaussianShells; m_gaussianShells = nullptr; emit finished(); } bool GaussianSetConcurrent::setUpCalculation(Core::Cube* cube, unsigned int state, void (*func)(GaussianShell&)) { if (!m_set || !m_tools) return false; m_set->initCalculation(); // Set up the points we want to calculate the density at. m_gaussianShells = new QVector(static_cast(cube->data()->size())); for (int i = 0; i < m_gaussianShells->size(); ++i) { (*m_gaussianShells)[i].tools = m_tools; (*m_gaussianShells)[i].tCube = cube; (*m_gaussianShells)[i].pos = i; (*m_gaussianShells)[i].state = state; } // Lock the cube until we are done. cube->lock()->lock(); // The main part of the mapped reduced function... m_future = QtConcurrent::map(*m_gaussianShells, func); // Connect our watcher to our future m_watcher.setFuture(m_future); return true; } void GaussianSetConcurrent::processOrbital(GaussianShell& shell) { Vector3 pos = shell.tCube->position(shell.pos); shell.tCube->setValue( shell.pos, shell.tools->calculateMolecularOrbital(pos, shell.state)); } void GaussianSetConcurrent::processDensity(GaussianShell& shell) { Vector3 pos = shell.tCube->position(shell.pos); shell.tCube->setValue(shell.pos, shell.tools->calculateElectronDensity(pos)); } void GaussianSetConcurrent::processSpinDensity(GaussianShell& shell) { Vector3 pos = shell.tCube->position(shell.pos); shell.tCube->setValue(shell.pos, shell.tools->calculateSpinDensity(pos)); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/gaussiansetconcurrent.h000066400000000000000000000041331506155467400241010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_GAUSSIANSETCONCURRENT_H #define AVOGADRO_QTGUI_GAUSSIANSETCONCURRENT_H #include "avogadroqtguiexport.h" #include #include #include namespace Avogadro { namespace Core { class Cube; class Molecule; class GaussianSet; class GaussianSetTools; } // namespace Core namespace QtGui { struct GaussianShell; /** * @brief The GaussianSetConcurrent class uses GaussianSetTools to calculate * values of electronic structure properties from quantum output read in. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT GaussianSetConcurrent : public QObject { Q_OBJECT public: explicit GaussianSetConcurrent(QObject* p = nullptr); ~GaussianSetConcurrent() override; void setMolecule(Core::Molecule* mol); bool calculateMolecularOrbital(Core::Cube* cube, unsigned int state, bool beta = false); bool calculateElectronDensity(Core::Cube* cube); bool calculateSpinDensity(Core::Cube* cube); QFutureWatcher& watcher() { return m_watcher; } signals: /** * Emitted when the calculation is complete. */ void finished(); private slots: /** * Slot to set the cube data once Qt Concurrent is done */ void calculationComplete(); private: QFuture m_future; QFutureWatcher m_watcher; Core::Cube* m_cube; QVector* m_gaussianShells; Core::GaussianSet* m_set; Core::GaussianSetTools* m_tools; bool setUpCalculation(Core::Cube* cube, unsigned int state, void (*func)(GaussianShell&)); static void processOrbital(GaussianShell& shell); static void processDensity(GaussianShell& shell); static void processSpinDensity(GaussianShell& shell); }; } // namespace QtGui } // namespace Avogadro #endif // GAUSSIANSETCONCURRENT_H avogadrolibs-1.101.0/avogadro/qtgui/generichighlighter.cpp000066400000000000000000000066531506155467400236470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "generichighlighter.h" #include namespace Avogadro::QtGui { GenericHighlighter::GenericHighlighter(QObject* parent_) : QSyntaxHighlighter(parent_) { } GenericHighlighter::~GenericHighlighter() {} GenericHighlighter::GenericHighlighter(const GenericHighlighter& other) : QSyntaxHighlighter(static_cast(nullptr)) { m_rules = other.m_rules; } GenericHighlighter& GenericHighlighter::operator=(GenericHighlighter other) { swap(*this, other); return *this; } GenericHighlighter& GenericHighlighter::operator+=( const GenericHighlighter& other) { m_rules.append(other.m_rules); return *this; } GenericHighlighter::Rule& GenericHighlighter::addRule() { m_rules.push_back(Rule()); return m_rules.back(); } int GenericHighlighter::ruleCount() const { return m_rules.size(); } GenericHighlighter::Rule& GenericHighlighter::rule(int idx) { assert("idx in bounds" && idx < m_rules.size()); return m_rules[idx]; } const GenericHighlighter::Rule& GenericHighlighter::rule(int idx) const { assert("idx in bounds" && idx < m_rules.size()); return m_rules[idx]; } QList GenericHighlighter::rules() const { return m_rules; } void GenericHighlighter::highlightBlock(const QString& text) { for (auto& m_rule : m_rules) m_rule.apply(text, *this); } void GenericHighlighter::Rule::apply(const QString& text, GenericHighlighter& highlighter) { for (auto& m_pattern : m_patterns) { // each m_pattern is a QRegularExpression // We want to highlight every occurrence of m_pattern QRegularExpressionMatchIterator iterator = m_pattern.globalMatch(text); while (iterator.hasNext()) { QRegularExpressionMatch match = iterator.next(); // If using a regex with capture groups defined, we explicitly don't want // to highlight the whole expression, only the capture groups if (m_pattern.captureCount() > 0) { // Iterate over capture groups, skipping the implicit group 0 for (int i = 1; i <= match.lastCapturedIndex(); ++i) { QString captured = match.captured(i); if (!captured.isNull()) { // According to StackOverflow user "peppe", who claims to have // written the whole QRegularExpression class, the index returned is // relative to the whole string, not to the current match // https://stackoverflow.com/questions/28725588/qregularexpression-match-position-in-the-source-string int index = match.capturedStart(i); int length = match.capturedLength(i); highlighter.setFormat(index, length, m_format); } } } else { // Straightforward regex with no capture groups, highlight whole match int index = match.capturedStart(0); int length = match.capturedLength(0); highlighter.setFormat(index, length, m_format); } } } } void GenericHighlighter::Rule::addPattern(const QRegularExpression& regexp) { m_patterns.append(regexp); } void GenericHighlighter::Rule::setFormat(const QTextCharFormat& format) { m_format = format; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/generichighlighter.h000066400000000000000000000054771506155467400233170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_GENERICHIGHLIGHTER_H #define AVOGADRO_QTPLUGINS_GENERICHIGHLIGHTER_H #include "avogadroqtguiexport.h" #include #include #include #include namespace Avogadro { namespace QtGui { /** * @brief The GenericHighlighter class provides a regexp-based programmable * syntax highlighter. */ class AVOGADROQTGUI_EXPORT GenericHighlighter : public QSyntaxHighlighter { Q_OBJECT public: /** * @brief The Rule class stores a syntax highlighting rule as a set of * QRegularExpression patterns and a text format. */ class AVOGADROQTGUI_EXPORT Rule { public: Rule() {} ~Rule() {} /** Add the pattern @a regexp to this Rule. */ void addPattern(const QRegularExpression& regexp); /** Set this Rule's text format. */ void setFormat(const QTextCharFormat& format); /** Apply this rule to the string of text, updating the highlighter if any * matches are found. */ void apply(const QString& text, GenericHighlighter& highlighter); private: QList m_patterns; QTextCharFormat m_format; }; /** Construct a highlighter with an empty rule set. */ explicit GenericHighlighter(QObject* parent_ = nullptr); ~GenericHighlighter() override; /** Construct a new highlighter using the rule set of @a other. */ GenericHighlighter(const GenericHighlighter& other); /** Replace this highlighter's rule set with that of @a other. */ GenericHighlighter& operator=(GenericHighlighter other); /** Concatenate @a other's rule set with this highlighter's rule set. */ GenericHighlighter& operator+=(const GenericHighlighter& other); /** Add a new rule to this highlighter, returning a reference to the new * rule. */ Rule& addRule(); /** @return The number of rules in this highlighter. */ int ruleCount() const; /** @return A reference to the rule at the specified zero-based index. */ Rule& rule(int idx); /** @return A reference to the rule at the specified zero-based index. */ const Rule& rule(int idx) const; /** @return An ordered list of this highlighter's rules. */ QList rules() const; friend void swap(GenericHighlighter& first, GenericHighlighter& second) { using std::swap; swap(first.m_rules, second.m_rules); } protected: void highlightBlock(const QString& text) override; private: QList m_rules; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_GENERICHIGHLIGHTER_H avogadrolibs-1.101.0/avogadro/qtgui/hydrogentools.cpp000066400000000000000000000176501506155467400227130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "hydrogentools.h" #include "../core/mdlvalence_p.h" #include #include #include #include #include // C'mon windows.... #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // Angle of tetrahedron #define M_TETRAHED 109.47122063449069389 using Avogadro::Vector3; using Avogadro::Core::Array; using Avogadro::Core::atomValence; using Avogadro::QtGui::RWAtom; using Avogadro::QtGui::RWBond; using Avogadro::QtGui::RWMolecule; namespace { typedef Avogadro::Core::Array NeighborListType; inline unsigned int countExistingBonds(const NeighborListType& bonds) { unsigned int result(0); for (auto bond : bonds) { result += static_cast(bond.order()); } return result; } inline unsigned int lookupValency(const RWAtom& atom, unsigned int numExistingBonds) { signed char charge = atom.formalCharge(); return atomValence(atom.atomicNumber(), charge, numExistingBonds); } inline float hydrogenBondDistance(unsigned char otherAtomicNumber) { static double hCovRadius(Avogadro::Core::Elements::radiusCovalent(1)); double covRadius = Avogadro::Core::Elements::radiusCovalent(otherAtomicNumber); return static_cast(hCovRadius + covRadius); } } // namespace namespace Avogadro::QtGui { void HydrogenTools::removeAllHydrogens(RWMolecule& molecule) { const Array atomicNums(molecule.atomicNumbers()); size_t atomIndex = molecule.atomCount() - 1; for (auto it = atomicNums.rbegin(), itEnd = atomicNums.rend(); it != itEnd; ++it, --atomIndex) { if (*it == 1) molecule.removeAtom(atomIndex); } } void HydrogenTools::adjustHydrogens(RWMolecule& molecule, Adjustment adjustment) { // This vector stores indices of hydrogens that need to be removed. Additions // are made first, followed by removals to keep indexing sane. std::vector badHIndices; // Temporary container for calls to generateNewHydrogenPositions. std::vector newHPos; // Convert the adjustment option to a couple of booleans bool doAdd(adjustment == Add || adjustment == AddAndRemove); bool doRemove(adjustment == Remove || adjustment == AddAndRemove); // Limit to only the original atoms: const size_t numAtoms = molecule.atomCount(); // Iterate through all atoms in the molecule, adding hydrogens as needed // and building up a list of hydrogens that should be removed. for (size_t atomIndex = 0; atomIndex < numAtoms; ++atomIndex) { const RWAtom atom(molecule.atom(atomIndex)); int hDiff = valencyAdjustment(atom); // Add hydrogens: if (doAdd && hDiff > 0) { newHPos.clear(); generateNewHydrogenPositions(atom, hDiff, newHPos); for (auto& newHPo : newHPos) { RWAtom newH(molecule.addAtom(1)); newH.setPosition3d(newHPo); molecule.addBond(atom, newH, 1); } } // Add bad hydrogens to our list of hydrogens to remove: else if (doRemove && hDiff < 0) { extraHydrogenIndices(atom, -hDiff, badHIndices); } } // Remove dead hydrogens now. Remove them in reverse-index order to keep // indexing sane. if (doRemove && !badHIndices.empty()) { std::sort(badHIndices.begin(), badHIndices.end()); auto newEnd(std::unique(badHIndices.begin(), badHIndices.end())); badHIndices.resize(std::distance(badHIndices.begin(), newEnd)); for (auto it = badHIndices.rbegin(), itEnd = badHIndices.rend(); it != itEnd; ++it) { molecule.removeAtom(*it); } } } void HydrogenTools::adjustHydrogens(RWAtom& atom, Adjustment adjustment) { // Convert the adjustment option to a couple of booleans bool doAdd(adjustment == Add || adjustment == AddAndRemove); bool doRemove(adjustment == Remove || adjustment == AddAndRemove); // convenience RWMolecule* molecule = atom.molecule(); if (molecule == nullptr) return; if (doRemove) { // get the list of hydrogens connected to this std::vector badHIndices; const NeighborListType bonds(molecule->bonds(atom)); for (auto bond : bonds) { const RWAtom otherAtom = bond.getOtherAtom(atom); if (otherAtom.atomicNumber() == 1) { badHIndices.push_back(otherAtom.index()); } } // end loop through bonds std::sort(badHIndices.begin(), badHIndices.end()); auto newEnd(std::unique(badHIndices.begin(), badHIndices.end())); badHIndices.resize(std::distance(badHIndices.begin(), newEnd)); for (auto it = badHIndices.rbegin(), itEnd = badHIndices.rend(); it != itEnd; ++it) { molecule->removeAtom(*it); } } // end removing H atoms on this one int hDiff = valencyAdjustment(atom); // Add hydrogens: if (doAdd && hDiff > 0) { // Temporary container for calls to generateNewHydrogenPositions. std::vector newHPos; generateNewHydrogenPositions(atom, hDiff, newHPos); for (auto& newHPo : newHPos) { RWAtom newH(molecule->addAtom(1)); newH.setPosition3d(newHPo); molecule->addBond(atom, newH, 1); } } } int HydrogenTools::valencyAdjustment(const RWAtom& atom) { int result = 0; if (atom.isValid()) { const NeighborListType bonds(atom.molecule()->bonds(atom)); // sum of bond orders const unsigned int numberOfBonds(countExistingBonds(bonds)); const unsigned int valency(lookupValency(atom, numberOfBonds)); result = static_cast(valency) - static_cast(numberOfBonds); } // qDebug() << " valence adjustment " << result; return result; } int HydrogenTools::extraHydrogenIndices(const RWAtom& atom, int numberOfHydrogens, std::vector& indices) { if (!atom.isValid()) return 0; int result = 0; const NeighborListType bonds(atom.molecule()->bonds(atom)); for (auto it = bonds.begin(), itEnd = bonds.end(); it != itEnd && result < numberOfHydrogens; ++it) { const RWAtom otherAtom = it->getOtherAtom(atom); if (otherAtom.atomicNumber() == 1) { indices.push_back(otherAtom.index()); ++result; } } return result; } void HydrogenTools::generateNewHydrogenPositions( const RWAtom& atom, int numberOfHydrogens, std::vector& positions) { if (!atom.isValid()) return; // Get the hybridization Core::AtomHybridization hybridization = atom.hybridization(); if (hybridization == Core::HybridizationUnknown) { // Perceive it hybridization = Core::AtomUtilities::perceiveHybridization( Core::Atom(dynamic_cast(&atom.molecule()->molecule()), atom.index())); } const Avogadro::Real bondLength = hydrogenBondDistance(atom.atomicNumber()); // Get a list of all bond vectors (normalized, pointing away from 'atom') std::vector allVectors; const NeighborListType bonds(atom.molecule()->bonds(atom)); allVectors.reserve(bonds.size() + static_cast(numberOfHydrogens)); for (auto bond : bonds) { RWAtom otherAtom = bond.getOtherAtom(atom); Vector3 delta = otherAtom.position3d() - atom.position3d(); if (!delta.isZero(1e-5)) { allVectors.push_back(delta.normalized()); } } for (int impHIndex = 0; impHIndex < numberOfHydrogens; ++impHIndex) { // First try to derive the bond vector based on the hybridization // Fallback will be to a random vector Vector3 newPos = Core::AtomUtilities::generateNewBondVector( Core::Atom(dynamic_cast(&atom.molecule()->molecule()), atom.index()), allVectors, hybridization); allVectors.push_back(newPos); positions.emplace_back(atom.position3d() + (newPos * bondLength)); } } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/hydrogentools.h000066400000000000000000000064011506155467400223500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the MoleQueue project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_HYDROGENTOOLS_H #define AVOGADRO_QTGUI_HYDROGENTOOLS_H #include "avogadroqtguiexport.h" #include #include #include namespace Avogadro { namespace QtGui { class RWAtom; class RWMolecule; class AVOGADROQTGUI_EXPORT HydrogenTools { public: /** * Remove all hydrogen atoms from @a molecule. */ static void removeAllHydrogens(RWMolecule& molecule); /** * Enum values that control the adjustHydrogen function's behavior. */ enum Adjustment { /** Only add hydrogens to underbonded atoms. */ Add = 0, /** Only remove hydrogens from overbonded atoms. */ Remove, /** Add hydrogens to underbonded atoms and remove hydrogens from * overbonded atoms. */ AddAndRemove }; /** * Add/remove hydrogens on @a molecule to satisfy valency. */ static void adjustHydrogens(RWMolecule& molecule, Adjustment adjustment = AddAndRemove); /** * Add/remove hydrogens on @a atom to satisfy valency. */ static void adjustHydrogens(RWAtom& atom, Adjustment adjustment = AddAndRemove); /** * @return The number of bonds that need to be added or removed from * @a atom to satisfy valency. A positive number indicates the number of * bonds to add, a negative number indicates the number of bonds that need to * be removed. */ static int valencyAdjustment(const RWAtom& atom); /** * Obtain the indices of hydrogens that can be removed from @a atom. * @param atom The atom from which to prune hydrogens. * @param numberOfHydrogens The maximum number of hydrogens to prune. * @param indices A vector that will be appended with the indices of the * hydrogens to remove. * @return The number of hydrogen indices appended to @a indices. * @note This function modifies neither @a atom nor its parent molecule. It * only pushes the indices of hydrogens to remove to the end of @a indices. */ static int extraHydrogenIndices(const RWAtom& atom, int numberOfHydrogens, std::vector& indices); /** * Generate positions for @a numberOfHydrogens hydrogens bonded to @a atom. * @param positions Vector of positions that will be appended with @a * numberOfHydrogens hydrogen locations. * @note There is nothing intelligent going on here. The new positions are * randomly oriented and have a length that is the sum of @a atom's and * hydrogen's covalent radii. Effort is made to prevent overlap with other * bonded atoms, but this is not guaranteed. */ static void generateNewHydrogenPositions(const RWAtom& atom, int numberOfHydrogens, std::vector& positions); private: HydrogenTools(); // Not implemented ~HydrogenTools(); // Not implemented }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_HYDROGENTOOLS_H avogadrolibs-1.101.0/avogadro/qtgui/icons/000077500000000000000000000000001506155467400204115ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/000077500000000000000000000000001506155467400222255ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/balance.png000066400000000000000000000001731506155467400243210ustar00rootroot00000000000000PNG  IHDRh6BIDATxcV4 !]}ΊV:uW޼Bo mv ݸņQ 5EtP3XIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/balance@2x.png000066400000000000000000000002621506155467400246720ustar00rootroot00000000000000PNG  IHDR yIDATxbq7FP@Ͼ꿳6[H*ϯzOwߍrQ n)}hkn|kvNNxc:w,ݮ.U1 \IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/bluedarkred.png000066400000000000000000000001751506155467400252220ustar00rootroot00000000000000PNG  IHDRh6DIDATxcP`z(k(׼OxO7V8Od 7 c F5wIP_IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/bluedarkred@2x.png000066400000000000000000000002611506155467400255700ustar00rootroot00000000000000PNG  IHDR xIDATx̓E6'ضNM 6!o&gӱ^($ꤪ*d #ԃ$gnn_x<\>Ǐ(P@ (P?),IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/cividis.png000066400000000000000000000001721506155467400243650ustar00rootroot00000000000000PNG  IHDRh6AIDATxc`Pa35tv I((--mi_lNvf=fXèQ Ib2;IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/cividis@2x.png000066400000000000000000000002371506155467400247410ustar00rootroot00000000000000PNG  IHDR fIDATxbdP70@Ow^]w,_o%q"N™G]X*".ldhwuwW-s @WmIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/coolwarm.png000066400000000000000000000001751506155467400245610ustar00rootroot00000000000000PNG  IHDRh6DIDATxc:']ݢS_}+_zƅ9ُ/xF5j7yl0IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/coolwarm@2x.png000066400000000000000000000002551506155467400251320ustar00rootroot00000000000000PNG  IHDR tIDATxbho (9m*&w.}r>Zt{=z{:x99>^\XyϤeRVL2k3g @ t0KӫIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/gray.png000066400000000000000000000001161506155467400236730ustar00rootroot00000000000000PNG  IHDR:IDATx^c޽qd ZIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/gray@2x.png000066400000000000000000000001251506155467400242450ustar00rootroot00000000000000PNG  IHDR V%(IDATxcǏ08`T2N IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/hot.png000066400000000000000000000001351506155467400235240ustar00rootroot00000000000000PNG  IHDRh6$IDATxcd``b`F! &0aTIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/hot@2x.png000066400000000000000000000001671506155467400241030ustar00rootroot00000000000000PNG  IHDR >IDATxbd``a`$Q< &*b7NM`9@ +%=8IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/inferno.png000066400000000000000000000001731506155467400243740ustar00rootroot00000000000000PNG  IHDRh6BIDATxc```fWSsLʫɝ ϝ˷[~/yTӳ? c F5d5<0gIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/inferno@2x.png000066400000000000000000000002541506155467400247460ustar00rootroot00000000000000PNG  IHDR sIDATx͵AY>GzbVJ݄jHvm$T7խ[)S>|8U8椛jlׂ}J;- @޼IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/magma.png000066400000000000000000000001731506155467400240160ustar00rootroot00000000000000PNG  IHDRh6BIDATxc```P-olXnX{غgƇ_qc F5֊^Ks^IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/magma@2x.png000066400000000000000000000002431506155467400243660ustar00rootroot00000000000000PNG  IHDR jIDATxAӌ17իf)%oe6}?V$ݐ/w28]'$$\9 @~]IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/parula.png000066400000000000000000000001741506155467400242210ustar00rootroot00000000000000PNG  IHDRh6CIDATxc0j3pØp%9WTؼc}5KefϿc&_(;5j!gЙץIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/parula@2x.png000066400000000000000000000002571506155467400245750ustar00rootroot00000000000000PNG  IHDR vIDATxb4jc+V@ b(_Itm.wlY39ЙQu-;++'3#=ɹ@((,ٟ+?jf<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/plasma.png000066400000000000000000000001731506155467400242110ustar00rootroot00000000000000PNG  IHDRh6BIDATxcao3fϴ(ay 6hmP %ME>?Ly[aaTèt`IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/plasma@2x.png000066400000000000000000000002431506155467400245610ustar00rootroot00000000000000PNG  IHDR jIDATxbao)ho a yQ8CҝtZ*."}AG0͹l9cl.zA"}_/,/V u>Zj\UmD!H06IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/spectral.png000066400000000000000000000001751506155467400245530ustar00rootroot00000000000000PNG  IHDRh6DIDATxcXfY}Kf^[r'g߷=X{nb'A}q c F5niIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/spectral@2x.png000066400000000000000000000002561506155467400251250ustar00rootroot00000000000000PNG  IHDR uIDATxb_ho(~13ߞY Eٛ3R{N9s3eCCeYoؚ/# 3ׯo{.&o_н*9*@3!VIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/turbo.png000066400000000000000000000001731506155467400240670ustar00rootroot00000000000000PNG  IHDRh6BIDATxc0r^jwmg7;ISmKrXèQ j~IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/turbo@2x.png000066400000000000000000000002671506155467400244450ustar00rootroot00000000000000PNG  IHDR ~IDATxb4攰d7F DbζDn;=Pѱ~ڍXؗ_uSVc cVi%%v6V Qr @ @/=2AIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/viridis.png000066400000000000000000000001721506155467400244040ustar00rootroot00000000000000PNG  IHDRh6AIDATxcpa vӯqpn6HլS蕛ѥt{EʋKo^z(00aTI~IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/colormap/viridis@2x.png000066400000000000000000000002511506155467400247540ustar00rootroot00000000000000PNG  IHDR pIDATxbta 7@1 @;.[?&>7&HK5OQT--\d"pT{2?7M]G?s;z̟/ H &LIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/000077500000000000000000000000001506155467400221505ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/000077500000000000000000000000001506155467400227315ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/cross.png000066400000000000000000000013011506155467400245630ustar00rootroot00000000000000PNG  IHDR szzniCCPicc(uKBQjePCEDc"!fբׯ@r4 QK_CAAkAADK@_K=*(r9y99`終BdDC=q'4SDt?T}^WxJӍpx+rߐ *=Y7:)6b9l '[Xqa_!_Q7qKRGdz02O/IʬD@jQ2k | lG8,]R3˳rwwղ>GՊeZV /p]l7$o+Mw ;pyԒp zH$L{&=ֳjsmy;8<1?g4K7% pHYsaa?iIDATX VK 0-J/t!\'ld~ځd>=&iHm (>?L6| yO~4gMS^`LxFS^)9-F#"GL`@\H 陑 6)5",r)EwF kg9Xl r"#GiMKRdD"1$͟dphb :2- I=ƂIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/dashed-preview-dark.png000066400000000000000000000015241506155467400272670ustar00rootroot00000000000000PNG  IHDR szzIDATxė[MQuF$dx<+J1 s̋LrɃċ)r}Рxƃ:c>[YLW{^[֞d*IE7 XUJb|+0 h|i Nߎ% =?:0H c@=X^jz}7\d((~G/;@z&i|AL)G@FO(gK`vD$`xNFࡘ㇘Ў_ 3 {5pU`* ^:hND[ >8 ߂3ީq̍bj67mHwV1IdO 黭b\5tZy7%(m"줬90f<P~BL)WZ t>9c:ύ~"o*1!">sovH؜\9W8Ebǹ]pu9LU̎v1yqTX|X ƾ10|]&4ؗöDjV5=#"ڊNk=|#RX0"«xNEu^sCIQ7WDd;gv}|nE1yh7;8+=%:k lETcp`:lo&۞(I.6IlȜ2>n0OaSFbЙE'V(_|Rr^,DuAByx1fxԜ5JqxLƼl$U>Fq)Y˩nބqy25 TmS?>`N8` #IQ/J{fn󆂕X^_ C Dk2-cv^`!7`CC#IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-close.png000066400000000000000000000023411506155467400263600ustar00rootroot00000000000000PNG  IHDR szzgAMA abKGD pHYs  tIME*(2^IDATx=lE]߿}g'v@(R[9БH'![$PQ,BH LI @(G*DŽN >~DE|~pCZ`ۅ[뎳ꍻm>qa, |dsLSJ@~b qD4WMA\WDa]Z\4꺔ked>y3&\gb]BnJ:-,bQRQRR>*bJ,O$@"́ӧS((Bxt$DU)ʮцkV@I%jb݊XJ}" xYc'an@"'c)n/.\QR+)3fJ$ҷ}Uj5 nB)Җy='W]‹G׀J Pu`v^U:oC>U_3{y1b-6ӄwUwhvpeJ!TOђmeI;2> DHHEd)F>/FgF  #'ڙ?Z|4|uϓOn#+IGǂ_JPnprوycd\ɠx{S!T&Hbim%p(Vx;S*T ѩQ"̱rww>JE Q0µdEv Y~Mݭ nb{5A913P9+X:V(J ݽeޑ޹C5( sB+.jy s{Y;_zwZ-6 */itE_11ׄ_>)u_ƗDuು!(6/([BHʃV\SYK2.nj%s$͖ٝlG} EyYzT3j /Dݧ|~mS-;#C_mpFhN=/H{>./oί9]3'A]8#75bf\o 8|K 8ruwc!:͇ؐ'}ɭIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-export.png000066400000000000000000000013041506155467400265720ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<fIDATxkAǿi6ҀU 9"7{͓7cS9֞/EP(iXHMbj̆fvݝ]gS0;o޼7, NZ6A$r  #DD4zUS m^?o ٔ Y-;=!+egؿWA4u${=pb[E#,OKnj0"gҟٷOBm!ܰR:臤A( Uy+Vt(^ wBw'|\B_s_EØcjv7XSS)Bc =sJ10հ bUZ">ІSʪN!@ճ2Yqx>Yq8<`mȥM\VxSuE|Vě8 KR~}'lqg_ ΏDv ,\,62UUqkb\d26|fkyT*aΤ q8.hr9LߔEޖÏԃ//H$^}k\v!vz.6?:";.xIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-import.png000066400000000000000000000012371506155467400265700ustar00rootroot00000000000000PNG  IHDR DgAMA a cHRMz&u0`:pQ<PLTE@@Ah9[<@@A]]^@@AJJK@@A@@AppqƵt=9I~@>L|?rjDk+Iz?c[z̭ęzwke\TSOKMCKGMHAx㺴ڢҌty]mVbTWQLOB爈vvwmmn͇v tRNS`0 bKGDHtIMExIDAT8c``ddxa !@@!A E (ǢEB$Ee0+(*)Jih`(70426*0!YA+:ś2,p$9xd[%*deħ (OP+6vF| 8Xs@qd=nN.F$ح3JH.%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-new.png000066400000000000000000000017371506155467400260540ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxW[HQfvv5/Y[kRhY*EQ[EeңOJ"D e=XAe]zkPHh|45WksuggfL猻e̹ۗ|3M4UNk{"|TQM ^% G$C':F! bLJn8 h&nMG |zb'?ݛP _N)r3VANW88p}<%d NLmzxw Xfr4cpM.ƞm?tgi5\l!  ;!-Dͽ7&ҭуG;1]-Rk$Om] 4p):uk WPFLh.E) & ȈgxY\L1P DKQE SKvy|L ѶD" n( uhGCpiQo.ROaa!k/cǠ:H`L.`4z@:yR_===hooZIB())Ep8W*<4+#5H'rÇAabP~b>EQpbCDy##K #SCCCx->oAE,˳r?!с;o1 ֟ '3 ?rD& \sTߎS0~0ЖIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-open-recent.png000066400000000000000000000020231506155467400274670ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxWmHSQ~tV^P2HO(W?B#) _!$7'}PʑXd_w1FN70{;ֵ]۟s<9{;F$DZ̛Ə9Ч8wL٣̧ghS\\, "s>j8DO`> tcc1{)""& Y&r iR/uWd@C%[DZޞS("pmך;X;/h}݄ y3Q}!&!uR""M{LHU0n/wy yMDPf32'O]^ewT`1g~睖Y%}oo/ʯdr^,ț2 q~_y#ZZ?/?"ZSrZp⥲* cCVT?nK nţW.cգju (,>TcaVz2Fsx܁V4yiQ%dGXwݏʷ^Tۑ8"دۂڳ vx`{ZC4X<3Xu>?糈0 U"W_nV.;z;zLϠ;p )!%';G>$(~{I(Åj'!wm==hr2B\w "vIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-open.png000066400000000000000000000015071506155467400262170ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxkAǿ٤[ -HQZ/=ŋAY<7wR4+^"Rk5̌M6tktN{of9:Y$t!dsvvOMMMI"1L&ۗnH$%/v.y ?<%a%wkJrx@b/n\8ƒ[NX8.J\+V}lR_ so `G*H(sx4wiOB1: A—oTLd3#F>l0G) û#J?~c&&+ !vHԘ` bpЭmsqB c]R4 T4(eL. (*aO_RAN]@2O֒Q Fe l{O-a(U$eMж@~KruUR bWrneԪ"nI@D11P(Գԕ7heXY%ĞI)"GZhp@v]})7M!i bX\IăܭU چ ^,f0``[ǂΑ<+j?8 Aؙ|@up ̐|${S^1`= ϭ~ΎIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-save-all.png000066400000000000000000000017461506155467400267670ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxWOA~j)&x@#hr6^=x ѨzbZMtw[4ngw|}ߛ7p$ 'tRsf&866>CiBr0 Ljwah(#w;k#FoXp]V܀Cx,d-8S9#Cg5<zfmRpج]07&>﹩_r?A.ُ51OQ4Nt|\YGOÞfv&‹'TJp@@.'Urݯl1`~oQsU1qL1g͕F1-Լm%8`ڦaiw/D=nP9&{JnLfK $hO`4.wC'`P"f,e =H0y: 5 C.,L?ĀrL9%i(IH$كA2bj5bU ,A<~jL;օd6Y 渆'#z0C @Ӵ&Xu@,7gg۱Jͭpr[Yh2OuD"། ]I2 4$zzR裵>! =5bZDO hEl :IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-save-as.png000066400000000000000000000027711506155467400266210ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxW{lSUmݮ؆ 5!͹PD-&*HРAI&M@D#ddFF!1 @Aacĭ{Ю9ki8/==}=ι }zc烎A<:c{δ!18X2^¢'a?w&< '|G9/CWG{.!Zcl lhw:(F2nPvхtΐow9ɸi PvP(B`6+f/Eޛ7eRQS,ܹm&\i-%FM:az8̚Ёf-(ل⩣pTaypwPOWdq[wai:TVHb$fP*:]طy"UA͆֗a]hMrɸE7_Uևgg᢭?@CWbE?D%s:;FfT *ZA0ğ=,mrŊ<\GHW'MK)'`2@եTbEqS)+@D"\.dvp`%FM?v V#c GJS'(V7lbF}Jb[i8y==.0 \ĈZF5J%wK3uj" XaJQB|˲>:hG~E\>7]uUDH,AM~ /c ­M#dP:,a~ ,âqIc!5 xT+}aRRkb>WA\DFBX>ްo=gepR%SM x\>SRvJucp?aJlb6[׎'JkQSS#mh8\G~5BrT>K`Qh8cqiR,Y?qoTXt\ܘjTW/*,++eoFi1gΡ8nk1}4(r%r#Gb^ÔWÇIEP__/S*pī3s\mCD9ɵ{B >w]VLP)?≐<8߯0@IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/document-save.png000066400000000000000000000014031506155467400262070ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxW;oA;84v]@P"=*[ !ZBDw>Μ .Hi{os.2 XN?xvh1ܾ9S]v{>y {vٹUظqQ8rC8VeKp9gF@p9 `t,C$ MIDƱG&bD CxذMH+\o@E΅ BaM~FY"0 ɠ%Q)yd„-Fj LXH$~x?"J:[1P-AzWxLhiin^[[˸j w(ټCyHzB0m9 K =o<`IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/dots-light.png000066400000000000000000000012711506155467400255160ustar00rootroot00000000000000PNG  IHDR szzoiCCPicc(u;KA?#Z(l€("M Fml"lⲛ VX6 (W#Iޏ3s.3g4\މEb|Iৗ?al ކTɔk@Ca;yIzV-mdICaG(|tϊ~WǦzj_b#dY`Tϣnґ-IهKa4t b'$5'}3ǐMGi2 ]SRMSYUtͱJ04?ywy_G} p$K5-xM8i.oAσpeQ4:um˕<5àlg``A pHYsaa?iIDATXG= 1Wx A Abg,z mml; !r @ =nyE:RԈP@vBO |Hm "|> щScsN@EX˘ۧwñn2+MF@o V@Sdzl㟌 ajeŵ h!2%a ~hw<+`ÈcLYh&NizZVI< $& A r3nĄ#2A%v[Z z^M/j팴Q'_H_oJcZa9 ^!^QQI&ٰ@ETE.O+C uW|!WiŶ8ԪT, Y,9\:q1GY\A7kʲv&)5e$'IR9xU`PQ>lc.@GL>!PC$Ƣc^C&=h-l\xf"oJ Xc4 Ob'=1=%Ik5(7ƞ>xҠ}̣j[ɚ Ȏ kJQj0rȧEoD"C D~ WE̓dFҼ>:2t%Qy`3 TsfڧJ*w[`5e'!ݬerV|ٻ˹䐬*YUÆubM+Df*eV8KeyF.]G{tp{UOKgv?3~0YϺO挮卾 ]N.ׇTy 7kvtx3ߍ*o.HS~7FD |h -Kn,H{%3PCm +Ck F&/yا_|;iE^xcox/hVyU!l&!3|btx.:޺@nl! 9 Awh ɋ0vjs͈I|]QLH@qor:KIL`3x47Qxq%:+ƶP>qSB OL"`PBtu~^*eDzք5]hvؾۙ?p$af6vD"͒Fsbgs(pf;B, h۬wEIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/edit-copy.png000066400000000000000000000015661506155467400253440ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATx_HSqǿwV_˿6S0iTXFBo !"#1{ fd F )SPX2nv)#>tllq9w~g MS`/2Lfs4"7q\KwJtYRMn`Џ#+Xp B$W#ؒ]TlR |u|<M1>OJys1y ![Rw_dz'7}Е%%ÙzX-)a@C!}CbooZw 7WT LM=f_^! %jjuȘo~P}vb*[J$Eb]2NBW4O0,d~k%ˈB)f0g\'&ې6 aU@b&*.5=l!SCscoQ]=I,2RQUc3E:=qLX IM0P$C8 =N$x ]9 |DP OL,Q^qnrM4VB#@^^ey׊2G<]zcf82R~;# I \礘^:w'5X"`YmRwNt!ѷ;'Ҩ2wvvbzfvsGJVp87 no =;o%];x!<O3%#߉Sh3 Pe SrIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/edit-cut.png000066400000000000000000000025051506155467400251570ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxW]LSg~JO TX !3[Q~vc^.3o]o %[24^lWk,2A)+m-:ŋ9}}"Jy^yxs@~ ņGq " zp#)+h>:>䂽)pN%PȄ(** m k: )j*͜IB֒kPTP]Ca&vIdffs@iIl~[|6 @Ei(C8] V(Y?UW,~}$IPq>8C6J#(!;O\bU[DO4RDUiBgv<((~}wwlﲥZL*=_ڒ2~r4 u%=Z[ t; dӨQa<aytݼ):ֆuuJ48G(UnACGǡj;M%H$QtL0f+ {Nyc6&H|OB`b6h/ի1]IYv;= N 2 [>y5kty՝aqMA|AK8h*mmBC=L1R49Tj05nj҄ rv;Kk_-C!1O=D-AHHȉ4P8=fhhLƽ{8~{՗_`<0_hS  ҖG%T@Ew|Л[Ja7PXgcVhaj-}ww5Ba ݀ygVtxs{BZ v&RMsZr~$Kv{)J Wx.Q5^r/ay<&s@ox 扟9wHG{+EQgfT)X\ /:_>ViŹau!JNuZ5Q}\DF0m9h>11#Yw,"$d]Y5|]Rc3t5`hp =7S ӁǻGyC'Gb%Ey=_%dywABE_WSg찹 0Fe8x: IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/edit-delete.png000066400000000000000000000023411506155467400256240ustar00rootroot00000000000000PNG  IHDR szzgAMA abKGD pHYs  tIME*(2^IDATx=lE]߿}g'v@(R[9БH'![$PQ,BH LI @(G*DŽN >~DE|~pCZ`ۅ[뎳ꍻm>qa, |dsLSJ@~b qD4WMA\WDa]Z\4꺔ked>y3&\gb]BnJ:-,bQRQRR>*bJ,O$@"́ӧS((Bxt$DU)ʮцkV@I%jb݊XJ}" xYc'an@"'c)n/.\QR+)3fJ$ҷ}Uj5 nB)Җy='W]‹G׀J Pu`v^U:oC>U_3{y1b-6ӄwUwhvpeJ!TOђmeI;2> DHHEd)F>/FgF  #'ڙ?Z|4|uϓOn#+IGǂ_JPnprوycd\ɠx{S!T&Hbim%p(Vx;S*T ѩQ"̱rww>JE Q0µdEv Y~Mݭ nb{5A913P9+X:V(J ݽeޑ޹C5( sB+.jy s{Y;_zwZ-6 */itE_11ׄ_>)u_ƗDuು!(6/([BHʃV\SYK2.nj%s$͖ٝlG} EyYzT3j /Dݧ|~mS-;#C_mpFhN=/H{>./oί9]3'A]8#75bf\o 8|K 8ruwc!:͇ؐ'}ɭIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/edit-paste.png000066400000000000000000000014141506155467400254760ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxb?@ȈSc?@ݻw=(qF,~0p28:0lټAZZ!>>aB?P1V ?y`=: pɯ1LAKDŽAYE߿ j rrr  'Nb̐!bpMRB L7#oA8 BBB AA  <}Ubش^d@_crp|(zr(:A&PP0el1?!8pëy7h @G?28x%Ucٹsxy )/EhB$ O=#b!AA`"% '-2H>?#I!o/_lX!#~ZiQ@ M0 `' AH CMꘁF- .b>~7GzzzPN:pԩ :hn.u''' A }D L @{{R /[a9YNcrs a|r"s .$ZӧOA@& zWoIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/help-about.png000066400000000000000000000030721506155467400255010ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxWKoU>vĤN"DPpєbS` +XE,iH!+QHUZ%4i~ܙ{scό {|uGs]O3?Mzc>?L4Sn2k!So^,3H?P/l>ɗNG1[5]H M-y4sUk#x7ZM F* @"B';{o.@wl* H:L_=[Iu6$<fskǓIHX. ;P$|JcCs"1X9BzƷp8PhLgRI5 `@W#'߀JWdVCf .>>xd:yB* dd,Q$F+Fkpހ b3 ^ؘ6VJ.Bz'噁)rГGKE  ѭČg'@u&d;찻Jk,%N\:ֳmG 5@4ъi]- G;<0"1gREV4 q\ X|QBAD3@ L D/@1b샢{ի-GR~=IE{%3@,9ѣ[&32'Zkq= t'`HŪ'Пq9jU[q  ;)H69٥Ar*0֨z4$=>Oxq 6&R#%3(2Bfo"Ϲm>=5 h #W$:-bjX~<#ڀM%0鄫5SaۂFMrxV2|S8EZke)j¶8{q$ ̹аPӹ6y9![v?r#[@HTřy L9LD7Z6 5l&r1@|x̲O#7g2̥ U1C:Y\cWMpi kA q"xP<35mP.K <nWIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/lock-open-dark.png000066400000000000000000000005111506155467400262420ustar00rootroot00000000000000PNG  IHDR szzIDATxb?= b;2b E1#gy XxA 0͂@Z@lq@|I-K$qjPXHQ H$B=RPBUbnJ2WLH>gHL$cB |1Gr@@8 b pvq j&`>)8A .b@iBuF0Q:b(P)vIXM&J!TSfb  WIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/lock-open-light.png000066400000000000000000000014671506155467400264430ustar00rootroot00000000000000PNG  IHDR szzoiCCPicc(u;KA?# -D,bRcaDQdIu V,D_@[VAA?૑1YfǙ935D}71Z0=4J4{Lu^sf OfQq}q(+Rzϊse~W%&!zjNװw-eyM {~Vj^<LC#ML|ڒ߾oUw(#G^!5+!IQ;O/;2\Ƨ x(JPvտ*9}]"бgU- ԏT/3)'ZYU9uykۇ~߱ PgVs` pHYsaa?inIDATXGcd 85Kw 4 _@$^%+P bsL F3.IN ;r @`$4 Dǫd7@A 9^Ӂ,9jNԃA1 2P"G}IdJrr 9TJ,vH{"d P" ~G@J8J@fb\Qrfd\){7>8`>(KQhh xS)]'\D@Bf r/6 @mBP2N dIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/plus-dark.png000066400000000000000000000003561506155467400253450ustar00rootroot00000000000000PNG  IHDR szzIDATxb?@R0:3 G:\ztx:`u5ďN-71?6wZ ڱ9@Nrl`Ct/cssq7K}@lO"\0Q:`8`y19bfTa&[IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/plus-light.png000066400000000000000000000013101506155467400255220ustar00rootroot00000000000000PNG  IHDR szzniCCPicc(uKBQjePCEDc"!fբׯ@r4 QK_CAAkAADK@_K=*(r9y99`終BdDC=q'4SDt?T}^WxJӍpx+rߐ *=Y7:)6b9l '[Xqa_!_Q7qKRGdz02O/IʬD@jQ2k | lG8,]R3˳rwwղ>GՊeZV /p]l7$o+Mw ;pyԒp zH$L{&=ֳjsmy;8<1?g4K7% pHYsaa?iIDATX c`逑US`ZAχZ Ħ3@=r$F2`"YD}İ(C"Puhz <b@71?cw jَ@AilsqZv^r%"XF[ ACn >.A*=D@b$4@fjhuh! K&Eu;&ƢDD& 5#wCBh, IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/preview-dark.png000066400000000000000000000012161506155467400260370ustar00rootroot00000000000000PNG  IHDR szzUIDATxKKUQǝ@J/IQ(Ph8QP7LHg5ȡ-s'Pq`PNՉ+|ꬃާ˝ ?8}^dQV:I w@ hjĖ*% 钜Pހ_,dY_+0 a} ۦȮLr[b_){ƶԎP ~䨘!pXhSq`Q'w?h(;=?|K[=I'Y]8GyԘ6;Que|IdV%C !_z%蔘wu|lT5ܒY3o8IqyLWoa$sk SGVq"IpGD>4;GP `Zk Ð:Ss1!' 7*:FA/2)1C1`H ek ˆv׾[J: &~vKL'﹚$ϥ_v=jgwIENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/32x32/preview-light.png000066400000000000000000000021271506155467400262270ustar00rootroot00000000000000PNG  IHDR szzoiCCPicc(u;KA?#Z(l€("M Fml"lⲛ VX6 (W#Iޏ3s.3g4\މEb|Iৗ?al ކTɔk@Ca;yIzV-mdICaG(|tϊ~WǦzj_b#dY`Tϣnґ-IهKa4t b'$5'}3ǐMGi2 ]SRMSYUtͱJ04?ywy_G} p$K5-xM8i.oAσpeQ4:um˕<5àlg``A pHYsaa?iIDATXG햹MQ/ ӋN3=D" JD4BAKSMm((jDAAC0wq]^goՕn # zt1c>h2vp|kN\mYf<մO0`Q8xx^%x)`!^2>vr8 9 ;H ]{ }Sߍ`^0*X8yO9FJQ)˙1Y0w2 z `=6ޣA Nk5}AC-q6"+ڤ{M;.KI\5<O~,+vcY<]R7HUw͹*4w3 0t-q.N5#3YR}ei8 w&?Ct 0|]*98׫iϣ5O w zqXZs^WZ\\? %\UpZ+0udQ2N7͈|ӷy%t,y~`Qbqz3metXVksђsqxȍH Uc_.~c=OjYlbC3"s ċZur& hDWFFB(IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/000077500000000000000000000000001506155467400227435ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-close.png000066400000000000000000000011241506155467400263700ustar00rootroot00000000000000PNG  IHDR@@XGlgAMA a cHRMz&u0`:pQ<PLTE@@Ar37y15 ( ȜtRNS@fbKGDHtIME IDATHDž 0a`0aL`$LZJW՟;q pӥ*ab\ǥffHLY&D"Ld%2h DVDR`D$&&mH BD3̈{Wsrܤ8OHGZBAMLJXe6 xBnIE[>7:rxhcPaRcE R,X̱`CᖄM"6Vn`&%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-export.png000066400000000000000000000026311506155467400266100ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTEBBCWWXKKLǵ[{=dVvt?;b9I&FKN#E:bEM!E>LD&vN6K~B)xQ,J~A9a%H~D{qqpponnmmmllkjii%|QHwL{u~pwjpdj_dY]TVMOILFLDJAJ>I;I7HHMD#zQ౹ܧ؜ԒЈ~shy\rXnWjUeT`S[RUQQPEDjFqAȷǶrF"zO!yO!yN yMxLxKxJwJwIwHwGvFvFvEuDuDuDtDsB/Hn>n6IrAfzbQUUV@@ARsuuv¿RRS||}QQRLLNJJKIIJHHIœɕ͐؊ႂNNOzz|闘ttvSSTNOPVVWKLM\\]!tRNS@fbKGDHtIME/ IDATXc`d"00+9mԀQF p9(42yx9(3hE qPf(eKHRf esPf"<()khji1 x 053wptrvqus`1~A!aQ1q I0LR&P`J @&AiX?@3 +;'7/jT?>FhR'735i@sK&EAk[;E%RGgEebwO/Er_O/B$0q)SNN3f΂s̝7Μ bK`%K/X@+,Yz ,YvzJ ذq%l޲m@;w1Pb{(1` p`AJ 80%r<@'NbgVes/\dM._khe Y$4w%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-import.png000066400000000000000000000021271506155467400266010ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTE@@AKKLɸ~ yM|v>yL0F/GKO,E&}QGM(F&}R?L"E%}R8KD.JC$}R'HCƴ0VEOsH k=kgd~`y]tTkUkRgNcJ_F[CV?Q;M8K7J5J3I1I/H,H?JqE౹ܧ؜ԓщ~thy]sXnWjVfT`S\RVQRP'~T%}QFFGQQRGGIPPQfghsstSSTopqVVWhij[[[ccd]]_`abZ[\jjlUVWppqTUVwwxLMNLLMJJKIIJHHIHHJEEFͶu)tRNS@fbKGDHtIMEurIDATXeSQk'(vwv(6vk_v``QvWG?~?̽{9w=\x^ W`@@RA U1`@Tt q`@Z@G58`R32sr KJ+*`jjtM-m]=}. p+ z[]>HQ/o^g- maˈ~q)01ԴQp5r37(e Iy`ڧzyce&r%{D`=<" NN/. lׄ7-!{OD'֋g޿U,ɬ%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-new.png000066400000000000000000000037141506155467400260630ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTEvyyLTRAECABBDJGT\Z~HNIELEQyMVN]Q\RXQUQOOLMEI@`FOWTIIJrtsIZGcQoVlViUfUaTJNEM@K?tG>GCekjWWXDHCcRy]uXrXgUVQSPNOGN=L8J6cEBJFJUH}fqsj~bPP@LEBeR!H6\E'I+zFqW|c~iszr\JLDL;J4H.H#H$GJmJOSPhlgdje HKvK\OJRNH_SZRUMcdd#HD[GZPKeJB}H9{GDM)FNxM*IlrpBoG~AA!fanR*f鞞aM Y9y`D@gZcVqIilYyEen\SP Ҭ&l@~mIi]}CcSY_3܀m i7U] 6tAO/0h@Ȁ,F%caRkdvBLSj4m3gΘ>d9sΝ7҂6 XDQ`R(X `PT4`E0!VR1Q\}j [G؀7WټeHnۈ1 rЎPKvڵ{n iff*,Amqa!(c&lf&^IЀTCEE]\\DECW 8 v# {"-MUU5-lP?&%";kA@1W8Ix@?~j3P`_7(0 ] N=A ~bsێS4%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-open-recent.png000066400000000000000000000040761506155467400275130ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTEHjR P OQR$Wf~ZqV Xvua"T~HHI`lw a h4_WWX[w U"+$ ~k] XY_ jx];c8I0e%hpԹ͑OX g{dFYZ%p.lxjtȂ`_rn^P T5`]H|iX2\2`z`fKKL@@Ac S.7WhWuZw \\ ]aS_yܬTZOp.pIIJNOPkklmmnoop NPnu@|Q UOl[[\LMNRRSTTUUUVԆ OiYվ[SVsSZ_&dp,ZɴҚѦΓwwxffg``aiij{tRNS@fbKGDHtIME/  IDATXc` 021spr! sxxDD%$HdA&k@ "h ZX[o/09GDFE{Xg@_B@bKpxRrJjjZzFfDV\vNn^>1pG$WTTVU$446c@smk[{phFGVWw P:.&N<ăՙ4ut `79rvZiscO kS8@,3]-d)!f/QXf<$cXȀΕ`j2=[%IȀrJkf]ֿh&nifB,ݢu[pfHʲ)ۻwܵk{ܴj?!T<44PPGtPXPc`#vČ >%$ kO8syM J๊DD3/ARrXhc@JyB\]"*bԵ5SPɓRk!ׁPUV]nC8td^?rAcfEeր4 8yAnx] %(ꭓ 1bN{W .]?DpB5vV_) 5=v"힎w+ KBQxǝڍ:&=~s xz1';GDa=vWK|։i: 'Y/P3i`m\xqMf[| X[. ־ xJZZw&ݓI7_v%HޜGg3I1`wz xy@](1 m[J %|>n@?0Pb3= ~/ l:Y@?v@d.bgvwkʫ.kP%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-open.png000066400000000000000000000035571506155467400262400ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTEFEEKKLQQQKJJJIIj[XKJHdcaEDBJGCBBB`IHHʏӮt֫ek`KŞaXVRVQEkgZ͏մtjHFCjksgJeҏسejhOKDDCBHHGٷfۺe׼t֏jĬaҍNNNiiܾeڏ֌‚isݏڽdڌw{zyg_gcqcŴ_ފpZzsLgc⍗f߲dTSfqbƹ_㊅bܰbsMWfbom[ٯbZdqƻ^ZYQ׭_`[Gͻ^ajɾb`ԫ\ac`ʿ`|~dbǽ]܆ҪZϨYcpb߁cǾ^}lkeϧXU[WCb__鉼u|˥VQqkIbclŢRL}M`a}|_ǿ^ꈒdQ`]`^~{`ƽMR_~{_]f^EW^^z^RRMHZȿ^D\]\w\ׂ_^XANLB\uztsm?[Zqv=[irh=Z[iOY^vt]b]?XY`_ULLIV||vc`EL~_TWAAArrqx0tRNS@fbKGDHtIMEu:IDATXyTLQRD ä&j" PY3 Q.iR[dM$=BF ,e;s;޹Av(ZWLAdJE=L#EN@gUj8UԕT*{^tb@7r@KW.]OOj 05 od4` u MX,ccdƃf&&fÈ9  tu3-mlٌLGڎCƎi7c3pho?I&;NqBƙ0p<L:+9}l"0gyy!sxZC ,"z~}}h> ɖ-㷢>^I IzMkCuaa#5**IhF-QM۶1qxɢ#bmg/ 11-?؀$G&'"'a'O(̬ p ;''+ٱyY9MXnK0p9JռkW"rq@zj18[ iH -,.cRi;G08@Y9{h¶4{* ++_J,PPʃZ V؛pw5dpRp#O|"|ލ4ml``%7_'}# ?~jID`f\K_d7`&E$VCȭ%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-save-all.png000066400000000000000000000044431506155467400267760ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTEAABHHICCEBBDEEF@@APPRXXZghjWXYfgiPPQWWYffhefhVWXcdf~VVX}~UVWbce|}UUWabdz{~TUVaacnnollmWWXNNOLLNEFGFGIDEGKLMMNOKMOWZ\cilejl_`bxy|GGHyz}VWYׅ\]_TTU__bwx{[\^YYZ^_avwzZ[]XYZ]^`tuxZZ\]]_stw`acXXYqruYY[[[]pqs~nor|}^_`YZ[mnq{|^^`lmoSVX}jknxy{\]^tYchijlSSU``bHHJTTV__aqrtoprSTVGGIRSTYZ\QRTUVXFGHPQSTUWFFGNOPDDEQQSBBC@@BMMOAACMMNdegCCDKKLJJKIIKBCDOOQNNPFFHBACKKMEEGDCECBDJJL\\^\\]VVWSSTOPRIJKDDFQQRRRTIIJPQRώ̍ȋĈkkk{{}uvxrsuABCZZ[A@Bggi؊/stRNS@fbKGDHtIMEuIDATXÝyXue$Rh( HPJ0B:J>226R+Ê Z][%N9J04KM-;;ҳ<ޏ>eFaHSӚcF é0ΏZytx&Z<'N V29(((,ZFƉ(9!av #WIk(*"::B%S``[ MH4hG BiMWHIfκ]:;--m]<ה~%2`\! 'iԄAxJ_s~\q)Q_LW^B(G3i i=g+tQQ Y-Q!EMUȦuebtQ B5B,_QtQCZ?Izᐊ{=R7кo_Lkm^UzYMMͪ |k3D,-++ ~GZig @aѻ*,5_']WZ)]ԦM"Ee|Vٌ n@< m6"|ǥ6 yq-t/@`k3/@A r\e@|8֎ `:F,4AAMttu-4OE%M4@*w*\.AV=?ttu=<=423݉vtRNS@fbKGDHtIME IDATXå\uۮ[!,! "eAD4i [(G)`cČ~YF?FԨSaď~nn4/ϓa29?&1> 43Εp"H5kv0Q9 RΝPfs5 5,9 =-bN.8GXF3:Y9 J`/.QlP@"(3UY Γ\P vvQTi  QNx oA(p!e._69 , 5'g1Kr e<)?0+XA<ܷ|fR "cJNXB")*Pqd~~~y&N&YED1@)f (&a`5H9+0c-kAkosp  wL5:{[ L9=sx"md 6JQ@%c 2J*i@kいj؄g'|&j -IB? $JrS~}o($Nj|FI( !lP[P'ޠ@_V3Yϲs @1nj6+" |r+<*4*p(]Pɋ# nQ 6o[«&kp|.]S+dickAK@xC|m (`cT4.+Pp+ 4c(#7\J{7Mlkkk,{B E{vR">p򑆒wX`@y(pOrP%tkPí{v^+QŇ+]">§2'Az]$6 Ѳ3 d)(WC;tz%0.$Q38pz͜P35uqo&_`u<5^9r؇%5r·}F>(R qS1b I)P _'w zapvgySwF+tqݎ};<#ʚ_9bL{l`_WH M 3B OK B{diTJWd X_Oæ? A8>49/vLMcшsJ1%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/document-save.png000066400000000000000000000041371506155467400262300ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTE@@A}~aacVXZmnpbceZ[\oprabdnor~[[\}`ac[[]|}``blmoz|~_`bklnyz}^_ajkmxy|^^`ijlwx{{|]^`hiluvy\]_ghktux\\^fgittv[\^ffirsuZ[]degqruZZ\ddgpqtstwYZ\cdfXY[bbenoqqrtXYZbbdWXYZZ[jknmnqVWYYZ[VWX_`aVVXUUWUXZTUVl{NSUefhhikFGHFFH]^_def__aWWYYY[BBCCCDBBDAABDDESSTXXZAACEEFDDFCCECBDBACPPRVVWҴGGIEEGDCEPPQUVWjkljjlggiOPQSTVTUWGGHMMOMMNLMNLLNKLMKKMKKLNNPRSULLMUVXSSURRTMNOQQSȳOOQϏƲˌFFG®JJLJJKĉ@@BIIKLMOHHJIIJHHI~{|~yz|NOPccd熈ڋ}̅xz|~"tRNS@fbKGDHtIMEu*IDATXí{XSu p Q !V"f*vE[J~tq1ȍ슱IIdffYhjif|}ٞy0`l@@{AGY` B`8h4;""ʘD! #Sd|G> ƌq@1p & ǁi񚥞@d@vb$ȢMipR4OClrOh& (&PH|hރb!P "93hS|(%Lʊ`Z43iS\ 'Er63h320+qDrQN86I<.{f.L#/1IG2*?Q\+L@ +*%%eՑ\#_M9Pu3i@apMݜGBs[o#0{TޡNF`nns@TH["DL̋my&(ԭJ([9RT%H jof빃h[(9-.WSn}*9ÏG`cU]G_!:ۃ5wj1oH20-&Yg%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/edit-copy.png000066400000000000000000000034041506155467400253470ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTEBBCWWXEEFoopey]jºߵoٵnwѵiɳq}eldy󸴩bv񰱦ks𩴩]kpZm햳VX^^_i돳SRKKLg뇸WicRgzvOd~qlJ^feJ}ff^OuffLkggKIdhdEeghM˪ٌGJeghhOUwooosfhN\?tGghh\NήXgiiNitsiiRjɩVgjijGgijjȦUhjHhjhjƤTùyGjjQƾMQkjQũTiijMNNON@ȥkklջşвֽ׿㗗JJKIIJҰHHI—͑؊MMNтyy{TTUqrs🟡ZZ[eefCtRNS@fbKGDHtIMEuIDATXw\q~9dό>##+ ٣G\RQ(3{K6 I{%dGݥ߯~_xx{ǡ\2A (A3_ 2 Y^ 4g+ ,'ؾpŊ+З(Yʒ[0d˔,GK~+URU5רi!U-kשkeMK84ldCKUc4%\ʌ[;lh8'gGelZCߦkLvm:Bߩsn\f }={| vл{È(}=0 4*pXp #E1N cA `aD5rx \@H8`^ ˔l*+ôJ܇PЯYծ _A ߴ9["m;;v~n+{XܗGI(_~裢<$9zw<:Fw"?)0%q 6A}&;~=} x/KHy9'_}ς?L1N.uN APLTE\cgʭ5yz}hnquvyڌ쵶jkl%{}~ϦױFeƝQtശBSZ+dzLLMvy{e0-Zp i9fuGjuBHMzpFNQR\WltLRUpWz򷷹$qyj~ХDW_\\^U^bK]dkuzb庼`inUUV;l3JXrrunyӚuIMPPPQ@BDTY]ZZuFPT~eefYYZ^t쎐4[lLUYΝkqv}:CJppqTWYSST:Zeuuvq檫:UcBPVu}bcdnz:މCMSaabw1Pb|^__OX\sfqvwDFH?T]˫Բ􃄅rtw[[\ nl v ~ٌ{ikN}KqY\_d}Y#{2v[u~k `q|b <`lwewDP$n#1`tLmfghMai.l瓓yDKOtRNSS%IDATxbO!`lp_PU:WlN(MLW YL%3 %  2j%L 2kQZEi ɋ\Z!.KYKg"ޠ!LeB䥃Sg=7jW E$gP$& T\.FbfgPSVuU6y'?qYҲX~B JK?Nfv.%3,Yh2MvYZZSބπTYϭ#gEܤ*avu! ڙ?_zd/H#X|ʳ>{. ]Mb5rՉ Mg gXlyΓ̓q]U¯8W&Tәa%$OΔrꖍP f& qrt =4 {~]h:dʱ b} k|k ~Ι9 == &lzHKpNNvv%ULIǹ E[ʩ SE*IOOύү \E̝@sgGqעs'rᦽHxݞEFIFۙ$CUReJ&ůK:3W"jƍ6G3Xk\4Kե>Q!1AuVh.o:`E5)#6Ā7i~ixt0mOJK˙h{QX1~竣-r1`@zPJu˭{ +&J%&q s/޸ m uδo eg)_7NPJd?Q%~fA"u QQ1q I)iY9y \(*)#.U5u MI -mm]d=}7BNp˧i e@( GDFGɨ'$&%'dqY>j9yZE%jeP TBAUu60kjKP'TPni@ wwvwwM>sTL3Pi̚MӀ#_ˀ,",inp8+05k1؀uX cf,` )lH2`e;H~N`€X{q}c$108b#XQpp@<| 8AB410mS3Y,؀)b 2 hhk"€KXeRDz p:\ "7#ǀ[XNo߾mq n=18!G1 xOJ׾P^ f[<w aGJ 0y>|aĀT ;%`qcĀ(1? 1=w,QUv-+v%tEXtdate:create2021-03-19T16:21:44+00:00~%tEXtdate:modify2021-03-19T16:21:44+00:00i#?tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/avogadro/qtgui/icons/fallback/64x64/help-about.png000066400000000000000000000063511506155467400255160ustar00rootroot00000000000000PNG  IHDR@@gAMA a cHRMz&u0`:pQ<PLTE֍dhjKQU@FJ?DF>AD@@AX`d-Vg#s{  q(cwW^bmrt:IOx:DJ| ~ rCJN:T] |zk4HTȉ(}{xtZ|=KQ$!%ws dU[^:Zc'&).1rkHMR9an+46:=CX^qsuCV^yvok1LZ?EIBJZ`pni/LOUWSujg2[`clhb0eilYpf Z3nrvdQq@CCQY6CNcn` UUStRNS@fbKGDHtIMEuIDATXÕ{\S#!9HQ[7ZpW @m\D FKP'XBbVBk@nbA.)vVVMj>{Ae?|ZDLئ.afFp9leG͟p-V\3Xdg%#Ж3-~ё%&zPtG!^|yA_|qڵ7 $jhK{$ s#@yHDz>07sWopas"iڠ5D @WoX&Q\U~\W}noC#4P;;t&,D1F u:!GR|y{>⬲E^0L/#F7 \r!ԀeҎ" Áw@42d fg'XC"Aۅ' vZqITtN8;9/ > 5'!psvD@ -[ @@0ܝ5L^ ! `BO<#4wA^i:?::$dVHHt4jϳ1ɝY#&O+&&fG#G'dn^qzraalPllaarz:=䏲~ ┪tcv:˂fP-mn'ȞނEǫ ,++܍MQM-jA|2mlǓ_gjX y:} #include #include #include #include #include #include #include #include #include namespace Avogadro::QtGui { class InsertFragmentDialog::Private { public: QFileSystemModel* model; SortFilterTreeProxyModel* proxy; QModelIndex proxyRoot; QString currentFileName; bool crystalFiles; // are we inserting crystals (i.e., don't center) ~Private() { delete model; // proxy is handled through the model } }; InsertFragmentDialog::InsertFragmentDialog(QWidget* aParent, QString directory) : QDialog(aParent), m_ui(new Ui::InsertFragmentDialog), m_implementation(new Private) { setWindowFlags(Qt::Dialog | Qt::Tool); m_ui->setupUi(this); // to start, hide the preview m_ui->preview->hide(); m_implementation->currentFileName.clear(); if (directory.contains(QLatin1String("crystals"))) m_implementation->crystalFiles = true; else m_implementation->crystalFiles = false; // we check for the downloaded version first QStringList dirs; QStringList stdPaths = QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation); foreach (const QString& dirStr, stdPaths) { QString path = dirStr + "/data"; dirs << path; // we'll check if these exist below } // add in paths relative to the binary (e.g. for development) dirs << QCoreApplication::applicationDirPath() + "/../" + QtGui::Utilities::dataDirectory() + "/avogadro2"; #ifdef Q_WS_X11 dirs << QString(INSTALL_PREFIX) + "/share/avogadro2/"; #else // Mac and Windows use relative path from application location dirs << QCoreApplication::applicationDirPath() + "/../share/avogadro2"; #endif QString m_directory; QDir dir; foreach (const QString& dirStr, dirs) { qDebug() << "Checking for " << directory << " data in" << dirStr; QDir testdir(dirStr + '/' + directory); if (testdir.exists() && testdir.isReadable()) { m_directory = testdir.absolutePath(); break; } } if (m_directory.isEmpty()) { // Can't really do anything! m_ui->directoryTreeView->setEnabled(false); m_ui->insertFragmentButton->setEnabled(false); m_ui->filterLineEdit->setEnabled(false); return; } m_implementation->model = new QFileSystemModel(this); m_implementation->model->setReadOnly(true); QModelIndex rootIndex = m_implementation->model->setRootPath(m_directory); QStringList filters; if (m_implementation->crystalFiles) filters << "*.cif"; else filters << "*.cjson" << "*.cml"; m_implementation->model->setNameFilters(filters); m_implementation->proxy = new SortFilterTreeProxyModel(this); m_implementation->proxy->setSourceModel(m_implementation->model); m_implementation->proxy->setSortLocaleAware(true); // important for files // map from the root path to the proxy index m_implementation->proxyRoot = m_implementation->proxy->mapFromSource(rootIndex); // Our custom class needs this to prevent becoming rootless m_implementation->proxy->setSourceRoot(rootIndex); m_ui->directoryTreeView->setModel(m_implementation->proxy); // remember to map from the source to the proxy index m_ui->directoryTreeView->setRootIndex(m_implementation->proxyRoot); // hide everything but the filename for (int i = 1; i < m_implementation->model->columnCount(); ++i) m_ui->directoryTreeView->hideColumn(i); m_ui->directoryTreeView->setSelectionMode(QAbstractItemView::SingleSelection); m_ui->directoryTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui->directoryTreeView->setUniformRowHeights(true); connect(m_ui->insertFragmentButton, SIGNAL(clicked(bool)), this, SLOT(activated())); connect(m_ui->directoryTreeView, SIGNAL(doubleClicked(const QModelIndex)), this, SLOT(activated())); connect(m_ui->directoryTreeView, SIGNAL(activated(const QModelIndex)), this, SLOT(activated())); connect(m_ui->filterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(filterTextChanged(const QString&))); connect(m_ui->directoryTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(currentChanged(const QModelIndex&, const QModelIndex&))); } InsertFragmentDialog::~InsertFragmentDialog() { delete m_ui; delete m_implementation; } QString InsertFragmentDialog::fileName() { if (m_implementation == nullptr || m_implementation->model == nullptr) return QString(); // The selected model index is in the proxy QModelIndexList selected = m_ui->directoryTreeView->selectionModel()->selectedIndexes(); if (selected.isEmpty()) { return QString(); } // Remember to map to the source model return selected.first().data(QFileSystemModel::FilePathRole).toString(); } void InsertFragmentDialog::currentChanged(const QModelIndex& selected, const QModelIndex& deselected) { Q_UNUSED(deselected) if (m_implementation == nullptr || m_implementation->model == nullptr) return; // Remember to map to the source model QString fileName = selected.data(QFileSystemModel::FilePathRole).toString(); QFileInfo info(fileName); if (!info.isDir()) { // get the image name -- default to svg QString imgName = info.absolutePath() + '/' + info.baseName() + ".svg"; // check if the svg exists, if not try png if (!QFile::exists(imgName)) { imgName = info.absolutePath() + '/' + info.baseName() + ".png"; } m_ui->preview->setIcon(QIcon(imgName)); m_ui->preview->show(); } else m_ui->preview->hide(); } void InsertFragmentDialog::refresh() { m_ui->directoryTreeView->update(); } void InsertFragmentDialog::filterTextChanged(const QString& newFilter) { if (!m_implementation || !m_implementation->proxy) return; // no dialog or proxy model to set // Allow things like "ti" to match "Ti" etc. QRegularExpression reg(newFilter, QRegularExpression::CaseInsensitiveOption); m_implementation->proxy->setFilterRegularExpression(reg); if (!newFilter.isEmpty()) { // user interface niceness -- show any file match m_ui->directoryTreeView->expandToDepth(2); } } void InsertFragmentDialog::activated() { QString currentFileName = fileName(); // check to see if it's an actual file and not a directory if (currentFileName.isEmpty() || !QFileInfo(currentFileName).isFile()) { return; } emit performInsert(currentFileName, m_implementation->crystalFiles); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/insertfragmentdialog.h000066400000000000000000000024741506155467400236660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_INSERTFRAGMENTDIALOG_H #define AVOGADRO_QTGUI_INSERTFRAGMENTDIALOG_H #include "avogadroqtguiexport.h" #include namespace Avogadro { namespace QtGui { namespace Ui { class InsertFragmentDialog; } /** * @brief Dialog to prompt a format and descriptor string. */ class AVOGADROQTGUI_EXPORT InsertFragmentDialog : public QDialog { Q_OBJECT public: explicit InsertFragmentDialog(QWidget* parent = nullptr, QString directory = "molecules"); ~InsertFragmentDialog() override; QString fileName(); public Q_SLOTS: void refresh(); void filterTextChanged(const QString&); void activated(); void currentChanged(const QModelIndex& selected, const QModelIndex& deselected); Q_SIGNALS: void performInsert(const QString& fileName, bool crystal); private: Ui::InsertFragmentDialog* m_ui; class Private; Private* m_implementation; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_InsertFragmentDIALOG_H avogadrolibs-1.101.0/avogadro/qtgui/insertfragmentdialog.ui000066400000000000000000000054551506155467400240560ustar00rootroot00000000000000 Avogadro::QtGui::InsertFragmentDialog 0 0 412 376 Insert Fragment… Filter: Type a name or part of a name to show only matching files. border-radius: 4px; true 128 128 Qt::Horizontal Qt::Horizontal 40 20 Insert true false Qt::Horizontal 40 20 avogadrolibs-1.101.0/avogadro/qtgui/interfacescript.cpp000066400000000000000000000714121506155467400231740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "interfacescript.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtGui { using QtGui::GenericHighlighter; using QtGui::PythonScript; InterfaceScript::InterfaceScript(const QString& scriptFilePath_, QObject* parent_) : QObject(parent_), m_interpreter(new PythonScript(scriptFilePath_, this)), m_moleculeExtension(QStringLiteral("Unknown")) { } InterfaceScript::InterfaceScript(QObject* parent_) : QObject(parent_), m_interpreter(new PythonScript(this)), m_moleculeExtension(QStringLiteral("Unknown")) { } InterfaceScript::~InterfaceScript() {} bool InterfaceScript::debug() const { return m_interpreter->debug(); } QJsonObject InterfaceScript::options() const { m_errors.clear(); if (m_options.isEmpty()) { qDeleteAll(m_highlightStyles.values()); m_highlightStyles.clear(); // Retrieve/set options QByteArray json = m_interpreter->execute( QStringList() << QStringLiteral("--print-options")); if (m_interpreter->hasErrors()) { m_errors << m_interpreter->errorList(); return m_options; } QJsonDocument doc; if (!parseJson(json, doc)) return m_options; if (!doc.isObject()) { m_errors << tr("script --print-options output must be an JSON object " "at top level. Received:\n%1") .arg(json.constData()); return m_options; } m_options = doc.object(); if (m_options.contains(QStringLiteral("highlightStyles")) && m_options.value(QStringLiteral("highlightStyles")).isArray()) { if (!parseHighlightStyles( m_options.value(QStringLiteral("highlightStyles")).toArray())) { qDebug() << "Failed to parse highlighting styles."; } } } // Check if the generator needs to read a molecule. m_moleculeExtension = QLatin1String("cjson"); if (m_options.contains(QStringLiteral("inputMoleculeFormat")) && m_options[QStringLiteral("inputMoleculeFormat")].isString()) { m_moleculeExtension = m_options[QStringLiteral("inputMoleculeFormat")].toString(); } return m_options; } QString InterfaceScript::displayName() const { m_errors.clear(); if (m_displayName.isEmpty()) { m_displayName = QString(m_interpreter->execute( QStringList() << QStringLiteral("--display-name"))); m_errors << m_interpreter->errorList(); m_displayName = m_displayName.trimmed(); } return m_displayName; } QString InterfaceScript::menuPath() const { m_errors.clear(); if (m_menuPath.isEmpty()) { m_menuPath = QString( m_interpreter->execute(QStringList() << QStringLiteral("--menu-path"))); m_errors << m_interpreter->errorList(); m_menuPath = m_menuPath.trimmed(); } return m_menuPath; } QString InterfaceScript::scriptFilePath() const { return m_interpreter->scriptFilePath(); } void InterfaceScript::setScriptFilePath(const QString& scriptFile) { reset(); m_interpreter->setScriptFilePath(scriptFile); } void InterfaceScript::reset() { m_interpreter->setDefaultPythonInterpreter(); m_interpreter->setScriptFilePath(QString()); m_moleculeExtension = QLatin1String("Unknown"); m_displayName = QString(); m_options = QJsonObject(); m_warnings.clear(); m_errors.clear(); m_filenames.clear(); m_mainFileName.clear(); m_files.clear(); m_fileHighlighters.clear(); m_highlightStyles.clear(); } bool InterfaceScript::runCommand(const QJsonObject& options_, Core::Molecule* mol) { m_errors.clear(); m_warnings.clear(); m_filenames.clear(); qDeleteAll(m_fileHighlighters.values()); m_fileHighlighters.clear(); m_mainFileName.clear(); m_files.clear(); // Add the molecule file to the options QJsonObject allOptions(options_); if (!insertMolecule(allOptions, *mol)) return false; connect(m_interpreter, &PythonScript::finished, this, &::Avogadro::QtGui::InterfaceScript::commandFinished); m_interpreter->asyncExecute(QStringList() << QStringLiteral("--run-command"), QJsonDocument(allOptions).toJson()); return true; } void InterfaceScript::commandFinished() { emit finished(); } bool InterfaceScript::processCommand(Core::Molecule* mol) { if (m_interpreter == nullptr) return false; QByteArray json(m_interpreter->asyncResponse()); if (m_interpreter->hasErrors()) { m_errors << m_interpreter->errorList(); return false; } QJsonDocument doc; if (!parseJson(json, doc)) { return false; } // Update cache bool result = true; if (doc.isObject()) { QJsonObject obj = doc.object(); // Check for any warnings: if (obj.contains(QStringLiteral("warnings"))) { if (obj[QStringLiteral("warnings")].isArray()) { foreach (const QJsonValue& warning, obj["warnings"].toArray()) { if (warning.isString()) m_warnings << warning.toString(); else m_errors << tr("Non-string warning returned."); } } else { m_errors << tr("'warnings' member is not an array."); } } m_moleculeExtension = "cjson"; if (obj.contains("moleculeFormat") && obj["moleculeFormat"].isString()) { m_moleculeExtension = obj["moleculeFormat"].toString(); } Io::FileFormatManager& formats = Io::FileFormatManager::instance(); QScopedPointer format( formats.newFormatFromFileExtension(m_moleculeExtension.toStdString())); if (format.isNull()) { m_errors << tr("Error reading molecule representation: " "Unrecognized file format: %1") .arg(m_moleculeExtension); return false; } auto* guiMol = static_cast(mol); QtGui::Molecule newMol(guiMol->parent()); if (m_moleculeExtension == "cjson") { // convert the "cjson" field to a string QJsonObject cjsonObj = obj["cjson"].toObject(); QJsonDocument doc2(cjsonObj); QString strCJSON(doc2.toJson(QJsonDocument::Compact)); if (!strCJSON.isEmpty()) { result = format->readString(strCJSON.toStdString(), newMol); } } else if (obj.contains(m_moleculeExtension) && obj[m_moleculeExtension].isString()) { QString strFile = obj[m_moleculeExtension].toString(); result = format->readString(strFile.toStdString(), newMol); } // check if the script wants us to perceive bonds first if (obj["bond"].toBool()) { newMol.perceiveBondsSimple(); newMol.perceiveBondOrders(); } // how do we handle this result? if (obj["readProperties"].toBool()) { guiMol->readProperties(newMol); guiMol->emitChanged(Molecule::Properties | Molecule::Added); } else if (obj["append"].toBool()) { guiMol->undoMolecule()->appendMolecule(newMol, m_displayName); } else { // replace the whole molecule Molecule::MoleculeChanges changes = (Molecule::Atoms | Molecule::Bonds | Molecule::Added | Molecule::Removed); guiMol->undoMolecule()->modifyMolecule(newMol, changes, m_displayName); } // select some atoms if (obj.contains("selectedAtoms") && obj["selectedAtoms"].isArray()) { QJsonArray selectedList = obj["selectedAtoms"].toArray(); for (auto&& i : selectedList) { if (i.isDouble()) { auto index = static_cast(i.toDouble()); if (index < guiMol->atomCount()) guiMol->undoMolecule()->setAtomSelected(index, true); } } guiMol->emitChanged(Molecule::Atoms); } // check if there are messages for the user if (obj.contains("message")) { QString message; if (obj["message"].isString()) message = obj["message"].toString(); else if (obj["message"].isArray()) { QJsonArray messageList = obj["message"].toArray(); for (int i = 0; i < messageList.size(); ++i) { if (messageList[i].isString()) message += messageList[i].toString() + "\n"; } } if (!message.isEmpty()) { QMessageBox::information(qobject_cast(parent()), tr("%1 Message").arg(m_displayName), message); } } } return result; } bool InterfaceScript::generateInput(const QJsonObject& options_, const Core::Molecule& mol) { m_errors.clear(); m_warnings.clear(); m_filenames.clear(); qDeleteAll(m_fileHighlighters.values()); m_fileHighlighters.clear(); m_mainFileName.clear(); m_files.clear(); // Add the molecule file to the options QJsonObject allOptions(options_); if (!insertMolecule(allOptions, mol)) return false; QByteArray json( m_interpreter->execute(QStringList() << QStringLiteral("--generate-input"), QJsonDocument(allOptions).toJson())); if (m_interpreter->hasErrors()) { m_errors << m_interpreter->errorList(); return false; } QJsonDocument doc; if (!parseJson(json, doc)) return false; // Update cache bool result = true; if (doc.isObject()) { QJsonObject obj = doc.object(); // Check for any warnings: if (obj.contains(QStringLiteral("warnings"))) { if (obj[QStringLiteral("warnings")].isArray()) { foreach (const QJsonValue& warning, obj["warnings"].toArray()) { if (warning.isString()) m_warnings << warning.toString(); else m_errors << tr("Non-string warning returned."); } } else { m_errors << tr("'warnings' member is not an array."); } } // Extract input file text: if (obj.contains(QStringLiteral("files"))) { if (obj[QStringLiteral("files")].isArray()) { foreach (const QJsonValue& file, obj["files"].toArray()) { if (file.isObject()) { QJsonObject fileObj = file.toObject(); if (fileObj[QStringLiteral("filename")].isString()) { QString fileName = fileObj[QStringLiteral("filename")].toString(); QString contents; if (fileObj[QStringLiteral("contents")].isString()) { contents = fileObj[QStringLiteral("contents")].toString(); } else if (fileObj[QStringLiteral("filePath")].isString()) { QFile refFile(fileObj[QStringLiteral("filePath")].toString()); if (refFile.exists() && refFile.open(QFile::ReadOnly)) { contents = QString(refFile.readAll()); } else { contents = tr("Reference file '%1' does not exist.") .arg(refFile.fileName()); m_warnings << tr("Error populating file %1: %2") .arg(fileName, contents); } } else { m_errors << tr("File '%1' poorly formed. Missing string " "'contents' or 'filePath' members.") .arg(fileName); contents = m_errors.back(); result = false; } replaceKeywords(contents, mol); m_filenames << fileName; m_files.insert(fileObj[QStringLiteral("filename")].toString(), contents); // Concatenate the requested styles for this input file. if (fileObj[QStringLiteral("highlightStyles")].isArray()) { auto* highlighter(new GenericHighlighter(this)); foreach (const QJsonValue& styleVal, fileObj["highlightStyles"].toArray()) { if (styleVal.isString()) { QString styleName(styleVal.toString()); if (m_highlightStyles.contains(styleName)) { *highlighter += *m_highlightStyles[styleName]; } else { qDebug() << "Cannot find highlight style '" << styleName << "' for file '" << fileName << "'"; } } } if (highlighter->ruleCount() > 0) m_fileHighlighters[fileName] = highlighter; else highlighter->deleteLater(); } } else { result = false; m_errors << tr("Malformed file entry: filename/contents missing" " or not strings:\n%1") .arg(QString(QJsonDocument(fileObj).toJson())); } // end if/else filename and contents are strings } else { result = false; m_errors << tr("Malformed file entry at index %1: Not an object.") .arg(m_filenames.size()); } // end if/else file is JSON object } // end foreach file } else { result = false; m_errors << tr("'files' member not an array."); } // end if obj["files"] is JSON array } else { result = false; m_errors << tr("'files' member missing."); } // end if obj contains "files" // Extract main input filename: if (obj.contains(QStringLiteral("mainFile"))) { if (obj[QStringLiteral("mainFile")].isString()) { QString mainFile = obj[QStringLiteral("mainFile")].toString(); if (m_filenames.contains(mainFile)) { m_mainFileName = mainFile; } else { result = false; m_errors << tr("'mainFile' member does not refer to an entry in " "'files'."); } // end if/else mainFile is known } else { result = false; m_errors << tr("'mainFile' member must be a string."); } // end if/else mainFile is string } else { // If no mainFile is specified and there is only one file, use it as the // main file. Otherwise, don't set a main input file -- all files will // be treated as supplemental input files if (m_filenames.size() == 1) m_mainFileName = m_filenames.first(); } // end if/else object contains mainFile } else { result = false; m_errors << tr("Response must be a JSON object at top-level."); } if (result == false) m_errors << tr("Script output:\n%1").arg(QString(json)); return result; } int InterfaceScript::numberOfInputFiles() const { return m_filenames.size(); } QStringList InterfaceScript::fileNames() const { return m_filenames; } QString InterfaceScript::mainFileName() const { return m_mainFileName; } QString InterfaceScript::fileContents(const QString& fileName) const { return m_files.value(fileName, QString()); } GenericHighlighter* InterfaceScript::createFileHighlighter( const QString& fileName) const { GenericHighlighter* toClone(m_fileHighlighters.value(fileName, nullptr)); return toClone ? new GenericHighlighter(*toClone) : toClone; } void InterfaceScript::setDebug(bool d) { m_interpreter->setDebug(d); } bool InterfaceScript::parseJson(const QByteArray& json, QJsonDocument& doc) const { QJsonParseError error; doc = QJsonDocument::fromJson(json, &error); if (error.error != QJsonParseError::NoError) { m_errors << tr("Parse error at offset %L1: '%2'\nRaw JSON:\n\n%3") .arg(error.offset) .arg(error.errorString()) .arg(QString(json)); return false; } return true; } bool InterfaceScript::insertMolecule(QJsonObject& json, const Core::Molecule& mol) const { // Update the cached options if the format is not set if (m_moleculeExtension == QLatin1String("Unknown")) options(); if (m_moleculeExtension == QLatin1String("None")) return true; // Always insert the selected atoms QJsonArray selectedList; for (Index i = 0; i < mol.atomCount(); ++i) { if (mol.atomSelected(i)) selectedList.append(static_cast(i)); } json.insert("selectedAtoms", selectedList); // insert the total charge json.insert("charge", mol.totalCharge()); // insert the spin multiplicity json.insert("spin", mol.totalSpinMultiplicity()); Io::FileFormatManager& formats = Io::FileFormatManager::instance(); QScopedPointer format( formats.newFormatFromFileExtension(m_moleculeExtension.toStdString())); QScopedPointer cjsonFormat( formats.newFormatFromFileExtension("cjson")); // If we want something *other* than CJSON, check that we can supply that // format if (format.isNull()) { m_errors << tr("Error writing molecule representation to string: " "Unrecognized file format: %1") .arg(m_moleculeExtension); return false; } std::string str; if (!format->writeString(str, mol)) { m_errors << tr("Error writing molecule representation to string: %1") .arg(QString::fromStdString(format->error())); return false; } // if we need a different format, insert it if (m_moleculeExtension != QLatin1String("cjson")) { json.insert(m_moleculeExtension, QJsonValue(QString::fromStdString(str))); } // We will *always* write the CJSON representation // Embed CJSON as actual JSON, rather than a string, // .. so we'll have to re-parse it cjsonFormat->writeString(str, mol); QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(str.c_str(), &error); if (error.error != QJsonParseError::NoError) { m_errors << tr("Error generating cjson object: Parse error at offset %1: " "%2\nRaw JSON:\n\n%3") .arg(error.offset) .arg(error.errorString()) .arg(QString::fromStdString(str)); return false; } if (!doc.isObject()) { m_errors << tr("Error generator cjson object: Parsed JSON is not an " "object:\n%1") .arg(QString::fromStdString(str)); return false; } json.insert("cjson", doc.object()); return true; } QString InterfaceScript::generateCoordinateBlock( const QString& spec, const Core::Molecule& mol) const { Core::CoordinateBlockGenerator gen; gen.setMolecule(&mol); gen.setSpecification(spec.toStdString()); std::string tmp(gen.generateCoordinateBlock()); if (!tmp.empty()) tmp.resize(tmp.size() - 1); // Pop off the trailing newline return QString::fromStdString(tmp); } void InterfaceScript::replaceKeywords(QString& str, const Core::Molecule& mol) const { // Simple keywords: str.replace(QLatin1String("$$atomCount$$"), QString::number(mol.atomCount())); str.replace(QLatin1String("$$bondCount$$"), QString::number(mol.bondCount())); // Find each coordinate block keyword in the file, then generate and replace // it with the appropriate values. QRegularExpression coordParser(R"(\$\$coords:([^\$]*)\$\$)"); QRegularExpressionMatch match; int ind = 0; // Not sure while this needs to be a while statement since we replace all in // one go? We never iterate ind... while ((match = coordParser.match(str, ind)).hasMatch()) { // Extract spec and prepare the replacement const QString keyword = match.captured(0); const QString spec = match.captured(1); // Replace all blocks with this signature str.replace(keyword, generateCoordinateBlock(spec, mol)); } // end for coordinate block } bool InterfaceScript::parseHighlightStyles(const QJsonArray& json) const { bool result(true); foreach (QJsonValue styleVal, json) { if (!styleVal.isObject()) { qDebug() << "Non-object in highlightStyles array."; result = false; continue; } QJsonObject styleObj(styleVal.toObject()); if (!styleObj.contains(QStringLiteral("style"))) { qDebug() << "Style object missing 'style' member."; result = false; continue; } if (!styleObj.value(QStringLiteral("style")).isString()) { qDebug() << "Style object contains non-string 'style' member."; result = false; continue; } QString styleName(styleObj.value(QStringLiteral("style")).toString()); if (m_highlightStyles.contains(styleName)) { qDebug() << "Duplicate highlight style: " << styleName; result = false; continue; } if (!styleObj.contains(QStringLiteral("rules"))) { qDebug() << "Style object" << styleName << "missing 'rules' member."; result = false; continue; } if (!styleObj.value(QStringLiteral("rules")).isArray()) { qDebug() << "Style object" << styleName << "contains non-array 'rules' member."; result = false; continue; } QJsonArray rulesArray(styleObj.value(QStringLiteral("rules")).toArray()); auto* highlighter( new GenericHighlighter(const_cast(this))); if (!parseRules(rulesArray, *highlighter)) { qDebug() << "Error parsing style" << styleName << '\n' << QString(QJsonDocument(styleObj).toJson()); highlighter->deleteLater(); result = false; continue; } m_highlightStyles.insert(styleName, highlighter); } return result; } bool InterfaceScript::parseRules(const QJsonArray& json, GenericHighlighter& highlighter) const { bool result(true); foreach (QJsonValue ruleVal, json) { if (!ruleVal.isObject()) { qDebug() << "Rule is not an object."; result = false; continue; } QJsonObject ruleObj(ruleVal.toObject()); if (!ruleObj.contains(QStringLiteral("patterns"))) { qDebug() << "Rule missing 'patterns' array:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } if (!ruleObj.value(QStringLiteral("patterns")).isArray()) { qDebug() << "Rule 'patterns' member is not an array:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } QJsonArray patternsArray( ruleObj.value(QStringLiteral("patterns")).toArray()); if (!ruleObj.contains(QStringLiteral("format"))) { qDebug() << "Rule missing 'format' object:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } if (!ruleObj.value(QStringLiteral("format")).isObject()) { qDebug() << "Rule 'format' member is not an object:" << '\n' << QString(QJsonDocument(ruleObj).toJson()); result = false; continue; } QJsonObject formatObj(ruleObj.value(QStringLiteral("format")).toObject()); GenericHighlighter::Rule& rule = highlighter.addRule(); foreach (QJsonValue patternVal, patternsArray) { QRegularExpression pattern; if (!parsePattern(patternVal, pattern)) { qDebug() << "Error while parsing pattern:" << '\n' << QString(QJsonDocument(patternVal.toObject()).toJson()); result = false; continue; } rule.addPattern(pattern); } QTextCharFormat format; if (!parseFormat(formatObj, format)) { qDebug() << "Error while parsing format:" << '\n' << QString(QJsonDocument(formatObj).toJson()); result = false; } rule.setFormat(format); } return result; } bool InterfaceScript::parseFormat(const QJsonObject& json, QTextCharFormat& format) const { // Check for presets first: if (json.contains(QStringLiteral("preset"))) { if (!json[QStringLiteral("preset")].isString()) { qDebug() << "Preset is not a string."; return false; } QString preset(json[QStringLiteral("preset")].toString()); /// @todo Store presets in a singleton that can be configured in the GUI, /// rather than hardcoding them. if (preset == QLatin1String("title")) { format.setFontFamily(QStringLiteral("serif")); format.setForeground(Qt::darkGreen); format.setFontWeight(QFont::Bold); } else if (preset == QLatin1String("keyword")) { format.setFontFamily(QStringLiteral("mono")); format.setForeground(Qt::darkBlue); } else if (preset == QLatin1String("property")) { format.setFontFamily(QStringLiteral("mono")); format.setForeground(Qt::darkRed); } else if (preset == QLatin1String("literal")) { format.setFontFamily(QStringLiteral("mono")); format.setForeground(Qt::darkMagenta); } else if (preset == QLatin1String("comment")) { format.setFontFamily(QStringLiteral("serif")); format.setForeground(Qt::darkGreen); format.setFontItalic(true); } else { qDebug() << "Invalid style preset: " << preset; return false; } return true; } // Extract an RGB tuple from 'array' as a QBrush: struct { QBrush operator()(const QJsonArray& array, bool* ok) { *ok = false; QBrush result; if (array.size() != 3) return result; int rgb[3]; for (int i = 0; i < 3; ++i) { if (!array.at(i).isDouble()) return result; rgb[i] = static_cast(array.at(i).toDouble()); if (rgb[i] < 0 || rgb[i] > 255) { qDebug() << "Warning: Color component value invalid: " << rgb[i] << " (Valid range is 0-255)."; } } result.setColor(QColor(rgb[0], rgb[1], rgb[2])); result.setStyle(Qt::SolidPattern); *ok = true; return result; } } colorParser; if (json.contains(QStringLiteral("foreground")) && json.value(QStringLiteral("foreground")).isArray()) { QJsonArray foregroundArray( json.value(QStringLiteral("foreground")).toArray()); bool ok; format.setForeground(colorParser(foregroundArray, &ok)); if (!ok) return false; } if (json.contains(QStringLiteral("background")) && json.value(QStringLiteral("background")).isArray()) { QJsonArray backgroundArray( json.value(QStringLiteral("background")).toArray()); bool ok; format.setBackground(colorParser(backgroundArray, &ok)); if (!ok) return false; } if (json.contains(QStringLiteral("attributes")) && json.value(QStringLiteral("attributes")).isArray()) { QJsonArray attributesArray( json.value(QStringLiteral("attributes")).toArray()); format.setFontWeight(attributesArray.contains(QLatin1String("bold")) ? QFont::Bold : QFont::Normal); format.setFontItalic(attributesArray.contains(QLatin1String("italic"))); format.setFontUnderline( attributesArray.contains(QLatin1String("underline"))); } if (json.contains(QStringLiteral("family")) && json.value(QStringLiteral("family")).isString()) { format.setFontFamily(json.value(QStringLiteral("family")).toString()); } return true; } bool InterfaceScript::parsePattern(const QJsonValue& json, QRegularExpression& pattern) const { if (!json.isObject()) return false; QJsonObject patternObj(json.toObject()); QString regexPattern; QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption; if (patternObj.contains(QStringLiteral("regexp")) && patternObj.value(QStringLiteral("regexp")).isString()) { // Use the provided regular expression as-is regexPattern = patternObj.value(QStringLiteral("regexp")).toString(); } else if (patternObj.contains(QStringLiteral("wildcard")) && patternObj.value(QStringLiteral("wildcard")).isString()) { // Convert wildcard pattern (* -> .* and ? -> .) QString wildcard = patternObj.value(QStringLiteral("wildcard")).toString(); regexPattern = QRegularExpression::escape(wildcard) .replace("\\*", ".*") .replace("\\?", "."); } else if (patternObj.contains(QStringLiteral("string")) && patternObj.value(QStringLiteral("string")).isString()) { // Escape the string so it is treated literally in the regex regexPattern = QRegularExpression::escape( patternObj.value(QStringLiteral("string")).toString()); } else { return false; } // Set case sensitivity if specified if (patternObj.contains(QStringLiteral("caseSensitive"))) { bool caseSensitive = patternObj.value(QStringLiteral("caseSensitive")).toBool(true); if (!caseSensitive) { patternOptions |= QRegularExpression::CaseInsensitiveOption; } } // Set the final pattern with options pattern = QRegularExpression(regexPattern, patternOptions); return pattern.isValid(); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/interfacescript.h000066400000000000000000000543171506155467400226460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_INTERFACESCRIPT_H #define AVOGADRO_QTGUI_INTERFACESCRIPT_H #include #include "avogadroqtguiexport.h" #include #include #include #include #include class QJsonDocument; class QProcess; class QTextCharFormat; namespace Avogadro { namespace Core { class Molecule; } namespace QtGui { class GenericHighlighter; class PythonScript; /** * @class InterfaceScript interfacescript.h * @brief The Interface class provides an interface to external scripts * @sa InterfaceWidget * * The QuantumInput extension provides a scriptable method for users to add * custom input generators to Avogadro. By writing an executable that implements * the interface defined below, new input generators can be created faster and * easier than writing full Avogadro extensions. * * Script Entry Points * =================== * * The script must handle the following command-line arguments: * - `--debug` Enable extra debugging output. Used with other commands. * It is not required that the script support extra debugging, but it should * not crash when this option is passed. * - `--print-options` Print the available options supported by the * script, e.g. simulation parameters, etc. See below for more details. * - `--generate-input` Read an option block from stdin and print * input files to stdout. See below for more details. * - `--display-name` Print a user-friendly name for the input generator. * This is used in the GUI for menu entries, window titles, etc. * * Specifying parameters with `--print-options` * ============================================ * * The format of the `--print-options` output must be a JSON object of * the following form: ~~~{.js} { "userOptions": { ... }, "highlightStyles": [ { "style": "Descriptive name", "rules": [ { "patterns": [ ... ], "format": { ... } }, ... ], }, ... ], "inputMoleculeFormat": "cjson" } ~~~ * The `userOptions` block contains a JSON object keyed with option names * (e.g. "First option name"), which are used in the GUI to label simulation * parameter settings. Various parameter types are supported: * * Fixed Mutually-Exclusive Parameter Lists * ---------------------------------------- * * Parameters that have a fixed number of mutually-exclusive string values will * be presented using a QComboBox. Such a parameter can be specified in the * `userOptions` block as: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "stringList", "values": ["Option 1", "Option 2", "Option 3"], "default": 0 } } } ~~~ * Here, `Parameter Name` is the label that will be displayed in the GUI as a * label next to the combo box. * The array of strings in `values` will be used as the available entries in * the combo box in the order they are written. * `default` is a zero-based index into the `values` array and indicates * which value should be initially selected by default. * * Short Free-Form Text Parameters * ------------------------------- * * A short text string can be requested (e.g. for the "title" of an * optimization) via: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "string", "default": "blah blah blah" } } } ~~~ * This will add a QLineEdit to the GUI, initialized with the text specified by * `default`. * * Existing files * -------------- * * An input generator can ask for the absolute path to an existing file using * the following option block: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "filePath", "default": "/path/to/some/file" } } } ~~~ * This will add an Avogadro::QtGui::FileBrowseWidget to the GUI, initialized to * the file pointed to by default. * * Clamped Integer Values * ---------------------- * * Scripts may request integer values from a specified range by adding a * user-option of the following form: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "integer", "minimum": -5, "maximum": 5, "default": 0, "prefix": "some text ", "suffix": " units" } } } ~~~ * This block will result in a QSpinBox, configured as follows: * - `minimum` and `maximum` indicate the valid range of integers for the * parameter. * - `default` is the integer value that will be shown initially. * - (optional) `prefix` and `suffix` are used to insert text before or * after the integer value in the spin box. * This is handy for specifying units. * Note that any prefix or suffix will be stripped out of the corresponding * entry in the call to `--generate-input`, and just the raw integer value * will be sent. * * Boolean Parameters * ------------------ * * If a simple on/off value is needed, a boolean type option can be requested: ~~~{.js} { "userOptions": { "Parameter Name": { "type": "boolean", "default": true, } } } ~~~ * This will result in a QCheckBox in the dynamically generated GUI, with * the initial check state shown in `default`. * * Special Parameters * ------------------ * * Some parameters are common to most calculation codes. * If the following parameter names are found, they will be handled specially * while creating the GUI. * It is recommended to use the names below for these options to provide a * consistent interface and ensure that MoleQueue job staging uses correct * values where appropriate. * * | Option name | type | description | * | :----------------: | :--------: | :------------------------------------------------------------------ | * | "Title" | string | Input file title comment, MoleQueue job description. | * | "Filename Base" | string | Input file base name, e.g. "job" in "job.inp". | * | "Processor Cores" | integer | Number of cores to use. Will be passed to MoleQueue. | * | "Calculation Type" | stringList | Type of calculation, e.g. "Single Point" or "Equilibrium Geometry". | * | "Theory" | stringList | Levels of QM theory, e.g. "RHF", "B3LYP", "MP2", "CCSD", etc. | * | "Basis" | stringList | Available basis sets, e.g. "STO-3G", "6-31G**", etc. | * | "Charge" | integer | Charge on the system. | * | "Multiplicity" | integer | Spin multiplicity of the system. | * * Syntax Highlighting * ------------------- * * Rules for syntax highlighting can be specified as a collection of regular * expressions or wildcard patterns and text format specifications in the * "highlightRules" array. The `highlightRules` format is: ~~~{.js} "highlightStyles": [ { "style": "Style 1", "rules": [ (list of highlight rules, see below) ], }, { "style": "Style 2", "rules": [ (list of highlight rules, see below) ], }, ... ], ~~~ * The `style` name is unique to the style object, and used to associate a * set of highlighting rules with particular output files. See the * `--generate-input` documentation for more details. * * The general form of a highlight rule is: ~~~{.js} { "patterns": [ { "regexp": "^Some regexp?$" }, { "wildcard": "A * wildcard expression" }, { "string": "An exact string to match.", "caseSensitive": false }, ... ], "format": { "preset": "" } } ~~~ * * or, * ~~~{.js} { "patterns": [ ... ], "format": { "foreground": [ 255, 128, 64 ], "background": [ 0, 128, 128 ], "attributes": ["bold", "italic", "underline"], "family": "serif" } } ~~~ * * The `patterns` array contains a collection of fixed strings, wildcard * expressions, and regular expressions (using the QRegularExpression syntax * flavor, see the QRegularExpression documentation) that are used to identify * strings that should be formatted. * There must be one of the following members present in each pattern object: * - `regexp` A QRegularExpression-style regular expression. If no capture * groups ("(...)") are defined, the entire match is formatted. If one or more * capture groups, only the captured texts will be marked. * - `wildcard` A wildcard expression * - `string` An exact string to match. * * Any pattern object may also set a boolean `caseSensitive` member to indicate * whether the match should consider character case. If omitted, a * case-sensitive match is assumed. * * The preferred form of the `format` member is simply a specification of a * preset format. * This allows for consistent color schemes across input generators. * The recognized presets are: * - `"title"`: A human readable title string. * - `"keyword"`: directives defined by the target input format specification * to have special meaning, such as tags indicating where coordinates are * to be found. * - `"property"`: A property of the simulation, such as level of theory, basis * set, minimization method, etc. * - `"literal"`: A numeric literal (i.e. a raw number, such as a coordinate). * - `"comment"`: Sections of the input that are ignored by the simulation code. * * If advanced formatting is desired, the second form of the `format` member * allows fine-tuning of the font properties: * - `foreground` color as an RGB tuple, ranged 0-255 * - `background` color as an RGB tuple, ranged 0-255 * - `attributes` array of font attributes, valid strings are `"bold"`, * `"italic"`, or `"underline"` * - `family` of font. Valid values are `"serif"`, `"sans"`, or `"mono"` * * Any of the font property members may be omitted and default QTextCharFormat * settings will be substituted. * * The input generator extension will apply the entries in the `highlightRules` * object to the text in the order they appear. Thus, later rules will * override the formatting of earlier rules should a conflict arise. * * Requesting Full Structure of Current Molecule * --------------------------------------------- * * The `inputMoleculeFormat` is optional, and can be used to request a * representation of the current molecule's geometry when * `--generate-input` is called. The corresponding value * indicates the format of the molecule that the script expects. If this value * is omitted, no representation of the structure will be provided. * * @note Currently valid options for inputMoleculeFormat are "cjson" for * Chemical JSON or "cml" for Chemical Markup Language. * * Handling User Selections: `--generate-input` * ============================================ * * When `--generate-input` is passed, the information needed to generate * the input file will be written to the script's standard input * channel as JSON string of the following form: ~~~{.js} { "cjson": {...}, "options": { "First option name": "Value 2", "Second option name": "Value 1", ... } } ~~~ * The `cjson` entry will contain a Chemical JSON representation * of the molecule if `inputMoleculeFormat` is set to "cjson" in the * `--print-options` output. * Similarly, a `cml` entry and CML string will exist if a Chemical Markup * Language representation was requested. * It will be omitted entirely if `inputMoleculeFormat` is not set. * The `options` block contains key/value * pairs for each of the options specified in the `userOptions` block of the * `--print-options` output. * * If the script is called with `--generate-input`, it must write a JSON * string to standard output with the following format: ~~~{.js} { "files": [ { "filename": "file1.ext", "contents": "...", "highlightStyles": [ ... ] }, { "filename": "file2.ext", "filePath": "/path/to/file/on/local/filesystem" }, ... ], "warnings": ["First warning.", "Second warning.", ... ], "mainFile": "file2.ext" } ~~~ * The `files` block is an array of objects, which define the actual input * files. The `filename` member provides the name of the file, and * either `contents` or `filePath` provide the text that goes into the file. * The `contents` string will be used as the file contents, and `filePath` * should contain an absolute path to a file on the filesystem to read and use * as the input file contents. * The optional `highlightStyles` member is an array of strings describing any * highlight styles to apply to the file (see `--print-options` documentation). * Each string in this array must match a `style` description in a highlighting * rule in the `--print-options` output. * Zero or more highlighting styles may be applied to any file. * The order of the files in the * GUI will match the order of the files in the array, and the first file will * be displayed first. * * The `warnings` member provides an array of strings that describe non-fatal * warnings to be shown to the users. This is useful for describing * the resolution of conflicting options, e.g. "Ignoring basis set for * semi-empirical calculation.". This member is optional and should be omitted * if no warnings are present. * * The `mainFile` member points to the primary input file for a calculation. * This is the file that will be used as a command line argument when executing * the simulation code (if applicable), and used by MoleQueue to set the * `$$inputFileName$$` and `$$inputFileBaseName$$` input template keywords. * This is optional; if present, the filename must exist in the `files` array. * If absent and only one file is specified in `files`, the single input file * will be used. Otherwise, the main file will be left unspecified. * * Automatic Generation of Geometry * ================================ * * The generation of molecular geometry descriptions may be skipped in the * script and deferred to the InterfaceScript class by use of a special keyword. * The "contents" string may contain a keyword of the form ~~~ $$coords:[coordSpec]$$ ~~~ * where `[coordSpec]` is a sequence of characters. * The characters in `[coordSpec]` indicate the information needed about each * atom in the coordinate block. * See the CoordinateBlockGenerator documentation for a list of recognized * characters. * * Other keywords that can be used in the input files are: * - `$$atomCount$$`: Number of atoms in the molecule. * - `$$bondCount$$`: Number of bonds in the molecule. * * Error Handling * ============== * * In general, these scripts should be written robustly so that they will not * fail under normal circumstances. However, if for some reason an error * occurs that must be reported to the user, simply write the error message to * standard output as plain text (i.e. not JSON), and it will be shown to the * user. * * Debugging * ========= * * Debugging may be enabled by defining AVO_QM_INPUT_DEBUG in the process's * environment. This will cause the --debug option to be passed in * all calls to generator scripts, and will print extra information to the * qDebug() stream from within avogadro. The script is free to handle the * debug flag as the author wishes. */ class AVOGADROQTGUI_EXPORT InterfaceScript : public QObject { Q_OBJECT public: /** * Constructor * @param scriptFilePath_ Absolute path to generator script. */ explicit InterfaceScript(const QString& scriptFilePath_, QObject* parent_ = nullptr); explicit InterfaceScript(QObject* parent_ = nullptr); ~InterfaceScript() override; /** * @return True if debugging is enabled. */ bool debug() const; /** * @return True if the generator is configured with a valid script. */ bool isValid() const; /** * Query the script for the available options (--generate-options) * and return the output as a JSON object. * @note The results will be cached the first time this function is called * and reused in subsequent calls. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ QJsonObject options() const; /** * Query the script for a user-friendly name (--display-name). * @note The results will be cached the first time this function is called * and reused in subsequent calls. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ QString displayName() const; /** * Query the script for the menu path (--menu-path). * @note The results will be cached the first time this function is called * and reused in subsequent calls. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ QString menuPath() const; /** * @return The path to the generator file. */ QString scriptFilePath() const; /** * Set the path to the input generator script file. This will reset any * cached data held by this class. */ void setScriptFilePath(const QString& scriptFile); /** * Clear any cached data and return to an uninitialized state. */ void reset(); /** * Asynchronously run a command script using the supplied options object and * molecule. See the class documentation for details on the @p options_ * object format. * * @return true on success and false on failure. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ bool runCommand(const QJsonObject& options_, Core::Molecule* mol); /** * Finish processing an aynchronous command script */ bool processCommand(Core::Molecule* mol); /** * Request input files from the script using the supplied options object and * molecule. See the class documentation for details on the @p options_ * object format. * * If the files are generated successfully, use the functions * numberOfInputFiles(), fileNames(), and fileContents() to retrieve them. * * @return true on success and false on failure. * @note If an error occurs, the error string will be set. Call hasErrors() * to check for success, and errorString() or errorList() to get a * user-friendly description of the error. */ bool generateInput(const QJsonObject& options_, const Core::Molecule& mol); /** * @return The number of input files stored by generateInput(). * @note This function is only valid after a successful call to * generateInput(). */ int numberOfInputFiles() const; /** * @return A list of filenames created by generateInput(). * @note This function is only valid after a successful call to * generateInput(). */ QStringList fileNames() const; /** * @return The "main" input file of the collection. This is the input file * used by MoleQueue to determine the $$inputFileName$$ and * $$inputFileBaseName$$ keywords. * @note This function is only valid after a successful call to * generateInput(). */ QString mainFileName() const; /** * @return A file contents corresponding to @p fileName. Must call * generateInput() first. * @sa fileNames */ QString fileContents(const QString& fileName) const; /** * @return A syntax highlighter for the file @a fileName. Must call * generateInput() first. The caller takes ownership of the returned object. * If no syntax highlighter is defined, this function returns nullptr. * @sa fileNames */ QtGui::GenericHighlighter* createFileHighlighter( const QString& fileName) const; /** * @return True if an error is set. */ bool hasErrors() const { return !m_errors.isEmpty(); } /** * Reset the error counter. */ void clearErrors() { m_errors.clear(); } /** * @return A QStringList containing all errors that occurred in the last call * to the input generator script. */ QStringList errorList() const { return m_errors; } /** * @return A QStringList containing all warnings returned by the input * generator script in the last call to generateInput. These are * script-specific warnings. */ QStringList warningList() const { return m_warnings; } signals: void finished(); public slots: /** * Enable/disable debugging. */ void setDebug(bool d); /** * Receives a signal when asynchronous command scripts are finished */ void commandFinished(); private: QtGui::PythonScript* m_interpreter; void setDefaultPythonInterpreter(); QByteArray execute(const QStringList& args, const QByteArray& scriptStdin = QByteArray()) const; bool parseJson(const QByteArray& json, QJsonDocument& doc) const; QString processErrorString(const QProcess& proc) const; bool insertMolecule(QJsonObject& json, const Core::Molecule& mol) const; QString generateCoordinateBlock(const QString& spec, const Core::Molecule& mol) const; void replaceKeywords(QString& str, const Core::Molecule& mol) const; bool parseHighlightStyles(const QJsonArray& json) const; bool parseRules(const QJsonArray& json, QtGui::GenericHighlighter& highligher) const; bool parseFormat(const QJsonObject& json, QTextCharFormat& format) const; bool parsePattern(const QJsonValue& json, QRegularExpression& pattern) const; // File extension of requested molecule format mutable QString m_moleculeExtension; mutable QString m_displayName; mutable QString m_menuPath; mutable QJsonObject m_options; mutable QStringList m_warnings; mutable QStringList m_errors; QStringList m_filenames; QString m_mainFileName; QMap m_files; QMap m_fileHighlighters; mutable QMap m_highlightStyles; }; inline bool InterfaceScript::isValid() const { displayName(); return !hasErrors(); } } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_MOLEQUEUE_INTERFACESCRIPT_H avogadrolibs-1.101.0/avogadro/qtgui/interfacewidget.cpp000066400000000000000000000052421506155467400231510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "interfacewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtGui { InterfaceWidget::InterfaceWidget(const QString& scriptFilePath, QWidget* parent_) : JsonWidget(parent_), m_interfaceScript(QString()) { this->setInterfaceScript(scriptFilePath); } InterfaceWidget::~InterfaceWidget() {} void InterfaceWidget::setInterfaceScript(const QString& scriptFile) { m_interfaceScript.setScriptFilePath(scriptFile); m_options = m_interfaceScript.options(); updateOptions(); } void InterfaceWidget::defaultsClicked() { setOptionDefaults(); } void InterfaceWidget::setWarningText(const QString& warn) { qWarning() << tr("Script returns warnings:\n") << warn; } QString InterfaceWidget::warningText() const { return QString(); } void InterfaceWidget::showError(const QString& err) { qWarning() << err; QWidget* theParent = this->isVisible() ? this : qobject_cast(parent()); QDialog dlg(theParent); auto* vbox = new QVBoxLayout(); auto* label = new QLabel(tr("An error has occurred:")); vbox->addWidget(label); auto* textBrowser = new QTextBrowser(); // adjust the size of the text browser to ~80 char wide, ~20 lines high QSize theSize = textBrowser->sizeHint(); QFontMetrics metrics(textBrowser->currentFont()); int charWidth = metrics.horizontalAdvance(QStringLiteral("i7OPlmWn9/")) / 10; int charHeight = metrics.lineSpacing(); theSize.setWidth(80 * charWidth); theSize.setHeight(20 * charHeight); textBrowser->setMinimumSize(theSize); textBrowser->setText(err); vbox->addWidget(textBrowser); dlg.setLayout(vbox); dlg.exec(); } QString InterfaceWidget::settingsKey(const QString& identifier) const { return QStringLiteral("scriptPlugin/%1/%2") .arg(m_interfaceScript.displayName(), identifier); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/interfacewidget.h000066400000000000000000000055151506155467400226210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_INTERFACEWIDGET_H #define AVOGADRO_QTGUI_INTERFACEWIDGET_H #include "avogadroqtguiexport.h" #include #include #include #include "interfacescript.h" #include "jsonwidget.h" class QJsonValue; class QTextEdit; class QWidget; namespace Avogadro { namespace QtGui { class Molecule; /** * @class InterfaceWidget interfacewidget.h * * @brief The InterfaceWidget class provides a user interface for * running external scripts * @sa InterfaceScript * * The InterfaceWidget creates a GUI to represent the options given by an * script, turning JSON from the script into a form and passing the results * back to the script via command-line */ class AVOGADROQTGUI_EXPORT InterfaceWidget : public JsonWidget { Q_OBJECT public: /** * Construct a widget that dynamically generates a GUI to configure the * script specified by scriptFilePath. */ explicit InterfaceWidget(const QString& scriptFilePath, QWidget* parent_ = nullptr); ~InterfaceWidget() override; /** * Use the script pointed to by scriptFilePath. * @param scriptFilePath Absolute path to script. */ void setInterfaceScript(const QString& scriptFilePath); /** * Access to the underlying input script object. */ const QtGui::InterfaceScript& interfaceScript() const { return m_interfaceScript; } private slots: /** * Triggered when the user resets the default values. */ void defaultsClicked(); /** * Show the user an warning. These are messages returned by the input * script. */ void setWarningText(const QString& warn); /** * Show the warning text. */ QString warningText() const; /** * Show the user an error message. These are errors that have occurred * in this extension, not necessarily in the input generator script. */ void showError(const QString& err); private: /** * Generate a QSettings key with the given identifier that is unique to this * input generator's display name. * @param identifier Setting key, e.g. "outputPath" * @return Script-specific key, e.g. "quantumInput/GAMESS/outputPath" * @todo Display names are not necessarily unique, but paths are too long. * Maybe add a namespace qualifier to the script display names? */ QString settingsKey(const QString& identifier) const; QtGui::Molecule* m_molecule; QtGui::InterfaceScript m_interfaceScript; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_INTERFACEWIDGET_H avogadrolibs-1.101.0/avogadro/qtgui/jsonwidget.cpp000066400000000000000000000763351506155467400221750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "jsonwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtGui { JsonWidget::JsonWidget(QWidget* parent_) : QWidget(parent_), m_molecule(nullptr), m_empty(true), m_batchMode(false), m_currentLayout(nullptr), m_centralWidget(nullptr) { } JsonWidget::~JsonWidget() {} void JsonWidget::setMolecule(QtGui::Molecule* mol) { if (m_molecule != nullptr) { // update charge and multiplicity if needed int charge = static_cast(m_molecule->totalCharge()); int multiplicity = static_cast(m_molecule->totalSpinMultiplicity()); setOption("Charge", charge); setOption("Multiplicity", multiplicity); // check the molecule for "inputParameters" from CJSON // e.g. // https://github.com/OpenChemistry/chemicaljson/blob/main/chemicaljson.py#L130 if (m_molecule->hasData("inputParameters")) { QByteArray inputData( m_molecule->data("inputParameters").toString().c_str()); QJsonDocument doc = QJsonDocument::fromJson(inputData); if (!doc.isNull() && doc.isObject()) { QJsonObject inputParameters = doc.object(); // check for a few known keys if (inputParameters.contains("processors")) setOption("Processor Cores", inputParameters["processors"].toInt()); else if (inputParameters.contains("memory")) setOption("Memory", inputParameters["memory"].toInt()); else if (inputParameters.contains("basis")) setOption("Basis", inputParameters["basis"].toString()); else if (inputParameters.contains("functional")) setOption("Theory", inputParameters["functional"].toString()); else if (inputParameters.contains("task")) setOption("Calculation Type", inputParameters["task"].toString()); } } } if (mol == m_molecule) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; } QString JsonWidget::lookupOptionType(const QString& name) const { if (!m_options.contains("userOptions")) { qWarning() << tr("'userOptions' missing."); return QString(); } QJsonObject userOptions; // if we have tabs, then userOptions is an array of objects // need to loop through to find the right one unsigned int size; bool isArray = m_options["userOptions"].isArray(); QJsonArray options; if (isArray) { size = m_options["userOptions"].toArray().size(); options = m_options["userOptions"].toArray(); } else size = 1; for (unsigned int i = 0; i < size; ++i) { if (isArray) { userOptions = options.at(i).toObject(); } else if (m_options["userOptions"].isObject()) { userOptions = m_options["userOptions"].toObject(); } else { break; } if (!userOptions.contains(name)) { continue; // look in next tab } if (!userOptions.value(name).isObject()) { qWarning() << tr("Option '%1' does not refer to an object.").arg(name); return QString(); } QJsonObject obj = userOptions[name].toObject(); if (!obj.contains("type") || !obj.value("type").isString()) { qWarning() << tr("'type' is not a string for option '%1'.").arg(name); return QString(); } return obj["type"].toString(); } qWarning() << tr("Could not find option '%1'.").arg(name); return QString(); } void JsonWidget::updateOptions() { // Create the widgets, etc for the gui if (!m_centralWidget) { m_centralWidget = this; } buildOptionGui(); setOptionDefaults(); } void JsonWidget::buildOptionGui() { if (m_centralWidget == nullptr) return; m_widgets.clear(); delete m_centralWidget->layout(); if (!m_options.contains("userOptions")) { return; } // if we have an array, we'll create tabs for each QTabWidget* tabs = nullptr; QWidget* currentPage = nullptr; // Create new widgets QJsonObject userOptions; unsigned int size; bool isArray = m_options["userOptions"].isArray(); QJsonArray options; if (isArray) { size = m_options["userOptions"].toArray().size(); options = m_options["userOptions"].toArray(); // create a layout for inserting the tabs tabs = new QTabWidget(this); auto* layout = new QVBoxLayout(this); layout->addWidget(tabs); m_centralWidget->setLayout(layout); } else { size = 1; // create the form layout for the widget auto* layout = new QFormLayout(); m_currentLayout = layout; m_centralWidget->setLayout(layout); } for (unsigned int i = 0; i < size; ++i) { QString tabName = tr("Tab %1").arg(i + 1); // default if (isArray) { userOptions = options.at(i).toObject(); // add a new tab if (userOptions.contains("tabName") && userOptions.value("tabName").isString()) { tabName = userOptions.value("tabName").toString(); userOptions.take("tabName"); } currentPage = new QWidget(this); auto* layout = new QFormLayout(currentPage); currentPage->setLayout(layout); m_currentLayout = layout; } else if (m_options["userOptions"].isObject()) { userOptions = m_options["userOptions"].toObject(); // don't need to set layout, we already did that } else { break; } // Title first if (userOptions.contains("Title")) addOptionRow("Title", tr("Title"), userOptions.take("Title")); // File basename next: if (userOptions.contains("Filename Base")) addOptionRow("Filename Base", tr("Filename Base"), userOptions.take("Filename Base")); // Number of cores next: if (userOptions.contains("Processor Cores")) addOptionRow("Processor Cores", tr("Processor Cores"), userOptions.take("Processor Cores")); // Calculation Type next: if (userOptions.contains("Calculation Type")) addOptionRow("Calculation Type", tr("Calculation Type"), userOptions.take("Calculation Type")); // Theory/basis next. Combine into one row if both present. combinedOptionRow("Theory", "Basis", tr("Theory"), tr("Basis"), userOptions); // Other special cases: Charge / Multiplicity if (userOptions.contains("Charge") && userOptions.contains("Multiplicity")) combinedOptionRow("Charge", "Multiplicity", tr("Charge"), tr("Multiplicity"), userOptions, true); // both labels else { if (userOptions.contains("Charge")) addOptionRow("Charge", tr("Charge"), userOptions.take("Charge")); if (userOptions.contains("Multiplicity")) addOptionRow("Multiplicity", tr("Multiplicity"), userOptions.take("Multiplicity")); } // Add remaining keys at bottom. // look for "order" key to determine order QMap keys; int order = 0; for (QJsonObject::const_iterator it = userOptions.constBegin(), itEnd = userOptions.constEnd(); it != itEnd; ++it) { if (it.value().isObject()) { QJsonObject obj = it.value().toObject(); if (obj.contains("order") && obj.value("order").isDouble()) { order = obj.value("order").toInt(); keys.insert(order, it.key()); } else { // object doesn't contain "order" keys.insert(order, it.key()); order++; } } else { keys.insert(order++, it.key()); } } // now loop over keys and add them for (QString key : std::as_const(keys)) addOptionRow(key, key, userOptions.take(key)); // Make connections for standard options: if (auto* combo = qobject_cast( m_widgets.value("Calculation Type", nullptr))) { connect(combo, SIGNAL(currentIndexChanged(int)), SLOT(updateTitlePlaceholder())); } if (auto* combo = qobject_cast(m_widgets.value("Theory", nullptr))) { connect(combo, SIGNAL(currentIndexChanged(int)), SLOT(updateTitlePlaceholder())); } if (auto* combo = qobject_cast(m_widgets.value("Basis", nullptr))) { connect(combo, SIGNAL(currentIndexChanged(int)), SLOT(updateTitlePlaceholder())); } // if we're adding tabs, add it now if (isArray) { tabs->addTab(currentPage, tabName); } } // end loop over tabs m_empty = m_widgets.isEmpty(); } void JsonWidget::combinedOptionRow(const QString& label1, const QString& label2, const QString& tr1, const QString& tr2, QJsonObject& options, bool bothLabels) { if (m_currentLayout == nullptr) return; bool option1 = options.contains(label1); bool option2 = options.contains(label2); if (option1 && option2) { QWidget* widget1 = createOptionWidget(options.take(label1)); QWidget* widget2 = createOptionWidget(options.take(label2)); auto* hbox = new QHBoxLayout; if (option1) { widget1->setObjectName(label1); hbox->addWidget(widget1); m_widgets.insert(label1, widget1); } if (bothLabels) { QLabel* label = new QLabel(tr2 + ":"); hbox->addWidget(label); } if (option2) { widget2->setObjectName(label2); hbox->addWidget(widget2); m_widgets.insert(label2, widget2); } hbox->addStretch(); m_currentLayout->addRow(tr1, hbox); } else { if (option1) addOptionRow(label1, tr1, options.take(label1)); if (option2) addOptionRow(label2, tr2, options.take(label2)); } } void JsonWidget::addOptionRow(const QString& key, const QString& name, const QJsonValue& option) { QWidget* widget = createOptionWidget(option); if (!widget) return; QFormLayout* form = m_currentLayout; if (!form) { qWarning() << "Cannot add option" << name << "to GUI -- layout is not a form."; widget->deleteLater(); return; } // For lookups during unit testing: widget->setObjectName(key); QString label(name); QJsonObject obj = option.toObject(); if (obj.contains(QStringLiteral("label")) && obj.value(QStringLiteral("label")).isString()) { label = obj[QStringLiteral("label")].toString(); } // also check for "User Name" or "Password" for translation // with case-insensitive comparison if (label.toLower() == "user name" || label.toLower() == "username") label = tr("User Name"); else if (label.toLower() == "password") { label = tr("Password"); // make sure the widget has the right echo if (auto* lineEdit = qobject_cast(widget)) { lineEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit); } } form->addRow(label + ":", widget); m_widgets.insert(key, widget); // optionally hide rows .. can be shown by the script later bool hide = false; if (obj.contains(QStringLiteral("hide")) && obj.value(QStringLiteral("hide")).isBool()) { hide = obj[QStringLiteral("hide")].toBool(); } if (hide) { widget->hide(); // find the label and hide that too auto fLabel = form->labelForField(widget); if (fLabel) fLabel->hide(); } } QWidget* JsonWidget::createOptionWidget(const QJsonValue& option) { if (!option.isObject()) return nullptr; QJsonObject obj = option.toObject(); if (!obj.contains(QStringLiteral("type")) || !obj.value(QStringLiteral("type")).isString()) return nullptr; QString type = obj[QStringLiteral("type")].toString(); if (type == QLatin1String("stringList")) return createStringListWidget(obj); else if (type == QLatin1String("string")) return createStringWidget(obj); else if (type == QLatin1String("filePath")) return createFilePathWidget(obj); else if (type == QLatin1String("integer")) return createIntegerWidget(obj); else if (type == QLatin1String("float")) return createFloatWidget(obj); else if (type == QLatin1String("boolean")) return createBooleanWidget(obj); else if (type == QLatin1String("text")) return createTextWidget(obj); else if (type == QLatin1String("table")) return createTableWidget(obj); qDebug() << "Unrecognized option type:" << type; return nullptr; } QWidget* JsonWidget::createStringListWidget(const QJsonObject& obj) { if (!obj.contains(QStringLiteral("values")) || !obj[QStringLiteral("values")].isArray()) { qDebug() << "JsonWidget::createStringListWidget()" "values missing, or not array!"; return nullptr; } QJsonArray valueArray = obj[QStringLiteral("values")].toArray(); auto* combo = new QComboBox(this); for (QJsonArray::const_iterator vit = valueArray.constBegin(), vitEnd = valueArray.constEnd(); vit != vitEnd; ++vit) { if ((*vit).isString()) { QString value = (*vit).toString(); if (value == '-') combo->insertSeparator(combo->count()); else combo->addItem((*vit).toString()); } else qDebug() << "Cannot convert value to string for stringList:" << *vit; } connect(combo, SIGNAL(currentIndexChanged(int)), SLOT(updatePreviewText())); if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { combo->setToolTip(obj[QStringLiteral("toolTip")].toString()); } return combo; } QWidget* JsonWidget::createStringWidget(const QJsonObject& obj) { auto* edit = new QLineEdit(this); connect(edit, SIGNAL(textChanged(QString)), SLOT(updatePreviewText())); if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { edit->setToolTip(obj[QStringLiteral("toolTip")].toString()); } if (obj.contains(QStringLiteral("placeholderText")) && obj.value(QStringLiteral("placeholderText")).isString()) { edit->setPlaceholderText(obj[QStringLiteral("placeholderText")].toString()); } // don't echo password fields if (obj.contains(QStringLiteral("password")) && obj.value(QStringLiteral("password")).isBool() && obj[QStringLiteral("password")].toBool()) { edit->setEchoMode(QLineEdit::PasswordEchoOnEdit); } return edit; } QWidget* JsonWidget::createTextWidget(const QJsonObject& obj) { auto* text = new QLabel(this); text->setWordWrap(true); if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { text->setToolTip(obj[QStringLiteral("toolTip")].toString()); } return text; } QWidget* JsonWidget::createFilePathWidget(const QJsonObject& obj) { auto* fileBrowse = new QtGui::FileBrowseWidget(this); connect(fileBrowse, SIGNAL(fileNameChanged(QString)), SLOT(updatePreviewText())); if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { fileBrowse->setToolTip(obj[QStringLiteral("toolTip")].toString()); } return fileBrowse; } QWidget* JsonWidget::createIntegerWidget(const QJsonObject& obj) { auto* spin = new QSpinBox(this); if (obj.contains(QStringLiteral("minimum")) && obj.value(QStringLiteral("minimum")).isDouble()) { spin->setMinimum( static_cast(obj[QStringLiteral("minimum")].toDouble() + 0.5)); } if (obj.contains(QStringLiteral("maximum")) && obj.value(QStringLiteral("maximum")).isDouble()) { spin->setMaximum( static_cast(obj[QStringLiteral("maximum")].toDouble() + 0.5)); } if (obj.contains(QStringLiteral("prefix")) && obj.value(QStringLiteral("prefix")).isString()) { spin->setPrefix(obj[QStringLiteral("prefix")].toString()); } if (obj.contains(QStringLiteral("suffix")) && obj.value(QStringLiteral("suffix")).isString()) { spin->setSuffix(obj[QStringLiteral("suffix")].toString()); } if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { spin->setToolTip(obj[QStringLiteral("toolTip")].toString()); } connect(spin, SIGNAL(valueChanged(int)), SLOT(updatePreviewText())); return spin; } QWidget* JsonWidget::createFloatWidget(const QJsonObject& obj) { auto* spin = new QDoubleSpinBox(this); if (obj.contains(QStringLiteral("minimum")) && obj.value(QStringLiteral("minimum")).isDouble()) { spin->setMinimum(obj[QStringLiteral("minimum")].toDouble()); } if (obj.contains(QStringLiteral("maximum")) && obj.value(QStringLiteral("maximum")).isDouble()) { spin->setMaximum(obj[QStringLiteral("maximum")].toDouble()); } if (obj.contains(QStringLiteral("precision")) && obj.value(QStringLiteral("precision")).isDouble()) { spin->setDecimals( static_cast(obj[QStringLiteral("precision")].toDouble())); } if (obj.contains(QStringLiteral("prefix")) && obj.value(QStringLiteral("prefix")).isString()) { spin->setPrefix(obj[QStringLiteral("prefix")].toString()); } if (obj.contains(QStringLiteral("suffix")) && obj.value(QStringLiteral("suffix")).isString()) { spin->setSuffix(obj[QStringLiteral("suffix")].toString()); } if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { spin->setToolTip(obj[QStringLiteral("toolTip")].toString()); } connect(spin, SIGNAL(valueChanged(double)), SLOT(updatePreviewText())); return spin; } QWidget* JsonWidget::createBooleanWidget(const QJsonObject& obj) { auto* checkBox = new QCheckBox(this); connect(checkBox, SIGNAL(toggled(bool)), SLOT(updatePreviewText())); if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { checkBox->setToolTip(obj[QStringLiteral("toolTip")].toString()); } return checkBox; } QWidget* JsonWidget::createTableWidget(const QJsonObject& obj) { auto* tableWidget = new QTableWidget(this); connect(tableWidget, SIGNAL(cellChanged(int, int)), SLOT(updatePreviewText())); if (obj.contains(QStringLiteral("toolTip")) && obj.value(QStringLiteral("toolTip")).isString()) { tableWidget->setToolTip(obj[QStringLiteral("toolTip")].toString()); } if (obj.contains(QStringLiteral("headers")) && obj.value("headers").isArray()) { QJsonArray headers = obj["headers"].toArray(); tableWidget->setColumnCount(headers.size()); for (int i = 0; i < headers.size(); ++i) { tableWidget->setHorizontalHeaderItem( i, new QTableWidgetItem(headers[i].toString())); } } if (obj.contains(QStringLiteral("delimiter")) && obj.value("delimiter").isString()) { tableWidget->setProperty("delimiter", obj["delimiter"].toString()); } // data might be supplied as columns or rows if (obj.contains(QStringLiteral("columns")) && obj.value("columns").isArray()) { QJsonArray columns = obj["columns"].toArray(); // get the row count from the first column tableWidget->setRowCount(columns[0].toArray().size()); for (int i = 0; i < columns.size(); ++i) { int j = 0; for (QJsonArray::const_iterator it = columns[i].toArray().constBegin(), itEnd = columns[i].toArray().constEnd(); it != itEnd; ++it) { tableWidget->setItem(i, j++, new QTableWidgetItem(it->toString())); } } } if (obj.contains(QStringLiteral("rows")) && obj.value("rows").isArray()) { QJsonArray rows = obj["rows"].toArray(); // get the column count from the first row tableWidget->setColumnCount(rows[0].toArray().size()); for (int j = 0; j < rows.size(); ++j) { int i = 0; for (QJsonArray::const_iterator it = rows[i].toArray().constBegin(), itEnd = rows[i].toArray().constEnd(); it != itEnd; ++it) { tableWidget->setItem(i++, j, new QTableWidgetItem(it->toString())); } } } return tableWidget; } void JsonWidget::setOptionDefaults() { if (!m_options.contains(QStringLiteral("userOptions"))) { return; } // if we have tabs, then userOptions is an array of objects // need to loop through to find the right one unsigned int size; bool isArray = m_options["userOptions"].isArray(); QJsonArray options; QJsonObject userOptions; if (isArray) { size = m_options["userOptions"].toArray().size(); options = m_options["userOptions"].toArray(); } else size = 1; for (unsigned int i = 0; i < size; ++i) { // loop over tabs if (isArray) { userOptions = options.at(i).toObject(); } else if (m_options["userOptions"].isObject()) { userOptions = m_options["userOptions"].toObject(); } else { break; } // loop over widgets in the tab for (QJsonObject::ConstIterator it = userOptions.constBegin(), itEnd = userOptions.constEnd(); it != itEnd; ++it) { QString label = it.key(); QJsonValue val = it.value(); if (!val.isObject()) { qWarning() << tr("Error: value must be object for key '%1'.").arg(label); continue; } QJsonObject obj = val.toObject(); if (obj.contains("default")) { // TODO - check QSettings for a value too setOption(label, obj[QStringLiteral("default")]); } } } } void JsonWidget::setOption(const QString& name, const QJsonValue& defaultValue) { QString type = lookupOptionType(name); if (type == QLatin1String("stringList")) return setStringListOption(name, defaultValue); else if (type == QLatin1String("string")) return setStringOption(name, defaultValue); else if (type == QLatin1String("filePath")) return setFilePathOption(name, defaultValue); else if (type == QLatin1String("integer")) return setIntegerOption(name, defaultValue); else if (type == QLatin1String("float")) return setFloatOption(name, defaultValue); else if (type == QLatin1String("boolean")) return setBooleanOption(name, defaultValue); else if (type == QLatin1String("text")) return setTextOption(name, defaultValue); else if (type == QLatin1String("table")) return setTableOption(name, defaultValue); qWarning() << tr("Unrecognized option type '%1' for option '%2'.").arg(type).arg(name); return; } void JsonWidget::setStringListOption(const QString& name, const QJsonValue& value) { auto* combo = qobject_cast(m_widgets.value(name, nullptr)); if (!combo) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isDouble() && !value.isString()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } int index = -1; if (value.isDouble()) index = static_cast(value.toDouble() + 0.5); else if (value.isString()) index = combo->findText(value.toString()); if (index < 0 || index > combo->count()) { qWarning() << tr("Error setting default for option '%1'. " "Could not find valid combo entry index from value:") .arg(name) << value; return; } combo->setCurrentIndex(index); } void JsonWidget::setStringOption(const QString& name, const QJsonValue& value) { auto* lineEdit = qobject_cast(m_widgets.value(name, nullptr)); if (!lineEdit) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isString()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } lineEdit->setText(value.toString()); } void JsonWidget::setTextOption(const QString& name, const QJsonValue& value) { auto* text = qobject_cast(m_widgets.value(name, nullptr)); if (text == nullptr) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isString()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } text->setText(value.toString()); } void JsonWidget::setTableOption(const QString& name, const QJsonValue& value) { auto* table = qobject_cast(m_widgets.value(name, nullptr)); if (table == nullptr) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isString()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } // parse the table (default delimiter is tab) QString delimiter; if (table->property("delimiter").isValid()) delimiter = table->property("delimiter").toString(); else delimiter = "\t"; // parse the table table->clearContents(); QStringList tableLines = value.toString().split("\n"); table->setRowCount(tableLines.size()); for (int i = 0; i < tableLines.size(); ++i) { QStringList entry = tableLines[i].split(delimiter); for (int j = 0; j < entry.size(); ++j) { table->setItem(i, j, new QTableWidgetItem(entry[j])); } } } void JsonWidget::setFilePathOption(const QString& name, const QJsonValue& value) { auto* fileBrowse = qobject_cast(m_widgets.value(name, nullptr)); if (!fileBrowse) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isString()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } fileBrowse->setFileName(value.toString()); } void JsonWidget::setIntegerOption(const QString& name, const QJsonValue& value) { auto* spin = qobject_cast(m_widgets.value(name, nullptr)); if (!spin) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isDouble()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } int intVal = static_cast(value.toDouble() + 0.5); spin->setValue(intVal); } void JsonWidget::setFloatOption(const QString& name, const QJsonValue& value) { auto* spin = qobject_cast(m_widgets.value(name, nullptr)); if (!spin) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isDouble()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } spin->setValue(value.toDouble()); } void JsonWidget::setBooleanOption(const QString& name, const QJsonValue& value) { auto* checkBox = qobject_cast(m_widgets.value(name, nullptr)); if (!checkBox) { qWarning() << tr("Error setting default for option '%1'. " "Bad widget type.") .arg(name); return; } if (!value.isBool()) { qWarning() << tr("Error setting default for option '%1'. " "Bad default value:") .arg(name) << value; return; } checkBox->setChecked(value.toBool()); } bool JsonWidget::optionString(const QString& option, QString& value) const { QWidget* widget = m_widgets.value(option, nullptr); bool retval = false; value.clear(); if (auto* edit = qobject_cast(widget)) { retval = true; value = edit->text(); } else if (auto* combo = qobject_cast(widget)) { retval = true; value = combo->currentText(); } else if (auto* spinbox = qobject_cast(widget)) { retval = true; value = QString::number(spinbox->value()); } else if (auto* dspinbox = qobject_cast(widget)) { retval = true; value = QString::number(dspinbox->value()); } else if (auto* fileBrowse = qobject_cast(widget)) { retval = true; value = fileBrowse->fileName(); } return retval; } QJsonObject JsonWidget::collectOptions() const { QJsonObject ret; foreach (QString label, m_widgets.keys()) { QWidget* widget = m_widgets.value(label, nullptr); if (auto* combo = qobject_cast(widget)) { ret.insert(label, combo->currentText()); } else if (auto* lineEdit = qobject_cast(widget)) { QString value(lineEdit->text()); if (value.isEmpty() && label == QLatin1String("Title")) value = generateJobTitle(); ret.insert(label, value); } else if (auto* spinBox = qobject_cast(widget)) { ret.insert(label, spinBox->value()); } else if (auto* doubleSpinBox = qobject_cast(widget)) { ret.insert(label, doubleSpinBox->value()); } else if (auto* checkBox = qobject_cast(widget)) { ret.insert(label, checkBox->isChecked()); } else if (auto* fileBrowse = qobject_cast(widget)) { ret.insert(label, fileBrowse->fileName()); } else { qWarning() << tr("Unhandled widget in collectOptions for option '%1'.").arg(label); } } return ret; } void JsonWidget::applyOptions(const QJsonObject& opts) { foreach (const QString& label, opts.keys()) { setOption(label, opts[label]); qDebug() << "Setting option" << label << "to" << opts[label]; } } QString JsonWidget::generateJobTitle() const { QString calculation; bool haveCalculation(optionString("Calculation Type", calculation)); QString theory; bool haveTheory(optionString("Theory", theory)); QString basis; bool haveBasis(optionString("Basis", basis)); // Merge theory/basis into theory if (haveBasis) { if (haveTheory) theory += "/"; theory += basis; theory.replace(QRegularExpression("\\s+"), ""); haveTheory = true; } if (m_batchMode) { QString result; result = haveCalculation ? calculation : QString(); result += haveTheory ? (result.size() != 0 ? " | " : QString()) + theory : QString(); return result; } QString formula(m_molecule ? QString::fromStdString(m_molecule->formula()) : tr("[no molecule]")); return QString("%1%2%3") .arg(formula) .arg(haveCalculation ? " | " + calculation : QString()) .arg(haveTheory ? " | " + theory : QString()); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/jsonwidget.h000066400000000000000000000111061506155467400216230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_JSONWIDGET_H #define AVOGADRO_QTGUI_JSONWIDGET_H #include "avogadroqtguiexport.h" #include #include #include #include class QJsonValue; class QTextEdit; class QWidget; namespace Avogadro { namespace QtGui { class Molecule; /** * @class JsonWidget jsonwidget.h * * @brief The JsonWidget class provides a user interface for * running external scripts * @sa InterfaceWidget * * The JsonWidget creates a GUI to represent the options given by an * script, turning JSON from the script into a form and passing the results * back to the script via command-line. * * It's used by a range of different scripts, including: * - Commands * - InterfaceGenerators */ class AVOGADROQTGUI_EXPORT JsonWidget : public QWidget { Q_OBJECT public: /** * Construct a widget that dynamically generates a GUI */ explicit JsonWidget(QWidget* parent_ = nullptr); ~JsonWidget() override; /** * Set the molecule used in the simulation. */ virtual void setMolecule(QtGui::Molecule* mol); /** * Collect all of the user-specified options into a JSON object, to be sent * to the generator script. */ QJsonObject collectOptions() const; /** * Apply the options in the passed QJsonObject to the GUI. Any widgets changed * by this method will have their signals blocked while modifying their * values. */ void applyOptions(const QJsonObject& opts); bool isEmpty() const { return m_empty; } protected: /** * Given the name of a user-option in m_options, return the type string. * If an error occurs, an empty string will be returned. */ QString lookupOptionType(const QString& name) const; /** * Used to construct the script-specific GUI. * @{ */ virtual void updateOptions(); void buildOptionGui(); void combinedOptionRow(const QString& label1, const QString& label2, const QString& tr1, const QString& tr2, QJsonObject& options, bool bothLabels = false); void addOptionRow(const QString& key, const QString& label, const QJsonValue& option); QWidget* createOptionWidget(const QJsonValue& option); QWidget* createStringListWidget(const QJsonObject& obj); QWidget* createStringWidget(const QJsonObject& obj); QWidget* createFilePathWidget(const QJsonObject& obj); QWidget* createIntegerWidget(const QJsonObject& obj); QWidget* createFloatWidget(const QJsonObject& obj); QWidget* createBooleanWidget(const QJsonObject& obj); QWidget* createTextWidget(const QJsonObject& obj); QWidget* createTableWidget(const QJsonObject& obj); /**@}*/ /** * Set the simulation settings to their default values. * @{ */ void setOptionDefaults(); void setOption(const QString& name, const QJsonValue& defaultValue); void setStringListOption(const QString& name, const QJsonValue& value); void setStringOption(const QString& name, const QJsonValue& value); void setFilePathOption(const QString& name, const QJsonValue& value); void setIntegerOption(const QString& name, const QJsonValue& value); void setFloatOption(const QString& name, const QJsonValue& value); void setBooleanOption(const QString& name, const QJsonValue& value); void setTextOption(const QString& name, const QJsonValue& value); void setTableOption(const QString& name, const QJsonValue& value); /**@}*/ /** * @brief Search for an option named @a option and convert its value to a * string. * @param option The name of the option. * @param value String to overwrite with option value. * @return True if value is overwritten, false if the option is not found or * cannot be converted to a string. */ bool optionString(const QString& option, QString& value) const; /** * Update the autogenerated job title in the GUI. */ QString generateJobTitle() const; QtGui::Molecule* m_molecule; QJsonObject m_options; QJsonObject m_optionCache; // For reverting changes QList m_dirtyTextEdits; bool m_empty; bool m_batchMode; QFormLayout* m_currentLayout; QWidget* m_centralWidget; QMap m_widgets; QMap m_textEdits; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_JSONWIDGET_H avogadrolibs-1.101.0/avogadro/qtgui/layermodel.cpp000066400000000000000000000175431506155467400221510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "layermodel.h" #include "molecule.h" #include "rwmolecule.h" #include #include #include #include #include namespace Avogadro::QtGui { using Core::LayerManager; namespace { const int QTTY_COLUMNS = 6; } LayerModel::LayerModel(QObject* p) : QAbstractItemModel(p), m_item(0) { // determine if need dark mode or light mode icons // e.g. https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5 const QPalette defaultPalette; // i.e., the text is lighter than the background bool darkMode = (defaultPalette.color(QPalette::WindowText).lightness() > defaultPalette.color(QPalette::Window).lightness()); loadIcons(darkMode); } void LayerModel::loadIcons(bool darkMode) { QString iconPath = ":icons/fallback/32x32/"; QString dashedPreviewIconPath = iconPath + "dashed-preview-light.png"; QString dotsIconPath = iconPath + "dots-light.png"; QString lockIconPath = iconPath + "lock-light.png"; QString openLockIconPath = iconPath + "lock-open-light.png"; QString plusIconPath = iconPath + "plus-light.png"; QString previewIconPath = iconPath + "preview-light.png"; if (darkMode) { dashedPreviewIconPath = iconPath + "dashed-preview-dark.png"; dotsIconPath = iconPath + "dots-dark.png"; lockIconPath = iconPath + "lock-dark.png"; openLockIconPath = iconPath + "lock-open-dark.png"; plusIconPath = iconPath + "plus-dark.png"; previewIconPath = iconPath + "preview-dark.png"; } m_plusIcon = QIcon::fromTheme("list-add", QIcon(plusIconPath)); m_removeIcon = QIcon::fromTheme( "list-remove", QIcon(":/icons/fallback/32x32/edit-delete.png")); m_dotsIcon = QIcon::fromTheme("view-more", QIcon(dotsIconPath)); m_previewIcon = QIcon::fromTheme("view-visible", QIcon(previewIconPath)); m_previewDashedIcon = QIcon::fromTheme("view-hidden", QIcon(dashedPreviewIconPath)); m_lockIcon = QIcon::fromTheme("lock", QIcon(lockIconPath)); m_openLockIcon = QIcon::fromTheme("unlock", QIcon(openLockIconPath)); } QModelIndex LayerModel::parent(const QModelIndex&) const { return QModelIndex(); } int LayerModel::rowCount(const QModelIndex& p) const { if (p.isValid()) return 0; else return m_item; } int LayerModel::columnCount(const QModelIndex&) const { return QTTY_COLUMNS; } Qt::ItemFlags LayerModel::flags(const QModelIndex&) const { return Qt::ItemIsEnabled; } bool LayerModel::setData(const QModelIndex&, const QVariant&, int) { return false; } QVariant LayerModel::data(const QModelIndex& idx, int role) const { if (!idx.isValid() || idx.column() > QTTY_COLUMNS) return QVariant(); auto names = activeMoleculeNames(); if (idx.row() == static_cast(names.size())) { if (idx.column() == 0) { switch (role) { case Qt::DecorationRole: return m_plusIcon; default: return QVariant(); } } return QVariant(); } auto name = names[idx.row()].second; auto layer = names[idx.row()].first; bool isLayer = (name == "Layer"); if (isLayer) { if (idx.column() == ColumnType::Name) { switch (role) { case Qt::DisplayRole: { return QString(tr("Layer %1")) .arg(layer + 1); // count starts at 0 internally } case Qt::ForegroundRole: if (layer == getMoleculeLayer().activeLayer()) return QVariant(QColor(Qt::red)); else { const QPalette defaultPalette; return QVariant(defaultPalette.color(QPalette::WindowText)); } default: return QVariant(); } } else if (idx.column() == ColumnType::Menu) { if (role == Qt::DecorationRole) return m_dotsIcon; } else if (idx.column() == ColumnType::Visible) { if (role == Qt::DecorationRole) { if (visible(layer)) return m_previewIcon; else return m_previewDashedIcon; } } else if (idx.column() == ColumnType::Lock) { if (role == Qt::DecorationRole) { if (locked(layer)) return m_lockIcon; else return m_openLockIcon; } } else if (idx.column() == ColumnType::Remove) { if (role == Qt::DecorationRole) return m_removeIcon; } } else { if (idx.column() == ColumnType::Name) { switch (role) { case Qt::DisplayRole: { return " " + getTranslatedName(name); // should already be translated } } } } return QVariant(); } QString LayerModel::getTranslatedName(const std::string& name) const { // This is a bad hack, but whatever.. // Put all the strings that show up as layer options if (name == "Ball and Stick") return tr("Ball and Stick"); else if (name == "Cartoons") return tr("Cartoons", "protein ribbon / cartoon rendering"); else if (name == "Close Contacts") return tr("Close Contacts", "rendering of non-covalent close contacts"); else if (name == "Crystal Lattice") return tr("Crystal Lattice"); else if (name == "Dipole Moment") return tr("Dipole Moment"); else if (name == "Force") return tr("Force"); else if (name == "Labels") return tr("Labels"); else if (name == "Licorice") return tr("Licorice", "stick / licorice rendering"); else if (name == "Surfaces") return tr("Surfaces"); else if (name == "Non-Covalent") return tr("Non-Covalent"); else if (name == "QTAIM") return tr("QTAIM", "quantum theory of atoms in molecules"); else if (name == "Symmetry Elements") return tr("Symmetry Elements"); else if (name == "Van der Waals") return tr("Van der Waals"); else if (name == "Wireframe") return tr("Wireframe"); qDebug() << "LayerModel: name didn't match: " << name.c_str(); return QString(name.c_str()); } QModelIndex LayerModel::index(int row, int column, const QModelIndex& p) const { if (!p.isValid()) if (row >= 0 && row <= static_cast(m_item)) return createIndex(row, column); return QModelIndex(); } void LayerModel::addLayer(RWMolecule* rwmolecule) { addItem(); RWLayerManager::addLayer(rwmolecule); } void LayerModel::addItem() { beginInsertRows(QModelIndex(), m_item, m_item); endInsertRows(); ++m_item; } void LayerModel::updateRows() { while (m_item > activeMoleculeNames().size()) { beginRemoveRows(QModelIndex(), m_item, m_item); endRemoveRows(); --m_item; } while (m_item <= activeMoleculeNames().size()) { addItem(); } emit dataChanged(createIndex(0, 0), createIndex(m_item, 0)); } void LayerModel::addMolecule(const Molecule* mol) { RWLayerManager::addMolecule(mol); m_item = 0; updateRows(); connect(mol, &Molecule::changed, this, &LayerModel::updateRows); } void LayerModel::setActiveLayer(int index, RWMolecule* rwmolecule) { auto names = activeMoleculeNames(); assert(index < static_cast(names.size())); RWLayerManager::setActiveLayer(names[index].first, rwmolecule); updateRows(); } void LayerModel::removeItem(int row, RWMolecule* rwmolecule) { if (row <= static_cast(m_item)) { auto names = activeMoleculeNames(); removeLayer(static_cast(names[row].first), rwmolecule); updateRows(); } } size_t LayerModel::items() const { return m_item; } void LayerModel::flipVisible(size_t row) { auto names = activeMoleculeNames(); auto layer = names[row].first; RWLayerManager::flipVisible(layer); } void LayerModel::flipLocked(size_t row) { auto names = activeMoleculeNames(); auto layer = names[row].first; RWLayerManager::flipLocked(layer); } size_t LayerModel::layerCount() const { return LayerManager::layerCount(); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/layermodel.h000066400000000000000000000043271506155467400216120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_LAYERMODEL_H #define AVOGADRO_QTGUI_LAYERMODEL_H #include "avogadroqtguiexport.h" #include "rwlayermanager.h" #include #include #include namespace Avogadro { namespace QtGui { class Molecule; class RWMolecule; /** * @class LayerModel layermodel.h * @brief UI for the layer dock. */ class AVOGADROQTGUI_EXPORT LayerModel : public QAbstractItemModel, public QtGui::RWLayerManager { Q_OBJECT public: enum ColumnType { Name = 0, Menu = 1, Visible = 2, Lock = 3, Remove = 5 }; explicit LayerModel(QObject* p = 0); void loadIcons(bool darkMode); QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; bool setData(const QModelIndex& index, const QVariant& value, int role) override; QVariant data(const QModelIndex& index, int role) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; void addItem(); void addLayer(RWMolecule* rwmolecule); void addMolecule(const Molecule* mol); void setActiveLayer(int index, RWMolecule* rwmolecule); void removeItem(int row, RWMolecule* rwmolecule); size_t items() const; void flipVisible(size_t row); void flipLocked(size_t row); size_t layerCount() const; public slots: void updateRows(); private: QString getTranslatedName(const std::string& name) const; size_t m_item; QIcon m_plusIcon; QIcon m_dotsIcon; QIcon m_previewIcon; QIcon m_previewDashedIcon; QIcon m_lockIcon; QIcon m_openLockIcon; QIcon m_removeIcon; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_LAYERMODEL_H avogadrolibs-1.101.0/avogadro/qtgui/meshgenerator.cpp000066400000000000000000001373461506155467400226630ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "meshgenerator.h" #include #include #include #include #include namespace Avogadro::QtGui { using Core::Cube; using Core::Mesh; MeshGenerator::MeshGenerator(QObject* p) : QThread(p), m_iso(0.0), m_passes(6), m_reverseWinding(false), m_cube(nullptr), m_mesh(nullptr), m_stepSize(0.0, 0.0, 0.0), m_min(0.0, 0.0, 0.0), m_dim(0, 0, 0), m_progmin(0), m_progmax(0) { } MeshGenerator::MeshGenerator(const Cube* cube_, Mesh* mesh_, float iso, int passes, bool reverse, QObject* p) : QThread(p), m_iso(0.0), m_passes(6), m_reverseWinding(reverse), m_cube(nullptr), m_mesh(nullptr), m_stepSize(0.0, 0.0, 0.0), m_min(0.0, 0.0, 0.0), m_dim(0, 0, 0), m_progmin(0), m_progmax(0) { initialize(cube_, mesh_, iso, passes); } MeshGenerator::~MeshGenerator() {} bool MeshGenerator::initialize(const Cube* cube_, Mesh* mesh_, float iso, int passes, bool reverse) { if (!cube_ || !mesh_) return false; m_cube = cube_; m_mesh = mesh_; m_iso = iso; m_passes = passes; m_reverseWinding = reverse; for (unsigned int i = 0; i < 3; ++i) m_stepSize[i] = static_cast(m_cube->spacing()[i]); m_min = m_cube->min().cast(); m_dim = m_cube->dimensions(); edgeCases.resize((m_dim.x() - 1) * (m_dim.y()) * (m_dim.z())); cubeCases.resize((m_dim.x() - 1) * (m_dim.y() - 1) * (m_dim.z() - 1)); gridEdges.resize(m_dim.y() * m_dim.z()); triCounter.resize((m_dim.y() - 1) * (m_dim.z() - 1)); m_progmax = m_dim.x(); return true; } void MeshGenerator::FlyingEdgesAlgorithmPass1() { for (int k = 0; k != m_dim.z(); ++k) { for (int j = 0; j != m_dim.y(); ++j) { auto curEdgeCases = edgeCases.begin() + (m_dim.x() - 1) * (k * m_dim.y() + j); std::array isGE; isGE[0] = (m_cube->getData(0, j, k) >= m_iso); for (int i = 1; i != m_dim.x(); ++i) { isGE[i % 2] = (m_cube->getData(i, j, k) >= m_iso); curEdgeCases[i - 1] = calcCaseEdge(isGE[(i + 1) % 2], isGE[i % 2]); } } } for (int k = 0; k != m_dim.z(); ++k) { for (int j = 0; j != m_dim.y(); ++j) { gridEdge& curGridEdge = gridEdges[k * m_dim.y() + j]; curGridEdge.xl = m_dim.x(); for (int i = 1; i != m_dim.x(); ++i) { // if the edge is cut if (isCutEdge(i - 1, j, k)) { if (curGridEdge.xl == m_dim.x()) { curGridEdge.xl = i - 1; } curGridEdge.xr = i; } } } } } void MeshGenerator::FlyingEdgesAlgorithmPass2() { for (int k = 0; k != m_dim.z() - 1; ++k) { for (int j = 0; j != m_dim.y() - 1; ++j) { int xl, xr; calcTrimValues(xl, xr, j, k); // xl, xr set in this function gridEdge& ge0 = gridEdges[k * m_dim.y() + j]; gridEdge& ge1 = gridEdges[k * m_dim.y() + j + 1]; gridEdge& ge2 = gridEdges[(k + 1) * m_dim.y() + j]; gridEdge& ge3 = gridEdges[(k + 1) * m_dim.y() + j + 1]; auto const& ec0 = edgeCases.begin() + (m_dim.x() - 1) * (k * m_dim.y() + j); auto const& ec1 = edgeCases.begin() + (m_dim.x() - 1) * (k * m_dim.y() + j + 1); auto const& ec2 = edgeCases.begin() + (m_dim.x() - 1) * ((k + 1) * m_dim.y() + j); auto const& ec3 = edgeCases.begin() + (m_dim.x() - 1) * ((k + 1) * m_dim.y() + j + 1); // Count the number of triangles along this row of cubes int& curTriCounter = *(triCounter.begin() + k * (m_dim.y() - 1) + j); auto curCubeCaseIds = cubeCases.begin() + (m_dim.x() - 1) * (k * (m_dim.y() - 1) + j); bool isYEnd = (j == m_dim.y() - 2); bool isZEnd = (k == m_dim.z() - 2); for (int i = xl; i != xr; ++i) { bool isXEnd = (i == m_dim.x() - 2); unsigned char caseId = calcCubeCase( ec0[i], ec1[i], ec2[i], ec3[i]); // todo cubeCase not decleared curCubeCaseIds[i] = caseId; if (caseId == 0 || caseId == 255) { continue; } curTriCounter += m_numTris[caseId]; // not declared const bool* isCutCase = m_isCut[caseId]; // size 12 ge0.xstart += isCutCase[0]; ge0.ystart += isCutCase[3]; ge0.zstart += isCutCase[8]; if (isXEnd) { ge0.ystart += isCutCase[1]; ge0.zstart += isCutCase[9]; } if (isYEnd) { ge1.xstart += isCutCase[2]; ge1.zstart += isCutCase[10]; } if (isZEnd) { ge2.xstart += isCutCase[4]; ge2.ystart += isCutCase[7]; } if (isXEnd && isYEnd) { ge1.zstart += isCutCase[11]; } if (isXEnd && isZEnd) { ge2.ystart += isCutCase[5]; } if (isYEnd && isZEnd) { ge3.xstart += isCutCase[6]; } } } } } void MeshGenerator::FlyingEdgesAlgorithmPass3() { int tmp; int triAccum = 0; for (int k = 0; k != m_dim.z() - 1; ++k) { for (int j = 0; j != m_dim.y() - 1; ++j) { int& curTriCounter = triCounter[k * (m_dim.y() - 1) + j]; tmp = curTriCounter; curTriCounter = triAccum; triAccum += tmp; } } int pointAccum = 0; for (int k = 0; k != m_dim.z(); ++k) { for (int j = 0; j != m_dim.y(); ++j) { gridEdge& curGridEdge = gridEdges[(k * m_dim.y()) + j]; tmp = curGridEdge.xstart; curGridEdge.xstart = pointAccum; pointAccum += tmp; tmp = curGridEdge.ystart; curGridEdge.ystart = pointAccum; pointAccum += tmp; tmp = curGridEdge.zstart; curGridEdge.zstart = pointAccum; pointAccum += tmp; } } m_vertices.resize(pointAccum); m_normals.resize(pointAccum); m_triangles.resize(triAccum); } void MeshGenerator::FlyingEdgesAlgorithmPass4() { for (int k = 0; k != m_dim.z() - 1; ++k) { for (int j = 0; j != m_dim.y() - 1; ++j) { // find adjusted trim values int xl, xr; calcTrimValues(xl, xr, j, k); // xl, xr set in this function if (xl == xr) continue; int triIdx = triCounter[(k * (m_dim.y() - 1)) + j]; auto curCubeCaseIds = cubeCases.begin() + (m_dim.x() - 1) * (k * (m_dim.y() - 1) + j); gridEdge const& ge0 = gridEdges[k * m_dim.y() + j]; gridEdge const& ge1 = gridEdges[k * m_dim.y() + j + 1]; gridEdge const& ge2 = gridEdges[(k + 1) * m_dim.y() + j]; gridEdge const& ge3 = gridEdges[(k + 1) * m_dim.y() + j + 1]; int x0counter = 0; int y0counter = 0; int z0counter = 0; int x1counter = 0; int z1counter = 0; int x2counter = 0; int y2counter = 0; int x3counter = 0; bool isYEnd = (j == m_dim.y() - 2); bool isZEnd = (k == m_dim.z() - 2); for (int i = xl; i != xr; ++i) { bool isXEnd = (i == m_dim.x() - 2); unsigned char caseId = curCubeCaseIds[i]; if (caseId == 0 || caseId == 255) { continue; } const bool* isCutCase = m_isCut[caseId]; // size 12 std::array, 8> pointCube = m_cube->getPosCube(i, j, k); std::array isovalCube = m_cube->getValsCube(i, j, k); std::array, 8> gradCube = m_cube->getGradCube(i, j, k); // Add Points and normals. // Calculate global indices for triangles std::array globalIdxs; if (isCutCase[0]) { // points-> array int idx = ge0.xstart + x0counter; std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 0); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 0); m_vertices[idx] = Vector3f(interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); globalIdxs[0] = idx; ++x0counter; } if (isCutCase[3]) { int idx = ge0.ystart + y0counter; std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 3); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 3); m_vertices[idx] = Vector3f(interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); globalIdxs[3] = idx; ++y0counter; } if (isCutCase[8]) { int idx = ge0.zstart + z0counter; std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 8); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 8); m_vertices[idx] = Vector3f(interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); globalIdxs[8] = idx; ++z0counter; } if (isCutCase[1]) { int idx = ge0.ystart + y0counter; if (isXEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 1); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 1); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); // y0counter counter doesn't need to be incremented // because it won't be used again. } globalIdxs[1] = idx; } if (isCutCase[9]) { int idx = ge0.zstart + z0counter; if (isXEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 9); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 9); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); // z0counter doesn't need to in incremented. } globalIdxs[9] = idx; } if (isCutCase[2]) { int idx = ge1.xstart + x1counter; if (isYEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 2); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 2); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); } globalIdxs[2] = idx; ++x1counter; } if (isCutCase[10]) { int idx = ge1.zstart + z1counter; if (isYEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 10); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 10); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); } globalIdxs[10] = idx; ++z1counter; } if (isCutCase[4]) { int idx = ge2.xstart + x2counter; if (isZEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 4); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 4); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); } globalIdxs[4] = idx; ++x2counter; } if (isCutCase[7]) { int idx = ge2.ystart + y2counter; if (isZEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 7); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 7); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); } globalIdxs[7] = idx; ++y2counter; } if (isCutCase[11]) { int idx = ge1.zstart + z1counter; if (isXEnd && isYEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 11); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 11); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); } globalIdxs[11] = idx; } if (isCutCase[5]) { int idx = ge2.ystart + y2counter; if (isXEnd && isZEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 5); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 5); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); // y2 counter does not need to be incremented. } globalIdxs[5] = idx; } if (isCutCase[6]) { int idx = ge3.xstart + x3counter; if (isYEnd && isZEnd) { std::array interpolatedPoint = interpolateOnCube(pointCube, isovalCube, 6); std::array interpolatedNormal = interpolateOnCube(gradCube, isovalCube, 6); m_vertices[idx] = Vector3f( interpolatedPoint[0], interpolatedPoint[1], interpolatedPoint[2]); m_normals[idx] = Vector3f(interpolatedNormal[0], interpolatedNormal[1], interpolatedNormal[2]); } globalIdxs[6] = idx; ++x3counter; } // Add triangles const signed char* caseTri = m_caseTriangles[caseId]; // size 16 for (int idx = 0; caseTri[idx] != -1; idx += 3) { m_triangles[triIdx][0] = globalIdxs[caseTri[idx]]; m_triangles[triIdx][1] = globalIdxs[caseTri[idx + 1]]; m_triangles[triIdx][2] = globalIdxs[caseTri[idx + 2]]; ++triIdx; } } } } } void MeshGenerator::run() { if (!m_cube || !m_mesh) { qDebug() << "No mesh or cube set - nothing to find isosurface of…"; return; } m_mesh->setStable(false); m_mesh->clear(); // flying-edges passes for the creation of normal, vertices and triangles FlyingEdgesAlgorithmPass1(); FlyingEdgesAlgorithmPass2(); FlyingEdgesAlgorithmPass3(); FlyingEdgesAlgorithmPass4(); m_mesh->setVertices(m_vertices); m_mesh->setNormals(m_normals); m_mesh->setTriangles(m_triangles); m_mesh->smooth(m_passes); m_mesh->setStable(true); // clearing the memory m_vertices.resize(0); m_normals.resize(0); m_triangles.resize(0); edgeCases.resize(0); cubeCases.resize(0); gridEdges.resize(0); triCounter.resize(0); } void MeshGenerator::clear() { m_iso = 0.0; m_passes = 6; m_cube = nullptr; m_mesh = nullptr; m_stepSize.setZero(); m_min.setZero(); m_dim.setZero(); m_progmin = 0; m_progmax = 0; } unsigned char MeshGenerator::calcCubeCase(unsigned char const& ec0, unsigned char const& ec1, unsigned char const& ec2, unsigned char const& ec3) const { unsigned char caseId = 0; if ((ec0 == 0) || (ec0 == 2)) // Vertex 0 at (i, j, k) caseId |= 1; if ((ec0 == 0) || (ec0 == 1)) // Vertex 1 at (i+1, j, k) caseId |= 2; if ((ec1 == 0) || (ec1 == 1)) // Vertex 2 at (i+1, j+1, k) caseId |= 4; if ((ec1 == 0) || (ec1 == 2)) // Vertex 3 at (i, j+1, k) caseId |= 8; if ((ec2 == 0) || (ec2 == 2)) // Vertex 4 at (i, j, k+1) caseId |= 16; if ((ec2 == 0) || (ec2 == 1)) // Vertex 5 at (i+1, j, k+1) caseId |= 32; if ((ec3 == 0) || (ec3 == 1)) // Vertex 6 at (i+1, j+1, k+1) caseId |= 64; if ((ec3 == 0) || (ec3 == 2)) // Vertex 7 at (i, j+1, k+1) caseId |= 128; return caseId; } bool MeshGenerator::isCutEdge(int const& i, int const& j, int const& k) const { // Assuming edgeCases are all set int edgeCaseIdx = k * ((m_dim.x() - 1) * m_dim.y()) + (j * (m_dim.x() - 1)) + i; unsigned char edgeCase = edgeCases[edgeCaseIdx]; if (edgeCase == 1 || edgeCase == 2) { return true; } if (j != m_dim.y() - 1) { int edgeCaseIdxY = (k * (m_dim.x() - 1) * m_dim.y()) + ((j + 1) * (m_dim.x() - 1)) + i; unsigned char edgeCaseY = edgeCases[edgeCaseIdxY]; // If the sum is odd, the edge along the y-axis is cut if ((edgeCase + edgeCaseY) % 2 == 1) { return true; } } if (k != m_dim.z() - 1) { int edgeCaseIdxZ = ((k + 1) * (m_dim.x() - 1) * m_dim.y()) + (j * (m_dim.x() - 1)) + i; unsigned char edgeCaseZ = edgeCases[edgeCaseIdxZ]; // If the sum is odd, the edge along the z-axis is cut if ((edgeCase + edgeCaseZ) % 2 == 1) { return true; } } return false; } unsigned char MeshGenerator::calcCaseEdge(bool const& prevEdge, bool const& currEdge) const { // o -- is greater than or equal to // case 0: prevEdge = true, currEdge = true // case 1: prevEdge = false, currEdge = true // case 2: prevEdge = true, currEdge = false // case 3: prevEdge = false, currEdge = false if (prevEdge && currEdge) return 0; if (!prevEdge && currEdge) return 1; if (prevEdge && !currEdge) return 2; else // !prevEdge && !currEdge return 3; } unsigned long MeshGenerator::duplicate(const Vector3i&, const Vector3f&) { // FIXME Not implemented yet. return 0; } void MeshGenerator::calcTrimValues(int& xl, int& xr, int const& j, int const& k) const { const gridEdge& ge0 = gridEdges[k * m_dim.y() + j]; const gridEdge& ge1 = gridEdges[k * m_dim.y() + j + 1]; const gridEdge& ge2 = gridEdges[(k + 1) * m_dim.y() + j]; const gridEdge& ge3 = gridEdges[(k + 1) * m_dim.y() + j + 1]; xl = std::min({ ge0.xl, ge1.xl, ge2.xl, ge3.xl }); xr = std::max({ ge0.xr, ge1.xr, ge2.xr, ge3.xr }); if (xl > xr) xl = xr; } inline std::array MeshGenerator::interpolateOnCube( std::array, 8> const& pts, std::array const& isovals, unsigned char const& edge) const { unsigned char i0 = m_edgeVertices[edge][0]; unsigned char i1 = m_edgeVertices[edge][1]; float weight = (m_iso - isovals[i0]) / (isovals[i1] - isovals[i0]); return interpolate(pts[i0], pts[i1], weight); } inline std::array MeshGenerator::interpolate( std::array const& a, std::array const& b, float const& weight) const { std::array ret; ret[0] = a[0] + (weight * (b[0] - a[0])); ret[1] = a[1] + (weight * (b[1] - a[1])); ret[2] = a[2] + (weight * (b[2] - a[2])); return ret; } // flying edges tables using: const unsigned char MeshGenerator::m_numTris[256] = { // clang-format off 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 2, 3, 4, 4, 3, 3, 4, 4, 3, 4, 5, 5, 2, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, 2, 3, 3, 4, 3, 4, 2, 3, 3, 4, 4, 5, 4, 5, 3, 2, 3, 4, 4, 3, 4, 5, 3, 2, 4, 5, 5, 4, 5, 2, 4, 1, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 4, 3, 4, 4, 5, 3, 2, 4, 3, 4, 3, 5, 2, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, 3, 4, 4, 3, 4, 5, 5, 4, 4, 3, 5, 2, 5, 4, 2, 1, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 2, 3, 3, 2, 3, 4, 4, 5, 4, 5, 5, 2, 4, 3, 5, 4, 3, 2, 4, 1, 3, 4, 4, 5, 4, 5, 3, 4, 4, 5, 5, 2, 3, 4, 2, 1, 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0 // clang-format on }; const bool MeshGenerator::m_isCut[256][12] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, { 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, { 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0 }, { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1 }, { 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1 }, { 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1 }, { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, { 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0 }, { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0 }, { 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1 }, { 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, { 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0 }, { 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0 }, { 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0 }, { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 }, { 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1 }, { 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1 }, { 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1 }, { 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0 }, { 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, { 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0 }, { 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0 }, { 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1 }, { 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1 }, { 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1 }, { 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1 }, { 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, { 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0 }, { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0 }, { 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1 }, { 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1 }, { 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1 }, { 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0 }, { 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 }, { 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0 }, { 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1 }, { 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1 }, { 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1 }, { 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0 }, { 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0 }, { 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0 }, { 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, { 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, { 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1 }, { 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0 }, { 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0 }, { 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, { 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1 }, { 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1 }, { 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1 }, { 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1 }, { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1 }, { 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 }, { 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1 }, { 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1 }, { 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, { 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0 }, { 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0 }, { 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0 }, { 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1 }, { 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1 }, { 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1 }, { 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1 }, { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0 }, { 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0 }, { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0 }, { 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0 }, { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1 }, { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1 }, { 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1 }, { 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1 }, { 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, { 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 }, { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0 }, { 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1 }, { 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1 }, { 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0 }, { 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 }, { 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 }, { 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0 }, { 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1 }, { 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1 }, { 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1 }, { 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1 }, { 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, { 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0 }, { 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0 }, { 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1 }, { 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1 }, { 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 }, { 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0 }, { 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0 }, { 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1 }, { 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1 }, { 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1 }, { 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0 }, { 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0 }, { 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 }, { 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1 }, { 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1 }, { 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1 }, { 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1 }, { 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0 }, { 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0 }, { 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, { 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0 }, { 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0 }, { 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0 }, { 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1 }, { 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1 }, { 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1 }, { 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1 }, { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 }, { 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, { 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0 }, { 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0 }, { 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1 }, { 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1 }, { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0 }, { 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0 }, { 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0 }, { 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0 }, { 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 }, { 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1 }, { 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, { 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1 }, { 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0 }, { 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0 }, { 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0 }, { 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, { 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1 }, { 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1 }, { 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1 }, { 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1 }, { 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0 }, { 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 }, { 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 }, { 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0 }, { 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1 }, { 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1 }, { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0 }, { 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 }, { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, { 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, { 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1 }, { 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1 }, { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1 }, { 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0 }, { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0 }, { 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0 }, { 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1 }, { 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1 }, { 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1 }, { 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1 }, { 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0 }, { 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0 }, { 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0 }, { 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, { 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1 }, { 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1 }, { 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 }, { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1 }, { 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1 }, { 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1 }, { 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1 }, { 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0 }, { 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, { 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0 }, { 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1 }, { 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1 }, { 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, { 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1 }, { 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, { 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0 }, { 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0 }, { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1 }, { 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1 }, { 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1 }, { 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0 }, { 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 }, { 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0 }, { 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1 }, { 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1 }, { 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1 }, { 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0 }, { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0 }, { 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1 }, { 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1 }, { 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1 }, { 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1 }, { 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0 }, { 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0 }, { 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, { 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0 }, { 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1 }, { 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1 }, { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 }, { 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0 }, { 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0 }, { 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0 }, { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, { 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1 }, { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1 }, { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1 }, { 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0 }, { 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0 }, { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, { 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1 }, { 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1 }, { 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1 }, { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0 }, { 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; const signed char MeshGenerator::m_caseTriangles[256][16]{ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, 1, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 11, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 3, 8, 2, 8, 11, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 2, 10, 8, 0, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 0, 9, 2, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 1, 10, 9, 9, 10, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 1, 11, 10, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 11, 0, 11, 8, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 10, 10, 9, 11, -1, -1, -1, -1, -1, -1, -1 }, { 9, 11, 8, 11, 10, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 7, 4, 3, 4, 0, 1, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, { 9, 11, 2, 9, 2, 0, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1 }, { 2, 9, 11, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 8, 7, 4, 3, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 4, 10, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 9, 1, 0, 8, 7, 4, 2, 10, 3, -1, -1, -1, -1, -1, -1, -1 }, { 4, 10, 7, 9, 10, 4, 9, 2, 10, 9, 1, 2, -1, -1, -1, -1 }, { 3, 1, 11, 3, 11, 10, 7, 4, 8, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 10, 1, 10, 4, 1, 4, 0, 7, 4, 10, -1, -1, -1, -1 }, { 4, 8, 7, 9, 10, 0, 9, 11, 10, 10, 3, 0, -1, -1, -1, -1 }, { 4, 10, 7, 4, 9, 10, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 5, 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 8, 0, 1, 11, 2, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1 }, { 5, 11, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, { 2, 5, 11, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, { 9, 4, 5, 2, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 2, 10, 0, 10, 8, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 0, 5, 1, 2, 10, 3, -1, -1, -1, -1, -1, -1, -1 }, { 2, 5, 1, 2, 8, 5, 2, 10, 8, 4, 5, 8, -1, -1, -1, -1 }, { 11, 10, 3, 11, 3, 1, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1 }, { 4, 5, 9, 0, 1, 8, 8, 1, 11, 8, 11, 10, -1, -1, -1, -1 }, { 5, 0, 4, 5, 10, 0, 5, 11, 10, 10, 3, 0, -1, -1, -1, -1 }, { 5, 8, 4, 5, 11, 8, 11, 10, 8, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 9, 7, 5, 11, 2, 1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 2, 1, 9, 0, 5, 5, 0, 3, 5, 3, 7, -1, -1, -1, -1 }, { 8, 2, 0, 8, 5, 2, 8, 7, 5, 11, 2, 5, -1, -1, -1, -1 }, { 2, 5, 11, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 7, 5, 9, 7, 9, 8, 3, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 10, 7, -1, -1, -1, -1 }, { 2, 10, 3, 0, 8, 1, 1, 8, 7, 1, 7, 5, -1, -1, -1, -1 }, { 10, 1, 2, 10, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 5, 8, 7, 5, 11, 3, 1, 11, 10, 3, -1, -1, -1, -1 }, { 5, 0, 7, 5, 9, 0, 7, 0, 10, 1, 11, 0, 10, 0, 11, -1 }, { 10, 0, 11, 10, 3, 0, 11, 0, 5, 8, 7, 0, 5, 0, 7, -1 }, { 10, 5, 11, 7, 5, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, 5, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 1, 0, 5, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 3, 8, 1, 8, 9, 5, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 6, 1, 6, 2, 3, 8, 0, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, { 2, 10, 3, 11, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 8, 0, 10, 0, 2, 11, 5, 6, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, 2, 10, 3, 5, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 5, 6, 11, 1, 2, 9, 9, 2, 10, 9, 10, 8, -1, -1, -1, -1 }, { 6, 10, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 10, 8, 0, 5, 10, 0, 1, 5, 5, 6, 10, -1, -1, -1, -1 }, { 3, 6, 10, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, { 6, 9, 5, 6, 10, 9, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 5, 6, 11, 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 0, 3, 4, 3, 7, 6, 11, 5, -1, -1, -1, -1, -1, -1, -1 }, { 1, 0, 9, 5, 6, 11, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 6, 1, 7, 9, 1, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 6, 2, 1, 6, 1, 5, 4, 8, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 2, 5, 6, 2, 3, 4, 0, 3, 7, 4, -1, -1, -1, -1 }, { 8, 7, 4, 9, 5, 0, 0, 5, 6, 0, 6, 2, -1, -1, -1, -1 }, { 7, 9, 3, 7, 4, 9, 3, 9, 2, 5, 6, 9, 2, 9, 6, -1 }, { 3, 2, 10, 7, 4, 8, 11, 5, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 6, 11, 4, 2, 7, 4, 0, 2, 2, 10, 7, -1, -1, -1, -1 }, { 0, 9, 1, 4, 8, 7, 2, 10, 3, 5, 6, 11, -1, -1, -1, -1 }, { 9, 1, 2, 9, 2, 10, 9, 10, 4, 7, 4, 10, 5, 6, 11, -1 }, { 8, 7, 4, 3, 5, 10, 3, 1, 5, 5, 6, 10, -1, -1, -1, -1 }, { 5, 10, 1, 5, 6, 10, 1, 10, 0, 7, 4, 10, 0, 10, 4, -1 }, { 0, 9, 5, 0, 5, 6, 0, 6, 3, 10, 3, 6, 8, 7, 4, -1 }, { 6, 9, 5, 6, 10, 9, 4, 9, 7, 7, 9, 10, -1, -1, -1, -1 }, { 11, 9, 4, 6, 11, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 6, 11, 4, 11, 9, 0, 3, 8, -1, -1, -1, -1, -1, -1, -1 }, { 11, 1, 0, 11, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 11, 1, -1, -1, -1, -1 }, { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, { 3, 8, 0, 1, 9, 2, 2, 9, 4, 2, 4, 6, -1, -1, -1, -1 }, { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 11, 9, 4, 11, 4, 6, 10, 3, 2, -1, -1, -1, -1, -1, -1, -1 }, { 0, 2, 8, 2, 10, 8, 4, 11, 9, 4, 6, 11, -1, -1, -1, -1 }, { 3, 2, 10, 0, 6, 1, 0, 4, 6, 6, 11, 1, -1, -1, -1, -1 }, { 6, 1, 4, 6, 11, 1, 4, 1, 8, 2, 10, 1, 8, 1, 10, -1 }, { 9, 4, 6, 9, 6, 3, 9, 3, 1, 10, 3, 6, -1, -1, -1, -1 }, { 8, 1, 10, 8, 0, 1, 10, 1, 6, 9, 4, 1, 6, 1, 4, -1 }, { 3, 6, 10, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, { 6, 8, 4, 10, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 6, 11, 7, 11, 8, 8, 11, 9, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 7, 0, 7, 11, 0, 11, 9, 6, 11, 7, -1, -1, -1, -1 }, { 11, 7, 6, 1, 7, 11, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, { 11, 7, 6, 11, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, { 2, 9, 6, 2, 1, 9, 6, 9, 7, 0, 3, 9, 7, 9, 3, -1 }, { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 3, 11, 8, 6, 11, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, { 2, 7, 0, 2, 10, 7, 0, 7, 9, 6, 11, 7, 9, 7, 11, -1 }, { 1, 0, 8, 1, 8, 7, 1, 7, 11, 6, 11, 7, 2, 10, 3, -1 }, { 10, 1, 2, 10, 7, 1, 11, 1, 6, 6, 1, 7, -1, -1, -1, -1 }, { 8, 6, 9, 8, 7, 6, 9, 6, 1, 10, 3, 6, 1, 6, 3, -1 }, { 0, 1, 9, 10, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 0, 8, 7, 6, 0, 3, 0, 10, 10, 0, 6, -1, -1, -1, -1 }, { 7, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 8, 0, 10, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, 10, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 9, 1, 8, 1, 3, 10, 6, 7, -1, -1, -1, -1, -1, -1, -1 }, { 11, 2, 1, 6, 7, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 3, 8, 0, 6, 7, 10, -1, -1, -1, -1, -1, -1, -1 }, { 2, 0, 9, 2, 9, 11, 6, 7, 10, -1, -1, -1, -1, -1, -1, -1 }, { 6, 7, 10, 2, 3, 11, 11, 3, 8, 11, 8, 9, -1, -1, -1, -1 }, { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 2, 6, 7, 2, 7, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 11, 6, 7, 11, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, { 11, 6, 7, 1, 11, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, { 0, 7, 3, 0, 11, 7, 0, 9, 11, 6, 7, 11, -1, -1, -1, -1 }, { 7, 11, 6, 7, 8, 11, 8, 9, 11, -1, -1, -1, -1, -1, -1, -1 }, { 6, 4, 8, 10, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 10, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 8, 10, 6, 8, 6, 4, 9, 1, 0, -1, -1, -1, -1, -1, -1, -1 }, { 9, 6, 4, 9, 3, 6, 9, 1, 3, 10, 6, 3, -1, -1, -1, -1 }, { 6, 4, 8, 6, 8, 10, 2, 1, 11, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 3, 10, 0, 0, 10, 6, 0, 6, 4, -1, -1, -1, -1 }, { 4, 8, 10, 4, 10, 6, 0, 9, 2, 2, 9, 11, -1, -1, -1, -1 }, { 11, 3, 9, 11, 2, 3, 9, 3, 4, 10, 6, 3, 4, 3, 6, -1 }, { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 0, 9, 2, 4, 3, 2, 6, 4, 4, 8, 3, -1, -1, -1, -1 }, { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 11, -1, -1, -1, -1 }, { 11, 0, 1, 11, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 6, 4, 8, 3, 6, 3, 11, 0, 9, 3, 11, 3, 9, -1 }, { 11, 4, 9, 6, 4, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 5, 9, 7, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, 4, 5, 9, 10, 6, 7, -1, -1, -1, -1, -1, -1, -1 }, { 5, 1, 0, 5, 0, 4, 7, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 7, 8, 4, 3, 3, 4, 5, 3, 5, 1, -1, -1, -1, -1 }, { 9, 4, 5, 11, 2, 1, 7, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 6, 7, 10, 1, 11, 2, 0, 3, 8, 4, 5, 9, -1, -1, -1, -1 }, { 7, 10, 6, 5, 11, 4, 4, 11, 2, 4, 2, 0, -1, -1, -1, -1 }, { 3, 8, 4, 3, 4, 5, 3, 5, 2, 11, 2, 5, 10, 6, 7, -1 }, { 7, 3, 2, 7, 2, 6, 5, 9, 4, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 5, 0, 6, 8, 0, 2, 6, 6, 7, 8, -1, -1, -1, -1 }, { 3, 2, 6, 3, 6, 7, 1, 0, 5, 5, 0, 4, -1, -1, -1, -1 }, { 6, 8, 2, 6, 7, 8, 2, 8, 1, 4, 5, 8, 1, 8, 5, -1 }, { 9, 4, 5, 11, 6, 1, 1, 6, 7, 1, 7, 3, -1, -1, -1, -1 }, { 1, 11, 6, 1, 6, 7, 1, 7, 0, 8, 0, 7, 9, 4, 5, -1 }, { 4, 11, 0, 4, 5, 11, 0, 11, 3, 6, 7, 11, 3, 11, 7, -1 }, { 7, 11, 6, 7, 8, 11, 5, 11, 4, 4, 11, 8, -1, -1, -1, -1 }, { 6, 5, 9, 6, 9, 10, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 10, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, { 0, 8, 10, 0, 10, 5, 0, 5, 1, 5, 10, 6, -1, -1, -1, -1 }, { 6, 3, 10, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 9, 10, 5, 9, 8, 10, 10, 6, 5, -1, -1, -1, -1 }, { 0, 3, 10, 0, 10, 6, 0, 6, 9, 5, 9, 6, 1, 11, 2, -1 }, { 10, 5, 8, 10, 6, 5, 8, 5, 0, 11, 2, 5, 0, 5, 2, -1 }, { 6, 3, 10, 6, 5, 3, 2, 3, 11, 11, 3, 5, -1, -1, -1, -1 }, { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 5, 1, 0, 8, 5, 8, 6, 3, 2, 8, 6, 8, 2, -1 }, { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 3, 1, 11, 6, 3, 6, 8, 5, 9, 6, 8, 6, 9, -1 }, { 11, 0, 1, 11, 6, 0, 9, 0, 5, 5, 0, 6, -1, -1, -1, -1 }, { 0, 8, 3, 5, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 11, 5, 7, 10, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 11, 5, 10, 5, 7, 8, 0, 3, -1, -1, -1, -1, -1, -1, -1 }, { 5, 7, 10, 5, 10, 11, 1, 0, 9, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 7, 11, 7, 10, 9, 1, 8, 8, 1, 3, -1, -1, -1, -1 }, { 10, 2, 1, 10, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, 1, 7, 2, 1, 5, 7, 7, 10, 2, -1, -1, -1, -1 }, { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 10, -1, -1, -1, -1 }, { 7, 2, 5, 7, 10, 2, 5, 2, 9, 3, 8, 2, 9, 2, 8, -1 }, { 2, 11, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 8, 0, 2, 8, 2, 5, 8, 5, 7, 11, 5, 2, -1, -1, -1, -1 }, { 9, 1, 0, 5, 3, 11, 5, 7, 3, 3, 2, 11, -1, -1, -1, -1 }, { 9, 2, 8, 9, 1, 2, 8, 2, 7, 11, 5, 2, 7, 2, 5, -1 }, { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 5, 4, 8, 5, 8, 11, 11, 8, 10, -1, -1, -1, -1, -1, -1, -1 }, { 5, 4, 0, 5, 0, 10, 5, 10, 11, 10, 0, 3, -1, -1, -1, -1 }, { 0, 9, 1, 8, 11, 4, 8, 10, 11, 11, 5, 4, -1, -1, -1, -1 }, { 11, 4, 10, 11, 5, 4, 10, 4, 3, 9, 1, 4, 3, 4, 1, -1 }, { 2, 1, 5, 2, 5, 8, 2, 8, 10, 4, 8, 5, -1, -1, -1, -1 }, { 0, 10, 4, 0, 3, 10, 4, 10, 5, 2, 1, 10, 5, 10, 1, -1 }, { 0, 5, 2, 0, 9, 5, 2, 5, 10, 4, 8, 5, 10, 5, 8, -1 }, { 9, 5, 4, 2, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 11, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, { 5, 2, 11, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 11, 3, 11, 5, 3, 5, 8, 4, 8, 5, 0, 9, 1, -1 }, { 5, 2, 11, 5, 4, 2, 1, 2, 9, 9, 2, 4, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 9, 5, 0, 0, 5, 3, -1, -1, -1, -1 }, { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 10, 4, 10, 9, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, 4, 7, 9, 9, 7, 10, 9, 10, 11, -1, -1, -1, -1 }, { 1, 10, 11, 1, 4, 10, 1, 0, 4, 7, 10, 4, -1, -1, -1, -1 }, { 3, 4, 1, 3, 8, 4, 1, 4, 11, 7, 10, 4, 11, 4, 10, -1 }, { 4, 7, 10, 9, 4, 10, 9, 10, 2, 9, 2, 1, -1, -1, -1, -1 }, { 9, 4, 7, 9, 7, 10, 9, 10, 1, 2, 1, 10, 0, 3, 8, -1 }, { 10, 4, 7, 10, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, { 10, 4, 7, 10, 2, 4, 8, 4, 3, 3, 4, 2, -1, -1, -1, -1 }, { 2, 11, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, { 9, 7, 11, 9, 4, 7, 11, 7, 2, 8, 0, 7, 2, 7, 0, -1 }, { 3, 11, 7, 3, 2, 11, 7, 11, 4, 1, 0, 11, 4, 11, 0, -1 }, { 1, 2, 11, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 0, 1, 8, 8, 1, 7, -1, -1, -1, -1 }, { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 11, 11, 8, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 9, 0, 3, 10, 9, 10, 11, 9, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 1, 0, 8, 11, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 11, 1, 10, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 10, 2, 1, 9, 10, 9, 8, 10, -1, -1, -1, -1, -1, -1, -1 }, { 3, 9, 0, 3, 10, 9, 1, 9, 2, 2, 9, 10, -1, -1, -1, -1 }, { 0, 10, 2, 8, 10, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 11, 8, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, { 9, 2, 11, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 11, 8, 0, 8, 1, 1, 8, 11, -1, -1, -1, -1 }, { 1, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }; const unsigned char MeshGenerator::m_edgeVertices[12][2] = { { 0, 1 }, { 1, 2 }, { 3, 2 }, { 0, 3 }, { 4, 5 }, { 5, 6 }, { 7, 6 }, { 4, 7 }, { 0, 4 }, { 1, 5 }, { 3, 7 }, { 2, 6 } }; } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/meshgenerator.h000066400000000000000000000204041506155467400223120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_MESHGENERATOR_H #define AVOGADRO_QTGUI_MESHGENERATOR_H #include "avogadroqtguiexport.h" #include #include #include namespace Avogadro { namespace Core { class Cube; class Mesh; } // namespace Core namespace QtGui { /** * @class MeshGenerator meshgenerator.h * @brief Class that can generate Mesh objects from Cube objects. * @author Marcus D. Hanwell * @author Perminder Singh * * This class implements a method of generating an isosurface Mesh from * volumetric data using the marching cubes algorithm. In the case of the * MeshGenerator class it expects a Cube as an input and an isosurface value. * The tables and the basic code is taken from the public domain code written * by Cory Bloyd (marchingsource.cpp) and available at, * http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/ * * You must first initialize the class and then call run() to actually * polygonize the isosurface. Connect to the classes finished() signal to * do something once the polygonization is complete. */ class AVOGADROQTGUI_EXPORT MeshGenerator : public QThread { Q_OBJECT public: /** * Constructor. */ explicit MeshGenerator(QObject* parent = nullptr); /** * Constructor. Can be used to initialize the MeshGenerator. * @param cube The source Cube with the volumetric data. * @param mesh The Mesh that will hold the isosurface. * @param iso The iso value of the surface. * @param passes Number of smoothing passes to perform. * @return True if the MeshGenerator was successfully initialized. */ MeshGenerator(const Core::Cube* cube, Core::Mesh* mesh, float iso, int passes = 6, bool reverse = false, QObject* parent = nullptr); /** * Destructor. */ ~MeshGenerator() override; /** * Initialization function, set up the MeshGenerator ready to find an * isosurface of the supplied Cube. * @param cube The source Cube with the volumetric data. * @param mesh The Mesh that will hold the isosurface. * @param iso The iso value of the surface. * @param passes Number of smoothing passes to perform. */ bool initialize(const Core::Cube* cube, Core::Mesh* mesh, float iso, int passes = 6, bool reverse = false); /** * Use this function to begin Mesh generation. Uses an asynchronous thread, * and so avoids locking the user interface while the isosurface is found. */ void run() override; /** * It holds the range and starting offsets of isosurface-intersected * edges along the x, y, and z axes for each grid cell. */ struct gridEdge { gridEdge() : xl(0), xr(0), xstart(0), ystart(0), zstart(0) {} // trim values // set on pass 1 int xl; int xr; // modified on pass 2 // set on pass 3 int xstart; int ystart; int zstart; }; /** * Handles duplicate vertices (Not implemented). Placeholder for future * functionality. */ unsigned long duplicate(const Vector3i& c, const Vector3f& pos); /** * @name Flying Edges * Methods to implement the "flying edges" method for isosurface mesh * generation. Flying edges: A high-performance scalable isocontouring * algorithm Schroeder; Maynard; Geveci; 2015 IEEE 5th Symposium on Large Data * Analysis and Visualization (LDAV) * [10.1109/LDAV.2015.7348069](https://doi.org/10.1109/LDAV.2015.7348069) * Alternate (non-VTK) implementation at * https://github.com/sandialabs/miniIsosurface/blob/master/flyingEdges/ * @{ */ /** * Pass 1 for flying edges. Pass1 detects and records * where the isosurface intersects each row of grid edges * along the x-axis. */ void FlyingEdgesAlgorithmPass1(); /** * Pass2 assigns case identifiers to each grid cell based on * intersected edges and tallies the number of triangles needed * for mesh construction. */ void FlyingEdgesAlgorithmPass2(); /** * Pass3 computes cumulative offsets for triangles * and vertices and allocates memory for the mesh structures. */ void FlyingEdgesAlgorithmPass3(); /** * Calculates normals, triangles and vertices. */ void FlyingEdgesAlgorithmPass4(); /**@}*/ /** * @return The Cube being used by the class. */ const Core::Cube* cube() const { return m_cube; } /** * Determines the x-range (xl to xr) where isosurface intersections * occur, optimizing calculations within this range. */ inline void calcTrimValues(int& xl, int& xr, int const& j, int const& k) const; /** * Indicates which edges intersects the isosurface. */ inline unsigned char calcCubeCase(unsigned char const& ec0, unsigned char const& ec1, unsigned char const& ec2, unsigned char const& ec3) const; /** * @return The Mesh being generated by the class. */ Core::Mesh* mesh() const { return m_mesh; } /** * Clears the contents of the MeshGenerator. */ void clear(); /** * @return The minimum value of the progress value. */ int progressMinimum() { return m_progmin; } /** * @return The maximum value of the progress value. */ int progressMaximum() { return m_progmax; } signals: /** * The current value of the calculation's progress. */ void progressValueChanged(int); protected: /** * isCutEdge checks whether the grid edge at position (i, j, k) is * intersected by the isosurface based on edge case conditions. * @return Boolean if it's intersected or not. */ bool isCutEdge(int const& i, int const& j, int const& k) const; /** * It computes the 3D intersection point on a cube edge via interpolation. */ inline std::array interpolateOnCube( std::array, 8> const& pts, std::array const& isovals, unsigned char const& edge) const; /** * It linearly interpolates between two 3D points, a and b, using * the given weight to determine the intermediate position. */ inline std::array interpolate(std::array const& a, std::array const& b, float const& weight) const; /** * calcCaseEdge determines an edge case code (0–3) based on two boolean edge * comparisons. */ inline unsigned char calcCaseEdge(bool const& prevEdge, bool const& currEdge) const; float m_iso; /** The value of the isosurface. */ int m_passes; /** Number of smoothing passes to perform. */ bool m_reverseWinding; /** Whether the winding and normals are reversed. */ const Core::Cube* m_cube; /** The cube that we are generating a Mesh from. */ Core::Mesh* m_mesh; /** The mesh that is being generated. */ Vector3f m_stepSize; /** The step size vector for cube. */ Vector3f m_min; /** The minimum point in the cube. */ Vector3i m_dim; /** The dimensions of the cube. */ Core::Array m_normals, m_vertices; std::vector gridEdges; // size (m_dim.y() * m_dim.z()) std::vector cubeCases; // size ((m_dim.x() - 1) * (m_dim.y() - 1) * (m_dim.z() - 1)) std::vector triCounter; // size ((m_dim.y() - 1) * (m_dim.z() - 1)) std::vector edgeCases; // size ((m_dim.x() - 1) * (m_dim.y()) * (m_dim.z())) Core::Array m_triangles; // triangles of a mesh int m_progmin; int m_progmax; /** * These are the lookup tables for flying edges. * Reference : * https://github.com/sandialabs/miniIsosurface/blob/master/flyingEdges/util/MarchingCubesTables.h */ static const unsigned char m_numTris[256]; static const bool m_isCut[256][12]; static const signed char m_caseTriangles[256][16]; static const unsigned char m_edgeVertices[12][2]; }; } // End namespace QtGui } // End namespace Avogadro #endif // AVOGADRO_QTGUI_MESHGENERATOR_H avogadrolibs-1.101.0/avogadro/qtgui/molecule.cpp000066400000000000000000000226631506155467400216200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molecule.h" #include "rwmolecule.h" #include // for HTML-formatted formulas #include namespace Avogadro::QtGui { using std::swap; Molecule::Molecule(QObject* p) : QObject(p), Core::Molecule(), m_undoMolecule(new RWMolecule(*this, this)) { m_undoMolecule->setInteractive(false); } Molecule::Molecule(const Molecule& other) : QObject(), Core::Molecule(other), m_undoMolecule(new RWMolecule(*this, this)) { m_undoMolecule->setInteractive(false); // Now assign the unique ids for (Index i = 0; i < atomCount(); i++) m_atomUniqueIds.push_back(i); for (Index i = 0; i < bondCount(); i++) m_bondUniqueIds.push_back(i); } Molecule::Molecule(const Core::Molecule& other) : QObject(), Core::Molecule(other) { // Now assign the unique ids for (Index i = 0; i < atomCount(); i++) m_atomUniqueIds.push_back(i); for (Index i = 0; i < bondCount(); i++) m_bondUniqueIds.push_back(i); } Molecule& Molecule::operator=(const Molecule& other) { // Call the base classes assignment operator Core::Molecule::operator=(other); // Copy over the unique ids m_atomUniqueIds = other.m_atomUniqueIds; m_bondUniqueIds = other.m_bondUniqueIds; return *this; } Molecule& Molecule::operator=(const Core::Molecule& other) { // Call the base classes assignment operator Core::Molecule::operator=(other); // Reset the unique ids. m_atomUniqueIds.clear(); for (Index i = 0; i < atomCount(); ++i) m_atomUniqueIds.push_back(i); m_bondUniqueIds.clear(); for (Index i = 0; i < bondCount(); ++i) m_bondUniqueIds.push_back(i); return *this; } Molecule::~Molecule() {} Molecule::AtomType Molecule::addAtom(unsigned char number) { m_atomUniqueIds.push_back(atomCount()); AtomType a = Core::Molecule::addAtom(number); return a; } Molecule::AtomType Molecule::addAtom(unsigned char number, Index uniqueId) { if (uniqueId >= static_cast(m_atomUniqueIds.size()) || m_atomUniqueIds[uniqueId] != MaxIndex) { return AtomType(); } m_atomUniqueIds[uniqueId] = atomCount(); AtomType a = Core::Molecule::addAtom(number); return a; } Molecule::AtomType Molecule::addAtom(unsigned char number, Vector3 position3d, Index uniqueId) { if (uniqueId >= static_cast(m_atomUniqueIds.size())) { m_atomUniqueIds.push_back(atomCount()); return Core::Molecule::addAtom(number, position3d); } else { auto atom = Molecule::addAtom(number, uniqueId); if (atom.isValid()) atom.setPosition3d(position3d); return atom; } } bool Molecule::removeAtom(Index index) { if (index >= atomCount()) return false; Index uniqueId = findAtomUniqueId(index); if (uniqueId == MaxIndex) return false; // Unique ID of an atom that was removed: m_atomUniqueIds[uniqueId] = MaxIndex; auto newSize = static_cast(atomCount() - 1); // Before removing the atom we must first remove any bonds to it. Core::Molecule::removeAtom(index); if (index != newSize) { // movedAtomUID uniqueId = findAtomUniqueId(newSize); assert(uniqueId != MaxIndex); m_atomUniqueIds[uniqueId] = index; } return true; } bool Molecule::removeAtom(const AtomType& atom_) { return removeAtom(atom_.index()); } Molecule::AtomType Molecule::atomByUniqueId(Index uniqueId) { if (uniqueId >= static_cast(m_atomUniqueIds.size()) || m_atomUniqueIds[uniqueId] == MaxIndex) { return AtomType(); } else { return AtomType(this, m_atomUniqueIds[uniqueId]); } } Index Molecule::atomUniqueId(const AtomType& a) const { if (a.molecule() != this) return MaxIndex; return findAtomUniqueId(a.index()); } Index Molecule::atomUniqueId(Index a) const { return findAtomUniqueId(a); } Molecule::BondType Molecule::addBond(const AtomType& a, const AtomType& b, unsigned char order) { m_bondUniqueIds.push_back(bondCount()); assert(a.isValid() && a.molecule() == this); assert(b.isValid() && b.molecule() == this); BondType bond_ = Core::Molecule::addBond(a.index(), b.index(), order); return bond_; } Molecule::BondType Molecule::addBond(Avogadro::Index atomId1, Avogadro::Index atomId2, unsigned char order) { m_bondUniqueIds.push_back(bondCount()); return Core::Molecule::addBond(atomId1, atomId2, order); } void Molecule::addBonds(const Core::Array>& bonds, const Core::Array& orders) { assert(orders.size() == bonds.size()); for (Index i = 0; i < orders.size(); ++i) { addBond(bonds[i].first, bonds[i].second, orders[i]); } } void Molecule::swapBond(Index a, Index b) { Index uniqueA = findBondUniqueId(a); Index uniqueB = findBondUniqueId(b); assert(uniqueA != MaxIndex && uniqueB != MaxIndex); swap(m_bondUniqueIds[uniqueA], m_bondUniqueIds[uniqueB]); Core::Molecule::swapBond(a, b); } void Molecule::swapAtom(Index a, Index b) { if (a == b) { return; } Index uniqueA = findAtomUniqueId(a); Index uniqueB = findAtomUniqueId(b); assert(uniqueA != MaxIndex && uniqueB != MaxIndex); swap(m_atomUniqueIds[uniqueA], m_atomUniqueIds[uniqueB]); Core::Molecule::swapAtom(a, b); } Molecule::BondType Molecule::addBond(Index a, Index b, unsigned char order, Index uniqueId) { if (uniqueId >= static_cast(m_bondUniqueIds.size()) || m_bondUniqueIds[uniqueId] != MaxIndex) { return BondType(); } m_bondUniqueIds[uniqueId] = bondCount(); return Core::Molecule::addBond(a, b, order); } Molecule::BondType Molecule::addBond(const AtomType& a, const AtomType& b, unsigned char order, Index uniqueId) { if (uniqueId >= static_cast(m_bondUniqueIds.size()) || m_bondUniqueIds[uniqueId] != MaxIndex) { return BondType(); } m_bondUniqueIds[uniqueId] = bondCount(); return Core::Molecule::addBond(a, b, order); } bool Molecule::removeBond(Index index) { if (index >= bondCount()) return false; Index uniqueId = findBondUniqueId(index); if (uniqueId == MaxIndex) return false; m_bondUniqueIds[uniqueId] = MaxIndex; // Unique ID of a bond that was removed. auto newSize = static_cast(bondCount() - 1); if (index != newSize) { Index movedBondUID = findBondUniqueId(newSize); assert(movedBondUID != MaxIndex); m_bondUniqueIds[movedBondUID] = index; } Core::Molecule::removeBond(index); return true; } bool Molecule::removeBond(const BondType& bond_) { return removeBond(bond_.index()); } bool Molecule::removeBond(const AtomType& a, const AtomType& b) { return removeBond(bond(a, b).index()); } bool Molecule::removeBond(Index a, Index b) { return removeBond(bond(a, b).index()); } Molecule::BondType Molecule::bondByUniqueId(Index uniqueId) { if (uniqueId >= static_cast(m_bondUniqueIds.size()) || m_bondUniqueIds[uniqueId] == MaxIndex) { return BondType(); } else { return BondType(this, static_cast(m_bondUniqueIds[uniqueId])); } } Index Molecule::bondUniqueId(const BondType& b) const { if (b.molecule() != this) return MaxIndex; return findBondUniqueId(b.index()); } Index Molecule::bondUniqueId(Index b) const { return findBondUniqueId(b); } void Molecule::emitChanged(unsigned int change) { if (change != NoChange) emit changed(change); } void Molecule::emitUpdate() const { emit update(); } Index Molecule::findAtomUniqueId(Index index) const { for (Index i = 0; i < static_cast(m_atomUniqueIds.size()); ++i) if (m_atomUniqueIds[i] == index) return i; return MaxIndex; } Index Molecule::findBondUniqueId(Index index) const { for (Index i = 0; i < static_cast(m_bondUniqueIds.size()); ++i) if (m_bondUniqueIds[i] == index) return i; return MaxIndex; } RWMolecule* Molecule::undoMolecule() { return m_undoMolecule; } QString Molecule::formattedFormula() const { QString formula = QString::fromStdString(this->formula()); QRegularExpression digitParser("(\\d+)"); QRegularExpressionMatchIterator i = digitParser.globalMatch(formula); unsigned int offset = 0; while (i.hasNext()) { const QRegularExpressionMatch match = i.next(); QString digits = match.captured(1); formula.replace(match.capturedStart(1) + offset, digits.size(), QString("%1").arg(digits)); offset += 11; // length of ... } // add total charge as a superscript int charge = totalCharge(); if (charge == -1) formula += QString("-"); else if (charge < -1) formula += QString("%1").arg(charge); else if (charge == 1) formula += QString("+"); else if (charge > 1) formula += QString("+%1").arg(charge); // add doublet or triplet for spin multiplicity as radical dot int spinMultiplicity = totalSpinMultiplicity(); if (spinMultiplicity == 2) formula += ""; else if (spinMultiplicity == 3) formula += "••"; return formula; } bool Molecule::isInteractive() const { if (m_undoMolecule == nullptr) return false; return m_undoMolecule->isInteractive(); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/molecule.h000066400000000000000000000213521506155467400212570ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_MOLECULE_H #define AVOGADRO_QTGUI_MOLECULE_H #include "avogadroqtguiexport.h" #include "persistentatom.h" #include "persistentbond.h" #include #include #include #include namespace Avogadro { namespace QtGui { class Mesh; class RWMolecule; /** * @class Molecule molecule.h * @brief A QObject derived molecule object with signals/slots. */ class AVOGADROQTGUI_EXPORT Molecule : public QObject, public Core::Molecule { Q_OBJECT public: /** Typedef for Atom class. */ typedef Core::Molecule::AtomType AtomType; /** Typedef for PersistentAtom class. */ typedef PersistentAtom PersistentAtomType; /** Typedef for Bond class. */ typedef Core::Molecule::BondType BondType; /** Typedef for PersistentBond class. */ typedef PersistentBond PersistentBondType; Molecule(QObject* parent_ = nullptr); ~Molecule() override; /** copy constructor */ Molecule(const Molecule& other); /** copy constructor to copy data from base instance */ Molecule(const Core::Molecule& other); /** Assignment operator */ Molecule& operator=(const Molecule& other); /** Assignment operator to copy data from base instance */ Molecule& operator=(const Core::Molecule& other); /** * \enum MoleculeChange *Enumeration of change types that can be given. */ enum MoleculeChange { /** Useful for initializing MoleculeChange variables. */ NoChange = 0, /** Object types that can be changed. */ Atoms = 1, Bonds = 2, UnitCell = 4, Selection = 8, Layers = 16, Properties = 32, Constraints = 64, /** Operations that can affect the above types. */ Added = 1024, Removed = 2048, Modified = 4096 }; Q_DECLARE_FLAGS(MoleculeChanges, MoleculeChange) /** * Add an atom with @p atomicNumber to the molecule. * @return The atom created. */ AtomType addAtom(unsigned char atomicNumber) override; /** * Add an atom with @p atomicNumber and @p uniqueId to the molecule. * @return The atom created. This can be invalid if the unique ID was already * in use. */ virtual AtomType addAtom(unsigned char atomicNumber, Index uniqueId); AtomType addAtom(unsigned char number, Vector3 position3d, Index uniqueId = MaxIndex); /** * @brief Remove the specified atom from the molecule. * @param index The index of the atom to be removed. * @return True on success, false if the atom was not found. */ bool removeAtom(Index index) override; /** * @brief Remove the specified atom from the molecule. * @param atom The atom to be removed. * @return True on success, false if the atom was not found. * @overload */ bool removeAtom(const AtomType& atom) override; /** * @brief Get the atom referenced by the @p uniqueId, the isValid method * should be queried to ensure the id still referenced a valid atom. * @param uniqueId The unique identifier for the atom. * @return An Atom object, check it is valid before using it. */ AtomType atomByUniqueId(Index uniqueId); /** * @brief Get the unique ID of the atom, this will uniquely reference the atom * as long as it exists. * @param atom The atom to obtain the unique ID of. * @return The unique identifier for the atom, MaxIndex if the atom is invalid * or does not belong to this molecule. * @{ */ Index atomUniqueId(const AtomType& atom) const; Index atomUniqueId(Index atom) const; /** @} */ Core::Array& atomUniqueIds() { return m_atomUniqueIds; } /** * @brief Add a bond between the specified atoms. * @param a The first atom in the bond. * @param b The second atom in the bond. * @param bondOrder The order of the bond. * @return The bond created. */ BondType addBond(const AtomType& a, const AtomType& b, unsigned char bondOrder = 1) override; /** * @brief Add a bond between the specified atoms. * @param atomId1 The index of the first atom in the bond. * @param atomId2 The index of the second atom in the bond. * @param bondOrder The order of the bond. * @return The bond created. */ BondType addBond(Index atomId1, Index atomId2, unsigned char bondOrder = 1) override; void addBonds(const Core::Array>& bonds, const Core::Array& orders); /** * @brief Add a bond between the specified atoms. * @param a The first atom in the bond. * @param b The second atom in the bond. * @param bondOrder The order of the bond. * @param uniqueId The unique ID to use for the bond. * @return The bond created. This can be invalid if the unique ID was already * in use. */ virtual BondType addBond(const AtomType& a, const AtomType& b, unsigned char bondOrder, Index uniqueId); /** * @brief Add a bond between the specified atoms. * @param a The index of the first atom in the bond. * @param b The index of the second atom in the bond. * @param bondOrder The order of the bond. * @param uniqueId The unique ID to use for the bond. * @return The bond created. This can be invalid if the unique ID was already * in use. */ virtual BondType addBond(Index atomId1, Index atomId2, unsigned char bondOrder, Index uniqueId); /** * @brief Remove the specified bond. * @param index The index of the bond to be removed. * @return True on success, false if the bond was not found. */ bool removeBond(Index index) override; /** * @brief Remove the specified bond. * @param bond The bond to be removed. * @return True on success, false if the bond was not found. * @overload */ bool removeBond(const BondType& bond) override; /** * @brief Remove the specified bond. * @param atom1 One atom in the bond. * @param atom2 The other atom in the bond. * @return True on success, false if the bond was not found. * @overload * @{ */ bool removeBond(const AtomType& atom1, const AtomType& atom2) override; bool removeBond(Index atom1, Index atom2) override; /** @} */ /** * @brief Get the bond referenced by the @p uniqueId, the isValid method * should be queried to ensure the id still referenced a valid bond. * @param uniqueId The unique identifier for the bond. * @return A Bond object, check it is valid before using it. */ BondType bondByUniqueId(Index uniqueId); /** * @brief Get the unique ID of the bond, this will uniquely reference the bond * as long as it exists. * @param bond The bond to obtain the unique ID of. * @return The unique identifier for the bond, MaxIndex if the bond is invalid * or does not belong to this molecule. * @{ */ Index bondUniqueId(const BondType& bond) const; Index bondUniqueId(Index bond) const; /** @} */ Core::Array& bondUniqueIds() { return m_bondUniqueIds; } Index findAtomUniqueId(Index index) const; Index findBondUniqueId(Index index) const; RWMolecule* undoMolecule(); /** * @brief Get the formatted chemical formula for the molecule. * @return The formatted chemical formula in HTML format. * This will include subscripts for atom counts and superscripts for charge * and spin multiplicity. */ QString formattedFormula() const; bool isInteractive() const; void swapBond(Index a, Index b); void swapAtom(Index a, Index b); public slots: /** * @brief Force the molecule to emit the changed() signal. * @param change See changed(). */ void emitChanged(unsigned int change); /** * @brief Request an update through the update() signal */ void emitUpdate() const; signals: /** * @brief Indicates that the molecule has changed. * @param change Use the MoleculeChange enum to check what has changed. * * The @p change variable indicates what has changed, i.e. if * change & Atoms == true then atoms were changed in some way, and if * change & Removed == true then one or more atoms were removed. */ void changed(unsigned int change) const; /** * @brief Request an update of the molecule. * (e.g., re-compute properties) */ void update() const; private: Core::Array m_atomUniqueIds; Core::Array m_bondUniqueIds; friend class RWMolecule; RWMolecule* m_undoMolecule; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Molecule::MoleculeChanges) } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_MOLECULE_H avogadrolibs-1.101.0/avogadro/qtgui/moleculemodel.cpp000066400000000000000000000124651506155467400226400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "moleculemodel.h" #include "molecule.h" #include #include #include #include namespace Avogadro::QtGui { MoleculeModel::MoleculeModel(QObject* p) : QAbstractItemModel(p), m_activeMolecule(nullptr) { } QModelIndex MoleculeModel::parent(const QModelIndex&) const { return QModelIndex(); } int MoleculeModel::rowCount(const QModelIndex& p) const { if (p.isValid()) return 0; else return m_molecules.size(); } int MoleculeModel::columnCount(const QModelIndex&) const { return 2; } Qt::ItemFlags MoleculeModel::flags(const QModelIndex& idx) const { if (idx.column() == 0) return static_cast(Qt::ItemIsEditable | Qt::ItemIsEnabled); else return Qt::ItemIsEnabled; } bool MoleculeModel::setData(const QModelIndex& idx, const QVariant& value, int role) { if (!idx.isValid() || idx.column() > 2) return false; auto* object = static_cast(idx.internalPointer()); auto* mol = qobject_cast(object); if (!mol) return false; switch (role) { case Qt::CheckStateRole: m_activeMolecule = mol; if (value == Qt::Checked /*&& !item->isEnabled()*/) { // item->setEnabled(true); emit moleculeStateChanged(mol); } else if (value == Qt::Unchecked /*&& item->isEnabled()*/) { // item->setEnabled(false); emit moleculeStateChanged(mol); } emit dataChanged(idx, idx); return true; case Qt::EditRole: if (!value.toString().isEmpty()) { // don't set an empty name mol->setData("name", std::string(value.toString().toLatin1())); emit dataChanged(idx, idx); } return true; } return false; } QVariant MoleculeModel::data(const QModelIndex& idx, int role) const { if (!idx.isValid() || idx.column() > 2) return QVariant(); auto* object = static_cast(idx.internalPointer()); auto* mol = qobject_cast(object); if (!mol) return QVariant(); if (idx.column() == 0) { switch (role) { case Qt::DisplayRole: { std::string name = tr("Untitled").toStdString(); if (mol && mol->hasData("name") && !(mol->data("name").toString().empty())) { // don't set an empty name name = mol->data("name").toString(); } else if (mol && mol->hasData("fileName")) { name = QFileInfo(mol->data("fileName").toString().c_str()) .fileName() .toStdString(); } if (mol) return QString("%1 (%2)") .arg(QString::fromStdString(name)) .arg(mol->formattedFormula()); else return tr("Edit molecule"); } case Qt::EditRole: return mol->data("name").toString().c_str(); case Qt::ToolTipRole: if (mol->hasData("fileName")) return mol->data("fileName").toString().c_str(); return tr("Not saved"); case Qt::WhatsThisRole: return mol->formula().c_str(); case Qt::ForegroundRole: if (mol == m_activeMolecule) return QVariant(QColor(Qt::red)); else { const QPalette defaultPalette; return QVariant(defaultPalette.color(QPalette::WindowText)); } default: return QVariant(); } } else if (idx.column() == 1) { if (role == Qt::DecorationRole) return QIcon::fromTheme("document-close"); } return QVariant(); } QModelIndex MoleculeModel::index(int row, int column, const QModelIndex& p) const { if (!p.isValid()) if (row >= 0 && row < m_molecules.size()) return createIndex(row, column, m_molecules[row]); return QModelIndex(); } void MoleculeModel::clear() { m_molecules.clear(); } QList MoleculeModel::molecules() const { return m_molecules; } QList MoleculeModel::activeMolecules() const { QList result; foreach (Molecule* mol, m_molecules) { if (true) result << mol; } return result; } void MoleculeModel::setActiveMolecule(QObject* active) { if (m_activeMolecule == active) return; m_activeMolecule = active; emit dataChanged(createIndex(0, 0), createIndex(m_molecules.size(), 0)); } void MoleculeModel::addItem(Molecule* item) { if (!m_molecules.contains(item)) { int row = m_molecules.size(); beginInsertRows(QModelIndex(), row, row); m_molecules.append(item); item->setParent(this); endInsertRows(); } } void MoleculeModel::removeItem(Molecule* item) { if (m_molecules.contains(item)) { int row = m_molecules.indexOf(item); beginRemoveRows(QModelIndex(), row, row); m_molecules.removeAt(row); // Do we want strong ownership of molecules? item->deleteLater(); endRemoveRows(); } } void MoleculeModel::itemChanged() { auto* item = qobject_cast(sender()); if (item) { int row = m_molecules.indexOf(item); if (row >= 0) emit dataChanged(createIndex(row, 0), createIndex(row, 0)); } } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/moleculemodel.h000066400000000000000000000041121506155467400222730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_MOLECULEMODEL_H #define AVOGADRO_QTGUI_MOLECULEMODEL_H #include "avogadroqtguiexport.h" #include #include namespace Avogadro { namespace QtGui { class Molecule; struct MoleculeSystem { MoleculeSystem() : m_molecule(nullptr), m_dirty(false), m_active(false) {} Molecule* m_molecule; Eigen::Affine3f m_modelView; bool m_dirty; bool m_active; }; /** * @class MoleculeModel moleculemodel.h * @brief A model containing molecules. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT MoleculeModel : public QAbstractItemModel { Q_OBJECT public: explicit MoleculeModel(QObject* p = nullptr); QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; bool setData(const QModelIndex& index, const QVariant& value, int role) override; QVariant data(const QModelIndex& index, int role) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; void clear(); QList molecules() const; QList activeMolecules() const; QObject* activeMolecule() const { return m_activeMolecule; } signals: void moleculeStateChanged(Avogadro::QtGui::Molecule*); public slots: void setActiveMolecule(QObject* active); void addItem(Avogadro::QtGui::Molecule* item); void removeItem(Avogadro::QtGui::Molecule* item); void itemChanged(); private: QList m_molecules; QObject* m_activeMolecule; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_MOLECULEMODEL_H avogadrolibs-1.101.0/avogadro/qtgui/multiviewwidget.cpp000066400000000000000000000164231506155467400232410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "multiviewwidget.h" #include "containerwidget.h" #include "viewfactory.h" #include #include #include #include #include #include namespace Avogadro::QtGui { class ActiveWidgetFilter : public QObject { Q_OBJECT public: ActiveWidgetFilter(MultiViewWidget* p = nullptr) : QObject(p), m_widget(p) {} signals: void activeWidget(QWidget* widget); protected: bool eventFilter(QObject* obj, QEvent* e) override { Q_ASSERT(m_widget); if (e->type() == QEvent::MouseButtonPress) { QWidget* w = qobject_cast(obj); if (w && w != m_widget->activeWidget()) { m_widget->setActiveWidget(w); return true; } } return QObject::eventFilter(obj, e); } MultiViewWidget* m_widget; }; MultiViewWidget::MultiViewWidget(QWidget* p, Qt::WindowFlags f) : QWidget(p, f), m_factory(nullptr), m_activeWidget(nullptr), m_activeFilter(new ActiveWidgetFilter(this)) { } MultiViewWidget::~MultiViewWidget() {} void MultiViewWidget::addWidget(QWidget* widget) { if (widget) { ContainerWidget* container = createContainer(widget); m_children << container; if (m_children.size() == 1) { auto* widgetLayout = qobject_cast(layout()); if (!widgetLayout) { widgetLayout = new QVBoxLayout; widgetLayout->setContentsMargins(0, 0, 0, 0); setLayout(widgetLayout); } widgetLayout->addWidget(container); } widget->installEventFilter(m_activeFilter); setActiveWidget(widget); } } QWidget* MultiViewWidget::activeWidget() { return m_activeWidget; } void MultiViewWidget::setActiveWidget(QWidget* widget) { if (m_activeWidget != widget) { ContainerWidget* container(nullptr); if (m_activeWidget) container = qobject_cast(m_activeWidget->parentWidget()); if (container) container->setActive(false); m_activeWidget = widget; container = nullptr; if (widget) container = qobject_cast(widget->parentWidget()); if (container) container->setActive(true); emit activeWidgetChanged(widget); } } void MultiViewWidget::splitHorizontal() { auto* container = qobject_cast(sender()); if (container) splitView(Qt::Horizontal, container); } void MultiViewWidget::splitVertical() { auto* container = qobject_cast(sender()); if (container) splitView(Qt::Vertical, container); } void MultiViewWidget::createView() { auto* button = qobject_cast(sender()); if (m_factory && button && button->parentWidget() && button->parentWidget()->parentWidget()) { QWidget* optionsWidget = button->parentWidget(); auto* container = qobject_cast(optionsWidget->parentWidget()); if (container) { auto widget = m_factory->createView(button->property("name").toString()); if (widget) { widget->installEventFilter(m_activeFilter); container->layout()->removeWidget(optionsWidget); container->layout()->addWidget(widget); optionsWidget->deleteLater(); setActiveWidget(widget); } } } } void MultiViewWidget::removeView() { auto* container = qobject_cast(sender()); if (container) { auto* splitter = qobject_cast(container->parent()); if (splitter && splitter->count() == 2) { // Get its parent, and insert the other widget into it, delete this // widget. auto* splitterParent = qobject_cast(splitter->parent()); QWidget* moveWidget = splitter->widget(0); if (moveWidget == container) moveWidget = splitter->widget(1); setActiveWidget(moveWidget); if (splitterParent) { int idx = splitterParent->indexOf(splitter); splitterParent->insertWidget(idx, moveWidget); splitter->deleteLater(); } else if (splitter->parent() == this) { // No more splits - back to single view widget. auto* layoutParent = qobject_cast(layout()); if (layoutParent) { layoutParent->addWidget(moveWidget); layoutParent->removeWidget(splitter); splitter->deleteLater(); } } } else if (container->parent() == this) { // Delete the current container, and create the option container. auto* vLayout = qobject_cast(layout()); container->deleteLater(); ContainerWidget* newContainer = createContainer(); vLayout->addWidget(newContainer); setActiveWidget(newContainer); } } } ContainerWidget* MultiViewWidget::createContainer(QWidget* widget) { auto* container = new ContainerWidget; connect(container, SIGNAL(splitHorizontal()), SLOT(splitHorizontal())); connect(container, SIGNAL(splitVertical()), SLOT(splitVertical())); connect(container, SIGNAL(closeView()), SLOT(removeView())); if (widget) { container->setViewWidget(widget); } // If we have a factory, then create the options widget too! else if (m_factory) { auto* optionsWidget = new QWidget; auto* v = new QVBoxLayout; optionsWidget->setLayout(v); v->addStretch(); foreach (const QString& name, m_factory->views()) { auto* button = new QPushButton(name); button->setProperty("name", name); button->setToolTip(tr("Create a new view")); connect(button, SIGNAL(clicked()), SLOT(createView())); auto* h = new QHBoxLayout; h->addStretch(); h->addWidget(button); h->addStretch(); v->addLayout(h); } v->addStretch(); container->layout()->addWidget(optionsWidget); } return container; } void MultiViewWidget::splitView(Qt::Orientation orient, ContainerWidget* container) { auto* widgetLayout = qobject_cast(container->parent()); auto* split = qobject_cast(container->parent()); if (!widgetLayout) if (container->parent() == this) widgetLayout = qobject_cast(layout()); if (widgetLayout) { auto* splitter = new QSplitter(orient, this); widgetLayout->removeWidget(container); widgetLayout->addWidget(splitter); splitter->addWidget(container); container = createContainer(); splitter->addWidget(container); QList sizes; int s = Qt::Horizontal ? splitter->size().width() / 2 : splitter->size().height() / 2; sizes << s << s; splitter->setSizes(sizes); } else if (split) { auto* splitter = new QSplitter(orient, this); int idx = split->indexOf(container); splitter->addWidget(container); container = createContainer(); splitter->addWidget(container); split->insertWidget(idx, splitter); QList sizes; int s = Qt::Horizontal ? splitter->size().width() / 2 : splitter->size().height() / 2; sizes << s << s; splitter->setSizes(sizes); } } } // namespace Avogadro::QtGui #include "multiviewwidget.moc" avogadrolibs-1.101.0/avogadro/qtgui/multiviewwidget.h000066400000000000000000000036431506155467400227060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_MULTIVIEWWIDGET_H #define AVOGADRO_QTGUI_MULTIVIEWWIDGET_H #include "avogadroqtguiexport.h" #include namespace Avogadro { namespace QtGui { class ContainerWidget; class ViewFactory; class ActiveWidgetFilter; /** * @class MultiViewWidget multiviewwidget.h * @brief A widget that contains other views, taking on responsibility for * defining the active view, the layout of the subviews and ownership of the * widgets. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT MultiViewWidget : public QWidget { Q_OBJECT public: explicit MultiViewWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~MultiViewWidget() override; void addWidget(QWidget* widget); /** * @brief Get the currently active widget. * @return The active widget. */ QWidget* activeWidget(); void setActiveWidget(QWidget* widget); void setFactory(ViewFactory* factory) { m_factory = factory; } void setDefaultView(const QString& viewName) { m_defaultView = viewName; } signals: void activeWidgetChanged(QWidget* widget); public slots: void splitHorizontal(); void splitVertical(); void createView(); void removeView(); private: QList m_children; ViewFactory* m_factory; QString m_defaultView; QWidget* m_activeWidget; ActiveWidgetFilter* m_activeFilter; ContainerWidget* createContainer(QWidget* widget = nullptr); void splitView(Qt::Orientation orient, ContainerWidget* container); }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_MULTIVIEWWIDGET_H avogadrolibs-1.101.0/avogadro/qtgui/periodictablescene_p.cpp000066400000000000000000000107511506155467400241510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "periodictablescene_p.h" #include "elementdetail_p.h" #include "elementitem_p.h" #include "elementtranslator.h" #include #include #include #include #include namespace Avogadro::QtGui { PeriodicTableScene::PeriodicTableScene(QObject* parent_) : QGraphicsScene(parent_) { int width_ = 26; int height_ = 26; m_detail = new ElementDetail(1); m_detail->setPos(6.5 * width_, 0.75 * height_); addItem(m_detail); auto* item = new ElementItem(1); item->setPos(0 * width_, 0 * height_); addItem(item); item = new ElementItem(2); item->setPos(17 * width_, 0 * height_); addItem(item); item = new ElementItem(3); item->setPos(0 * width_, 1 * height_); addItem(item); item = new ElementItem(4); item->setPos(1 * width_, 1 * height_); addItem(item); item = new ElementItem(5); item->setPos(12 * width_, 1 * height_); addItem(item); item = new ElementItem(6); item->setPos(13 * width_, 1 * height_); addItem(item); item = new ElementItem(7); item->setPos(14 * width_, 1 * height_); addItem(item); item = new ElementItem(8); item->setPos(15 * width_, 1 * height_); addItem(item); item = new ElementItem(9); item->setPos(16 * width_, 1 * height_); addItem(item); item = new ElementItem(10); item->setPos(17 * width_, 1 * height_); addItem(item); item = new ElementItem(11); item->setPos(0 * width_, 2 * height_); addItem(item); item = new ElementItem(12); item->setPos(1 * width_, 2 * height_); addItem(item); item = new ElementItem(13); item->setPos(12 * width_, 2 * height_); addItem(item); item = new ElementItem(14); item->setPos(13 * width_, 2 * height_); addItem(item); item = new ElementItem(15); item->setPos(14 * width_, 2 * height_); addItem(item); item = new ElementItem(16); item->setPos(15 * width_, 2 * height_); addItem(item); item = new ElementItem(17); item->setPos(16 * width_, 2 * height_); addItem(item); item = new ElementItem(18); item->setPos(17 * width_, 2 * height_); addItem(item); int element = 19; for (int i = 3; i < 5; ++i) { for (int j = 0; j < 18; ++j) { item = new ElementItem(element++); item->setPos(j * width_, i * height_); addItem(item); } } item = new ElementItem(element++); item->setPos(0 * width_, 5 * height_); addItem(item); item = new ElementItem(element++); item->setPos(1 * width_, 5 * height_); addItem(item); element = 71; for (int i = 2; i < 18; ++i) { item = new ElementItem(element++); item->setPos(i * width_, 5 * height_); addItem(item); } item = new ElementItem(element++); item->setPos(0 * width_, 6 * height_); addItem(item); item = new ElementItem(element++); item->setPos(1 * width_, 6 * height_); addItem(item); element = 103; // Goes up to element 118 for (int i = 2; i < 18; ++i) { item = new ElementItem(element++); item->setPos(i * width_, 6 * height_); addItem(item); } // Now for the weird ones at the bottom... element = 57; for (int i = 2; i < 16; ++i) { item = new ElementItem(element++); item->setPos(i * width_, 7.5 * height_); addItem(item); } element = 89; for (int i = 2; i < 16; ++i) { item = new ElementItem(element++); item->setPos(i * width_, 8.5 * height_); addItem(item); } } void PeriodicTableScene::mousePressEvent(QGraphicsSceneMouseEvent* event_) { if (event_->button() != Qt::LeftButton) return; QGraphicsItem* item = QGraphicsScene::itemAt(event_->scenePos(), QTransform()); if (item->data(0).toInt() > 0 && item->data(0).toInt() < 119) { emit(elementChanged(item->data(0).toInt())); m_detail->setElement(item->data(0).toInt()); } QGraphicsScene::mousePressEvent(event_); } void PeriodicTableScene::changeElement(int element) { // Find the item to select foreach (QGraphicsItem* item, items()) { if (item->data(0).toInt() == element) item->setSelected(true); else item->setSelected(false); } // Emit a signal the element changed, and update the detail item. emit(elementChanged(element)); m_detail->setElement(element); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/periodictablescene_p.h000066400000000000000000000027341506155467400236200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_PERIODICTABLESCENE_P_H #define AVOGADRO_QTGUI_PERIODICTABLESCENE_P_H #include namespace Avogadro { namespace QtGui { class ElementDetail; /** * @class PeriodicTableScene * @internal * @author Marcus D. Hanwell * @brief This class encapsulates the scene, all items are contained in it. * * This class implements a QGraphicsScene that holds all of the element items. * Any items owned by this class are automatically deleted by it. */ class PeriodicTableScene : public QGraphicsScene { Q_OBJECT public: /** Constructor. */ explicit PeriodicTableScene(QObject* parent = nullptr); signals: /** * This signal is emitted when an element item is clicked. */ void elementChanged(int element); public slots: /** * This slot is called when an element is changed (e.g., by keyboard or code). */ void changeElement(int element); protected: /** * Handles the mouse press events to change the active element. */ void mousePressEvent(QGraphicsSceneMouseEvent* event) override; private: ElementDetail* m_detail; }; } // End namespace QtGui } // End namespace Avogadro #endif // AVOGADRO_QTGUI_PERIODICTABLESCENE_P_H avogadrolibs-1.101.0/avogadro/qtgui/periodictableview.cpp000066400000000000000000000102371506155467400235060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "periodictableview.h" #include "periodictablescene_p.h" #include #include #include namespace Avogadro::QtGui { using Core::Elements; PeriodicTableView::PeriodicTableView(QWidget* parent_) : QGraphicsView(parent_), m_element(6) // Everyone loves carbon. { // Make the periodic table view a standard dialog. setWindowFlags(Qt::Dialog); auto* table = new PeriodicTableScene; table->setSceneRect(-20, -20, 480, 260); table->setItemIndexMethod(QGraphicsScene::NoIndex); table->setBackgroundBrush(Qt::white); table->changeElement(m_element); setScene(table); setRenderHint(QPainter::Antialiasing); setWindowTitle(tr("Periodic Table")); resize(490, 270); connect(table, SIGNAL(elementChanged(int)), this, SLOT(elementClicked(int))); } PeriodicTableView::~PeriodicTableView() { delete scene(); } void PeriodicTableView::setElement(int element_) { m_element = element_; auto* table = qobject_cast(scene()); if (table) table->changeElement(element_); } void PeriodicTableView::elementClicked(int id) { m_element = id; emit(elementChanged(id)); } void PeriodicTableView::mouseDoubleClickEvent(QMouseEvent*) { close(); } void PeriodicTableView::clearKeyPressBuffer() { m_keyPressBuffer.clear(); } void PeriodicTableView::keyPressEvent(QKeyEvent* event_) { if (m_keyPressBuffer.isEmpty()) { // This is the first character typed. // Wait for 2 seconds, then clear the buffer, // this ensures we can get multi-character elements. QTimer::singleShot(2000, this, SLOT(clearKeyPressBuffer())); } if (event_->key() == Qt::Key_Escape || event_->key() == Qt::Key_Return || event_->key() == Qt::Key_Enter) { close(); return; } // check for arrow keys int elem = m_element; if (event_->key() == Qt::Key_Left) { elem -= 1; } else if (event_->key() == Qt::Key_Right) { elem += 1; } else if (event_->key() == Qt::Key_Up) { // What row are we? if (elem == 3) { // ignore other 2nd row elem = 1; } else if (elem >= 10 && elem <= 20) { elem -= 8; } else if (elem >= 21 && elem <= 30) { // nothing to do, top row of transition metals } else if (elem >= 31 && elem <= 56) { elem -= 18; // go up a row } else if (elem >= 57 && elem <= 70) { // nothing to do, top row of lanthanides } else if (elem >= 71 && elem <= 118) { elem -= 32; // go up a row } } else if (event_->key() == Qt::Key_Down) { // What row are we? if (elem == 1) { elem = 3; } else if (elem == 2) { elem = 10; } else if (elem >= 3 && elem <= 12) { elem += 8; // down one row } else if (elem >= 13 && elem <= 38) { elem += 18; // down one row } else if (elem >= 39 && elem <= 86) { elem += 32; // down one row } else if (elem >= 87 && elem <= 118) { // last row, nothing to do } } else { // This is a normal character. m_keyPressBuffer += event_->text(); // Try setting an element symbol from this string. elem = m_keyPressBuffer.toInt(); if (elem <= 0 || elem > 119) { // Not a valid number, have we tried 2- and 3-character symbols? if (m_keyPressBuffer.length() > 3) { clearKeyPressBuffer(); } else { // try parsing as a symbol .. first character should be uppercase m_keyPressBuffer[0] = m_keyPressBuffer[0].toUpper(); elem = static_cast( Elements::atomicNumberFromSymbol(m_keyPressBuffer.toLatin1().data())); } } } // got a valid symbol if (elem > 0 && elem < 119) setElement(elem); QGraphicsView::keyPressEvent(event_); } void PeriodicTableView::resizeEvent(QResizeEvent* e) { double scale_(double(e->size().width()) / 500.0); QTransform scaleTransform(QTransform::fromScale(scale_, scale_)); setTransform(scaleTransform); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/periodictableview.h000066400000000000000000000043751506155467400231610ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_PERIODICTABLEVIEW_H #define AVOGADRO_QTGUI_PERIODICTABLEVIEW_H #include "avogadroqtguiexport.h" #include namespace Avogadro { namespace QtGui { /** * @class PeriodicTableView periodictableview.h * * @author Marcus D. Hanwell * @brief This class implements the view of the periodic table showing all * elements. * * This is the class that actually draws the widget onto screen. This is * the class that should normally be instantiated in order to display a * Periodic Table. */ class AVOGADROQTGUI_EXPORT PeriodicTableView : public QGraphicsView { Q_OBJECT public: /** * Constructor - constructs a new PeriodicTableView with an internal instance * of PeriodicTableScene. */ explicit PeriodicTableView(QWidget* parent_ = nullptr); ~PeriodicTableView() override; /** * @return The currently selected element. */ int element() const { return m_element; } public slots: /** * @param element_ The currently selected element. */ void setElement(int element_); /** * This slot is called to clear the key buffer (e.g. after a delay in typing). */ void clearKeyPressBuffer(); private slots: /** * Use this slot to change the active element. */ void elementClicked(int element); signals: /** * Signal emitted when the active element in the PeriodicTableView changes. */ void elementChanged(int element); protected: /** * Double click event - select an element and hide the PeriodicTableView. */ void mouseDoubleClickEvent(QMouseEvent* event) override; /** * Handles the keyboard events to change the active element. */ void keyPressEvent(QKeyEvent* event_) override; /** * Handle resize events. */ void resizeEvent(QResizeEvent* event) override; private: /** * Proton number of the active element. */ int m_element; QString m_keyPressBuffer; }; } // namespace QtGui } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtgui/persistentatom.h000066400000000000000000000067651506155467400225460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_PERSISTENTATOM_H #define AVOGADRO_QTGUI_PERSISTENTATOM_H #include namespace Avogadro { namespace QtGui { /** * @class PersistentAtom persistentatom.h * @brief The PersistentAtom object provides a container for a persistent atom * reference that can be held onto. The atom() method gets the underlying atom * using the unique ID mechanism of the molecule. */ template class PersistentAtom { public: typedef Molecule_T MoleculeType; typedef typename Molecule_T::AtomType AtomType; /** * @brief Create a persistent atom, with the specified unique id. * @param m The molecule the persistent atom belongs to. * @param uniqueId The unique identifier for the atom. */ explicit PersistentAtom(MoleculeType* m = nullptr, Index uniqueId = MaxIndex) : m_molecule(m), m_uniqueId(uniqueId) { } /** * @brief Create a persistent atom from a standard atom object. * @param a The atom that a persistent reference should be created for. */ explicit PersistentAtom(const AtomType& a); /** * @brief Set the molecule and unique ID for the persistent object. * @param m The molecule that contains the atom. * @param uniqueId The unique ID of the atom. */ void set(MoleculeType* m, Index uniqueId); /** * @brief Set the persistent atom from a standard atom object. * @param a The atom that a persistent reference should be created for. */ void set(const AtomType& a); /** * @brief Reset the object to an invalid state. */ void reset(); /** * @return True if the persistent atom is valid. */ bool isValid() const; /** * @return The molecule the atom is a part of. */ MoleculeType* molecule() const { return m_molecule; } /** * @brief The persistent unique ID of the atom. * @return The unique ID of the atom. */ Index uniqueIdentifier() const { return m_uniqueId; } /** * @brief Obtain the atom being held by the persistent object. * @return A reference to the atom held by the object. */ AtomType atom() const; private: MoleculeType* m_molecule; Index m_uniqueId; }; template PersistentAtom::PersistentAtom(const AtomType& a) : m_molecule(dynamic_cast(a.molecule())) { m_uniqueId = m_molecule ? m_molecule->atomUniqueId(a) : MaxIndex; } template void PersistentAtom::set(MoleculeType* m, Index uniqueId) { m_molecule = m; m_uniqueId = uniqueId; } template void PersistentAtom::set(const AtomType& a) { m_molecule = dynamic_cast(a.molecule()); m_uniqueId = m_molecule ? m_molecule->atomUniqueId(a) : MaxIndex; } template void PersistentAtom::reset() { set(nullptr, MaxIndex); } template bool PersistentAtom::isValid() const { return atom().isValid(); } template typename Molecule_T::AtomType PersistentAtom::atom() const { return m_molecule ? m_molecule->atomByUniqueId(m_uniqueId) : AtomType(); } } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_PERSISTENTATOM_H avogadrolibs-1.101.0/avogadro/qtgui/persistentbond.h000066400000000000000000000067651506155467400225300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_PERSISTENTBOND_H #define AVOGADRO_QTGUI_PERSISTENTBOND_H #include namespace Avogadro { namespace QtGui { /** * @class PersistentBond persistentbond.h * @brief The PersistentBond object provides a container for a persistent bond * reference that can be held onto. The bond() method gets the underlying bond * using the unique ID mechanism of the molecule. */ template class PersistentBond { public: typedef Molecule_T MoleculeType; typedef typename Molecule_T::BondType BondType; /** * @brief Create a persistent bond, with the specified unique id. * @param m The molecule the persistent bond belongs to. * @param uniqueId The unique identifier for the bond. */ explicit PersistentBond(MoleculeType* m = nullptr, Index uniqueId = MaxIndex) : m_molecule(m), m_uniqueId(uniqueId) { } /** * @brief Create a persistent bond from a standard bond object. * @param b The bond that a persistent reference should be created for. */ explicit PersistentBond(const BondType& b); /** * @brief Set the molecule and unique ID for the persistent object. * @param m The molecule that contains the bond. * @param uniqueId The unique ID of the bond. */ void set(MoleculeType* m, Index uniqueId); /** * @brief Set the persistent bond from a standard bond object. * @param b The bond that a persistent reference should be created for. */ void set(const BondType& b); /** * @brief Reset the object to an invalid state. */ void reset(); /** * @return True if the persistent bond is valid. */ bool isValid() const; /** * @return The molecule the bond is a part of. */ MoleculeType* molecule() const { return m_molecule; } /** * @brief The persistent unique ID of the bond. * @return The unique ID of the bond. */ Index uniqueIdentifier() const { return m_uniqueId; } /** * @brief Obtain the bond being held by the persistent object. * @return A reference to the bond held by the object. */ BondType bond() const; private: MoleculeType* m_molecule; Index m_uniqueId; }; template PersistentBond::PersistentBond(const BondType& b) : m_molecule(dynamic_cast(b.molecule())) { m_uniqueId = m_molecule ? m_molecule->bondUniqueId(b) : MaxIndex; } template void PersistentBond::set(MoleculeType* m, Index uniqueId) { m_molecule = m; m_uniqueId = uniqueId; } template void PersistentBond::set(const BondType& b) { m_molecule = dynamic_cast(b.molecule()); m_uniqueId = m_molecule ? m_molecule->bondUniqueId(b) : MaxIndex; } template void PersistentBond::reset() { set(nullptr, MaxIndex); } template bool PersistentBond::isValid() const { return bond().isValid(); } template typename Molecule_T::BondType PersistentBond::bond() const { return m_molecule ? m_molecule->bondByUniqueId(m_uniqueId) : BondType(); } } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_PERSISTENTBOND_H avogadrolibs-1.101.0/avogadro/qtgui/pluginlayermanager.cpp000066400000000000000000000103441506155467400236720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "pluginlayermanager.h" #include #include #include #include #include namespace Avogadro::QtGui { using std::string; using std::vector; PluginLayerManager::PluginLayerManager(const string& name) : m_name(name) {} PluginLayerManager::~PluginLayerManager() { for (auto& info : m_molToInfo) { auto itEnable = info.second->enable.find(m_name); if (itEnable != info.second->enable.end()) { info.second->enable.erase(itEnable); } auto itSettings = info.second->settings.find(m_name); if (itSettings != info.second->settings.end()) { info.second->settings.erase(itSettings); } } } bool PluginLayerManager::isEnabled() const { if (m_activeMolecule == nullptr || m_molToInfo[m_activeMolecule] == nullptr || m_molToInfo[m_activeMolecule]->enable.find(m_name) == m_molToInfo[m_activeMolecule]->enable.end()) { return false; } for (const auto& b : m_molToInfo[m_activeMolecule]->enable[m_name]) { if (b) { return true; } } return false; } bool PluginLayerManager::isActiveLayerEnabled() const { if (m_activeMolecule == nullptr || m_molToInfo[m_activeMolecule] == nullptr || m_molToInfo[m_activeMolecule]->enable.find(m_name) == m_molToInfo[m_activeMolecule]->enable.end()) { return false; } auto& molecule = m_molToInfo[m_activeMolecule]; size_t active = molecule->layer.activeLayer(); if (active < molecule->enable[m_name].size()) { return molecule->enable[m_name][active]; } return false; } void PluginLayerManager::setEnabled(bool enable) { if (m_activeMolecule == nullptr || m_molToInfo[m_activeMolecule] == nullptr) { return; } auto& molecule = m_molToInfo[m_activeMolecule]; auto it = molecule->enable.find(m_name); if (it == molecule->enable.end()) { molecule->enable[m_name] = vector(); } size_t qttyLayers = molecule->layer.layerCount(); if (molecule->enable[m_name].size() != qttyLayers) { molecule->enable[m_name].resize(qttyLayers, false); } size_t activeLayer = molecule->layer.activeLayer(); molecule->enable[m_name][activeLayer] = enable; } bool PluginLayerManager::atomEnabled(Index atom) const { if (m_activeMolecule == nullptr || m_molToInfo[m_activeMolecule] == nullptr || m_molToInfo[m_activeMolecule]->enable.find(m_name) == m_molToInfo[m_activeMolecule]->enable.end()) { return false; } auto& molecule = m_molToInfo[m_activeMolecule]; size_t layer = molecule->layer.getLayerID(atom); if (layer == MaxIndex) { return false; } return layer < molecule->enable[m_name].size() && molecule->enable[m_name][layer] && molecule->visible[layer]; } size_t PluginLayerManager::getLayerID(Index atom) const { assert(m_activeMolecule != nullptr); auto& molecule = m_molToInfo[m_activeMolecule]; assert(atom < molecule->layer.atomCount()); return molecule->layer.getLayerID(atom); } bool PluginLayerManager::atomEnabled(size_t layerFilter, Index atom) const { bool enabled = atomEnabled(atom); if (!enabled) { return false; } auto& molecule = m_molToInfo[m_activeMolecule]; size_t layer = molecule->layer.getLayerID(atom); return layer == layerFilter; } bool PluginLayerManager::bondEnabled(Index atom1, Index atom2) const { return atomEnabled(atom1) || atomEnabled(atom2); } bool PluginLayerManager::activeLayerLocked() const { assert(m_activeMolecule != nullptr); auto& molecule = m_molToInfo[m_activeMolecule]; size_t active = molecule->layer.activeLayer(); return molecule->locked[active]; } bool PluginLayerManager::atomLocked(size_t atom) const { assert(m_activeMolecule != nullptr); auto& molecule = m_molToInfo[m_activeMolecule]; size_t layer = molecule->layer.getLayerID(atom); return molecule->locked[layer]; } size_t PluginLayerManager::layerCount() const { return LayerManager::layerCount(); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/pluginlayermanager.h000066400000000000000000000063601506155467400233420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_LAYERMANAGER_H #define AVOGADRO_QTGUI_LAYERMANAGER_H #include "avogadroqtguiexport.h" #include #include #include namespace Avogadro { namespace QtGui { /** * @class PluginLayerManager pluginlayermanager.h * * @brief The PluginLayerManager class is a set of common layer dependent * operators useful for Layer dependent QtPlugins. */ class AVOGADROQTGUI_EXPORT PluginLayerManager : protected Core::LayerManager { public: PluginLayerManager(const std::string& name = "undef"); ~PluginLayerManager(); /** @return if the active layer in the molecule is locked. */ bool activeLayerLocked() const; bool atomLocked(size_t atom) const; /** check if there's existent data in the key and reload it in the custom * class. */ template void load() { if (m_activeMolecule != nullptr) { auto& info = m_molToInfo[m_activeMolecule]; if (info->loaded.find(m_name) == info->loaded.end()) { for (size_t i = 0; i < info->settings[m_name].size(); ++i) { auto serial = info->settings[m_name][i]->getSave(); if (serial != "") { T* aux = new T; aux->deserialize(serial); delete info->settings[m_name][i]; info->settings[m_name][i] = aux; } } info->loaded.insert(m_name); } } } /** @return if the plugin is enabled in any layer */ bool isEnabled() const; /** @return if the plugin is enabled in the active layer */ bool isActiveLayerEnabled() const; /** set active layer @p enable */ void setEnabled(bool enable); /** @return @p atom layer enabled globally and in plugin */ bool atomEnabled(Index atom) const; /** @return @p atom layer enabled globally, in plugin and in @p layer */ bool atomEnabled(size_t layer, Index atom) const; /** @return if @p atom1 or @p atom2 is enabled */ bool bondEnabled(Index atom1, Index atom2) const; /** @return layer id from @p atom */ size_t getLayerID(Index atom) const; /** @return layer count */ size_t layerCount() const; /** @return custom data T derived from LayerData. if @p layer is equal to * MaxIndex returns activeLayer */ template T* getSetting(size_t layer = MaxIndex) { auto info = m_molToInfo[m_activeMolecule]; if (layer == MaxIndex) { layer = info->layer.activeLayer(); } assert(layer <= info->layer.maxLayer()); if (info->settings.find(m_name) == info->settings.end()) { info->settings[m_name] = Core::Array(); } // do we need to create new layers in the array? while (info->settings[m_name].size() < layer + 1) { info->settings[m_name].push_back(new T()); } auto* result = static_cast(info->settings[m_name][layer]); return result; } private: // layer key identifier std::string m_name; }; } // namespace QtGui } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtgui/pythonscript.cpp000066400000000000000000000205051506155467400225520ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "pythonscript.h" #include "avogadropython.h" #include "utilities.h" #include #include #include #include #include namespace Avogadro::QtGui { PythonScript::PythonScript(const QString& scriptFilePath_, QObject* parent_) : QObject(parent_), m_debug(!qgetenv("AVO_PYTHON_SCRIPT_DEBUG").isEmpty()), m_scriptFilePath(scriptFilePath_), m_process(nullptr) { setDefaultPythonInterpreter(); } PythonScript::PythonScript(QObject* parent_) : QObject(parent_), m_debug(!qgetenv("AVO_PYTHON_SCRIPT_DEBUG").isEmpty()), m_process(nullptr) { setDefaultPythonInterpreter(); } PythonScript::~PythonScript() {} void PythonScript::setScriptFilePath(const QString& scriptFile) { m_scriptFilePath = scriptFile; } void PythonScript::setDefaultPythonInterpreter() { if (m_pythonInterpreter.isEmpty()) { m_pythonInterpreter = QSettings().value(QStringLiteral("interpreters/python")).toString(); } if (m_pythonInterpreter.isEmpty()) // compiled-in default m_pythonInterpreter = pythonInterpreterPath; // check to see if the interpreter exists and is executable QFileInfo info(m_pythonInterpreter); if (!info.isExecutable()) { qWarning() << "Python interpreter" << m_pythonInterpreter << "does not exist trying \"python\" in your path." << "Please set a path to the python interpreter."; // let's try to find a python #ifdef Q_OS_WIN QString python("python.exe"); #else QString python("python3"); #endif QString path = Utilities::findExecutablePath(python); if (path.isEmpty()) { qWarning() << "Can't find python in your path"; } else { // add a "/" to the end path.append('/'); } m_pythonInterpreter = path + python; } } QByteArray PythonScript::execute(const QStringList& args, const QByteArray& scriptStdin) { clearErrors(); QProcess proc; // Merge stdout and stderr proc.setProcessChannelMode(QProcess::MergedChannels); // Add debugging flag if needed. QStringList realArgs(args); if (m_debug) realArgs.prepend(QStringLiteral("--debug")); // Add the global language / locale to *all* calls realArgs.append("--lang"); realArgs.append(QLocale().name()); // Start script realArgs.prepend(m_scriptFilePath); if (m_debug) { qDebug() << "Executing" << m_pythonInterpreter << realArgs.join(QStringLiteral(" ")) << "<" << scriptStdin; } proc.start(m_pythonInterpreter, realArgs); // Write scriptStdin to the process's stdin if (!scriptStdin.isNull()) { if (!proc.waitForStarted(5000) && m_debug) { m_errors << tr("Error running script '%1 %2': Timed out waiting for " "start (%3).") .arg(m_pythonInterpreter, realArgs.join(QStringLiteral(" ")), processErrorString(proc)); return QByteArray(); } qint64 len = proc.write(scriptStdin); if (len != static_cast(scriptStdin.size()) && m_debug) { m_errors << tr("Error running script '%1 %2': failed to write to stdin " "(len=%3, wrote %4 bytes, QProcess error: %5).") .arg(m_pythonInterpreter) .arg(realArgs.join(QStringLiteral(" "))) .arg(scriptStdin.size()) .arg(len) .arg(processErrorString(proc)); return QByteArray(); } proc.closeWriteChannel(); } if (!proc.waitForFinished(5000) && m_debug) { m_errors << tr("Error running script '%1 %2': Timed out waiting for " "finish (%3).") .arg(m_pythonInterpreter, realArgs.join(QStringLiteral(" ")), processErrorString(proc)); return QByteArray(); } if (proc.exitStatus() != QProcess::NormalExit || proc.exitCode() != 0) { if (m_debug) m_errors << tr("Error running script '%1 %2': Abnormal exit status %3 " "(%4: %5)\n\nOutput:\n%6") .arg(m_pythonInterpreter) .arg(realArgs.join(QStringLiteral(" "))) .arg(proc.exitCode()) .arg(processErrorString(proc)) .arg(proc.errorString()) .arg(QString(proc.readAll())); else m_errors << tr("Warning '%1'").arg(proc.errorString()); return QByteArray(); } QByteArray result(proc.readAll()); if (m_debug) qDebug() << "Output:" << result; return result; } void PythonScript::asyncExecute(const QStringList& args, const QByteArray& scriptStdin) { clearErrors(); if (m_process != nullptr) { // bad news m_process->terminate(); disconnect(m_process, SIGNAL(finished()), this, SLOT(processsFinished())); m_process->deleteLater(); } m_process = new QProcess(parent()); // Merge stdout and stderr m_process->setProcessChannelMode(QProcess::MergedChannels); // Add debugging flag if needed. QStringList realArgs(args); if (m_debug) realArgs.prepend(QStringLiteral("--debug")); // Add the global language / locale to *all* calls realArgs.append("--lang"); realArgs.append(QLocale().name()); // Start script realArgs.prepend(m_scriptFilePath); if (m_debug) { qDebug() << "Executing" << m_pythonInterpreter << realArgs.join(QStringLiteral(" ")) << "<" << scriptStdin; } m_process->start(m_pythonInterpreter, realArgs); // Write scriptStdin to the process's stdin if (!scriptStdin.isNull()) { if (!m_process->waitForStarted(5000)) { m_errors << tr("Error running script '%1 %2': Timed out waiting for " "start (%3).") .arg(m_pythonInterpreter, realArgs.join(QStringLiteral(" ")), processErrorString(*m_process)); return; } qint64 len = m_process->write(scriptStdin); if (len != static_cast(scriptStdin.size())) { m_errors << tr("Error running script '%1 %2': failed to write to stdin " "(len=%3, wrote %4 bytes, QProcess error: %5).") .arg(m_pythonInterpreter) .arg(realArgs.join(QStringLiteral(" "))) .arg(scriptStdin.size()) .arg(len) .arg(processErrorString(*m_process)); return; } m_process->closeWriteChannel(); } // let the script run connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus))); } void PythonScript::processFinished(int, QProcess::ExitStatus) { emit finished(); } void PythonScript::asyncTerminate() { if (m_process != nullptr) { disconnect(m_process, nullptr, nullptr, nullptr); m_process->kill(); m_process->deleteLater(); m_process = nullptr; } } QByteArray PythonScript::asyncWriteAndResponse(QByteArray input) { if (m_process == nullptr) { return QByteArray(); // wait } m_process->write(input); QByteArray buffer; bool ready = m_process->waitForReadyRead(); if (ready) { while (m_process->canReadLine()) { buffer += m_process->readLine(); } } return buffer; } QByteArray PythonScript::asyncResponse() { if (m_process == nullptr || m_process->state() == QProcess::Running) { return QByteArray(); // wait } return m_process->readAll(); } QString PythonScript::processErrorString(const QProcess& proc) const { QString result; switch (proc.error()) { case QProcess::FailedToStart: result = tr("Script failed to start."); break; case QProcess::Crashed: result = tr("Script crashed."); break; case QProcess::Timedout: result = tr("Script timed out."); break; case QProcess::ReadError: result = tr("Read error."); break; case QProcess::WriteError: result = tr("Write error."); break; default: case QProcess::UnknownError: result = tr("Unknown error."); break; } return result; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/pythonscript.h000066400000000000000000000077641506155467400222330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_PYTHONSCRIPT_H #define AVOGADRO_QTGUI_PYTHONSCRIPT_H #include "avogadroqtguiexport.h" #include #include #include #include #include #include namespace Avogadro { namespace QtGui { /** * @brief The PythonScript class implements a interface for calling short-lived * python utility scripts. */ class AVOGADROQTGUI_EXPORT PythonScript : public QObject { Q_OBJECT public: /** * Constructors * @param scriptFilePath_ Absolute path to python script. * @{ */ explicit PythonScript(const QString& scriptFilePath_, QObject* parent_ = nullptr); explicit PythonScript(QObject* parent_ = nullptr); /**@}*/ ~PythonScript() override; /** * @return True if debugging of python I/O is enabled. */ bool debug() const { return m_debug; } /** * @return The path to the generator file. */ QString scriptFilePath() const { return m_scriptFilePath; } /** * Set the path to the input generator script file. This will reset any * cached data held by this class. */ void setScriptFilePath(const QString& scriptFile); /** * @return True if an error is set. */ bool hasErrors() const { return !m_errors.isEmpty(); } /** * Reset the error counter. */ void clearErrors() { m_errors.clear(); } /** * @return A QStringList containing all errors that occurred in the last call * to the input generator script. */ QStringList errorList() const { return m_errors; } /** * Reset the python interpretor path. The following are checked, in order: * - The AVO_PYTHON_INTERPRETER environment variable * - The "interpreters/python" QSettings value * - The path specified in avogadropython.h. */ void setDefaultPythonInterpreter(); /** * Start a new process to execute: * " [args ...]", * optionally passing scriptStdin to the processes standard input. Returns * the standard output of the process when finished. */ QByteArray execute(const QStringList& args, const QByteArray& scriptStdin = QByteArray()); /** * Start a new process to execute asynchronously * " [args ...]", * optionally passing scriptStdin to the processes standard input. * * Will send asyncFinished() signal when finished */ void asyncExecute(const QStringList& args, const QByteArray& scriptStdin = QByteArray()); /** * Write input to the asynchronous process' standard input and return the * standard output when ready. Does not wait for the process to terminate * before returning (e.g. "server mode"). * * @param input The input to write to the process' standard input * @return The standard output of the process */ QByteArray asyncWriteAndResponse(QByteArray input); /** * Terminate the asynchronous process. */ void asyncTerminate(); /** * Returns the standard output of the asynchronous process when finished. */ QByteArray asyncResponse(); signals: /** * The asynchronous execution is finished or timed out */ void finished(); public slots: /** * Enable/disable debugging. */ void setDebug(bool d) { m_debug = d; } /** * Handle a finished process; */ void processFinished(int exitCode, QProcess::ExitStatus exitStatus); protected: bool m_debug; QString m_pythonInterpreter; QString m_scriptFilePath; QStringList m_errors; QProcess* m_process; private: QString processErrorString(const QProcess& proc) const; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_PYTHONSCRIPT_H avogadrolibs-1.101.0/avogadro/qtgui/qtgui.qrc000066400000000000000000000073571506155467400211520ustar00rootroot00000000000000 icons/fallback/32x32/cross.png icons/fallback/32x32/dashed-preview-light.png icons/fallback/32x32/dashed-preview-dark.png icons/fallback/32x32/document-close.png icons/fallback/32x32/document-export.png icons/fallback/32x32/document-import.png icons/fallback/32x32/document-new.png icons/fallback/32x32/document-open-recent.png icons/fallback/32x32/document-open.png icons/fallback/32x32/document-save-all.png icons/fallback/32x32/document-save-as.png icons/fallback/32x32/document-save.png icons/fallback/32x32/dots-light.png icons/fallback/32x32/dots-dark.png icons/fallback/32x32/edit-add.png icons/fallback/32x32/edit-copy.png icons/fallback/32x32/edit-cut.png icons/fallback/32x32/edit-paste.png icons/fallback/32x32/edit-delete.png icons/fallback/32x32/help-about.png icons/fallback/64x64/document-close.png icons/fallback/64x64/document-export.png icons/fallback/64x64/document-import.png icons/fallback/64x64/document-new.png icons/fallback/64x64/document-open-recent.png icons/fallback/64x64/document-open.png icons/fallback/64x64/document-save-all.png icons/fallback/64x64/document-save-as.png icons/fallback/64x64/document-save.png icons/fallback/64x64/edit-copy.png icons/fallback/64x64/edit-cut.png icons/fallback/64x64/edit-paste.png icons/fallback/64x64/help-about.png icons/fallback/32x32/lock-open-light.png icons/fallback/32x32/lock-open-dark.png icons/fallback/32x32/lock-light.png icons/fallback/32x32/lock-dark.png icons/fallback/32x32/plus-light.png icons/fallback/32x32/plus-dark.png icons/fallback/32x32/preview-light.png icons/fallback/32x32/preview-dark.png icons/fallback/index.theme icons/colormap/balance.png icons/colormap/balance@2x.png icons/colormap/bluedarkred.png icons/colormap/bluedarkred@2x.png icons/colormap/cividis.png icons/colormap/cividis@2x.png icons/colormap/coolwarm.png icons/colormap/coolwarm@2x.png icons/colormap/gray.png icons/colormap/gray@2x.png icons/colormap/hot.png icons/colormap/hot@2x.png icons/colormap/inferno.png icons/colormap/inferno@2x.png icons/colormap/magma.png icons/colormap/magma@2x.png icons/colormap/parula.png icons/colormap/parula@2x.png icons/colormap/plasma.png icons/colormap/plasma@2x.png icons/colormap/spectral.png icons/colormap/spectral@2x.png icons/colormap/turbo.png icons/colormap/turbo@2x.png icons/colormap/viridis.png icons/colormap/viridis@2x.png avogadrolibs-1.101.0/avogadro/qtgui/richtextdelegate.cpp000066400000000000000000000033061506155467400233310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "richtextdelegate.h" #include namespace Avogadro::QtGui { // See for example // https://gist.github.com/jniemann66/dbc298b35a840bf3f1a2206ea6284c7b // and https://stackoverflow.com/a/66412883/131896 RichTextDelegate::RichTextDelegate(QObject* parent_) : QStyledItemDelegate(parent_) { } RichTextDelegate::~RichTextDelegate() {}; QSize RichTextDelegate::sizeHint(const QStyleOptionViewItem& o, const QModelIndex& index) const { if (o.text.isEmpty()) { // This is nothing this function is supposed to handle return QStyledItemDelegate::sizeHint(o, index); } QStyleOptionViewItem ov = o; initStyleOption(&ov, index); QTextDocument doc; doc.setHtml(ov.text); doc.setTextWidth(ov.rect.width()); doc.setDefaultFont(ov.font); return QSize(std::ceil(doc.idealWidth()), std::ceil(doc.size().height())); } void RichTextDelegate::paint(QPainter* p, const QStyleOptionViewItem& o, const QModelIndex& index) const { QStyleOptionViewItem ov = o; initStyleOption(&ov, index); p->save(); QTextDocument doc; doc.setHtml(ov.text); doc.setDefaultFont(ov.font); ov.text = ""; ov.widget->style()->drawControl(QStyle::CE_ItemViewItem, &ov, p); p->translate(ov.rect.left(), ov.rect.top()); QRect clip(0, 0, ov.rect.width(), ov.rect.height()); doc.drawContents(p, clip); p->restore(); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/richtextdelegate.h000066400000000000000000000020041506155467400227700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_RICHTEXTDELEGATE_H #define AVOGADRO_QTPLUGINS_RICHTEXTDELEGATE_H #include #include #include #include "avogadroqtguiexport.h" namespace Avogadro { namespace QtGui { class AVOGADROQTGUI_EXPORT RichTextDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit RichTextDelegate(QObject* parent = 0); ~RichTextDelegate() override; QSize sizeHint(const QStyleOptionViewItem& o, const QModelIndex& index) const override; void paint(QPainter* p, const QStyleOptionViewItem& o, const QModelIndex& index) const override; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_RICHTEXTDELEGATE_H avogadrolibs-1.101.0/avogadro/qtgui/rwlayermanager.cpp000066400000000000000000000206051506155467400230250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "rwlayermanager.h" #include "rwmolecule.h" #include #include #include #include #include namespace Avogadro::QtGui { using Core::Array; using Core::LayerData; using Core::MoleculeInfo; using std::map; using std::set; using std::shared_ptr; using std::string; using std::vector; namespace { class AddLayerCommand : public QUndoCommand { public: AddLayerCommand(shared_ptr mol) : QUndoCommand(QObject::tr("Modify Layers")), m_moleculeInfo(mol) { m_visible = true; m_locked = false; const auto activeLayer = m_moleculeInfo->layer.activeLayer(); // we loop through the layers to find enabled settings for the active layer for (const auto& names : m_moleculeInfo->enable) { bool value = names.second[activeLayer]; m_enable[names.first] = value; } // now we do the same thing for settings for (const auto& names : m_moleculeInfo->settings) { auto value = names.second[activeLayer]; m_settings[names.first] = value; } } void redo() override { m_moleculeInfo->visible.push_back(m_visible); m_moleculeInfo->locked.push_back(m_locked); // it's confusing to create an empty layer // .. so we just create a layer that matches the active layer for (const auto& enable : m_enable) { m_moleculeInfo->enable[enable.first].push_back(enable.second); } for (const auto& settings : m_settings) { // create newSettings pointer with the same type as settings.second auto* newSettings = settings.second->clone(); m_moleculeInfo->settings[settings.first].push_back(newSettings); } m_moleculeInfo->layer.addLayer(); } void undo() override { m_visible = m_moleculeInfo->visible.back(); m_locked = m_moleculeInfo->locked.back(); m_moleculeInfo->visible.pop_back(); m_moleculeInfo->locked.pop_back(); size_t qttyLayer = m_moleculeInfo->layer.layerCount(); for (auto& enable : m_moleculeInfo->enable) { if (enable.second.size() == qttyLayer) { m_enable[enable.first] = enable.second[enable.second.size() - 1]; enable.second.pop_back(); } } for (auto& setting : m_moleculeInfo->settings) { if (setting.second.size() == qttyLayer) { m_settings[setting.first] = setting.second[setting.second.size() - 1]; setting.second.pop_back(); } } m_moleculeInfo->layer.removeLayer(m_moleculeInfo->layer.maxLayer()); } protected: shared_ptr m_moleculeInfo; map m_enable; map m_settings; bool m_visible; bool m_locked; }; class ActiveLayerCommand : public QUndoCommand { public: ActiveLayerCommand(shared_ptr mol, size_t layer) : QUndoCommand(QObject::tr("Modify Layers")), m_moleculeInfo(mol), m_newActiveLayer(layer) { m_oldActiveLayer = m_moleculeInfo->layer.activeLayer(); } void redo() override { m_moleculeInfo->layer.setActiveLayer(m_newActiveLayer); } void undo() override { m_moleculeInfo->layer.setActiveLayer(m_oldActiveLayer); } protected: shared_ptr m_moleculeInfo; size_t m_oldActiveLayer; size_t m_newActiveLayer; }; class RemoveLayerCommand : public QUndoCommand { public: RemoveLayerCommand(shared_ptr mol, size_t layer) : QUndoCommand(QObject::tr("Modify Layers")), m_moleculeInfo(mol), m_layer(layer) { } void redo() override { m_visible = m_moleculeInfo->visible[m_layer]; m_moleculeInfo->visible.erase( std::next(m_moleculeInfo->visible.begin(), m_layer)); m_locked = m_moleculeInfo->locked[m_layer]; m_moleculeInfo->locked.erase( std::next(m_moleculeInfo->locked.begin(), m_layer)); for (auto& enable : m_moleculeInfo->enable) { if (m_layer < enable.second.size()) { m_enable[enable.first] = enable.second[m_layer]; enable.second.erase(std::next(enable.second.begin(), m_layer)); } } for (auto& setting : m_moleculeInfo->settings) { if (m_layer < setting.second.size()) { m_settings[setting.first] = setting.second[m_layer]; setting.second.erase(std::next(setting.second.begin(), m_layer)); } } m_moleculeInfo->layer.removeLayer(m_layer); } void undo() override { auto itVisible = m_moleculeInfo->visible.begin() + m_layer; m_moleculeInfo->visible.insert(itVisible, m_visible); auto itLocked = m_moleculeInfo->locked.begin() + m_layer; m_moleculeInfo->locked.insert(itLocked, m_locked); for (const auto& enable : m_enable) { auto itEnable = m_moleculeInfo->enable[enable.first].begin() + m_layer; m_moleculeInfo->enable[enable.first].insert(itEnable, enable.second); } for (const auto& setting : m_settings) { auto itSetting = m_moleculeInfo->settings[setting.first].begin() + m_layer; m_moleculeInfo->settings[setting.first].insert(itSetting, setting.second); } m_moleculeInfo->layer.addLayer(m_layer); } protected: shared_ptr m_moleculeInfo; size_t m_layer; bool m_visible; bool m_locked; map m_settings; map m_enable; }; } // namespace void RWLayerManager::removeLayer(size_t layer, RWMolecule* rwmolecule) { assert(m_activeMolecule != nullptr); assert(rwmolecule != nullptr); rwmolecule->undoStack().beginMacro(QObject::tr("Remove Layer")); auto atoms = rwmolecule->molecule().getAtomsAtLayer(layer); for (const Index& atom : atoms) { rwmolecule->removeAtom(atom); } auto& molecule = m_molToInfo[m_activeMolecule]; auto* comm = new RemoveLayerCommand(molecule, layer); comm->setText(QObject::tr("Remove Layer Info")); rwmolecule->undoStack().push(comm); rwmolecule->undoStack().endMacro(); } void RWLayerManager::addLayer(RWMolecule* rwmolecule) { assert(m_activeMolecule != nullptr); assert(rwmolecule != nullptr); rwmolecule->undoStack().beginMacro(QObject::tr("Add Layer")); auto& molecule = m_molToInfo[m_activeMolecule]; auto* comm = new AddLayerCommand(molecule); comm->setText(QObject::tr("Add Layer Info")); rwmolecule->undoStack().push(comm); rwmolecule->undoStack().endMacro(); } void RWLayerManager::setActiveLayer(size_t layer, RWMolecule* rwmolecule) { rwmolecule->undoStack().beginMacro(QObject::tr("Change Layer")); auto& molecule = m_molToInfo[m_activeMolecule]; auto* comm = new ActiveLayerCommand(molecule, layer); comm->setText(QObject::tr("Change Layer")); rwmolecule->undoStack().push(comm); rwmolecule->undoStack().endMacro(); } bool RWLayerManager::visible(size_t layer) const { return m_molToInfo[m_activeMolecule]->visible[layer]; } bool RWLayerManager::locked(size_t layer) const { return m_molToInfo[m_activeMolecule]->locked[layer]; } void RWLayerManager::flipVisible(size_t layer) { auto& molecule = m_molToInfo[m_activeMolecule]; molecule->visible[layer] = !molecule->visible[layer]; } void RWLayerManager::flipLocked(size_t layer) { auto& molecule = m_molToInfo[m_activeMolecule]; molecule->locked[layer] = !molecule->locked[layer]; } void RWLayerManager::addMolecule(const Core::Molecule* mol) { m_activeMolecule = mol; auto it = m_molToInfo.find(mol); if (it == m_molToInfo.end()) { m_molToInfo[mol] = std::make_shared(mol); } } Array> RWLayerManager::activeMoleculeNames() const { if (m_activeMolecule == nullptr || m_molToInfo[m_activeMolecule] == nullptr) { return Array>(); } auto& molecule = m_molToInfo[m_activeMolecule]; size_t qttyLayer = molecule->layer.layerCount(); vector> active(qttyLayer, set()); for (const auto& names : molecule->enable) { for (size_t i = 0; i < names.second.size(); ++i) { if (names.second[i]) { active[i].insert(names.first); } } } Array> result; size_t layer = 0; for (const auto& names : active) { result.push_back(std::make_pair(layer, "Layer")); for (const auto& name : names) { result.push_back(std::make_pair(layer, name)); } ++layer; } return result; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/rwlayermanager.h000066400000000000000000000030731506155467400224720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_RWLAYERMANAGER_H #define AVOGADRO_QTGUI_RWLAYERMANAGER_H #include "avogadroqtguiexport.h" #include #include namespace Avogadro { namespace Core { class Molecule; } namespace QtGui { class RWMolecule; /** * @class RWLayerManager rwlayermanager.h * @brief The RWLayerManager do and undo for layer actions. */ class AVOGADROQTGUI_EXPORT RWLayerManager : protected Core::LayerManager { public: void removeLayer(size_t layer, RWMolecule* rwmolecule); void addLayer(RWMolecule* rwmolecule); void setActiveLayer(size_t layer, RWMolecule* rwmolecule); /** @return if @p layer is visible */ bool visible(size_t layer) const; protected: /** @return if @p layer is locked */ bool locked(size_t layer) const; /** flip the visible value in layer */ void flipVisible(size_t layer); /** flip the locked value in layer */ void flipLocked(size_t layer); /** set mol as active molecule and add it if not exist */ void addMolecule(const Core::Molecule* mol); /** @return a sorted array by layer ID, containing the ID and the key name */ Core::Array> activeMoleculeNames() const; }; } // namespace QtGui } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtgui/rwmolecule.cpp000066400000000000000000000524771506155467400221770ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "rwmolecule.h" #include "rwmolecule_undo.h" #include #include #ifdef USE_SPGLIB #include #endif #include #include #include namespace Avogadro::QtGui { using Core::Array; using Core::AtomHybridization; using Core::CrystalTools; using Core::UnitCell; using std::swap; RWMolecule::RWMolecule(Molecule& mol, QObject* p) : QObject(p), m_molecule(mol), m_interactive(false) { } RWMolecule::~RWMolecule() {} RWMolecule::AtomType RWMolecule::addAtom(unsigned char num, bool usingPositions) { auto atomId = static_cast(m_molecule.atomCount()); auto atomUid = static_cast(m_molecule.m_atomUniqueIds.size()); auto* comm = new AddAtomCommand(*this, num, usingPositions, atomId, atomUid); comm->setText(tr("Add Atom")); m_undoStack.push(comm); return AtomType(this, atomId); } RWMolecule::AtomType RWMolecule::addAtom(unsigned char num, const Vector3& position3d) { // We will combine the actions in this command. m_undoStack.beginMacro(tr("Add Atom")); AtomType atom = addAtom(num); setAtomPosition3d(atomCount() - 1, position3d); m_undoStack.endMacro(); return atom; } Index RWMolecule::atomCount(unsigned char num) const { return m_molecule.atomCount(num); } bool RWMolecule::removeAtom(Index atomId) { if (atomId >= atomCount()) return false; Index uniqueId = findAtomUniqueId(atomId); if (uniqueId == MaxIndex) return false; // Lump all operations into a single undo command: m_undoStack.beginMacro(tr("Remove Atom")); // Remove any bonds containing this atom first. Array atomBonds = bonds(atomId); while (atomBonds.size()) { // Ensure that indices aren't invalidated as we remove them: assert("atomBonds have ascending indices" && (atomBonds.size() == 1 || ((atomBonds.end() - 2)->index() < (atomBonds.end() - 1)->index()))); removeBond(atomBonds.back()); atomBonds.pop_back(); } auto* comm = new RemoveAtomCommand( *this, atomId, uniqueId, atomicNumber(atomId), atomPosition3d(atomId)); comm->setText(tr("Remove Atom")); m_undoStack.push(comm); m_undoStack.endMacro(); return true; } void RWMolecule::clearAtoms() { m_undoStack.beginMacro(tr("Clear Atoms")); while (atomCount() != 0) removeAtom(0); m_undoStack.endMacro(); } void RWMolecule::adjustHydrogens(Index atomId) { RWAtom atom = this->atom(atomId); if (atom.isValid()) { m_undoStack.beginMacro(tr("Adjust Hydrogens")); QtGui::HydrogenTools::adjustHydrogens(atom); m_undoStack.endMacro(); } } void RWMolecule::adjustHydrogens(const Core::Array& atomIds) { m_undoStack.beginMacro(tr("Adjust Hydrogens")); // convert the indexes to unique ids // since we're adding and removing atoms // (so the index will change) Core::Array uniqueIds; for (auto id : atomIds) { Index uniqueId = findAtomUniqueId(id); if (uniqueId != MaxIndex) uniqueIds.push_back(uniqueId); } for (auto uniqueId : uniqueIds) { RWAtom atom = this->atomByUniqueId(uniqueId); if (atom.isValid()) { QtGui::HydrogenTools::adjustHydrogens(atom); } } m_undoStack.endMacro(); } bool RWMolecule::setAtomicNumbers(const Core::Array& nums) { if (nums.size() != m_molecule.atomCount()) return false; auto* comm = new SetAtomicNumbersCommand(*this, m_molecule.atomicNumbers(), nums); comm->setText(tr("Change Elements")); m_undoStack.push(comm); return true; } bool RWMolecule::setAtomicNumber(Index atomId, unsigned char num) { if (atomId >= atomCount()) return false; auto* comm = new SetAtomicNumberCommand(*this, atomId, m_molecule.atomicNumber(atomId), num); comm->setText(tr("Change Element")); m_undoStack.push(comm); return true; } bool RWMolecule::setAtomPositions3d(const Core::Array& pos, const QString& undoText) { if (pos.size() != m_molecule.atomCount()) return false; auto* comm = new SetPositions3dCommand(*this, m_molecule.m_positions3d, pos); comm->setText(undoText); comm->setCanMerge(m_interactive); m_undoStack.push(comm); return true; } bool RWMolecule::setAtomLabel(Index atomId, const std::string& label, const QString& undoText) { if (atomId >= atomCount()) return false; auto* comm = new ModifyAtomLabelCommand(*this, atomId, label); comm->setText(undoText); m_undoStack.push(comm); return true; } bool RWMolecule::setBondLabel(Index bondId, const std::string& label, const QString& undoText) { if (bondId >= bondCount()) return false; auto* comm = new ModifyBondLabelCommand(*this, bondId, label); comm->setText(undoText); m_undoStack.push(comm); return true; } bool RWMolecule::setAtomPosition3d(Index atomId, const Vector3& pos, const QString& undoText) { if (atomId >= atomCount()) return false; if (m_molecule.m_positions3d.size() != m_molecule.atomCount()) m_molecule.m_positions3d.resize(m_molecule.atomCount(), Vector3::Zero()); auto* comm = new SetPosition3dCommand(*this, atomId, m_molecule.m_positions3d[atomId], pos); comm->setText(undoText); comm->setCanMerge(m_interactive); m_undoStack.push(comm); return true; } void RWMolecule::setAtomSelected(Index atomId, bool selected, const QString& undoText) { auto* comm = new ModifySelectionCommand(*this, atomId, selected); comm->setText(undoText); comm->setCanMerge(true); m_undoStack.push(comm); // m_molecule.setAtomSelected(atomId, selected); } bool RWMolecule::atomSelected(Index atomId) const { return m_molecule.atomSelected(atomId); } bool RWMolecule::setHybridization(Index atomId, Core::AtomHybridization hyb) { if (atomId >= atomCount()) return false; auto* comm = new SetAtomicNumberCommand( *this, atomId, m_molecule.hybridization(atomId), hyb); comm->setText(tr("Change Atom Hybridization")); m_undoStack.push(comm); return true; } bool RWMolecule::setFormalCharge(Index atomId, signed char charge) { if (atomId >= atomCount()) return false; auto* comm = new SetAtomFormalChargeCommand( *this, atomId, m_molecule.formalCharge(atomId), charge); comm->setText(tr("Change Atom Formal Charge")); m_undoStack.push(comm); return true; } bool RWMolecule::setColor(Index atomId, Vector3ub color) { if (atomId >= atomCount()) return false; auto* comm = new SetAtomColorCommand(*this, atomId, m_molecule.color(atomId), color); comm->setText(tr("Change Atom Color")); m_undoStack.push(comm); return true; } bool RWMolecule::setLayer(Index atomId, size_t layer) { if (atomId >= atomCount()) return false; auto* comm = new SetLayerCommand(*this, atomId, m_molecule.layer(atomId), layer); comm->setText(tr("Change Atom Layer")); m_undoStack.push(comm); return true; } RWMolecule::BondType RWMolecule::addBond(Index atom1, Index atom2, unsigned char order) { if (atom1 == atom2 || std::max(atom1, atom2) >= atomCount()) return BondType(); Index bondId = bondCount(); auto bondUid = static_cast(m_molecule.m_bondUniqueIds.size()); auto* comm = new AddBondCommand( *this, order, Molecule::makeBondPair(atom1, atom2), bondId, bondUid); comm->setText(tr("Add Bond")); m_undoStack.push(comm); return BondType(this, bondId); } RWMolecule::BondType RWMolecule::bond(Index atom1, Index atom2) const { Molecule::BondType b = m_molecule.bond(atom1, atom2); if (b.isValid()) return BondType(const_cast(this), b.index()); else return BondType(); } bool RWMolecule::removeBond(Index bondId) { if (bondId >= bondCount()) return false; Index bondUid = findBondUniqueId(bondId); if (bondUid == MaxIndex) return false; auto* comm = new RemoveBondCommand(*this, bondId, bondUid, m_molecule.bondPair(bondId), m_molecule.bondOrder(bondId)); comm->setText(tr("Removed Bond")); m_undoStack.push(comm); return true; } void RWMolecule::clearBonds() { m_undoStack.beginMacro(tr("Clear Bonds")); while (bondCount() != 0) removeBond(0); m_undoStack.endMacro(); } bool RWMolecule::setBondOrders(const Core::Array& orders) { if (orders.size() != m_molecule.bondCount()) return false; auto* comm = new SetBondOrdersCommand(*this, m_molecule.bondOrders(), orders); comm->setText(tr("Set Bond Orders")); m_undoStack.push(comm); return true; } bool RWMolecule::setBondOrder(Index bondId, unsigned char order) { if (bondId >= bondCount()) return false; auto* comm = new SetBondOrderCommand(*this, bondId, m_molecule.bondOrder(bondId), order); comm->setText(tr("Change Bond Order")); // Always allow merging, but only if bondId is the same. comm->setCanMerge(true); m_undoStack.push(comm); return true; } bool RWMolecule::setBondPairs(const Array>& pairs) { if (pairs.size() != m_molecule.bondCount()) return false; // Correct any pairs that are ordered improperly: typedef std::pair BondPair; Array p(pairs); // Use for reading to prevent copies unless needed (Array is copy-on-write): const Array& p_const = p; for (size_t i = 0; i < p.size(); ++i) if (p_const[i].first > p_const[i].second) swap(p[i].first, p[i].second); auto* comm = new SetBondPairsCommand(*this, m_molecule.bondPairs(), p); comm->setText(tr("Update Bonds")); m_undoStack.push(comm); return true; } bool RWMolecule::setBondPair(Index bondId, const std::pair& pair) { if (bondId >= bondCount() || pair.first == pair.second) return false; SetBondPairCommand* comm = nullptr; if (pair.first < pair.second) { comm = new SetBondPairCommand(*this, bondId, m_molecule.bondPair(bondId), pair); } else { comm = new SetBondPairCommand(*this, bondId, m_molecule.bondPair(bondId), Molecule::makeBondPair(pair.first, pair.second)); } comm->setText(tr("Update Bond")); m_undoStack.push(comm); return true; } void RWMolecule::addUnitCell() { // If there is already a unit cell, there is nothing to do if (m_molecule.unitCell()) return; auto* cell = new UnitCell; cell->setCellParameters( static_cast(3.0), static_cast(3.0), static_cast(3.0), static_cast(90.0) * DEG_TO_RAD, static_cast(90.0) * DEG_TO_RAD, static_cast(90.0) * DEG_TO_RAD); m_molecule.setUnitCell(cell); auto* comm = new AddUnitCellCommand(*this, *m_molecule.unitCell()); comm->setText(tr("Add Unit Cell…")); m_undoStack.push(comm); emitChanged(Molecule::UnitCell | Molecule::Added); } void RWMolecule::removeUnitCell() { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return; auto* comm = new RemoveUnitCellCommand(*this, *m_molecule.unitCell()); comm->setText(tr("Remove Unit Cell")); m_undoStack.push(comm); m_molecule.setUnitCell(nullptr); emitChanged(Molecule::UnitCell | Molecule::Removed); } void RWMolecule::modifyMolecule(const Molecule& newMolecule, Molecule::MoleculeChanges changes, const QString& undoText) { auto* comm = new ModifyMoleculeCommand(*this, m_molecule, newMolecule); comm->setText(undoText); m_undoStack.push(comm); m_molecule = newMolecule; emitChanged(changes); } void RWMolecule::appendMolecule(const Molecule& mol, const QString& undoText) { // We add atoms and bonds, nothing else Molecule::MoleculeChanges changes = (Molecule::Atoms | Molecule::Bonds | Molecule::Added); beginMergeMode(undoText); // loop through and add the atoms Index offset = atomCount(); for (size_t i = 0; i < mol.atomCount(); ++i) { Core::Atom atom = mol.atom(i); AtomType new_atom = addAtom(atom.atomicNumber(), atom.position3d()); new_atom.setFormalCharge(atom.formalCharge()); setAtomSelected(atomCount() - 1, true); } // now loop through and add the bonds for (size_t i = 0; i < mol.bondCount(); ++i) { Core::Bond bond = mol.bond(i); addBond(bond.atom1().index() + offset, bond.atom2().index() + offset, bond.order()); } endMergeMode(); emitChanged(changes); } void RWMolecule::editUnitCell(Matrix3 cellMatrix, CrystalTools::Options options) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return; if (!UnitCell::isRegular(cellMatrix)) { qWarning() << "cell matrix is singular"; return; } // Make a copy of the molecule to edit so we can store the old one // If the user has "TransformAtoms" set in the options, then // the atom positions will move as well. Molecule newMolecule = m_molecule; CrystalTools::setCellMatrix(newMolecule, cellMatrix, options); // We will just modify the whole molecule since there may be many changes Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Modified; // If TransformAtoms is set in the options, then the atoms may be modified // as well. if (options & CrystalTools::TransformAtoms) changes |= Molecule::Atoms | Molecule::Modified; QString undoText = tr("Edit Unit Cell"); modifyMolecule(newMolecule, changes, undoText); } void RWMolecule::wrapAtomsToCell() { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return; Core::Array oldPos = m_molecule.atomPositions3d(); CrystalTools::wrapAtomsToUnitCell(m_molecule); Core::Array newPos = m_molecule.atomPositions3d(); auto* comm = new SetPositions3dCommand(*this, oldPos, newPos); comm->setText(tr("Wrap Atoms to Cell")); m_undoStack.push(comm); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Modified; emitChanged(changes); } void RWMolecule::setCellVolume(double newVolume, CrystalTools::Options options) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return; // Make a copy of the molecule to edit so we can store the old one // The unit cell and atom positions may change Molecule newMolecule = m_molecule; CrystalTools::setVolume(newMolecule, newVolume, options); // We will just modify the whole molecule since there may be many changes Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Modified; if (options & CrystalTools::TransformAtoms) changes |= Molecule::Atoms | Molecule::Modified; QString undoText = tr("Scale Cell Volume"); modifyMolecule(newMolecule, changes, undoText); } void RWMolecule::buildSupercell(unsigned int a, unsigned int b, unsigned int c) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return; // Make a copy of the molecule to edit so we can store the old one // The unit cell and atom positions may change Molecule newMolecule = m_molecule; CrystalTools::buildSupercell(newMolecule, a, b, c); // We will just modify the whole molecule since there may be many changes Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Modified | Molecule::Atoms | Molecule::Added; QString undoText = tr("Build Super Cell"); modifyMolecule(newMolecule, changes, undoText); } void RWMolecule::niggliReduceCell() { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return; // Make a copy of the molecule to edit so we can store the old one // The unit cell and atom positions may change Molecule newMolecule = m_molecule; // We need to perform all three of these operations... CrystalTools::niggliReduce(newMolecule, CrystalTools::TransformAtoms); CrystalTools::rotateToStandardOrientation(newMolecule, CrystalTools::TransformAtoms); CrystalTools::wrapAtomsToUnitCell(newMolecule); // We will just modify the whole molecule since there may be many changes Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms | Molecule::Modified; QString undoText = tr("Niggli Reduction"); modifyMolecule(newMolecule, changes, undoText); } void RWMolecule::rotateCellToStandardOrientation() { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return; // Store a copy of the old molecule // The atom positions may move as well. Molecule newMolecule = m_molecule; CrystalTools::rotateToStandardOrientation(newMolecule, CrystalTools::TransformAtoms); // Since most components of the molecule will be modified (atom positions // and the unit cell), we will just modify the whole thing... Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms | Molecule::Modified; QString undoText = tr("Rotate to Standard Orientation"); modifyMolecule(newMolecule, changes, undoText); } bool RWMolecule::reduceCellToPrimitive(double cartTol) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return false; // Make a copy of the molecule to edit so we can store the old one // The unit cell, atom positions, and numbers of atoms may change Molecule newMolecule = m_molecule; #ifdef USE_SPGLIB if (!Core::AvoSpglib::reduceToPrimitive(newMolecule, cartTol)) return false; #else return false; #endif // Since most components of the molecule will be modified, // we will just modify the whole thing... Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms | Molecule::Added; QString undoText = tr("Reduce to Primitive"); modifyMolecule(newMolecule, changes, undoText); return true; } bool RWMolecule::conventionalizeCell(double cartTol) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return false; // Make a copy of the molecule to edit so we can store the old one // The unit cell, atom positions, and numbers of atoms may all change Molecule newMolecule = m_molecule; #ifdef USE_SPGLIB if (!Core::AvoSpglib::conventionalizeCell(newMolecule, cartTol)) return false; #else return false; #endif Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms | Molecule::Added; QString undoText = tr("Conventionalize Cell"); modifyMolecule(newMolecule, changes, undoText); return true; } bool RWMolecule::symmetrizeCell(double cartTol) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return false; // Make a copy of the molecule to edit so we can store the old one // The unit cell, atom positions, and numbers of atoms may all change Molecule newMolecule = m_molecule; #ifdef USE_SPGLIB if (!Core::AvoSpglib::symmetrize(newMolecule, cartTol)) return false; #else return false; #endif Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms | Molecule::Added; QString undoText = tr("Symmetrize Cell"); modifyMolecule(newMolecule, changes, undoText); return true; } bool RWMolecule::fillUnitCell(unsigned short hallNumber, double cartTol) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return false; // Make a copy of the molecule to edit so we can store the old one // The atom positions and numbers of atoms may change Molecule newMolecule = m_molecule; Core::SpaceGroups::fillUnitCell(newMolecule, hallNumber, cartTol); Molecule::MoleculeChanges changes = Molecule::Added | Molecule::Atoms; QString undoText = tr("Fill Unit Cell"); modifyMolecule(newMolecule, changes, undoText); return true; } bool RWMolecule::reduceCellToAsymmetricUnit(unsigned short hallNumber, double cartTol) { // If there is no unit cell, there is nothing to do if (!m_molecule.unitCell()) return false; // Make a copy of the molecule to edit so we can store the old one // The atom positions and numbers of atoms may change Molecule newMolecule = m_molecule; Core::SpaceGroups::reduceToAsymmetricUnit(newMolecule, hallNumber, cartTol); Molecule::MoleculeChanges changes = Molecule::Removed | Molecule::Atoms; QString undoText = tr("Reduce Cell to Asymmetric Unit"); modifyMolecule(newMolecule, changes, undoText); return true; } void RWMolecule::emitChanged(unsigned int change) { m_molecule.emitChanged(change); } Index RWMolecule::findAtomUniqueId(Index atomId) const { return m_molecule.findAtomUniqueId(atomId); } Index RWMolecule::findBondUniqueId(Index bondId) const { return m_molecule.findBondUniqueId(bondId); } bool RWMolecule::setForceVector(Index atomId, const Vector3& forces, const QString& undoText) { if (atomId >= atomCount()) return false; if (m_molecule.m_positions3d.size() != m_molecule.atomCount()) m_molecule.m_positions3d.resize(m_molecule.atomCount(), Vector3::Zero()); auto* comm = new SetForceVectorCommand( *this, atomId, m_molecule.m_positions3d[atomId], forces); comm->setText(undoText); comm->setCanMerge(m_interactive); m_undoStack.push(comm); return true; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/rwmolecule.h000066400000000000000000000705211506155467400216320ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_RWMOLECULE_H #define AVOGADRO_QTGUI_RWMOLECULE_H #include "avogadroqtguiexport.h" #include #include "molecule.h" //qtgui::Molecule #include "persistentatom.h" #include "persistentbond.h" #include #include #include #include #include #include #include #include namespace Avogadro { namespace QtGui { /** Concrete atom/bond proxy classes for RWMolecule. @{ */ class RWAtom; class RWBond; /** @} */ /** * @class RWMolecule rwmolecule.h * @brief The RWMolecule class is an editable molecule class that automatically * populates an undo stack. * * This class implements the molecule API and composes a QUndoStack * (undoStack()). New undo commands are automatically generated and push each * time a non-const method is called. * * An interactive mode is supported that causes "noisy" commands, such as * atom position changes, to be merged into a single command, saving memory * keeping the stack usable during interactive editing of the molecule. Use * setInteractive(bool) to toggle interactive mode. * * Similarly, multiple sequences of commands can be compressed into a single * named action using the QUndoStack's macro capability. Call * undoStack().beginMacro(tr("User Description Of Change")) to begin a macro, * and undoStack().endMacro() when finished. */ class AVOGADROQTGUI_EXPORT RWMolecule : public QObject { Q_OBJECT public: /** Typedef for Atom class. */ typedef RWAtom AtomType; /** Typedef for PersistentAtom class. */ typedef PersistentAtom PersistentAtomType; /** Typedef for Bond class. */ typedef RWBond BondType; /** Typedef for PersistentBond class. */ typedef PersistentBond PersistentBondType; /** Construct a molecule with the atoms/bonds of mol. */ explicit RWMolecule(Molecule& mol, QObject* parent = nullptr); ~RWMolecule() override; Molecule& molecule() { return m_molecule; } const Molecule& molecule() const { return m_molecule; } /** * Add a new atom to the molecule. * @param atomicNumber The atomic number of the new atom. * @param usingPositions Whether or not to use positions for this atom. * Default is true. Set to false if the atom * will not be using coordinates. * @return The new Atom object. */ AtomType addAtom(unsigned char atomicNumber, bool usingPositions = true); /** * Add a new atom to the molecule and set its position. * @param atomicNumber The atomic number of the new atom. * @param position3d The position of the atom. * @return The new Atom object. */ AtomType addAtom(unsigned char atomicNumber, const Vector3& position3d); /** * Obtain an atom object. * @param atomId The index of the atom to return. * @return The requested atom object. Will be invalid if @a atomId >= @a * atomCount(). */ AtomType atom(Index atomId) const; /** * Obtain an atom object from it's unique id. * @param atomUId The unique of the requested atom. * @return The requested atom object. Will be invalid if @a atomUId is not * in use. */ AtomType atomByUniqueId(Index atomUId) const; /** * @return The unique id of the atom. * @{ */ Index atomUniqueId(Index atomId) const; Index atomUniqueId(const AtomType& atom) const; /** @} */ /** * @return The number of atoms in the molecule. */ Index atomCount() const; /** * @return The number of atoms in the molecule with the atomic number @a * atomicNumber. */ Index atomCount(unsigned char atomicNumber) const; /** * Delete the specified atom from this molecule. * @return True on success, false otherwise. * @note This also removes all bonds connected to the atom. * @{ */ bool removeAtom(Index atomId); bool removeAtom(const AtomType& atom); /** @} */ /** * Delete all atoms from this molecule. * @note This also removes all bonds. */ void clearAtoms(); /** * Adjust hydrogens for an atom. * @param atomId The index of the atom. * @note Checks to make sure the atom is valid before adjusting the hydrogens. */ void adjustHydrogens(Index atomId); /** * Adjust hydrogens for multiple atoms. * @param atomIds The indices for the atoms. * @note Checks to make sure the atoms are valid before adjusting the * hydrogens. */ void adjustHydrogens(const Core::Array& atomIds); /** * @return An array containing atomic numbers for all atoms in the molecule, * indexed by atom index. */ const Core::Array& atomicNumbers() const; /** * Get the atomic number for the requested atom. * @param atomId The index of the atom. * @return The atomic number of the atom indexed at @a atomId, or * Avogadro::InvalidElement if @a atomId is invalid. */ unsigned char atomicNumber(Index atomId) const; /** * Replace the current array of atomic numbers. * @param nums The new atomic number array. Must be of length atomCount(). * @return True on success, false otherwise. */ bool setAtomicNumbers(const Core::Array& nums); /** * Set the atomic number of a single atom. * @param atomId The index of the atom to modify. * @param atomicNumber The new atomic number. * @return True on success, false otherwise. */ bool setAtomicNumber(Index atomId, unsigned char atomicNumber); /** * @return An array containing 3D coordinates for all atoms in the molecule, * indexed by atom index. * @note May be empty if position information has not been set for any atoms. */ const Core::Array& atomPositions3d() const; /** * Get the 3D position of a single atom. * @param atomId The index of the atom. * @return The position of the atom, or Vector3::Zero() if no position * information has been set. */ Vector3 atomPosition3d(Index atomId) const; /** * Replace the current array of 3D atomic coordinates. * @param pos The new coordinate array. Must be of length atomCount(). * @param undoText The undo text to be displayed for undo commands. * @return True on success, false otherwise. */ bool setAtomPositions3d( const Core::Array& pos, const QString& undoText = tr("Change Atom Positions")); /** * Set the 3D position of a single atom. * @param atomId The index of the atom to modify. * @param pos The new position of the atom. * @param undoText The undo text to be displayed for undo commands. * @return True on success, false otherwise. */ bool setAtomPosition3d(Index atomId, const Vector3& pos, const QString& undoText = tr("Change Atom Position")); /** * Get the label on an atom. * @param atomId The index of the atom. * @return The label of the atom indexed at @a atomId, or an empty string if * @a atomId is invalid or has no label. */ std::string atomLabel(Index atomId) const; /** * Set the label of a single atom. * @param atomId The index of the atom to modify. * @param label The new label. * @param undoText The undo text to be displayed for undo commands. * @return True on success, false otherwise. */ bool setAtomLabel(Index atomId, const std::string& label, const QString& undoText = tr("Change Atom Label")); /** * Get the label on a bond * @param bondId The index of the bond. * @return The label of the bond indexed at @a bondId, or an empty string if * @a bondId is invalid or has no label. */ std::string bondLabel(Index bondId) const; /** * Set the label of a single bond. * @param bondId The index of the bond to modify. * @param label The new label. * @param undoText The undo text to be displayed for undo commands. * @return True on success, false otherwise. */ bool setBondLabel(Index bondId, const std::string& label, const QString& undoText = tr("Change Bond Label")); /** * Set whether the specified atom is selected or not. */ void setAtomSelected(Index atomId, bool selected, const QString& undoText = tr("Change Selection")); /** * Query whether the supplied atom index has been selected. */ bool atomSelected(Index atomId) const; bool setAtomPosition2d(Index, const Vector2&) { return false; } Vector2 atomPosition2d(Index) { return Vector2(0, 0); } const Core::Array& atomPositions2d() const { return m_molecule.m_positions2d; } /** * Get the hybridization for the requested atom. * @param atomId The index of the atom. * @return The hybridization of the atom indexed at @a atomId, or * 0 if @a atomId is invalid. */ Core::AtomHybridization hybridization(Index atomId) const; /** * Set the hybridization of a single atom. * @param atomId The index of the atom to modify. * @param hyb The new hybridization. * @return True on success, false otherwise. */ bool setHybridization(Index atomId, Core::AtomHybridization hyb); /** * Get the formal charge for the requested atom. * @param atomId The index of the atom. * @return The formal charge of the atom indexed at @a atomId, or * 0 if @a atomId is invalid. */ signed char formalCharge(Index atomId) const; /** * Set the formal charge of a single atom. * @param atomId The index of the atom to modify. * @param charge The new formal charge. * @return True on success, false otherwise. */ bool setFormalCharge(Index atomId, signed char charge); /** * Get the color for the requested atom. * @param atomId The index of the atom. * @return The color of the atom indexed at @a atomId, or * (0, 0, 0) if @a atomId is invalid. If no color is set for the * given atomId, the default color for the atomic number of * the atomId is returned. */ Vector3ub color(Index atomId) const; /** * Set the color of a single atom. * @param atomId The index of the atom to modify. * @param color The new color. * @return True on success, false otherwise. */ bool setColor(Index atomId, Vector3ub color); bool setLayer(Index atomId, size_t layer); size_t layer(Index atomId) const; /** * Create a new bond in the molecule. * @param atom1 The first atom in the bond. * @param atom2 The second order in the bond. * @param order The bond order. * @return The new bond object. Will be invalid if @a atom1 or @a atom2 does * not exist. * @{ */ BondType addBond(Index atom1, Index atom2, unsigned char order = 1); BondType addBond(const AtomType& atom1, const AtomType& atom2, unsigned char order = 1); /** @} */ /** * Get a bond object. * @param bondId The index of the requested bond. * @return The requested bond object. Will be invalid if @a bondId >= * @a bondCount(). */ BondType bond(Index bondId) const; /** * Get a bond object. * @param atom1 The index of one atom in the bond. * @param atom2 The index of the other atom in bond. * @return The requested bond object. Will be invalid if @a atom1 or @a atom2 * do not exist. */ BondType bond(Index atom1, Index atom2) const; /** * Get a bond object. * @param atom1 One atom in the bond. * @param atom2 The other atom in bond. * @return The requested bond object. Will be invalid if @a atom1 or @a atom2 * are invalid. */ BondType bond(const AtomType& atom1, const AtomType& atom2) const; /** * Get a bond object from its unique id. * @param bondUid The unique id of the bond. * @return The requested bond object. Will be invalid if @a bondUid is not in * use. */ BondType bondByUniqueId(Index bondUid) const; /** * Get the unique id of a bond. * @param bondId The index of the requested bond. * @return The unique id currently assigned to the bond at index @a bondId */ Index bondUniqueId(Index bondId) const; /** * Get the unique id of a bond. * @param bond The requested bond object. * @return The unique id currently assigned to @a bond. */ Index bondUniqueId(const BondType& bond) const; /** * @return The number of bonds in the molecule. */ Index bondCount() const; /** * Remove the requested bond. * @return True on success, false otherwise. * @{ */ bool removeBond(Index bondId); bool removeBond(const BondType& bond); bool removeBond(Index atom1, Index atom2); bool removeBond(const AtomType& atom1, const AtomType& atom2); /** @} */ /** * Remove all bonds from the molecule. */ void clearBonds(); /** * Find bonds connected to an atom. * @param atom The atom of interest. * @return An array of bond objects that are attached to the specified atom. */ Core::Array bonds(const AtomType& atom) const; /** * Find bonds connected to an atom. * @param atomId The index for the atom of interest. * @return An array of bond objects that are attached to the specified atom. */ Core::Array bonds(const Index& atomId) const; /** * @return An array of bond orders for all bonds in the molecule, indexed by * bond index. */ const Core::Array& bondOrders() const; /** * Get the order of a bond. * @param bondId The id of the bond. * @return The bond order. */ unsigned char bondOrder(Index bondId) const; /** * Replace the current array of bond orders. * @param orders The new array. * @return True on success, false on failure. */ bool setBondOrders(const Core::Array& orders); /** * Set the order of a bond in the molecule. * @param bondId The bond's index. * @param order The new order of the bond. * @return True on success, false on failure. */ bool setBondOrder(Index bondId, unsigned char order); /** * @return An array of all bonded atoms in the molecule, indexed by bond * index. * Each bond pair is represented by a pair of atom indices. */ const Core::Array>& bondPairs() const; /** * Get the set of bonded atoms corresponding to @a bondId. * @param bondId The index of the requested bond. * @return The bonded atom pair, represented as a pair of atom indices. */ std::pair bondPair(Index bondId) const; /** * Replace the current array of bonded atoms. * @param pairs The array. * @return True on success, false on failure. * @note The bonded atoms are represented as a pair of bond indices. * @note If needed, the elements in @a pairs will be modified to ensure that * the first atom index is less than the second. */ bool setBondPairs(const Core::Array>& pairs); /** * Set the bonded atoms for a bond. * @param bondId The bond to modify. * @param pair The new bond pair. * @return True on success, false otherwise. * @note If needed, @a pair will be modified to ensure that the first atom * index is less than the second. */ bool setBondPair(Index bondId, const std::pair& pair); /** * Add a default unit cell to the molecule. Does nothing if there already * is a unit cell. Changes are emitted. */ void addUnitCell(); /** * Remove the unit cell from the molecule. Does nothing if there is * no unit cell. Changes are emitted. */ void removeUnitCell(); /** * Generic edit that changes the current molecule to be @a newMolecule. * Also sets the text for the undo command to be @a undoText. Changes are * emitted. * @param newMolecule The new molecule to be set. * @param changes The changes to be emitted. * @param undoText The text description for the undo command. */ void modifyMolecule( const Molecule& newMolecule, Molecule::MoleculeChanges changes, const QString& undoText = QStringLiteral("Modify Molecule")); /** * Generic edit that adds @a newMolecule to the current molecule. * Also sets the text for the undo command to be @a undoText. Changes are * emitted. * @param addMolecule The new molecule to be set. * @param undoText The text description for the undo command. */ void appendMolecule( const Molecule& addMolecule, const QString& undoText = QStringLiteral("Append Molecule")); /** * Edit the unit cell by replacing the current cell matrix with a new cell * matrix. Changes are emitted. * @param cellMatrix The new cell matrix to be set. * @param opts If TransformAtoms is specified, the atoms in @a molecule are * adjusted so that their fractional (lattice) coordinates are preserved. This * option is ignored if the input molecule has no unit cell. */ void editUnitCell(Matrix3 cellMatrix, Core::CrystalTools::Options opts); /** * Wrap atoms to the unit cell. Changes are emitted. */ void wrapAtomsToCell(); /** * Rotate cell to standard orientation. Changes are emitted. */ void rotateCellToStandardOrientation(); /** * Scale a cell's volume. Changes are emitted. * @param newVolume The new volume to be set. * @param options If CrystalTools::TransformAtoms is set, then * the atoms will be transformed during the scaling. */ void setCellVolume(double newVolume, Core::CrystalTools::Options options); /** * Build a supercell. Changes are emitted. * @param a The final number of units along the A vector (at least 1). * @param b The final number of units along the B vector (at least 1). * @param c The final number of units along the C vector (at least 1). */ void buildSupercell(unsigned int a, unsigned int b, unsigned int c); /** * Perform a Niggli reduction on the cell. Changes are emitted. */ void niggliReduceCell(); /** * Use spglib to reduce the cell to its primitive form. Changes are emitted. * @param cartTol Cartesian tolerance for primitive reduction. * @return True if the algorithm succeeded, and false if it failed. */ bool reduceCellToPrimitive(double cartTol = 1e-5); /** * Use spglib to convert the cell to its conventional form. Changes are * emitted. * @param cartTol Cartesian tolerance for conventionalization. * @return True if the algorithm succeeded, and false if it failed. */ bool conventionalizeCell(double cartTol = 1e-5); /** * Use spglib to symmetrize the cell. Changes are emitted. * @param cartTol Cartesian tolerance for symmetrization. * @return True if the algorithm succeeded, and false if it failed. */ bool symmetrizeCell(double cartTol = 1e-5); /** * Fill unit cell using transforms for the space group. Changes are emitted. * @param hallNumber The hall number to be used for transforming the cell. * @param cartTol Cartesian tolerance for comparing atom positions. * @return True if the algorithm succeeded, and false if it failed. */ bool fillUnitCell(unsigned short hallNumber, double cartTol = 1e-5); /** * Use transforms to reduce a cell to its asymmetric unit. Changes are * emitted. * @param hallNumber The hall number to be used for obtaining the transforms. * @param cartTol Cartesian tolerance for comparing atom positions. * @return True if the algorithm succeeded, and false if it failed. */ bool reduceCellToAsymmetricUnit(unsigned short hallNumber, double cartTol = 1e-5); /** * Call this function when you wish to merge all undo commands. * It turns on interactive mode to merge similar undo commands in a series * (in order to save space), and it uses QUndoStack's beginMacro() to merge * dissimilar undo commands together. You must call endMergeMode() to end * the merging section (undo and redo are unavailable while merge mode is * on). * @param undoName The name of the undo command */ void beginMergeMode(const QString& undoName = QStringLiteral("Draw")); /** * Call this function when you wish merge mode to end. This will turn off * interactive mode, and it will call QUndoStack's endMacro(). All of the * undo commands pushed between beginMergeMode() and endMergeMode() will be * merged into one undo command. beginMergeMode() should have been called * before this is called. */ void endMergeMode(); /** * @brief Begin or end an interactive edit. * * If enabled, certain undo operations will be merged together. For instance, * an editor dragging an atom through space in response to mouse movement will * only generate a single undo command containing the initial and final * positions and discard the intermediate states. If disabled, each * intermediate action will appear in the undo log. */ void setInteractive(bool b); /** * @return True if interactive mode is enabled, false otherwise. * @sa setInteractive */ bool isInteractive() const; /** * @return The QUndoStack for this molecule. * @{ */ QUndoStack& undoStack(); const QUndoStack& undoStack() const; /** @} */ class UndoCommand; friend class UndoCommand; /** Returns a vector of forces for the atoms in the molecule. */ const Core::Array& forceVectors() const; /** * Replace the current array of force vectors. * @param pos The new force vector array. Must be of length atomCount(). * @param undoText The undo text to be displayed for undo commands. * @return True on success, false otherwise. */ bool setForceVector( Index atomId, const Vector3& pos, const QString& undoText = QStringLiteral("Change Force Vectors")); public slots: /** * @brief Force the molecule to emit the changed() signal. * @param change See changed(). */ void emitChanged(unsigned int change); signals: /** * @brief Indicates that the molecule has changed. * @param change Use the MoleculeChange enum to check what has changed. * * The @p change variable indicates what has changed, i.e. if * change & Atoms == true then atoms were changed in some way, and if * change & Removed == true then one or more atoms were removed. */ void changed(unsigned int change); protected: Index findAtomUniqueId(Index atomId) const; Index findBondUniqueId(Index bondId) const; /** * @brief m_molecule still stored all data, this class acts upon it and builds * an undo/redo stack that can be used to offer undo and redo. */ Molecule& m_molecule; bool m_interactive; QUndoStack m_undoStack; friend class Molecule; }; class AVOGADROQTGUI_EXPORT RWAtom : public Core::AtomTemplate { public: RWAtom() : Core::AtomTemplate() {} RWAtom(RWMolecule* m, Index i) : Core::AtomTemplate(m, i) {} }; class AVOGADROQTGUI_EXPORT RWBond : public Core::BondTemplate { public: RWBond() : Core::BondTemplate() {} RWBond(RWMolecule* m, Index i) : Core::BondTemplate(m, i) {} }; inline RWMolecule::AtomType RWMolecule::atom(Index atomId) const { return AtomType(const_cast(this), atomId); } inline RWMolecule::AtomType RWMolecule::atomByUniqueId(Index atomUId) const { return atomUId < m_molecule.m_atomUniqueIds.size() ? AtomType(const_cast(this), m_molecule.m_atomUniqueIds[atomUId]) : AtomType(); } inline Index RWMolecule::atomUniqueId(Index atomId) const { return findAtomUniqueId(atomId); } inline Index RWMolecule::atomUniqueId(const RWMolecule::AtomType& a) const { return a.molecule() == this ? findAtomUniqueId(a.index()) : MaxIndex; } inline Index RWMolecule::atomCount() const { return m_molecule.atomCount(); } inline bool RWMolecule::removeAtom(const AtomType& a) { return a.molecule() == this ? removeAtom(a.index()) : false; } inline const Core::Array& RWMolecule::atomicNumbers() const { return m_molecule.atomicNumbers(); } inline unsigned char RWMolecule::atomicNumber(Index atomId) const { return m_molecule.atomicNumber(atomId); } inline const Core::Array& RWMolecule::atomPositions3d() const { return m_molecule.atomPositions3d(); } inline Vector3 RWMolecule::atomPosition3d(Index atomId) const { return m_molecule.atomPosition3d(atomId); } inline std::string RWMolecule::atomLabel(Index atomId) const { return m_molecule.atomLabel(atomId); } inline std::string RWMolecule::bondLabel(Index bondId) const { return m_molecule.bondLabel(bondId); } inline Core::AtomHybridization RWMolecule::hybridization(Index atomId) const { return m_molecule.hybridization(atomId); } inline signed char RWMolecule::formalCharge(Index atomId) const { return m_molecule.formalCharge(atomId); } inline Vector3ub RWMolecule::color(Index atomId) const { return m_molecule.color(atomId); } inline size_t RWMolecule::layer(Index atomId) const { return m_molecule.layer(atomId); } inline RWMolecule::BondType RWMolecule::addBond(const AtomType& atom1, const AtomType& atom2, unsigned char order) { if (atom1.molecule() != atom2.molecule() || atom1.molecule() != this) return BondType(); return addBond(atom1.index(), atom2.index(), order); } inline RWMolecule::BondType RWMolecule::bond(Index bondId) const { return BondType(const_cast(this), bondId); } inline RWMolecule::BondType RWMolecule::bond( const RWMolecule::AtomType& atom1, const RWMolecule::AtomType& atom2) const { if (atom1.molecule() == atom2.molecule() && atom1.molecule() == this) return bond(atom1.index(), atom2.index()); return BondType(); } inline RWMolecule::BondType RWMolecule::bondByUniqueId(Index bondUid) const { return bondUid < m_molecule.m_bondUniqueIds.size() ? BondType(const_cast(this), m_molecule.m_bondUniqueIds[bondUid]) : BondType(); } inline Index RWMolecule::bondUniqueId(Index bondId) const { return findBondUniqueId(bondId); } inline Index RWMolecule::bondUniqueId(const RWMolecule::BondType& b) const { return b.molecule() == this ? findBondUniqueId(b.index()) : MaxIndex; } inline Index RWMolecule::bondCount() const { return m_molecule.bondCount(); } inline bool RWMolecule::removeBond(const RWMolecule::BondType& b) { return b.molecule() == this ? removeBond(b.index()) : false; } inline bool RWMolecule::removeBond(Index atom1, Index atom2) { return removeBond(bond(atom1, atom2).index()); } inline bool RWMolecule::removeBond(const RWMolecule::AtomType& atom1, const RWMolecule::AtomType& atom2) { if (atom1.molecule() != atom2.molecule() || atom1.molecule() != this) return false; return removeBond(bond(atom1.index(), atom2.index()).index()); } inline Core::Array RWMolecule::bonds( const RWMolecule::AtomType& a) const { return a.molecule() == this ? bonds(a.index()) : Core::Array(); } inline Core::Array RWMolecule::bonds( const Index& atomId) const { auto atomBonds = m_molecule.bonds(atomId); Core::Array result; for (Index i = 0; i < atomBonds.size(); ++i) { result.push_back( BondType(const_cast(this), atomBonds[i].index())); } return result; } inline const Core::Array& RWMolecule::bondOrders() const { return m_molecule.bondOrders(); } inline unsigned char RWMolecule::bondOrder(Index bondId) const { return m_molecule.bondOrder(bondId); } inline const Core::Array>& RWMolecule::bondPairs() const { return m_molecule.bondPairs(); } inline std::pair RWMolecule::bondPair(Index bondId) const { return m_molecule.bondPair(bondId); } inline void RWMolecule::beginMergeMode(const QString& undoName) { m_interactive = true; m_undoStack.beginMacro(undoName); } inline void RWMolecule::endMergeMode() { m_interactive = false; m_undoStack.endMacro(); } inline void RWMolecule::setInteractive(bool b) { m_interactive = b; } inline bool RWMolecule::isInteractive() const { return m_interactive; } inline QUndoStack& RWMolecule::undoStack() { return m_undoStack; } inline const QUndoStack& RWMolecule::undoStack() const { return m_undoStack; } inline const Core::Array& RWMolecule::forceVectors() const { return m_molecule.forceVectors(); } } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_RWMOLECULE_H avogadrolibs-1.101.0/avogadro/qtgui/rwmolecule_undo.h000066400000000000000000000547561506155467400226730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_RWMOLECULE_UNDO_H #define AVOGADRO_QTGUI_RWMOLECULE_UNDO_H #include "rwmolecule.h" #include #include namespace Avogadro { namespace QtGui { using Core::Array; using Core::AtomHybridization; using Core::UnitCell; // Base class for all undo commands used by this class. // Used to expose molecule internals without needing to add explicit friendships // between all undo commands and the container. // Subclasses that want to support automatic merging should use the // MergeUndoCommand interface below. // Subclass implementations are inline in the code below. Specific undo // operations are kept near the RWMolecule methods that use them. class RWMolecule::UndoCommand : public QUndoCommand { public: UndoCommand(RWMolecule& m) : QUndoCommand(tr("Modify Molecule")), m_mol(m), m_molecule(m.m_molecule) { } protected: Array& positions3d() { return m_molecule.atomPositions3d(); } Array& atomUniqueIds() { return m_mol.m_molecule.atomUniqueIds(); } Array& bondUniqueIds() { return m_mol.m_molecule.bondUniqueIds(); } RWMolecule& m_mol; QtGui::Molecule& m_molecule; }; namespace { enum MergeIds { SetPositions3dMergeId = 0, SetPosition3dMergeId, SetForceVectorMergeId, SetBondOrderMergeId, ModifySelectionMergeId, ModifyColorsMergeId }; // Base class for undo commands that can be merged together, overriding the // "after" state of an old command with that of the new command. // Intended for use with RWMolecule's interactive mode. // To add a new class, add a new entry to the MergeIds enum above and use that // symbolic value as the template parameter. Implement the mergeWith() method // to update the "after" state. See SetPositions3dCommand for an example. template class MergeUndoCommand : public RWMolecule::UndoCommand { bool m_canMerge; public: MergeUndoCommand(RWMolecule& m) : UndoCommand(m), m_canMerge(false) {} void setCanMerge(bool merge) { m_canMerge = merge; } bool canMerge() const { return m_canMerge; } int id() const override { return m_canMerge ? Id : -1; } }; } // namespace namespace { class AddAtomCommand : public RWMolecule::UndoCommand { unsigned char m_atomicNumber; bool m_usingPositions; Index m_atomId; Index m_atomUid; size_t m_layer; public: AddAtomCommand(RWMolecule& m, unsigned char aN, bool usingPositions, Index atomId, Index uid) : UndoCommand(m), m_atomicNumber(aN), m_usingPositions(usingPositions), m_atomId(atomId), m_atomUid(uid) { m_layer = m_molecule.layer().activeLayer(); } void redo() override { assert(m_molecule.atomCount() == m_atomId); if (m_usingPositions) m_molecule.addAtom(m_atomicNumber, Vector3::Zero(), m_atomUid); else m_molecule.addAtom(m_atomicNumber, m_atomUid); m_molecule.layer().addAtom(m_layer, m_atomId); } void undo() override { assert(m_molecule.atomCount() == m_atomId + 1); m_layer = m_molecule.layer().getLayerID(m_atomId); m_molecule.removeAtom(m_atomId); } }; } // namespace namespace { class RemoveAtomCommand : public RWMolecule::UndoCommand { Index m_atomId; Index m_atomUid; unsigned char m_atomicNumber; Vector3 m_position3d; Array> m_bonds; Array m_orders; size_t m_layer; public: RemoveAtomCommand(RWMolecule& m, Index atomId, Index uid, unsigned char aN, const Vector3& pos) : UndoCommand(m), m_atomId(atomId), m_atomUid(uid), m_atomicNumber(aN), m_position3d(pos) { } void redo() override { assert(m_atomId < m_molecule.atomCount()); m_layer = m_molecule.layer().getLayerID(m_atomId); m_bonds = m_molecule.getAtomBonds(m_atomId); m_orders = m_molecule.getAtomOrders(m_atomId); m_molecule.removeAtom(m_atomId); } void undo() override { m_molecule.addAtom(m_atomicNumber, m_position3d, m_atomUid); // Swap the moved and unremoved atom data if needed Index movedId = m_mol.atomCount() - 1; m_molecule.layer().addAtom(m_layer, movedId); m_molecule.swapAtom(m_atomId, movedId); m_molecule.addBonds(m_bonds, m_orders); m_bonds.clear(); } }; } // namespace namespace { class SetAtomicNumbersCommand : public RWMolecule::UndoCommand { Core::Array m_oldAtomicNumbers; Core::Array m_newAtomicNumbers; public: SetAtomicNumbersCommand(RWMolecule& m, const Core::Array& oldAtomicNumbers, const Core::Array& newAtomicNumbers) : UndoCommand(m), m_oldAtomicNumbers(oldAtomicNumbers), m_newAtomicNumbers(newAtomicNumbers) { } void redo() override { m_molecule.setAtomicNumbers(m_newAtomicNumbers); } void undo() override { m_molecule.setAtomicNumbers(m_oldAtomicNumbers); } }; } // namespace namespace { class SetAtomicNumberCommand : public RWMolecule::UndoCommand { Index m_atomId; unsigned char m_oldAtomicNumber; unsigned char m_newAtomicNumber; public: SetAtomicNumberCommand(RWMolecule& m, Index atomId, unsigned char oldAtomicNumber, unsigned char newAtomicNumber) : UndoCommand(m), m_atomId(atomId), m_oldAtomicNumber(oldAtomicNumber), m_newAtomicNumber(newAtomicNumber) { } void redo() override { m_molecule.setAtomicNumber(m_atomId, m_newAtomicNumber); } void undo() override { m_molecule.setAtomicNumber(m_atomId, m_oldAtomicNumber); } }; } // namespace namespace { class SetPositions3dCommand : public MergeUndoCommand { Core::Array m_oldPositions3d; Core::Array m_newPositions3d; public: SetPositions3dCommand(RWMolecule& m, const Core::Array& oldPositions3d, const Core::Array& newPositions3d) : MergeUndoCommand(m), m_oldPositions3d(oldPositions3d), m_newPositions3d(newPositions3d) { } void redo() override { positions3d() = m_newPositions3d; } void undo() override { positions3d() = m_oldPositions3d; } bool mergeWith(const QUndoCommand* other) override { const SetPositions3dCommand* o = dynamic_cast(other); if (o) { m_newPositions3d = o->m_newPositions3d; return true; } return false; } }; } // namespace namespace { class SetPosition3dCommand : public MergeUndoCommand { Array m_atomIds; Array m_oldPosition3ds; Array m_newPosition3ds; public: SetPosition3dCommand(RWMolecule& m, Index atomId, const Vector3& oldPosition3d, const Vector3& newPosition3d) : MergeUndoCommand(m), m_atomIds(1, atomId), m_oldPosition3ds(1, oldPosition3d), m_newPosition3ds(1, newPosition3d) { } void redo() override { for (size_t i = 0; i < m_atomIds.size(); ++i) positions3d()[m_atomIds[i]] = m_newPosition3ds[i]; } void undo() override { for (size_t i = 0; i < m_atomIds.size(); ++i) positions3d()[m_atomIds[i]] = m_oldPosition3ds[i]; } bool mergeWith(const QUndoCommand* o) override { const SetPosition3dCommand* other = dynamic_cast(o); if (!other) return false; size_t numAtoms = other->m_atomIds.size(); if (numAtoms != other->m_oldPosition3ds.size() || numAtoms != other->m_newPosition3ds.size()) { return false; } for (size_t i = 0; i < numAtoms; ++i) { const Index& atomId = other->m_atomIds[i]; const Vector3& oldPos = other->m_oldPosition3ds[i]; const Vector3& newPos = other->m_newPosition3ds[i]; Array::const_iterator idsBegin = m_atomIds.begin(); Array::const_iterator idsEnd = m_atomIds.end(); Array::const_iterator match = std::find(idsBegin, idsEnd, atomId); if (match == idsEnd) { // Append a new atom: m_atomIds.push_back(atomId); m_oldPosition3ds.push_back(oldPos); m_newPosition3ds.push_back(newPos); } else { // Overwrite the existing movement: size_t offset = std::distance(idsBegin, match); assert(m_atomIds[offset] == atomId); m_newPosition3ds[offset] = newPos; } } return true; } }; } // namespace namespace { class SetAtomHybridizationCommand : public RWMolecule::UndoCommand { Index m_atomId; AtomHybridization m_oldHybridization; AtomHybridization m_newHybridization; public: SetAtomHybridizationCommand(RWMolecule& m, Index atomId, AtomHybridization oldHybridization, AtomHybridization newHybridization) : UndoCommand(m), m_atomId(atomId), m_oldHybridization(oldHybridization), m_newHybridization(newHybridization) { } void redo() override { m_molecule.setHybridization(m_atomId, m_newHybridization); } void undo() override { m_molecule.setHybridization(m_atomId, m_oldHybridization); } }; } // namespace namespace { class SetAtomFormalChargeCommand : public RWMolecule::UndoCommand { Index m_atomId; signed char m_oldCharge; signed char m_newCharge; public: SetAtomFormalChargeCommand(RWMolecule& m, Index atomId, signed char oldCharge, signed char newCharge) : UndoCommand(m), m_atomId(atomId), m_oldCharge(oldCharge), m_newCharge(newCharge) { } void redo() override { m_molecule.setFormalCharge(m_atomId, m_newCharge); } void undo() override { m_molecule.setFormalCharge(m_atomId, m_oldCharge); } }; } // namespace namespace { class SetAtomColorCommand : public RWMolecule::UndoCommand { Index m_atomId; Vector3ub m_oldColor; Vector3ub m_newColor; public: SetAtomColorCommand(RWMolecule& m, Index atomId, Vector3ub oldColor, Vector3ub newColor) : UndoCommand(m), m_atomId(atomId), m_oldColor(oldColor), m_newColor(newColor) { } void redo() override { m_molecule.setColor(m_atomId, m_newColor); } void undo() override { m_molecule.setColor(m_atomId, m_oldColor); } }; class SetLayerCommand : public RWMolecule::UndoCommand { Index m_atomId; size_t m_oldLayer; size_t m_newLayer; public: SetLayerCommand(RWMolecule& m, Index atomId, size_t oldLayer, size_t newLayer) : UndoCommand(m), m_atomId(atomId), m_oldLayer(oldLayer), m_newLayer(newLayer) { } void redo() override { m_molecule.setLayer(m_atomId, m_newLayer); } void undo() override { m_molecule.setLayer(m_atomId, m_oldLayer); } }; class AddBondCommand : public RWMolecule::UndoCommand { unsigned char m_bondOrder; std::pair m_bondPair; Index m_bondId; Index m_bondUid; public: AddBondCommand(RWMolecule& m, unsigned char order, const std::pair& bondPair, Index bondId, Index uid) : UndoCommand(m), m_bondOrder(order), m_bondPair(bondPair), m_bondId(bondId), m_bondUid(uid) { } void redo() override { assert(m_molecule.bondCount() == m_bondId); m_molecule.addBond(m_bondPair.first, m_bondPair.second, m_bondOrder); } void undo() override { // we know this is the top so just a simple remove m_molecule.removeBond(m_bondId); } }; } // namespace namespace { class RemoveBondCommand : public RWMolecule::UndoCommand { Index m_bondId; Index m_bondUid; std::pair m_bondPair; unsigned char m_bondOrder; public: RemoveBondCommand(RWMolecule& m, Index bondId, Index bondUid, const std::pair& bondPair, unsigned char bondOrder) : UndoCommand(m), m_bondId(bondId), m_bondUid(bondUid), m_bondPair(bondPair), m_bondOrder(bondOrder) { } void redo() override { m_molecule.removeBond(m_bondId); } void undo() override { m_molecule.addBond(m_bondPair.first, m_bondPair.second, m_bondOrder, m_bondUid); Index movedId = m_molecule.bondCount() - 1; m_molecule.swapBond(m_bondId, movedId); } }; } // namespace namespace { class SetBondOrdersCommand : public RWMolecule::UndoCommand { Array m_oldBondOrders; Array m_newBondOrders; public: SetBondOrdersCommand(RWMolecule& m, const Array& oldBondOrders, const Array& newBondOrders) : UndoCommand(m), m_oldBondOrders(oldBondOrders), m_newBondOrders(newBondOrders) { } void redo() override { m_molecule.setBondOrders(m_newBondOrders); } void undo() override { m_molecule.setBondOrders(m_oldBondOrders); } }; } // namespace namespace { class SetBondOrderCommand : public MergeUndoCommand { Index m_bondId; unsigned char m_oldBondOrder; unsigned char m_newBondOrder; public: SetBondOrderCommand(RWMolecule& m, Index bondId, unsigned char oldBondOrder, unsigned char newBondOrder) : MergeUndoCommand(m), m_bondId(bondId), m_oldBondOrder(oldBondOrder), m_newBondOrder(newBondOrder) { } void redo() override { m_molecule.setBondOrder(m_bondId, m_newBondOrder); } void undo() override { m_molecule.setBondOrder(m_bondId, m_oldBondOrder); } bool mergeWith(const QUndoCommand* other) override { const SetBondOrderCommand* o = dynamic_cast(other); // Only merge when the bondIds match. if (o && o->m_bondId == this->m_bondId) { this->m_newBondOrder = o->m_newBondOrder; return true; } return false; } }; } // namespace namespace { class SetBondPairsCommand : public RWMolecule::UndoCommand { Array> m_oldBondPairs; Array> m_newBondPairs; public: SetBondPairsCommand(RWMolecule& m, const Array>& oldBondPairs, const Array>& newBondPairs) : UndoCommand(m), m_oldBondPairs(oldBondPairs), m_newBondPairs(newBondPairs) { } void redo() override { m_molecule.setBondPairs(m_newBondPairs); } void undo() override { m_molecule.setBondPairs(m_oldBondPairs); } }; } // namespace namespace { class SetBondPairCommand : public RWMolecule::UndoCommand { Index m_bondId; std::pair m_oldBondPair; std::pair m_newBondPair; public: SetBondPairCommand(RWMolecule& m, Index bondId, const std::pair& oldBondPair, const std::pair& newBondPair) : UndoCommand(m), m_bondId(bondId), m_oldBondPair(oldBondPair), m_newBondPair(newBondPair) { } void redo() override { m_molecule.setBondPair(m_bondId, m_newBondPair); } void undo() override { m_molecule.setBondPair(m_bondId, m_oldBondPair); } }; } // namespace namespace { class AddUnitCellCommand : public RWMolecule::UndoCommand { UnitCell m_newUnitCell; public: AddUnitCellCommand(RWMolecule& m, const UnitCell& newUnitCell) : UndoCommand(m), m_newUnitCell(newUnitCell) { } void redo() override { m_mol.molecule().setUnitCell(new UnitCell(m_newUnitCell)); } void undo() override { m_mol.molecule().setUnitCell(nullptr); } }; } // namespace namespace { class RemoveUnitCellCommand : public RWMolecule::UndoCommand { UnitCell m_oldUnitCell; public: RemoveUnitCellCommand(RWMolecule& m, const UnitCell& oldUnitCell) : UndoCommand(m), m_oldUnitCell(oldUnitCell) { } void redo() override { m_mol.molecule().setUnitCell(nullptr); } void undo() override { m_mol.molecule().setUnitCell(new UnitCell(m_oldUnitCell)); } }; } // namespace namespace { class ModifyMoleculeCommand : public RWMolecule::UndoCommand { Molecule m_oldMolecule; Molecule m_newMolecule; public: ModifyMoleculeCommand(RWMolecule& m, const Molecule& oldMolecule, const Molecule& newMolecule) : UndoCommand(m), m_oldMolecule(oldMolecule), m_newMolecule(newMolecule) { } void redo() override { m_mol.molecule() = m_newMolecule; } void undo() override { m_mol.molecule() = m_oldMolecule; } }; } // namespace namespace { class SetForceVectorCommand : public MergeUndoCommand { Array m_atomIds; Array m_oldForceVectors; Array m_newForceVectors; public: SetForceVectorCommand(RWMolecule& m, Index atomId, const Vector3& oldForceVector, const Vector3& newForceVector) : MergeUndoCommand(m), m_atomIds(1, atomId), m_oldForceVectors(1, oldForceVector), m_newForceVectors(1, newForceVector) { } void redo() override { for (size_t i = 0; i < m_atomIds.size(); ++i) m_molecule.setForceVector(m_atomIds[i], m_newForceVectors[i]); } void undo() override { for (size_t i = 0; i < m_atomIds.size(); ++i) m_molecule.setForceVector(m_atomIds[i], m_oldForceVectors[i]); } bool mergeWith(const QUndoCommand* o) override { const SetForceVectorCommand* other = dynamic_cast(o); if (!other) return false; size_t numAtoms = other->m_atomIds.size(); if (numAtoms != other->m_oldForceVectors.size() || numAtoms != other->m_newForceVectors.size()) { return false; } for (size_t i = 0; i < numAtoms; ++i) { const Index& atomId = other->m_atomIds[i]; const Vector3& oldPos = other->m_oldForceVectors[i]; const Vector3& newPos = other->m_newForceVectors[i]; Array::const_iterator idsBegin = m_atomIds.begin(); Array::const_iterator idsEnd = m_atomIds.end(); Array::const_iterator match = std::find(idsBegin, idsEnd, atomId); if (match == idsEnd) { // Append a new atom: m_atomIds.push_back(atomId); m_oldForceVectors.push_back(oldPos); m_newForceVectors.push_back(newPos); } else { // Overwrite the existing movement: size_t offset = std::distance(idsBegin, match); assert(m_atomIds[offset] == atomId); m_newForceVectors[offset] = newPos; } } return true; } }; } // namespace namespace { class ModifyAtomLabelCommand : public RWMolecule::UndoCommand { Index m_atomId; std::string m_newLabel; std::string m_oldLabel; public: ModifyAtomLabelCommand(RWMolecule& m, Index atomId, const std::string& label) : UndoCommand(m), m_atomId(atomId), m_newLabel(label) { m_oldLabel = m_mol.molecule().atomLabel(m_atomId); } void redo() override { m_mol.molecule().setAtomLabel(m_atomId, m_newLabel); } void undo() override { m_mol.molecule().setAtomLabel(m_atomId, m_oldLabel); } }; } // namespace namespace { class ModifyBondLabelCommand : public RWMolecule::UndoCommand { Index m_bondId; std::string m_newLabel; std::string m_oldLabel; public: ModifyBondLabelCommand(RWMolecule& m, Index bondId, const std::string& label) : UndoCommand(m), m_bondId(bondId), m_newLabel(label) { m_oldLabel = m_mol.molecule().bondLabel(m_bondId); } void redo() override { m_mol.molecule().setBondLabel(m_bondId, m_newLabel); } void undo() override { m_mol.molecule().setBondLabel(m_bondId, m_oldLabel); } }; } // namespace namespace { class ModifySelectionCommand : public MergeUndoCommand { std::vector m_newSelectedAtoms; std::vector m_oldSelectedAtoms; public: ModifySelectionCommand(RWMolecule& m, Index atomId, bool selected) : MergeUndoCommand(m) { Index atomCount = m_mol.molecule().atomCount(); m_oldSelectedAtoms.resize(atomCount); m_newSelectedAtoms.resize(atomCount); for (Index i = 0; i < atomCount; ++i) { m_oldSelectedAtoms[i] = m_molecule.atomSelected(i); m_newSelectedAtoms[i] = m_molecule.atomSelected(i); } m_newSelectedAtoms[atomId] = selected; } void redo() override { Index atomCount = m_mol.molecule().atomCount(); for (Index i = 0; i < atomCount; ++i) m_mol.molecule().setAtomSelected(i, m_newSelectedAtoms[i]); } void undo() override { Index atomCount = m_mol.molecule().atomCount(); for (Index i = 0; i < atomCount; ++i) m_mol.molecule().setAtomSelected(i, m_oldSelectedAtoms[i]); } bool mergeWith(const QUndoCommand* other) override { const ModifySelectionCommand* o = dynamic_cast(other); if (!o) return false; // check if the atom count matches Index numAtoms = m_oldSelectedAtoms.size(); if (numAtoms != o->m_oldSelectedAtoms.size() || numAtoms != o->m_newSelectedAtoms.size()) return false; // merge these, by grabbing the new selected atoms m_newSelectedAtoms = o->m_newSelectedAtoms; return true; } }; } // namespace /* namespace { class ModifyColorCommand : public MergeUndoCommand { Array m_newSelectedAtoms; Array m_oldSelectedAtoms; public: ModifySelectionCommand(RWMolecule& m, Index atomId, bool selected) : MergeUndoCommand(m) { Index atomCount = m_mol.molecule().atomCount(); m_oldSelectedAtoms.reserve(atomCount); m_newSelectedAtoms.reserve(atomCount); for (Index i = 0; i < atomCount; ++i) { m_oldSelectedAtoms[i] = m_molecule.atomSelected(i); m_newSelectedAtoms[i] = m_molecule.atomSelected(i); } m_newSelectedAtoms[atomId] = selected; } void redo() override { Index atomCount = m_mol.molecule().atomCount(); for (Index i = 0; i < atomCount; ++i) m_mol.molecule().setAtomSelected(i, m_newSelectedAtoms[i]); } void undo() override { Index atomCount = m_mol.molecule().atomCount(); for (Index i = 0; i < atomCount; ++i) m_mol.molecule().setAtomSelected(i, m_oldSelectedAtoms[i]); } bool mergeWith(const QUndoCommand* other) override { const ModifySelectionCommand* o = dynamic_cast(other); if (o) { // check if the atom count matches Index numAtoms = m_oldSelectedAtoms.size(); if (numAtoms != o->m_oldSelectedAtoms.size() || numAtoms != o->m_newSelectedAtoms.size()) { return false; } // merge these, by grabbing the new selected atoms for (Index i = 0; i < numAtoms; ++i) m_newSelectedAtoms[i] = o->m_newSelectedAtoms[i]; return true; } return false; } }; } // namespace */ } // namespace QtGui } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtgui/sceneplugin.cpp000066400000000000000000000016731506155467400223250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "sceneplugin.h" namespace Avogadro::QtGui { ScenePlugin::ScenePlugin(QObject* parent_) : QObject(parent_) {} ScenePlugin::~ScenePlugin() {} void ScenePlugin::process(const QtGui::Molecule&, Rendering::GroupNode&) {} void ScenePlugin::processEditable(const RWMolecule&, Rendering::GroupNode&) {} QWidget* ScenePlugin::setupWidget() { return nullptr; } bool ScenePlugin::isEnabled() const { return m_layerManager.isEnabled(); } bool ScenePlugin::isActiveLayerEnabled() const { return m_layerManager.isActiveLayerEnabled(); } void ScenePlugin::setEnabled(bool enable) { m_layerManager.setEnabled(enable); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/sceneplugin.h000066400000000000000000000067311506155467400217720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_SCENEPLUGIN_H #define AVOGADRO_QTGUI_SCENEPLUGIN_H #include "avogadroqtguiexport.h" #include "pluginlayermanager.h" #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace Rendering { class GroupNode; } namespace QtGui { class RWMolecule; class Molecule; /** * @class ScenePluginFactory sceneplugin.h * @brief The base class for scene plugin factories in Avogadro. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT ScenePlugin : public QObject { Q_OBJECT public: explicit ScenePlugin(QObject* parent = nullptr); ~ScenePlugin() override; /** * Process the supplied atom, and add the necessary primitives to the scene. */ virtual void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node); virtual void processEditable(const RWMolecule& molecule, Rendering::GroupNode& node); /** * The name of the scene plugin, will be displayed in the user interface. */ virtual QString name() const = 0; /** * A description of the scene plugin, may be displayed in the user interface. */ virtual QString description() const = 0; /** * Returns true if the scene plugin has been enabled and is active. */ virtual bool isEnabled() const; /** * Returns true if the scene plugin has been enabled and is active in the * active scene. */ virtual bool isActiveLayerEnabled() const; /** * Set the enabled state of the plugin (default should be false). */ virtual void setEnabled(bool enable); /** * @return whether this plugin works with the current molecule. * This is used to determine if the plugin should be enabled in the UI. * For example, a plugin that only works with unit cells would return * false if the current molecule does not include a unit cell. */ virtual bool isApplicable() const { return true; } /** * @return the widget for plugin settings (e.g., colors, widths, etc.) */ virtual QWidget* setupWidget(); /** * This method exists to query without creating the widget. * @return true if the plugin has a setup widget */ virtual bool hasSetupWidget() const { return false; } /** * Returns if this plugin should be considered in the default behavior, * or it should reset to true or false. */ enum DefaultBehavior { Ignore, False, True }; virtual DefaultBehavior defaultBehavior() const { return Ignore; } signals: void drawablesChanged(); protected: PluginLayerManager m_layerManager; }; /** * @class ScenePluginFactory sceneplugin.h * @brief The base class for scene plugin factories in Avogadro. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT ScenePluginFactory : public Avogadro::QtPlugins::PluginFactory { public: ~ScenePluginFactory() override {} }; } // namespace QtGui } // namespace Avogadro Q_DECLARE_INTERFACE(Avogadro::QtGui::ScenePluginFactory, "org.openchemistry.avogadro.ScenePluginFactory") #endif // AVOGADRO_QTGUI_SCENEPLUGIN_H avogadrolibs-1.101.0/avogadro/qtgui/scenepluginmodel.cpp000066400000000000000000000116361506155467400233460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "scenepluginmodel.h" #include "sceneplugin.h" namespace Avogadro::QtGui { ScenePluginModel::ScenePluginModel(QObject* parent_) : QAbstractItemModel(parent_) { } QModelIndex ScenePluginModel::parent(const QModelIndex&) const { return QModelIndex(); } int ScenePluginModel::rowCount(const QModelIndex& parent_) const { if (parent_.isValid()) return 0; else return m_scenePlugins.size(); } int ScenePluginModel::columnCount(const QModelIndex&) const { return 2; } Qt::ItemFlags ScenePluginModel::flags(const QModelIndex& index_) const { auto* item = qobject_cast(static_cast(index_.internalPointer())); if (!item) return Qt::NoItemFlags; Qt::ItemFlags flags = Qt::ItemIsEnabled; if (!item->isApplicable()) flags = Qt::NoItemFlags; if (index_.column() == 0) return flags | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; else return flags; } bool ScenePluginModel::setData(const QModelIndex& index_, const QVariant& value, int role) { if (!index_.isValid() || index_.column() > 1) return false; auto* item = qobject_cast(static_cast(index_.internalPointer())); if (!item) return false; switch (role) { case Qt::CheckStateRole: if (value == Qt::Checked && !item->isActiveLayerEnabled()) { item->setEnabled(true); emit pluginStateChanged(item); } else if (value == Qt::Unchecked && item->isActiveLayerEnabled()) { item->setEnabled(false); emit pluginStateChanged(item); } emit dataChanged(index_, index_); return true; } return false; } QVariant ScenePluginModel::data(const QModelIndex& index_, int role) const { if (!index_.isValid() || index_.column() > 2) return QVariant(); auto* object = static_cast(index_.internalPointer()); auto* item = qobject_cast(object); if (!item) return QVariant(); // Simple lambda to convert QFlags to variant as in Qt 6 this needs help. auto toVariant = [&](auto flags) { return static_cast(flags); }; // check if setupWidget() returns something if (index_.column() == 1) { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return (item->hasSetupWidget()) ? "•••" : " "; case Qt::ToolTipRole: case Qt::WhatsThisRole: return tr("Settings"); case Qt::TextAlignmentRole: return toVariant(Qt::AlignLeft | Qt::AlignVCenter); default: return QVariant(); } } if (index_.column() == 0) { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return item->name(); case Qt::CheckStateRole: if (item->isActiveLayerEnabled()) return Qt::Checked; else return Qt::Unchecked; case Qt::ToolTipRole: case Qt::WhatsThisRole: return item->description(); case Qt::TextAlignmentRole: return toVariant(Qt::AlignLeft); default: return QVariant(); } } return QVariant(); } QModelIndex ScenePluginModel::index(int row, int column, const QModelIndex& parent_) const { if (!parent_.isValid() && row >= 0 && row < m_scenePlugins.size()) return createIndex(row, column, m_scenePlugins[row]); else return QModelIndex(); } void ScenePluginModel::clear() { m_scenePlugins.clear(); } ScenePlugin* ScenePluginModel::scenePlugin(const QModelIndex& index) const { if (!index.isValid() || index.column() != 0) return nullptr; return m_scenePlugins[index.row()]; } ScenePlugin* ScenePluginModel::scenePlugin(int row) const { if (row < 0 || row >= m_scenePlugins.size()) return nullptr; return m_scenePlugins[row]; } QList ScenePluginModel::scenePlugins() const { return m_scenePlugins; } QList ScenePluginModel::activeScenePlugins() const { QList result; foreach (ScenePlugin* plugin, m_scenePlugins) { if (plugin->isEnabled()) result << plugin; } return result; } void ScenePluginModel::addItem(ScenePlugin* item) { if (!m_scenePlugins.contains(item)) { m_scenePlugins.append(item); item->setParent(this); connect(item, SIGNAL(drawablesChanged()), SIGNAL(pluginConfigChanged())); } } void ScenePluginModel::removeItem(ScenePlugin* item) { m_scenePlugins.removeAll(item); } void ScenePluginModel::itemChanged() { auto* item = qobject_cast(sender()); if (item) { int row = m_scenePlugins.indexOf(item); if (row >= 0) emit dataChanged(createIndex(row, 0), createIndex(row, 0)); } } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/scenepluginmodel.h000066400000000000000000000037111506155467400230060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_SCENEPLUGINMODEL_H #define AVOGADRO_QTGUI_SCENEPLUGINMODEL_H #include "avogadroqtguiexport.h" #include namespace Avogadro { namespace QtGui { class ScenePlugin; /** * @class ScenePluginModel scenepluginmodel.h * * @brief A model containing scene plugins that will build up the scene. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT ScenePluginModel : public QAbstractItemModel { Q_OBJECT public: explicit ScenePluginModel(QObject* parent = nullptr); QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; bool setData(const QModelIndex& index, const QVariant& value, int role) override; QVariant data(const QModelIndex& index, int role) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; void clear(); QList scenePlugins() const; QList activeScenePlugins() const; ScenePlugin* scenePlugin(const QModelIndex& index) const; ScenePlugin* scenePlugin(int row) const; signals: void pluginStateChanged(Avogadro::QtGui::ScenePlugin*); void pluginConfigChanged(); public slots: void addItem(Avogadro::QtGui::ScenePlugin* item); void removeItem(Avogadro::QtGui::ScenePlugin* item); void itemChanged(); private: QList m_scenePlugins; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_SCENEPLUGINMODEL_H avogadrolibs-1.101.0/avogadro/qtgui/scriptloader.cpp000066400000000000000000000150541506155467400225020ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "scriptloader.h" #include "interfacescript.h" #include "utilities.h" #include #include #include #include #include #include #include #include namespace Avogadro::QtGui { ScriptLoader::ScriptLoader(QObject* parent_) : QObject(parent_) {} ScriptLoader::~ScriptLoader() {} bool ScriptLoader::queryProgramName(const QString& scriptFilePath, QString& displayName) { InterfaceScript gen(scriptFilePath); displayName = gen.displayName(); if (gen.hasErrors()) { displayName.clear(); qWarning() << tr("Cannot load script %1").arg(scriptFilePath); return false; } return true; } QMultiMap ScriptLoader::scriptList(const QString& type) { // List of directories to check. /// @todo Custom script locations QStringList dirs; QMultiMap scriptList; QSettings settings; // to cache the names of scripts QStringList scriptFiles = settings.value("scripts/" + type).toStringList(); QStringList scriptNames = settings.value("scripts/" + type + "/names").toStringList(); // hash from the last modified time and size of the scripts QStringList scriptHashes = settings.value("scripts/" + type + "/hashes").toStringList(); // add the default paths QStringList stdPaths = QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation); foreach (const QString& dirStr, stdPaths) { QString path = dirStr + '/' + type; dirs << path; // we'll check if these exist below } dirs << QCoreApplication::applicationDirPath() + "/../" + QtGui::Utilities::libraryDirectory() + "/avogadro2/scripts/" + type; // build up a list of possible files, then we check if they're real scripts QStringList fileList; foreach (const QString& dirStr, dirs) { #ifndef NDEBUG qDebug() << tr("Checking for %1 scripts in path %2").arg(type).arg(dirStr); #endif QDir dir(dirStr); if (dir.exists() && dir.isReadable()) { foreach ( const QFileInfo& file, dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot)) { QString filePath = file.absoluteFilePath(); if (file.isDir()) { // handle subdirectory packages with plugins.json files QFileInfo jsonManifest(filePath + "/plugin.json"); if (jsonManifest.isReadable()) { // load the JSON QFile jsonFile(jsonManifest.absoluteFilePath()); jsonFile.open(QIODevice::ReadOnly | QIODevice::Text); QByteArray data = jsonFile.readAll(); jsonFile.close(); QJsonDocument d = QJsonDocument::fromJson(data); if (!d.isNull() && d.isObject()) { /* find the "commands" array { "name": "Gaussian", "command": "gaussian" }, { "name": "MOPAC", "command": "mopac" }, { "name": "ORCA", "command": "orca" }, */ QJsonValue commands = d.object()["commands"]; if (commands.type() == QJsonValue::Array) { // check if "command.*" exists as a file QJsonArray list = commands.toArray(); for (auto&& i : list) { QJsonValue command = i.toObject()["command"]; QString name = command.toString(); if (name.isEmpty() || name.isNull()) continue; QFileInfo commandFile(filePath + '/' + name); if (commandFile.isReadable()) { fileList << commandFile.absoluteFilePath(); continue; } // doesn't exist, so try the .py version // TODO: set this up as a loop with name filters commandFile.setFile(filePath + '/' + name + ".py"); if (commandFile.isReadable()) { fileList << commandFile.absoluteFilePath(); continue; } } } // "commands" JSON is array } // document is json } // plugin.json file exists continue; } // end reading subdirectories with plugin.json if (file.isReadable()) fileList << filePath; } } // end dir.exists() } // end for directory list // go through the list of files to see if they're actually scripts foreach (const QString& filePath, fileList) { QFileInfo file(filePath); // check if we have this from the last time if (scriptFiles.contains(filePath)) { int index = scriptFiles.indexOf(filePath); if (index != -1) { QString hash = scriptHashes.at(index); // got a match? if (hash == QString::number(file.size()) + file.lastModified().toString()) { scriptList.insert(scriptNames.at(index), filePath); continue; } } } QString displayName; if (queryProgramName(filePath, displayName)) { if (displayName.isEmpty()) continue; // don't add empty menu items // Might be another script with the same name if (scriptList.contains(displayName)) { // check the last-modified-time of the existing case QFileInfo existingFile(scriptList.value(displayName)); if (file.lastModified() > existingFile.lastModified()) { // replace existing with this new entry scriptList.replace(displayName, filePath); // update the cache int index = scriptFiles.indexOf(filePath); if (index != -1) { scriptFiles.replace(index, filePath); scriptNames.replace(index, displayName); scriptHashes.replace(index, QString::number(file.size()) + file.lastModified().toString()); } } } else { // new entry scriptList.insert(displayName, filePath); // update the cache scriptFiles << filePath; scriptNames << displayName; scriptHashes << QString::number(file.size()) + file.lastModified().toString(Qt::ISODate); } } // run queryProgramName } // foreach files return scriptList; } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/scriptloader.h000066400000000000000000000023621506155467400221450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_SCRIPTLOADER_H #define AVOGADRO_QTGUI_SCRIPTLOADER_H #include "avogadroqtguiexport.h" #include #include #include namespace Avogadro { namespace QtGui { /** * @brief The ScriptLoader class finds and verifies different types of * python utility scripts. * * For example, finding all the "charge" scripts * auto chargeScripts = ScriptLoader::scriptList("charge"); */ class AVOGADROQTGUI_EXPORT ScriptLoader : public QObject { Q_OBJECT public: explicit ScriptLoader(QObject* parent_ = nullptr); ~ScriptLoader() override; /** * @return A map of name -> path for all scripts of the requested @arg type */ static QMultiMap scriptList(const QString& type); static bool queryProgramName(const QString& scriptFilePath, QString& displayName); }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_SCRIPTLOADER_H avogadrolibs-1.101.0/avogadro/qtgui/slatersetconcurrent.cpp000066400000000000000000000067671506155467400241330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "slatersetconcurrent.h" #include #include #include #include #include #include namespace Avogadro::QtGui { using Core::Cube; using Core::SlaterSet; using Core::SlaterSetTools; struct SlaterShell { SlaterSetTools* tools; // A pointer to the tools, cannot write to member vars Cube* tCube; // The target cube, used to initialise temp cubes too unsigned int pos; // The index of the point to calculate the MO for unsigned int state; // The MO number to calculate }; SlaterSetConcurrent::SlaterSetConcurrent(QObject* p) : QObject(p), m_shells(nullptr), m_set(nullptr), m_tools(nullptr) { // Watch for the future connect(&m_watcher, SIGNAL(finished()), this, SLOT(calculationComplete())); } SlaterSetConcurrent::~SlaterSetConcurrent() { delete m_shells; } void SlaterSetConcurrent::setMolecule(Core::Molecule* mol) { if (!mol) return; m_set = dynamic_cast(mol->basisSet()); delete m_tools; m_tools = new SlaterSetTools(mol); } bool SlaterSetConcurrent::calculateMolecularOrbital(Core::Cube* cube, unsigned int state) { return setUpCalculation(cube, state, SlaterSetConcurrent::processOrbital); } bool SlaterSetConcurrent::calculateElectronDensity(Core::Cube* cube) { return setUpCalculation(cube, 0, SlaterSetConcurrent::processDensity); } bool SlaterSetConcurrent::calculateSpinDensity(Core::Cube* cube) { return setUpCalculation(cube, 0, SlaterSetConcurrent::processSpinDensity); } void SlaterSetConcurrent::calculationComplete() { (*m_shells)[0].tCube->lock()->unlock(); delete m_shells; m_shells = nullptr; emit finished(); } bool SlaterSetConcurrent::setUpCalculation(Core::Cube* cube, unsigned int state, void (*func)(SlaterShell&)) { if (!m_set || !m_tools) return false; m_set->initCalculation(); // Set up the points we want to calculate the density at. m_shells = new QVector(static_cast(cube->data()->size())); for (int i = 0; i < m_shells->size(); ++i) { (*m_shells)[i].tools = m_tools; (*m_shells)[i].tCube = cube; (*m_shells)[i].pos = i; (*m_shells)[i].state = state; } // Lock the cube until we are done. cube->lock()->lock(); // The main part of the mapped reduced function... m_future = QtConcurrent::map(*m_shells, func); // Connect our watcher to our future m_watcher.setFuture(m_future); return true; } void SlaterSetConcurrent::processOrbital(SlaterShell& shell) { Vector3 pos = shell.tCube->position(shell.pos); shell.tCube->setValue( shell.pos, shell.tools->calculateMolecularOrbital(pos, shell.state)); } void SlaterSetConcurrent::processDensity(SlaterShell& shell) { Vector3 pos = shell.tCube->position(shell.pos); shell.tCube->setValue(shell.pos, shell.tools->calculateElectronDensity(pos)); } void SlaterSetConcurrent::processSpinDensity(SlaterShell& shell) { Vector3 pos = shell.tCube->position(shell.pos); shell.tCube->setValue(shell.pos, shell.tools->calculateSpinDensity(pos)); } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/slatersetconcurrent.h000066400000000000000000000040121506155467400235550ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_SLATERSETCONCURRENT_H #define AVOGADRO_QTGUI_SLATERSETCONCURRENT_H #include "avogadroqtguiexport.h" #include #include #include namespace Avogadro { namespace Core { class Cube; class Molecule; class SlaterSet; class SlaterSetTools; } // namespace Core namespace QtGui { struct SlaterShell; /** * @brief The SlaterSetConcurrent class uses SlaterSetTools to calculate values * of electronic structure properties from quantum output read in. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT SlaterSetConcurrent : public QObject { Q_OBJECT public: explicit SlaterSetConcurrent(QObject* p = nullptr); ~SlaterSetConcurrent() override; void setMolecule(Core::Molecule* mol); bool calculateMolecularOrbital(Core::Cube* cube, unsigned int state); bool calculateElectronDensity(Core::Cube* cube); bool calculateSpinDensity(Core::Cube* cube); QFutureWatcher& watcher() { return m_watcher; } signals: /** * Emitted when the calculation is complete. */ void finished(); private slots: /** * Slot to set the cube data once Qt Concurrent is done */ void calculationComplete(); private: QFuture m_future; QFutureWatcher m_watcher; Core::Cube* m_cube; QVector* m_shells; Core::SlaterSet* m_set; Core::SlaterSetTools* m_tools; bool setUpCalculation(Core::Cube* cube, unsigned int state, void (*func)(SlaterShell&)); static void processOrbital(SlaterShell& shell); static void processDensity(SlaterShell& shell); static void processSpinDensity(SlaterShell& shell); }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_SLATERSETCONCURRENT_H avogadrolibs-1.101.0/avogadro/qtgui/sortfiltertreeproxymodel.cpp000066400000000000000000000056541506155467400252140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "sortfiltertreeproxymodel.h" namespace Avogadro::QtGui { // Custom class for Avogadro to handle filtering files // Directories are at most 2 levels deep until we get to files bool SortFilterTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent) const { // First we see if we're the source root node QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); if (!sourceIndex.isValid() || !sourceParent.isValid()) return true; // viewer will handle filtering // Make sure the root is always accepted, or we become rootless // See // http://stackoverflow.com/questions/3212392/qtreeview-qfilesystemmodel-setrootpath-and-qsortfilterproxymodel-with-regexp-fo if (m_sourceRoot.isValid() && sourceIndex == m_sourceRoot) { return true; // true root, always accept } QString data = sourceModel()->data(sourceIndex).toString(); // ignore any image files if (data.endsWith(".png") || data.endsWith(".svg")) return false; // ignore any .smi files if (data.endsWith(".smi")) return false; // Now we see if we're a child of the root // If not, we accept -- only filter under *our* tree // Along the way, we'll see if a parent matches the filter if (sourceParent != m_sourceRoot) { bool childOfRoot = false; QModelIndex parent = sourceParent; for (int depth = 3; depth > 0; depth--) { if (sourceModel()->data(parent).toString().contains( filterRegularExpression())) return true; // a parent matches the pattern parent = parent.parent(); if (!parent.isValid()) return true; // tree view handles filtering, and we ascended too far if (parent == m_sourceRoot) { childOfRoot = true; break; } } // OK, we've gone up the tree, did we find our root? if (!childOfRoot) return true; } // else, sourceParent is a root, so we're good to filter // Check if the data for this row matches. If so, the default is to accept if (data.contains(filterRegularExpression())) return true; // Now we have to check the child nodes // We'll show the row if any child is accepted // (i.e., if a file matches, we'll show the directory path to it) // Try to fetchMore() first sourceModel()->fetchMore(sourceIndex); for (int i = 0; i < sourceModel()->rowCount(sourceIndex); ++i) { QModelIndex subRow = sourceModel()->index(i, 0, sourceIndex); if (!subRow.isValid()) continue; QString rowData = sourceModel()->data(subRow).toString(); if (rowData.contains(filterRegularExpression())) return true; } return false; // nothing matched } } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/sortfiltertreeproxymodel.h000066400000000000000000000023601506155467400246500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_SORTFILTERTREEPROXYMODEL_H #define AVOGADRO_QTGUI_SORTFILTERTREEPROXYMODEL_H #include "avogadroqtguiexport.h" #include namespace Avogadro::QtGui { class AVOGADROQTGUI_EXPORT SortFilterTreeProxyModel : public QSortFilterProxyModel { Q_OBJECT public: SortFilterTreeProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent), m_sourceRoot() { } // From http://kodeclutz.blogspot.com/2008/12/filtering-qtreeview.html bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; // This is a hack to prevent us from becoming root-less // See // http://stackoverflow.com/questions/3212392/qtreeview-qfilesystemmodel-setrootpath-and-qsortfilterproxymodel-with-regexp-fo void setSourceRoot(const QModelIndex& sourceRoot) { m_sourceRoot = sourceRoot; } private: QModelIndex m_sourceRoot; }; } // namespace Avogadro::QtGui #endif avogadrolibs-1.101.0/avogadro/qtgui/toolplugin.cpp000066400000000000000000000024131506155467400221760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "toolplugin.h" namespace Avogadro::QtGui { ToolPlugin::ToolPlugin(QObject* parent_) : QObject(parent_) {} ToolPlugin::~ToolPlugin() {} QUndoCommand* ToolPlugin::mousePressEvent(QMouseEvent*) { return nullptr; } QUndoCommand* ToolPlugin::mouseReleaseEvent(QMouseEvent*) { return nullptr; } QUndoCommand* ToolPlugin::mouseMoveEvent(QMouseEvent*) { return nullptr; } QUndoCommand* ToolPlugin::mouseDoubleClickEvent(QMouseEvent*) { return nullptr; } QUndoCommand* ToolPlugin::wheelEvent(QWheelEvent*) { return nullptr; } QUndoCommand* ToolPlugin::keyPressEvent(QKeyEvent*) { return nullptr; } QUndoCommand* ToolPlugin::keyReleaseEvent(QKeyEvent*) { return nullptr; } void ToolPlugin::draw(Rendering::GroupNode&) {} bool ToolPlugin::handleCommand(const QString& command, const QVariantMap& options) { Q_UNUSED(command); Q_UNUSED(options); return false; } ToolPluginFactory::~ToolPluginFactory() {} } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/toolplugin.h000066400000000000000000000122301506155467400216410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_TOOLPLUGIN_H #define AVOGADRO_QTGUI_TOOLPLUGIN_H #include "avogadroqtguiexport.h" #include class QAction; class QKeyEvent; class QMouseEvent; class QUndoCommand; class QWheelEvent; namespace Avogadro { namespace Rendering { class GroupNode; class GLRenderer; } // namespace Rendering namespace QtOpenGL { class GLWidget; } namespace QtGui { class Molecule; class RWMolecule; /** * @class ToolPlugin toolplugin.h * @brief The base class for plugins that interact with QtOpenGL::GLWidget. * @author Allison Vacanti */ class AVOGADROQTGUI_EXPORT ToolPlugin : public QObject { Q_OBJECT public: explicit ToolPlugin(QObject* parent = nullptr); ~ToolPlugin() override; /** * The name of the tool, will be displayed in the user interface. */ virtual QString name() const = 0; /** * A description of the tool, may be displayed in the user interface. */ virtual QString description() const = 0; /** * A priority of the tool for sorting in the user interface. */ virtual unsigned char priority() const = 0; /** * @return The QAction that will cause this tool to become active. */ virtual QAction* activateAction() const = 0; /** * Set the tool icon (based on dark / light theme). */ virtual void setIcon(bool darkTheme = false) = 0; /** * @return A QWidget that will be displayed to the user while this tool is * active. */ virtual QWidget* toolWidget() const = 0; /** * Respond to user-input events. * @param e The QEvent object. * @return A QUndoCommand that can be used to undo any changes to the * molecule. If no undoable change is made, the method may return nullptr. * @{ */ virtual QUndoCommand* mousePressEvent(QMouseEvent* e); virtual QUndoCommand* mouseReleaseEvent(QMouseEvent* e); virtual QUndoCommand* mouseMoveEvent(QMouseEvent* e); virtual QUndoCommand* mouseDoubleClickEvent(QMouseEvent* e); virtual QUndoCommand* wheelEvent(QWheelEvent* e); virtual QUndoCommand* keyPressEvent(QKeyEvent* e); virtual QUndoCommand* keyReleaseEvent(QKeyEvent* e); /**@}*/ /** * Override this method to add drawables to the scene graph. */ virtual void draw(Rendering::GroupNode& node); /** * Called by the app to handle a command registered by the plugin. * (e.g., "renderMovie" or "drawAtom", etc.) * * The app will turn the command into a string and pass it to the tool. * and any options will go from a JSON dictionary to a QVariantMap. * * @return true if the command was handled, false otherwise. */ virtual bool handleCommand(const QString& command, const QVariantMap& options); /** * Called by the app to tell the tool to register commands. * If the tool has commands, it should emit the registerCommand signals. */ virtual void registerCommands() {} signals: /** * Emitted when draw() needs to be called again due to updates. */ void drawablesChanged(); /** * Emitted when something changed (camera, etc) and the molecule should be * redrawn. */ void updateRequested(); /** * Register a new command with the application. The command will be available * through scripting (e.g., "renderMovie" or "generateSurface", etc.) * * @param command The name of the command to register. * @param description A description of the command. * * @sa handleCommand */ void registerCommand(QString command, QString description); /** * Request a specific display type (or types) are made active. * This can be useful when loading a specific type of data that * would be most readily viewed with a specialized view. */ void requestActiveDisplayTypes(QStringList displayTypes); public slots: /** * Called when the current molecule changes. */ virtual void setMolecule(QtGui::Molecule* mol) = 0; virtual void setEditMolecule(QtGui::RWMolecule*) {} /** * Set the GLWidget used by the tool. */ virtual void setGLWidget(QtOpenGL::GLWidget*) {} /** * Set the active widget used by the tool, this can be anything derived from * QWidget. */ virtual void setActiveWidget(QWidget*) {} /** * Set the GLRenderer used by the tool. */ virtual void setGLRenderer(Rendering::GLRenderer*) {} }; /** * @class ToolPluginFactory toolplugin.h * @brief The base class for tool plugin factories in Avogadro. * @author Allison Vacanti */ class AVOGADROQTGUI_EXPORT ToolPluginFactory { public: virtual ~ToolPluginFactory(); virtual ToolPlugin* createInstance(QObject* parent = nullptr) = 0; virtual QString identifier() const = 0; virtual QString description() const = 0; }; } // namespace QtGui } // namespace Avogadro Q_DECLARE_INTERFACE(Avogadro::QtGui::ToolPluginFactory, "org.openchemistry.avogadro.ToolPluginFactory") #endif // AVOGADRO_QTGUI_TOOLPLUGIN_H avogadrolibs-1.101.0/avogadro/qtgui/utilities.cpp000066400000000000000000000043471506155467400220250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "utilities.h" #include #include #include namespace Avogadro::QtGui::Utilities { QString libraryDirectory() { return QString(AvogadroLibs_LIB_DIR); } QString dataDirectory() { return QString(AvogadroLibs_DATA_DIR); } QString findExecutablePath(QString program) { // we want to return the path to a program if it exists // using the PATH system environment variable QProcessEnvironment system = QProcessEnvironment::systemEnvironment(); QString path = system.value("PATH"); #ifdef Q_OS_WIN32 QStringList paths = path.split(';'); #else QStringList paths = path.split(':'); // check standard locations first paths.prepend("/usr/bin"); paths.prepend("/usr/local/bin"); #endif // check to see if we find the program in that path for (const auto& dir : paths) { QFileInfo test(dir + '/' + program); if (test.isExecutable()) { // must exist to be executable, so we're done return dir; } } return QString(); } QStringList findExecutablePaths(QStringList programs) { QStringList result; // we want to return the path to a program if it exists // using the PATH system environment variable QProcessEnvironment system = QProcessEnvironment::systemEnvironment(); QString path = system.value("PATH"); #ifdef Q_OS_WIN32 QStringList paths = path.split(';'); #else QStringList paths = path.split(':'); // check standard locations first paths.prepend("/usr/bin"); paths.prepend("/usr/local/bin"); #endif // loop through all programs and all possible paths for (const auto& program : programs) { for (const auto& dir : paths) { QFileInfo test(dir + '/' + program); if (test.isExecutable() && !result.contains(test.absoluteFilePath())) { // must exist to be executable, so add it to the list result << test.absoluteFilePath(); } } } return result; } } // namespace Avogadro::QtGui::Utilities avogadrolibs-1.101.0/avogadro/qtgui/utilities.h000066400000000000000000000017361506155467400214710ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_UTILITIES_H #define AVOGADRO_QTGUI_UTILITIES_H #include "avogadroqtguiexport.h" #include namespace Avogadro { namespace QtGui { namespace Utilities { AVOGADROQTGUI_EXPORT QString libraryDirectory(); AVOGADROQTGUI_EXPORT QString dataDirectory(); //! \return a fully-qualified path for a program or an empty string if not found AVOGADROQTGUI_EXPORT QString findExecutablePath(QString program); //! \return a list of all fully-qualified paths for programs that are found AVOGADROQTGUI_EXPORT QStringList findExecutablePaths(QStringList programs); } // namespace Utilities } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_UTILITIES_H avogadrolibs-1.101.0/avogadro/qtgui/viewfactory.cpp000066400000000000000000000006721506155467400223510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "viewfactory.h" namespace Avogadro::QtGui { ViewFactory::ViewFactory() {} ViewFactory::~ViewFactory() {} } // namespace Avogadro::QtGui avogadrolibs-1.101.0/avogadro/qtgui/viewfactory.h000066400000000000000000000024241506155467400220130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_VIEWFACTORY_H #define AVOGADRO_QTGUI_VIEWFACTORY_H #include "avogadroqtguiexport.h" #include namespace Avogadro { namespace QtGui { /** * @class ViewFactory viewfactory.h * @brief The ViewFactory class is a pure virtual that provides a method of * dynamically adding views to the MultiViewWidget class. * @author Marcus D. Hanwell */ class AVOGADROQTGUI_EXPORT ViewFactory { public: ViewFactory(); virtual ~ViewFactory(); /** * @brief Get the list of views the factory provides. * @return A list of supported views. */ virtual QStringList views() const = 0; /** * @brief Create a named view, the caller assumes ownership of the widget. * @param view The name of the view. * @return A new widget of the requested type, nullptr if none exists. */ virtual QWidget* createView(const QString& view) = 0; }; } // namespace QtGui } // namespace Avogadro #endif // AVOGADRO_QTGUI_VIEWFACTORY_H avogadrolibs-1.101.0/avogadro/qtopengl/000077500000000000000000000000001506155467400177765ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtopengl/CMakeLists.txt000066400000000000000000000010631506155467400225360ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 REQUIRED COMPONENTS Widgets OpenGLWidgets) else() find_package(Qt5 COMPONENTS Widgets REQUIRED) endif() add_library(QtOpenGL) avogadro_headers(QtOpenGL activeobjects.h glwidget.h qttextrenderstrategy.h ) target_sources(QtOpenGL PRIVATE activeobjects.cpp glwidget.cpp qttextrenderstrategy.cpp ) avogadro_add_library(QtOpenGL) target_link_libraries(QtOpenGL PUBLIC Avogadro::Rendering Avogadro::QtGui Qt::Widgets) if(QT_VERSION EQUAL 6) target_link_libraries(QtOpenGL PUBLIC Qt6::OpenGLWidgets) endif() avogadrolibs-1.101.0/avogadro/qtopengl/activeobjects.cpp000066400000000000000000000024001506155467400233230ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "activeobjects.h" #include "glwidget.h" #include namespace Avogadro::QtOpenGL { ActiveObjects::ActiveObjects() = default; ActiveObjects::~ActiveObjects() = default; ActiveObjects& ActiveObjects::instance() { static ActiveObjects singletonInstance; return singletonInstance; } GLWidget* ActiveObjects::activeGLWidget() const { return m_glWidget; } QWidget* ActiveObjects::activeWidget() const { return m_widget; } QtGui::Molecule* ActiveObjects::activeMolecule() const { return m_molecule; } void ActiveObjects::setActiveGLWidget(GLWidget* glWidget) { if (m_glWidget != glWidget) { m_widget = nullptr; m_glWidget = glWidget; emit activeGLWidgetChanged(m_glWidget); setActiveWidget(glWidget); } } void ActiveObjects::setActiveWidget(QWidget* widget) { if (m_widget != widget) { m_glWidget = nullptr; m_widget = widget; emit activeWidgetChanged(widget); } } void ActiveObjects::setActiveMolecule(QtGui::Molecule* molecule) { if (m_molecule != molecule) { m_molecule = molecule; emit activeMoleculeChanged(molecule); } } } // namespace Avogadro::QtOpenGL avogadrolibs-1.101.0/avogadro/qtopengl/activeobjects.h000066400000000000000000000045071506155467400230020ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef AVOGADRO_QTOPENGL_ACTIVEOBJECTS_H #define AVOGADRO_QTOPENGL_ACTIVEOBJECTS_H #include "avogadroqtopenglexport.h" #include // #include "glwidget.h" #include class QWidget; namespace Avogadro { namespace QtGui { class Molecule; } namespace QtOpenGL { class GLWidget; /** * @class ActiveObjects activeobjects.h * @brief Singleton to provide access to active objects. * * This class provides access to the active objects in the running application. * If you write an application using the Avogadro libraries you need to keep * this class updated with changes in active objects in order for built in * features to work as expected. * * All returned objects are owned by the running application, nullptr indicates * that there is no currently active object of that type. */ class AVOGADROQTOPENGL_EXPORT ActiveObjects : public QObject { Q_OBJECT public: /** Return a reference to the singleton instance that can be queried. */ static ActiveObjects& instance(); /** Get the active GLWidget. **/ GLWidget* activeGLWidget() const; /** * Get the active widget (more general, could be GLWidget, vtkGLWidget, etc). */ QWidget* activeWidget() const; /** * Get the active molecule. */ QtGui::Molecule* activeMolecule() const; public slots: /** Set the active GLWidget. */ void setActiveGLWidget(GLWidget* glWidget); /** Set the active widget (GLWidget, vtkGLWidget, etc). */ void setActiveWidget(QWidget* widget); /** Set the active widget (GLWidget, vtkGLWidget, etc). */ void setActiveMolecule(QtGui::Molecule* molecule); signals: /** The active GL widget changed. */ void activeGLWidgetChanged(GLWidget* glWidget); /** The active widget changed (GLWidget, vtkGLWidget, etc). */ void activeWidgetChanged(QWidget* widget); /** The active molecule changed. */ void activeMoleculeChanged(QtGui::Molecule* molecule); private: ActiveObjects(); ~ActiveObjects() override; Q_DISABLE_COPY(ActiveObjects) GLWidget* m_glWidget = nullptr; QWidget* m_widget = nullptr; QtGui::Molecule* m_molecule = nullptr; }; } // namespace QtOpenGL } // namespace Avogadro #endif // AVOGADRO_QTOPENGL_ACTIVEOBJECTS_H avogadrolibs-1.101.0/avogadro/qtopengl/glwidget.cpp000066400000000000000000000200131506155467400223040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "glwidget.h" #include "qttextrenderstrategy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtOpenGL { GLWidget::GLWidget(QWidget* p) : QOpenGLWidget(p), m_activeTool(nullptr), m_defaultTool(nullptr), m_renderTimer(nullptr) { setFocusPolicy(Qt::ClickFocus); connect(&m_scenePlugins, &QtGui::ScenePluginModel::pluginStateChanged, this, &GLWidget::updateScene); connect(&m_scenePlugins, &QtGui::ScenePluginModel::pluginConfigChanged, this, &GLWidget::updateScene); m_renderer.setTextRenderStrategy(new QtTextRenderStrategy); } GLWidget::~GLWidget() {} void GLWidget::setMolecule(QtGui::Molecule* mol) { clearScene(); if (m_molecule) disconnect(m_molecule, nullptr, nullptr, nullptr); m_molecule = mol; foreach (QtGui::ToolPlugin* tool, m_tools) tool->setMolecule(m_molecule); if (m_molecule != nullptr) { // update properties like dipole rendering QTimer::singleShot(500, m_molecule, &QtGui::Molecule::update); } connect(m_molecule, &QtGui::Molecule::changed, this, &GLWidget::updateScene); } QtGui::Molecule* GLWidget::molecule() { return m_molecule; } const QtGui::Molecule* GLWidget::molecule() const { return m_molecule; } void GLWidget::updateMolecule() { if (m_molecule != nullptr) { // update properties like dipole rendering QTimer::singleShot(500, m_molecule, &QtGui::Molecule::update); } } void GLWidget::updateScene() { // Build up the scene with the scene plugins, creating the appropriate nodes. QtGui::Molecule* mol = m_molecule; if (!mol) mol = new QtGui::Molecule(this); if (mol) { Rendering::GroupNode& node = m_renderer.scene().rootNode(); node.clear(); auto* moleculeNode = new Rendering::GroupNode(&node); QtGui::RWMolecule* rwmol = mol->undoMolecule(); foreach (QtGui::ScenePlugin* scenePlugin, m_scenePlugins.activeScenePlugins()) { auto* engineNode = new Rendering::GroupNode(moleculeNode); scenePlugin->process(*mol, *engineNode); scenePlugin->processEditable(*rwmol, *engineNode); } // Let the tools perform any drawing they need to do. if (m_activeTool) { auto* toolNode = new Rendering::GroupNode(moleculeNode); m_activeTool->draw(*toolNode); } if (m_defaultTool) { auto* toolNode = new Rendering::GroupNode(moleculeNode); m_defaultTool->draw(*toolNode); } m_renderer.resetGeometry(); update(); } if (mol != m_molecule) delete mol; } void GLWidget::clearScene() { m_renderer.scene().clear(); } void GLWidget::resetCamera() { m_renderer.resetCamera(); update(); } void GLWidget::resetGeometry() { m_renderer.resetGeometry(); } void GLWidget::setTools(const QList& toolList) { foreach (QtGui::ToolPlugin* tool, toolList) addTool(tool); } void GLWidget::addTool(QtGui::ToolPlugin* tool) { if (m_tools.contains(tool)) return; connect(tool, &QtGui::ToolPlugin::updateRequested, this, &GLWidget::requestUpdate); tool->setParent(this); tool->setGLWidget(this); tool->setActiveWidget(this); tool->setMolecule(m_molecule); tool->setGLRenderer(&m_renderer); m_tools << tool; } void GLWidget::setActiveTool(const QString& name) { foreach (QtGui::ToolPlugin* tool, m_tools) { QAction* toolAction = tool->activateAction(); if (tool->objectName() == name || (toolAction && toolAction->text() == name)) { setActiveTool(tool); return; } } } void GLWidget::setActiveTool(QtGui::ToolPlugin* tool) { if (tool == m_activeTool) return; if (m_activeTool && m_activeTool != m_defaultTool) { disconnect(m_activeTool, &QtGui::ToolPlugin::drawablesChanged, this, &GLWidget::updateScene); } if (tool) addTool(tool); m_activeTool = tool; if (m_activeTool && m_activeTool != m_defaultTool) { connect(m_activeTool, &QtGui::ToolPlugin::drawablesChanged, this, &GLWidget::updateScene); } } void GLWidget::setDefaultTool(const QString& name) { foreach (QtGui::ToolPlugin* tool, m_tools) { QAction* toolAction = tool->activateAction(); if (tool->objectName() == name || tool->name() == name || (toolAction && toolAction->text() == name)) { setDefaultTool(tool); return; } } } void GLWidget::setDefaultTool(QtGui::ToolPlugin* tool) { if (tool == m_defaultTool) return; if (m_defaultTool && m_activeTool != m_defaultTool) { disconnect(m_defaultTool, &QtGui::ToolPlugin::drawablesChanged, this, &GLWidget::updateScene); } if (tool) addTool(tool); m_defaultTool = tool; if (m_defaultTool && m_activeTool != m_defaultTool) { connect(m_defaultTool, &QtGui::ToolPlugin::drawablesChanged, this, &GLWidget::updateScene); } } void GLWidget::requestUpdate() { if (!m_renderTimer) { m_renderTimer = new QTimer(this); connect(m_renderTimer, &QTimer::timeout, this, &GLWidget::updateTimeout); m_renderTimer->setSingleShot(1000 / 30); // 30 fps m_renderTimer->start(); } } void GLWidget::updateTimeout() { if (m_renderTimer) { m_renderTimer->deleteLater(); m_renderTimer = nullptr; } update(); } void GLWidget::initializeGL() { m_renderer.initialize(); if (!m_renderer.isValid()) emit rendererInvalid(); } void GLWidget::resizeGL(int width_, int height_) { float pixelRatio = window()->windowHandle()->devicePixelRatio(); m_renderer.setPixelRatio(pixelRatio); m_renderer.resize(width_, height_); } void GLWidget::paintGL() { m_renderer.render(); } void GLWidget::mouseDoubleClickEvent(QMouseEvent* e) { e->ignore(); if (m_activeTool) m_activeTool->mouseDoubleClickEvent(e); if (m_defaultTool && !e->isAccepted()) m_defaultTool->mouseDoubleClickEvent(e); if (!e->isAccepted()) QOpenGLWidget::mouseDoubleClickEvent(e); } void GLWidget::mousePressEvent(QMouseEvent* e) { e->ignore(); if (m_activeTool) m_activeTool->mousePressEvent(e); if (m_defaultTool && !e->isAccepted()) m_defaultTool->mousePressEvent(e); if (!e->isAccepted()) QOpenGLWidget::mousePressEvent(e); } void GLWidget::mouseMoveEvent(QMouseEvent* e) { e->ignore(); if (m_activeTool) m_activeTool->mouseMoveEvent(e); if (m_defaultTool && !e->isAccepted()) m_defaultTool->mouseMoveEvent(e); if (!e->isAccepted()) QOpenGLWidget::mouseMoveEvent(e); } void GLWidget::mouseReleaseEvent(QMouseEvent* e) { e->ignore(); if (m_activeTool) m_activeTool->mouseReleaseEvent(e); if (m_defaultTool && !e->isAccepted()) m_defaultTool->mouseReleaseEvent(e); if (!e->isAccepted()) QOpenGLWidget::mouseReleaseEvent(e); } void GLWidget::wheelEvent(QWheelEvent* e) { e->ignore(); if (m_activeTool) m_activeTool->wheelEvent(e); if (m_defaultTool && !e->isAccepted()) m_defaultTool->wheelEvent(e); if (!e->isAccepted()) QOpenGLWidget::wheelEvent(e); } void GLWidget::keyPressEvent(QKeyEvent* e) { e->ignore(); if (m_activeTool) m_activeTool->keyPressEvent(e); if (m_defaultTool && !e->isAccepted()) m_defaultTool->keyPressEvent(e); if (!e->isAccepted()) QOpenGLWidget::keyPressEvent(e); } void GLWidget::keyReleaseEvent(QKeyEvent* e) { e->ignore(); if (m_activeTool) m_activeTool->keyReleaseEvent(e); if (m_defaultTool && !e->isAccepted()) m_defaultTool->keyReleaseEvent(e); if (!e->isAccepted()) QOpenGLWidget::keyReleaseEvent(e); } } // namespace Avogadro::QtOpenGL avogadrolibs-1.101.0/avogadro/qtopengl/glwidget.h000066400000000000000000000133261506155467400217620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTOPENGL_GLWIDGET_H #define AVOGADRO_QTOPENGL_GLWIDGET_H #include "avogadroqtopenglexport.h" #include #include #include #include #if QT_VERSION >= 0x060000 #include #else #include #endif class QTimer; namespace Avogadro { namespace QtGui { class Molecule; } namespace QtOpenGL { /** * @class GLWidget glwidget.h * @brief QOpenGLGLWidget derived object for displaying 3D molecular geometry. * @author Marcus D. Hanwell * * This class creates the GL context, and contains a renderer to render the * 3D molecular geometry. * * The GLWidget also manages a collection of ToolPlugins that are used to * respond to user input events. Use setTools() or addTool() to add tools to the * widget. Use setActiveTool() to indicate which tool is active. The active tool * will be given the opportunity to handle input events first. If the active * tool does not handle the event, the default tool will be used. If the default * tool also ignores the event, it will be passed to QOpenGLWidget's handlers. */ class AVOGADROQTOPENGL_EXPORT GLWidget : public QOpenGLWidget { Q_OBJECT public: explicit GLWidget(QWidget* parent = nullptr); ~GLWidget() override; /** Set the molecule the widget will render. */ void setMolecule(QtGui::Molecule* molecule); /** * Get the molecule being rendered by the widget. * @{ */ QtGui::Molecule* molecule(); const QtGui::Molecule* molecule() const; /** @}*/ /** Get a reference to the renderer for the widget. */ Rendering::GLRenderer& renderer() { return m_renderer; } /** * @return A list of the ToolPlugins owned by the GLWidget. */ QList tools() const { return m_tools; } /** * @return The active tool. */ QtGui::ToolPlugin* activeTool() const { return m_activeTool; } /** * @return The default tool. */ QtGui::ToolPlugin* defaultTool() const { return m_defaultTool; } /** * Get the GLWidget's ScenePluginModel, used to add, delete and modify the * scene plugin items. * @{ */ QtGui::ScenePluginModel& sceneModel() { return m_scenePlugins; } const QtGui::ScenePluginModel& sceneModel() const { return m_scenePlugins; } /** @}*/ /** * Check if the GLWidget was able to acquire a context, and set up the * renderer correctly. If not valid, the error method may provide more * information. * @return true if value, false if not. */ bool isValid() const { return m_renderer.isValid(); } /** * Get the error(s), if any, encountered when setting up the GLWidget. * @return A free form string containing errors encountered. */ QString error() const { return m_renderer.error().c_str(); } signals: void rendererInvalid(); public slots: /** * Update the scene plugins for the widget, this will generate geometry in * the scene etc. */ void updateScene(); /** * Request update of molecule properties (e.g., dipole moment) */ void updateMolecule(); /** * Clear the contents of the scene. */ void clearScene(); /** Reset the view to fit the entire scene. */ void resetCamera(); /** Reset the geometry when the molecule etc changes. */ void resetGeometry(); /** * Make the tools in toolList available to the GLWidget. The GLWidget takes * ownership of the tools. */ void setTools(const QList& toolList); /** * Make tool available to the GLWidget. The GLWidget takes ownership of the * tool. */ void addTool(QtGui::ToolPlugin* tool); /** * Set the active tool. This is the tool that will be used to handle input * events first. * @{ */ void setActiveTool(const QString& name); void setActiveTool(QtGui::ToolPlugin* tool); /**@}*/ /** * Set the default tool. This is the tool that will be used to handle input * events that are ignored by the active tool. * @{ */ void setDefaultTool(const QString& name); void setDefaultTool(QtGui::ToolPlugin* tool); /**@}*/ /** * Request an update, this will by default initiate a timer that will trigger * in a specified time, enabling us to compress multiple events such as * camera moves to maintain interactivity. */ void requestUpdate(); protected slots: /** * Perform the update of the render, this should only be called by the timer. */ void updateTimeout(); protected: /** This is where the GL context is initialized. */ void initializeGL() override; /** Take care of resizing the context. */ void resizeGL(int width, int height) override; /** Main entry point for all GL rendering. */ void paintGL() override; /** Reimplemented from QOpenGLWidget @{ */ void mouseDoubleClickEvent(QMouseEvent*) override; void mousePressEvent(QMouseEvent*) override; void mouseMoveEvent(QMouseEvent*) override; void mouseReleaseEvent(QMouseEvent*) override; void wheelEvent(QWheelEvent*) override; void keyPressEvent(QKeyEvent*) override; void keyReleaseEvent(QKeyEvent*) override; /** @} */ private: QPointer m_molecule; QList m_tools; QtGui::ToolPlugin* m_activeTool; QtGui::ToolPlugin* m_defaultTool; Rendering::GLRenderer m_renderer; QtGui::ScenePluginModel m_scenePlugins; QTimer* m_renderTimer; }; } // namespace QtOpenGL } // namespace Avogadro #endif // AVOGADRO_QTOPENGL_GLWIDGET_H avogadrolibs-1.101.0/avogadro/qtopengl/qttextrenderstrategy.cpp000066400000000000000000000165461506155467400250320ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "qttextrenderstrategy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Rendering::TextProperties; namespace { inline Qt::Alignment textPropertiesToAlignment(const TextProperties& prop) { // This is initialized with no flags set as we want, no need to initialize. Qt::Alignment result; switch (prop.hAlign()) { default: case TextProperties::HLeft: result |= Qt::AlignLeft; break; case TextProperties::HCenter: result |= Qt::AlignHCenter; break; case TextProperties::HRight: result |= Qt::AlignRight; break; } switch (prop.vAlign()) { default: case TextProperties::VTop: result |= Qt::AlignTop; break; case TextProperties::VCenter: result |= Qt::AlignVCenter; break; case TextProperties::VBottom: result |= Qt::AlignBottom; break; } return result; } inline QFont textPropertiesToQFont(const TextProperties& prop) { QString family; switch (prop.fontFamily()) { case TextProperties::SansSerif: #ifndef Q_OS_MAC family = "SansSerif"; #else family = QApplication::font().family(); #endif break; case TextProperties::Serif: #ifndef Q_OS_MAC family = "serif"; #else family = "Times"; #endif break; case TextProperties::Mono: #ifndef Q_OS_MAC family = "mono"; #else family = "Menlo"; #endif break; default: qWarning() << "Unknown font family id: " << prop.fontFamily() << "Defaulting to SansSerif."; family = QApplication::font().family(); } TextProperties::FontStyles style = prop.fontStyles(); QFont result(family, static_cast(prop.pixelHeight()) / 2 + 1, static_cast(style & TextProperties::Bold) ? QFont::Bold : QFont::Normal, static_cast(style & TextProperties::Italic)); result.setUnderline(static_cast(style & TextProperties::Underline)); // Scale the font size to match the specified pixel height: QFontMetricsF metrics(result); int iterLimiter = 0; // no more than 5 iterations below: do { result.setPointSizeF(result.pointSizeF() * static_cast(prop.pixelHeight()) / metrics.height()); metrics = QFontMetricsF(result); } while ( std::fabs(metrics.height() - static_cast(prop.pixelHeight())) > static_cast(0.5) && iterLimiter++ < 5); return result; } } // namespace namespace Avogadro::QtOpenGL { QtTextRenderStrategy::QtTextRenderStrategy() {} QtTextRenderStrategy::~QtTextRenderStrategy() {} Rendering::TextRenderStrategy* QtTextRenderStrategy::newInstance() const { return new QtTextRenderStrategy; } void QtTextRenderStrategy::boundingBox(const std::string& string, const Rendering::TextProperties& tprop, int bbox[]) const { QRect rect; const QFont font(textPropertiesToQFont(tprop)); const Qt::Alignment align(textPropertiesToAlignment(tprop)); rect = QFontMetrics(font).boundingRect(rect, align, QString::fromStdString(string)); // Rotate if needed if (tprop.rotationDegreesCW() != 0.f) { // Build transformation QTransform transform; transform.rotate(static_cast(-tprop.rotationDegreesCW())); // Transform a floating point representation of the bbox QRectF tmpRect(rect); tmpRect = transform.mapRect(tmpRect); // Update the bbox, rounding values to give the largest containing bbox rect.setLeft(static_cast(std::floor(tmpRect.left()))); rect.setRight(static_cast(std::floor(tmpRect.right()))); rect.setTop(static_cast(std::ceil(tmpRect.top()))); rect.setBottom(static_cast(std::floor(tmpRect.bottom()))); } bbox[0] = rect.left(); bbox[1] = rect.right(); bbox[2] = rect.top(); bbox[3] = rect.bottom(); } void QtTextRenderStrategy::render(const std::string& string, const Rendering::TextProperties& tprop, unsigned char* buffer, const Vector2i& dims) const { // Convert inputs to Qt QString str = QString::fromStdString(string); Qt::Alignment flags = textPropertiesToAlignment(tprop); QFont font = textPropertiesToQFont(tprop); // Construct our target image to use the input buffer and clear it. QImage target(buffer, dims[0], dims[1], QImage::Format_ARGB32_Premultiplied); target.fill(qRgba(0, 0, 0, 0)); // Painter setup QPainter painter(&target); painter.setFont(font); // Get a tight bbox for the text without rotations: QRectF textRect(painter.boundingRect(QRectF(), flags, str)); // Undo any alignment translations of the bbox. textRect.translate(-textRect.topLeft()); // Apply rotation, if any auto rot(static_cast(tprop.rotationDegreesCW())); if (rot != 0.f) { // Rotate the painter: painter.rotate(static_cast(rot)); // Convert the bbox into a polygon and rotate it QPolygonF textCorners(textRect); textCorners = painter.transform().map(textCorners); // Find the new origin in the rotated space: QPointF newOrigin(-textCorners.boundingRect().topLeft()); // Rotate the point back (drawText will reapply the rotation) newOrigin = painter.transform().inverted().map(newOrigin); // Account for the rotation in the bbox: textRect.translate(newOrigin); } // Draw the text painter.setPen( QColor(tprop.red(), tprop.green(), tprop.blue(), tprop.alpha())); painter.drawText(textRect, flags, str); painter.end(); // Convert the buffer from ARGB --> RGBA for openGL by default if (!m_preserveArgb) argbToRgba(buffer, dims[0] * dims[1]); } namespace { template inline void argbToRgbaWorker(quint32 in, quint32& out) { Q_UNUSED(in) Q_UNUSED(out) qWarning("QtTextRenderStrategy::argbToRgba: Invalid byte order."); } template <> inline void argbToRgbaWorker(quint32 in, quint32& out) { out = ((in >> 24) & 0xff) | (in << 8); } template <> inline void argbToRgbaWorker(quint32 in, quint32& out) { out = ((in << 16) & 0xff0000) | ((in >> 16) & 0xff) | (in & 0xff00ff00); } } // namespace void QtTextRenderStrategy::argbToRgba(unsigned char* buffer, size_t pixels) { // Adapted from QGLWidget::convertToGLFormat to perform the conversion // in-place. // input: 0xAARRGGBB // output: 0xRRGGBBAA (big endian) // output: 0xAABBGGRR (little endian) auto* cur = reinterpret_cast(buffer); quint32* end = &cur[pixels]; while (cur < end) { // Ignore empty pixels if (*cur) argbToRgbaWorker(*cur, *cur); cur++; } } } // namespace Avogadro::QtOpenGL avogadrolibs-1.101.0/avogadro/qtopengl/qttextrenderstrategy.h000066400000000000000000000034721506155467400244710ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_QTTEXTRENDERSTRATEGY_H #define AVOGADRO_QTGUI_QTTEXTRENDERSTRATEGY_H #include "avogadroqtopenglexport.h" #include #include namespace Avogadro { namespace QtOpenGL { /** * @class QtTextRenderStrategy qttextrenderstrategy.h * * @brief The QtTextRenderStrategy class uses the Qt toolkit to render text. */ class AVOGADROQTOPENGL_EXPORT QtTextRenderStrategy : public Rendering::TextRenderStrategy { public: QtTextRenderStrategy(); ~QtTextRenderStrategy() override; TextRenderStrategy* newInstance() const override; void boundingBox(const std::string& string, const Rendering::TextProperties& tprop, int bbox[4]) const override; void render(const std::string& string, const Rendering::TextProperties& tprop, unsigned char* buffer, const Vector2i& dims) const override; /** * Keep the buffer as a QImage::Format_ARGB32_Premultiplied image. Useful * for testing. * @note The result buffer may or may not actually be ARGB ordered depending * on system endianness. See the QImage docs for more info. * @{ */ bool preserveArgb() const { return m_preserveArgb; } void setPreserveArgb(bool b) { m_preserveArgb = b; } /** @} */ private: static void argbToRgba(unsigned char* buffer, size_t pixels); bool m_preserveArgb; }; } // namespace QtOpenGL } // namespace Avogadro #endif // AVOGADRO_QTOPENGL_QTTEXTRENDERSTRATEGY_H avogadrolibs-1.101.0/avogadro/qtplugins/000077500000000000000000000000001506155467400201735ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/3dmol/000077500000000000000000000000001506155467400212115ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/3dmol/3dmol.cpp000066400000000000000000000032641506155467400227400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2016 Barry E Moore II This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "3dmol.h" #include "3dmoldialog.h" #include #include namespace Avogadro::QtPlugins { ThreeDMol::ThreeDMol(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_action(new QAction(this)), m_dialog(nullptr), m_molecule(nullptr) { m_action->setEnabled(true); m_action->setText("&3DMol HTML Snippet…"); connect(m_action, SIGNAL(triggered()), SLOT(showDialog())); } QString ThreeDMol::description() const { return tr("3DMol HTML Block."); } QList ThreeDMol::actions() const { return QList() << m_action; } QStringList ThreeDMol::menuPath(QAction*) const { return QStringList() << tr("&File") << tr("&Export"); } void ThreeDMol::setMolecule(QtGui::Molecule* mol) { if (mol == m_molecule) return; m_molecule = mol; if (m_dialog) m_dialog->setMolecule(m_molecule); } void ThreeDMol::showDialog() { if (!m_dialog) { m_dialog = new ThreeDMolDialog(m_molecule, qobject_cast(this->parent())); } m_dialog->show(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/3dmol/3dmol.h000066400000000000000000000031151506155467400224000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2016 Barry E Moore II This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_ThreeDMOL_H #define AVOGADRO_QTPLUGINS_ThreeDMOL_H #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { class ThreeDMolDialog; /** * @brief The ThreeDMol class is an extension to launch * a ThreeDMolDialog. */ class ThreeDMol : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit ThreeDMol(QObject* parent_ = nullptr); ~ThreeDMol() override = default; QString name() const override { return tr("ThreeDMol"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void showDialog(); private: QAction* m_action; ThreeDMolDialog* m_dialog; QtGui::Molecule* m_molecule; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_ThreeDMOLEXTENSION_H avogadrolibs-1.101.0/avogadro/qtplugins/3dmol/3dmoldialog.cpp000066400000000000000000000066721506155467400241260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2016 Barry E Moore II This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "3dmoldialog.h" #include "ui_3dmoldialog.h" #include #include #include #include #include using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { ThreeDMolDialog::ThreeDMolDialog(QtGui::Molecule* mol, QWidget* parent_) : QDialog(parent_), m_molecule(nullptr), m_ui(new Ui::ThreeDMolDialog) { m_ui->setupUi(this); setMolecule(mol); } ThreeDMolDialog::~ThreeDMolDialog() { delete m_ui; } void ThreeDMolDialog::setMolecule(QtGui::Molecule* mol) { if (mol == m_molecule) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (!m_molecule) return; connect(m_molecule, SIGNAL(changed(unsigned int)), SLOT(updateLabels())); connect(m_molecule, SIGNAL(destroyed()), SLOT(moleculeDestroyed())); connect(m_ui->closeButton, SIGNAL(clicked()), SLOT(close())); connect(m_ui->copyButton, SIGNAL(clicked()), SLOT(copyToClipboard())); updateLabels(); } void ThreeDMolDialog::updateLabels() { if (m_molecule) { updateTextBrowser(); } else { m_ui->plainTextEdit->clear(); } } void ThreeDMolDialog::updateTextBrowser() { QString text = "\n"; // MDL representation std::string mol; bool writeSDF = Io::FileFormatManager::instance().writeString(*m_molecule, mol, "sdf"); text.append("
\n"); else text.append(" data-type='xyz' data-backgroundcolor='0xffffff' " "data-style='stick'>\n"); text.append(""); m_ui->plainTextEdit->setPlainText(text); } void ThreeDMolDialog::moleculeDestroyed() { m_molecule = nullptr; updateLabels(); } void ThreeDMolDialog::copyToClipboard() { QApplication::clipboard()->setText(m_ui->plainTextEdit->toPlainText()); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/3dmol/3dmoldialog.h000066400000000000000000000032521506155467400235620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2016 Barry E Moore II This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef AVOGADRO_QTGUI_ThreeDMOLDIALOG_H #define AVOGADRO_QTGUI_ThreeDMOLDIALOG_H #include namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { namespace Ui { class ThreeDMolDialog; } /** * @class ThreeDMolDialog 3dmoldialog.h * @brief The ThreeDMolDialog class provides a dialog which displays * basic molecular properties. * @author Barry E. Moore II * * @todo IUPAC name fetch (need inchi key). */ class ThreeDMolDialog : public QDialog { Q_OBJECT public: explicit ThreeDMolDialog(QtGui::Molecule* mol, QWidget* parent_ = nullptr); ~ThreeDMolDialog() override; QtGui::Molecule* molecule() { return m_molecule; } public slots: void setMolecule(QtGui::Molecule* mol); private slots: void updateLabels(); void updateTextBrowser(); void moleculeDestroyed(); void copyToClipboard(); private: QtGui::Molecule* m_molecule; Ui::ThreeDMolDialog* m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTGUI_ThreeDMOLDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/3dmol/3dmoldialog.ui000066400000000000000000000026351506155467400237540ustar00rootroot00000000000000 Avogadro::QtPlugins::ThreeDMolDialog true 0 0 370 257 3DMol HTML Snippet true &Copy to Clipboard Ctrl+C Close avogadrolibs-1.101.0/avogadro/qtplugins/3dmol/CMakeLists.txt000066400000000000000000000002301506155467400237440ustar00rootroot00000000000000avogadro_plugin(ThreeDMol "The 3DMol HTML Block Generator" ExtensionPlugin 3dmol.h ThreeDMol "3dmol.cpp;3dmoldialog.cpp" "3dmoldialog.ui" ) avogadrolibs-1.101.0/avogadro/qtplugins/CMakeLists.txt000066400000000000000000000174651506155467400227500ustar00rootroot00000000000000find_package(Qt${QT_VERSION} COMPONENTS Widgets Network Concurrent REQUIRED) if(QT_VERSION EQUAL 6) find_package(Qt6 COMPONENTS OpenGLWidgets REQUIRED) endif() if(WIN32) # used for HTTPS (e.g., PQR, downloads, etc.) find_package(OpenSSL REQUIRED) endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) # Modify the output directory for the build tree. set(original_library_output_dir "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${original_library_output_dir}/avogadro2/plugins") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${original_library_output_dir}/avogadro2/staticplugins") # Optionally build all plugins statically. option(BUILD_STATIC_PLUGINS "Build static plugins by default" ON) # Allow GPL plugins to be disabled. option(BUILD_GPL_PLUGINS "Build plugins that are licensed under the GNU Public License." OFF) # Create a plugin for Avogadro. # name is the name of the plugin, this will be the name of the target created. # description Free text description of the plugin. # type The base class of the plugin. # header is the header(s) for the class to be instantiated for the plugin. # pluginClass is the class to be instantiated for the plugin. # sources is the list of source files for the plugin. # uis is the list of UI files that need to be compiled (optional). # rcs is the list of qrc files that need to be compiled (optional). function(avogadro_plugin name description type header pluginClass sources) set(uis "") set(rcs "") if(${ARGC} GREATER 6) set(uis ${ARGV6}) endif() if(${ARGC} GREATER 7) set(rcs ${ARGV7}) endif() qt_wrap_ui(ui_srcs ${uis}) qt_add_resources(rc_srcs ${rcs}) unset(PluginIncludes) foreach(_header ${header}) set(PluginIncludes "${PluginIncludes}#include \"${CMAKE_CURRENT_SOURCE_DIR}/${_header}\"\n") endforeach() set(PluginName "${name}") set(PluginDescription "${description}") set(PluginType "${type}") set(PluginClass "${pluginClass}") configure_file("${AvogadroLibs_SOURCE_DIR}/cmake/avogadroplugin.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/${name}Plugin.cpp") # Figure out which type of plugin is being added, and put it in the right list if(BUILD_STATIC_PLUGINS) set(_plugin_object "STATIC") set_property(GLOBAL APPEND PROPERTY AvogadroLibs_STATIC_PLUGINS ${name}) if(rcs) get_filename_component(_name_we ${rcs} NAME_WE) set_property(GLOBAL APPEND PROPERTY AvogadroLibs_STATIC_RCS ${_name_we}) endif() else() set(_plugin_object "MODULE") set_property(GLOBAL APPEND PROPERTY AvogadroLibs_PLUGINS ${name}) endif() add_library(${name} ${_plugin_object} ${sources} ${ui_srcs} ${rc_srcs} ${name}Plugin.cpp ) target_link_libraries(${name} PRIVATE Avogadro::QtGui) if("${_plugin_object}" STREQUAL "STATIC") set_target_properties(${name} PROPERTIES COMPILE_DEFINITIONS "QT_STATICPLUGIN") if(UNIX) set_target_properties(${name} PROPERTIES POSITION_INDEPENDENT_CODE ON) endif() endif() set_target_properties(${name} PROPERTIES AUTOMOC TRUE PREFIX "") install(TARGETS ${name} EXPORT "AvogadroLibsTargets" RUNTIME DESTINATION "${INSTALL_RUNTIME_DIR}" LIBRARY DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/plugins" ARCHIVE DESTINATION "${INSTALL_ARCHIVE_DIR}/avogadro2/staticplugins") endfunction() # Now to make the plugins. add_subdirectory(3dmol) add_subdirectory(alchemy) add_subdirectory(aligntool) add_subdirectory(apbs) add_subdirectory(applycolors) add_subdirectory(bondcentrictool) add_subdirectory(bonding) add_subdirectory(cartoons) add_subdirectory(centroid) add_subdirectory(configurepython) add_subdirectory(coordinateeditor) add_subdirectory(copypaste) add_subdirectory(cp2kinput) add_subdirectory(crystal) add_subdirectory(customelements) add_subdirectory(dipole) add_subdirectory(editor) add_subdirectory(fetchpdb) add_subdirectory(focus) add_subdirectory(forcefield) add_subdirectory(gamessinput) add_subdirectory(hydrogens) add_subdirectory(importpqr) add_subdirectory(insertdna) add_subdirectory(insertfragment) add_subdirectory(label) add_subdirectory(lammpsinput) add_subdirectory(lineformatinput) add_subdirectory(manipulator) add_subdirectory(measuretool) add_subdirectory(molecularproperties) add_subdirectory(navigator) add_subdirectory(networkdatabases) add_subdirectory(openbabel) add_subdirectory(openmminput) add_subdirectory(playertool) add_subdirectory(ply) add_subdirectory(povray) add_subdirectory(propertytables) add_subdirectory(resetview) add_subdirectory(select) add_subdirectory(selectiontool) if(USE_SPGLIB) add_subdirectory(spacegroup) endif() add_subdirectory(surfaces) add_subdirectory(svg) add_subdirectory(templatetool) add_subdirectory(vibrations) add_subdirectory(vrml) # Plugins that require VTK if(USE_VTK) add_subdirectory(coloropacitymap) add_subdirectory(spectra) add_subdirectory(plotpdf) add_subdirectory(plotrmsd) add_subdirectory(plotxrd) if(USE_SPGLIB) add_subdirectory(yaehmop) endif() endif() # script plugins (input generators, etc.) add_subdirectory(commandscripts) add_subdirectory(quantuminput) add_subdirectory(scriptcharges) add_subdirectory(scriptfileformats) if(USE_LIBARCHIVE) add_subdirectory(plugindownloader) endif() if(USE_LIBMSYM) add_subdirectory(symmetry) endif() # The scene plugins add_subdirectory(ballandstick) add_subdirectory(closecontacts) add_subdirectory(force) add_subdirectory(licorice) add_subdirectory(meshes) add_subdirectory(noncovalent) add_subdirectory(vanderwaals) add_subdirectory(wireframe) if (USE_OPENGL) # needs some raw OpenGL code add_subdirectory(overlayaxes) endif() # other optional plugins if(BUILD_GPL_PLUGINS) # qtaimcurvature.h/cpp contains GPL licensed code: add_subdirectory(qtaim) endif() #kantundpeterpan add_subdirectory(constraints) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${original_library_output_dir}") # Add all of the static plugins to the initialization file. get_property(AvogadroLibs_STATIC_PLUGINS GLOBAL PROPERTY AvogadroLibs_STATIC_PLUGINS) get_property(AvogadroLibs_STATIC_RCS GLOBAL PROPERTY AvogadroLibs_STATIC_RCS) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h.in" "// Automatically generated file. Do not edit. #ifndef AVOGADRO_STATIC_QTPLUGINS_H #define AVOGADRO_STATIC_QTPLUGINS_H #include \n\n") foreach(_plugin ${AvogadroLibs_STATIC_PLUGINS}) file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h.in" "Q_IMPORT_PLUGIN(${_plugin}Factory)\n") endforeach() file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h.in" "\nvoid initAvogadroPluginResources() {\n") foreach(_rcs ${AvogadroLibs_STATIC_RCS}) file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h.in" " Q_INIT_RESOURCE(${_rcs});\n") endforeach() file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h.in" "}\n\n") file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h.in" "\n#endif // AVOGADRO_STATIC_QTPLUGINS_H\n") # Configure the static plugin header, ensuring it only changes if the contents # are modified - otherwise the original timestamp will be maintained. configure_file("${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h.in" "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h") # Now to build the plugin library, which can also statically link plugins # and initialize them for the application. If Avogadro is built statically # then the static plugin header must be included by the application. add_library(QtPlugins) avogadro_headers(QtPlugins pluginmanager.h pluginfactory.h "${CMAKE_CURRENT_BINARY_DIR}/avogadrostaticqtplugins.h" ) target_sources(QtPlugins PRIVATE pluginmanager.cpp ) avogadro_add_library(QtPlugins ${HEADERS} ${SOURCES}) target_link_libraries(QtPlugins PUBLIC Qt::Core PRIVATE ${AvogadroLibs_STATIC_PLUGINS} Avogadro::QtGui Avogadro::Calc) if(QT_VERSION EQUAL 6) target_link_libraries(QtPlugins PRIVATE Qt6::OpenGLWidgets Qt6::Network) endif() avogadrolibs-1.101.0/avogadro/qtplugins/alchemy/000077500000000000000000000000001506155467400216155ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/alchemy/CMakeLists.txt000066400000000000000000000002351506155467400243550ustar00rootroot00000000000000include_directories(${CMAKE_CURRENT_BINARY_DIR}) avogadro_plugin(Alchemy "Change elements" ExtensionPlugin alchemy.h Alchemy "alchemy.cpp" "" ) avogadrolibs-1.101.0/avogadro/qtplugins/alchemy/alchemy.cpp000066400000000000000000000044341506155467400237500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "alchemy.h" #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Array; using Core::Elements; Alchemy::Alchemy(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_action(new QAction(tr("Change Elements…"), this)) { m_action->setProperty("menu priority", 750); connect(m_action, &QAction::triggered, this, &Alchemy::changeElements); } Alchemy::~Alchemy() {} QList Alchemy::actions() const { QList result; return result << m_action; } QStringList Alchemy::menuPath(QAction*) const { return QStringList() << tr("&Build"); } void Alchemy::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void Alchemy::changeElements() { if (!m_molecule) return; // assemble the list of elements QStringList choices; for (unsigned char i = 0; i < Elements::elementCount(); ++i) { QString choice("%1: %2"); choice = choice.arg(i).arg(Elements::name(i)); choices << choice; } // get the element of the first selected atom unsigned char firstElement = 0; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (m_molecule->atomSelected(i)) { firstElement = m_molecule->atom(i).atomicNumber(); break; } } bool ok = false; QString currentChoice = QInputDialog::getItem( qobject_cast(parent()), tr("Change Elements"), tr("Element:"), choices, static_cast(firstElement), false, &ok); if (!ok) return; unsigned char newElement = currentChoice.section(':', 0, 0).toUShort(); // loop through the selected atoms and change their elements for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (m_molecule->atomSelected(i)) m_molecule->atom(i).setAtomicNumber(newElement); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/alchemy/alchemy.h000066400000000000000000000024171506155467400234140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_ALCHEMY_H #define AVOGADRO_QTPLUGINS_ALCHEMY_H #include #include #include namespace Ui { class BondingDialog; } namespace Avogadro { namespace QtPlugins { /** * @brief The Bonding class performs bonding operations on demand. */ class Alchemy : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Alchemy(QObject* parent_ = nullptr); ~Alchemy() override; QString name() const override { return tr("Alchemy"); } QString description() const override { return tr("Change elements of atoms."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void changeElements(); private: QtGui::Molecule* m_molecule; QAction* m_action; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_ALCHEMY_H avogadrolibs-1.101.0/avogadro/qtplugins/aligntool/000077500000000000000000000000001506155467400221635ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/aligntool/CMakeLists.txt000066400000000000000000000011041506155467400247170ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 COMPONENTS Gui REQUIRED) else() find_package(Qt5 COMPONENTS Widgets REQUIRED) endif() set(aligntool_srcs aligntool.cpp ) set(aligntool_uis ) set(aligntool_rcs aligntool.qrc ) avogadro_plugin(AlignTool "AlignTool" ToolPlugin aligntool.h AlignTool "${aligntool_srcs}" "${aligntool_uis}" "${aligntool_rcs}" ) target_link_libraries(AlignTool PRIVATE Avogadro::QtOpenGL) if(QT_VERSION EQUAL 6) target_link_libraries(AlignTool PRIVATE Qt6::Gui) else() target_link_libraries(AlignTool PRIVATE Qt5::Widgets) endif() avogadrolibs-1.101.0/avogadro/qtplugins/aligntool/align_dark.svg000066400000000000000000000045421506155467400250040ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/aligntool/align_light.svg000066400000000000000000000044021506155467400251650ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/aligntool/aligntool.cpp000066400000000000000000000234461506155467400246700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "aligntool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Core::contrastColor; using Avogadro::Core::Elements; using Avogadro::Rendering::GeometryNode; using Avogadro::Rendering::Identifier; using Avogadro::Rendering::TextLabel3D; using Avogadro::Rendering::TextProperties; namespace Avogadro::QtPlugins { AlignTool::AlignTool(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_renderer(nullptr), m_axis(0), m_alignType(0), m_toolWidget(nullptr) { m_activateAction->setText(tr("Align")); m_activateAction->setToolTip( tr("Align Molecules\n\n" "Left Mouse:\tSelect up to two atoms.\n" "\tThe first atom is centered at the origin.\n" "\tThe second atom is aligned to the selected axis.\n" "Right Mouse:\tReset alignment.\n" "Double-Click:\tCenter the atom at the origin.")); setIcon(); } AlignTool::~AlignTool() { if (m_toolWidget) m_toolWidget->deleteLater(); } void AlignTool::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/align_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/align_light.svg")); } QWidget* AlignTool::toolWidget() const { if (!m_toolWidget) { m_toolWidget = new QWidget; auto* labelAxis = new QLabel(tr("Axis:"), m_toolWidget); labelAxis->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); labelAxis->setMaximumHeight(15); // Combo box to select desired aixs to align to auto* comboAxis = new QComboBox(m_toolWidget); comboAxis->addItem("x"); comboAxis->addItem("y"); comboAxis->addItem("z"); comboAxis->setCurrentIndex(m_axis); // Button to actually perform actions auto* buttonAlign = new QPushButton(m_toolWidget); buttonAlign->setText(tr("Align")); connect(buttonAlign, SIGNAL(clicked()), this, SLOT(align())); auto* gridLayout = new QGridLayout(); gridLayout->addWidget(labelAxis, 0, 0, 1, 1, Qt::AlignRight); auto* hLayout = new QHBoxLayout; hLayout->addWidget(comboAxis); hLayout->addStretch(1); gridLayout->addLayout(hLayout, 0, 1); auto* hLayout3 = new QHBoxLayout(); hLayout3->addStretch(1); hLayout3->addWidget(buttonAlign); hLayout3->addStretch(1); auto* layout = new QVBoxLayout(); layout->addLayout(gridLayout); layout->addLayout(hLayout3); layout->addStretch(1); m_toolWidget->setLayout(layout); connect(comboAxis, SIGNAL(currentIndexChanged(int)), this, SLOT(axisChanged(int))); connect(m_toolWidget, SIGNAL(destroyed()), this, SLOT(toolWidgetDestroyed())); } return m_toolWidget; } void AlignTool::axisChanged(int axis) { // Axis to use - x=0, y=1, z=2 m_axis = axis; } void AlignTool::alignChanged(int align) { // Type of alignment - 0=everything, 1=molecule m_alignType = align; } void AlignTool::align() { if (m_atoms.size() == 0) return; if (m_atoms.size() >= 1) shiftAtomToOrigin(m_atoms[0].index); if (m_atoms.size() == 2) alignAtomToAxis(m_atoms[1].index, m_axis); } void AlignTool::shiftAtomToOrigin(Index atomIndex) { // Shift the atom to the origin Vector3 shift = m_molecule->atom(atomIndex).position3d(); const Core::Array& coords = m_molecule->atomPositions3d(); Core::Array newCoords(coords.size()); for (Index i = 0; i < coords.size(); ++i) newCoords[i] = coords[i] - shift; m_molecule->setAtomPositions3d(newCoords, tr("Align at Origin")); m_molecule->emitChanged(QtGui::Molecule::Atoms); } void AlignTool::alignAtomToAxis(Index atomIndex, int axis) { // Align the atom to the specified axis [[maybe_unused]] Vector3 align = m_molecule->atom(atomIndex).position3d(); const Core::Array& coords = m_molecule->atomPositions3d(); Core::Array newCoords(coords.size()); [[maybe_unused]] double alpha; double beta, gamma; alpha = beta = gamma = 0.0; Vector3 pos = m_molecule->atom(atomIndex).position3d(); pos.normalize(); Vector3 axisVector; if (axis == 0) // x-axis axisVector = Vector3(1., 0., 0.); else if (axis == 1) // y-axis axisVector = Vector3(0., 1., 0.); else if (axis == 2) // z-axis axisVector = Vector3(0., 0., 1.); // Calculate the angle of the atom from the axis double angle = acos(axisVector.dot(pos)); // Get the vector for the rotation axisVector = axisVector.cross(pos); axisVector.normalize(); // Now to rotate the fragment for (Index i = 0; i < coords.size(); ++i) newCoords[i] = Eigen::AngleAxisd(-angle, axisVector) * coords[i]; m_molecule->setAtomPositions3d(newCoords, tr("Align to Axis")); m_molecule->emitChanged(QtGui::Molecule::Atoms); } void AlignTool::toolWidgetDestroyed() { m_toolWidget = nullptr; } QUndoCommand* AlignTool::mousePressEvent(QMouseEvent* e) { // If the click is released on an atom, add it to the list if (e->button() != Qt::LeftButton || !m_renderer) return nullptr; Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y()); // Now add the atom on release. if (hit.type == Rendering::AtomType) { if (toggleAtom(hit)) emit drawablesChanged(); e->accept(); } return nullptr; } QUndoCommand* AlignTool::mouseDoubleClickEvent(QMouseEvent* e) { // Reset the atom list if (e->button() == Qt::LeftButton && !m_atoms.isEmpty()) { m_atoms.clear(); emit drawablesChanged(); e->accept(); } return nullptr; } bool AlignTool::toggleAtom(const Rendering::Identifier& atom) { int ind = m_atoms.indexOf(atom); if (ind >= 0) { m_atoms.remove(ind); return true; } if (m_atoms.size() >= 2) return false; m_atoms.push_back(atom); return true; } void AlignTool::draw(Rendering::GroupNode& node) { if (m_atoms.size() == 0) return; auto* geo = new GeometryNode; node.addChild(geo); // Add labels, extract positions QVector positions(m_atoms.size(), Vector3()); TextProperties atomLabelProp; atomLabelProp.setFontFamily(TextProperties::SansSerif); atomLabelProp.setAlign(TextProperties::HCenter, TextProperties::VCenter); for (int i = 0; i < m_atoms.size(); ++i) { Identifier& ident = m_atoms[i]; Q_ASSERT(ident.type == Rendering::AtomType); Q_ASSERT(ident.molecule != nullptr); auto atom = m_molecule->atom(ident.index); Q_ASSERT(atom.isValid()); unsigned char atomicNumber(atom.atomicNumber()); positions[i] = atom.position3d(); // get the color of the atom const unsigned char* color = Elements::color(atomicNumber); atomLabelProp.setColorRgb(contrastColor(Vector3ub(color)).data()); auto* label = new TextLabel3D; label->setText(QString("#%1").arg(i + 1).toStdString()); label->setTextProperties(atomLabelProp); label->setAnchor(positions[i].cast()); label->setRadius( static_cast(Elements::radiusCovalent(atomicNumber)) + 0.1f); geo->addDrawable(label); } } void AlignTool::registerCommands() { emit registerCommand("centerAtom", tr("Center the atom at the origin.")); emit registerCommand( "alignAtom", tr("Rotate the molecule to align the atom to the specified axis.")); } bool AlignTool::handleCommand(const QString& command, const QVariantMap& options) { if (m_molecule == nullptr) return false; // No molecule to handle the command. if (command == "centerAtom") { if (options.contains("id")) { Index atomIndex = options["id"].toInt(); if (atomIndex < m_molecule->atomCount()) shiftAtomToOrigin(atomIndex); return true; } else if (options.contains("index")) { Index atomIndex = options["index"].toInt(); if (atomIndex < m_molecule->atomCount()) shiftAtomToOrigin(atomIndex); return true; } return false; } else if (command == "alignAtom") { int axis = -1; if (options.contains("axis") && options["axis"].type() == QVariant::Int) { axis = options["axis"].toInt(); } else if (options.contains("axis") && options["axis"].type() == QVariant::String) { QString axisString = options["axis"].toString(); if (axisString == "x") axis = 0; else if (axisString == "y") axis = 1; else if (axisString == "z") axis = 2; } if (axis >= 0 && axis < 3) { if (options.contains("id")) { Index atomIndex = options["id"].toInt(); if (atomIndex < m_molecule->atomCount()) alignAtomToAxis(atomIndex, axis); return true; } else if (options.contains("index")) { Index atomIndex = options["index"].toInt(); if (atomIndex < m_molecule->atomCount()) alignAtomToAxis(atomIndex, axis); return true; } } return false; // invalid options } return true; // nothing to handle } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/aligntool/aligntool.h000066400000000000000000000050261506155467400243270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_ALIGNTOOL_H #define AVOGADRO_QTPLUGINS_ALIGNTOOL_H #include #include #include namespace Avogadro::QtPlugins { /** * @class AlignTool aligntool.h * * @brief The Align Tool class aligns molecules to a frame of reference. * @author Geoffrey Hutchison */ class AlignTool : public QtGui::ToolPlugin { Q_OBJECT public: explicit AlignTool(QObject* parent_ = nullptr); ~AlignTool() override; QString name() const override { return tr("Align tool"); } QString description() const override { return tr("Align molecules to a Cartesian axis"); } unsigned char priority() const override { return 90; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule* mol) override { if (mol) m_molecule = mol->undoMolecule(); } void setEditMolecule(QtGui::RWMolecule* mol) override { m_molecule = mol; } void setGLRenderer(Rendering::GLRenderer* renderer) override { m_renderer = renderer; } QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseDoubleClickEvent(QMouseEvent* e) override; void draw(Rendering::GroupNode& node) override; void shiftAtomToOrigin(Index atomIndex); void alignAtomToAxis(Index atomIndex, int axis); bool toggleAtom(const Rendering::Identifier& atom); bool handleCommand(const QString& command, const QVariantMap& options) override; /** * Called by the app to tell the tool to register commands. * If the tool has commands, it should emit the registerCommand signals. */ void registerCommands() override; public Q_SLOTS: void axisChanged(int axis); void alignChanged(int align); void align(); private: QAction* m_activateAction; QtGui::RWMolecule* m_molecule; Rendering::GLRenderer* m_renderer; QVector m_atoms; int m_axis; int m_alignType; mutable QWidget* m_toolWidget; private Q_SLOTS: void toolWidgetDestroyed(); }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTOPENGL_ALIGNTOOL_H avogadrolibs-1.101.0/avogadro/qtplugins/aligntool/aligntool.qrc000066400000000000000000000002071506155467400246610ustar00rootroot00000000000000 align_light.svg align_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/apbs/000077500000000000000000000000001506155467400211205ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/apbs/CMakeLists.txt000066400000000000000000000005021506155467400236550ustar00rootroot00000000000000set(apbs_srcs apbs.cpp apbsdialog.cpp apbsoutputdialog.cpp opendxreader.cpp ) set(apbs_uis apbsdialog.ui apbsoutputdialog.ui ) avogadro_plugin(apbs "APBS Extension" ExtensionPlugin apbs.h Apbs "${apbs_srcs}" "${apbs_uis}" ) target_link_libraries(apbs PRIVATE Avogadro::IO Avogadro::MoleQueue) avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbs.cpp000066400000000000000000000116641506155467400225610ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "apbs.h" #include "apbsdialog.h" #include "opendxreader.h" #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Mesh; Apbs::Apbs(QObject* parent_) : QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_progressDialog(nullptr), m_dialog(nullptr) { auto* action = new QAction(this); action->setText(tr("Run APBS…")); connect(action, SIGNAL(triggered()), this, SLOT(onRunApbs())); m_actions.append(action); action = new QAction(this); action->setText(tr("Open Output File…")); connect(action, SIGNAL(triggered()), this, SLOT(onOpenOutputFile())); m_actions.append(action); } Apbs::~Apbs() { delete m_dialog; delete m_progressDialog; } QStringList Apbs::menuPath(QAction*) const { return QStringList() << tr("&Input") << tr("&APBS"); } void Apbs::setMolecule(QtGui::Molecule* mol) { if (mol != m_molecule) m_molecule = mol; } void Apbs::onOpenOutputFile() { QString fileName = QFileDialog::getOpenFileName( qobject_cast(parent()), tr("Open Output File"), QString(), tr("OpenDX File (*.dx)")); if (fileName.isEmpty()) return; if (!m_molecule) return; loadOpenDxFile(fileName, *m_molecule); } void Apbs::meshGeneratorFinished() { auto* generator = qobject_cast(sender()); if (!generator) { return; } // delete the generator generator->deleteLater(); m_progressDialog->setValue(m_progressDialog->maximum()); m_progressDialog->hide(); } void Apbs::onMeshGeneratorProgress(int value) { m_progressDialog->setValue(value); qApp->processEvents(); } void Apbs::onRunApbs() { if (!m_dialog) m_dialog = new ApbsDialog(qobject_cast(parent())); m_dialog->setMolecule(m_molecule); int code = m_dialog->exec(); m_dialog->hide(); if (code == QDialog::Accepted) { m_pqrFileName = m_dialog->pqrFileName(); m_cubeFileName = m_dialog->cubeFileName(); emit moleculeReady(1); } } bool Apbs::readMolecule(QtGui::Molecule& molecule) { bool ok = Io::FileFormatManager::instance().readFile( molecule, m_pqrFileName.toStdString()); if (!ok) { QMessageBox::critical( qobject_cast(parent()), tr("IO Error"), tr("Error reading structure file (%1).").arg(m_pqrFileName)); return false; } if (!m_cubeFileName.isEmpty()) { // load the cube file and generate meshes ok = loadOpenDxFile(m_cubeFileName, molecule); if (!ok) return false; } return true; } bool Apbs::loadOpenDxFile(const QString& fileName, QtGui::Molecule& molecule) { OpenDxReader reader; bool ok = reader.readFile(fileName); if (!ok) { QMessageBox::critical( qobject_cast(parent()), tr("OpenDX Error"), tr("Error reading OpenDX file: %1").arg(reader.errorString())); } else { const Core::Cube* cube = reader.cube(); if (!cube) { QMessageBox::critical(qobject_cast(parent()), tr("OpenDX Error"), tr("Error reading OpenDX file: No cube found")); } else { if (!m_progressDialog) m_progressDialog = new QProgressDialog(qobject_cast(parent())); // generate positive mesh m_progressDialog->setLabelText("Generating Positive Potential Mesh"); m_progressDialog->setRange(0, 100); m_progressDialog->setValue(1); qApp->processEvents(); Mesh* mesh = molecule.addMesh(); auto* meshGenerator = new QtGui::MeshGenerator(cube, mesh, 0.1f); connect(meshGenerator, SIGNAL(finished()), this, SLOT(meshGeneratorFinished())); connect(meshGenerator, SIGNAL(progressValueChanged(int)), this, SLOT(onMeshGeneratorProgress(int))); meshGenerator->run(); // generate negative mesh m_progressDialog->setLabelText("Generating Negative Potential Mesh"); m_progressDialog->setRange(0, 100); m_progressDialog->setValue(1); qApp->processEvents(); mesh = molecule.addMesh(); meshGenerator = new QtGui::MeshGenerator(cube, mesh, -0.1f); connect(meshGenerator, SIGNAL(finished()), this, SLOT(meshGeneratorFinished())); connect(meshGenerator, SIGNAL(progressValueChanged(int)), this, SLOT(onMeshGeneratorProgress(int))); meshGenerator->run(); m_progressDialog->setValue(100); m_progressDialog->hide(); } } return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbs.h000066400000000000000000000033261506155467400222220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_APBS_APBS_H #define AVOGADRO_QTPLUGINS_APBS_APBS_H #include #include class QProgressDialog; namespace Avogadro::QtPlugins { class ApbsDialog; /** * @brief The Apbs class provides integration with the APBS package, primarily * reading the OpenDX output files produced by it at this point. */ class Apbs : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Apbs(QObject* parent_ = nullptr); ~Apbs() override; QString name() const override { return tr("APBS"); } QString description() const override { return tr("Interact with APBS utilities."); } QList actions() const override { return m_actions; } QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule*) override; bool readMolecule(QtGui::Molecule&) override; private slots: void onOpenOutputFile(); void onMeshGeneratorProgress(int value); void meshGeneratorFinished(); void onRunApbs(); private: /** * Loads the cube from the OpenDX file and adds the meshes to the molecule. */ bool loadOpenDxFile(const QString& fileName, QtGui::Molecule& molecule); private: QtGui::Molecule* m_molecule; QList m_actions; QProgressDialog* m_progressDialog; ApbsDialog* m_dialog; QString m_pqrFileName; QString m_cubeFileName; }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_APBS_APBS_H avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbsdialog.cpp000066400000000000000000000142171506155467400237360ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include #include "apbsdialog.h" #include "apbsoutputdialog.h" #include "ui_apbsdialog.h" #include #include #include #include #include #include namespace Avogadro::QtPlugins { using MoleQueue::InputGenerator; ApbsDialog::ApbsDialog(QWidget* parent_) : QDialog(parent_), m_ui(new Ui::ApbsDialog), m_molecule(nullptr), m_inputGenerator( new InputGenerator(QCoreApplication::applicationDirPath() + "/../" + QtGui::Utilities::libraryDirectory() + "/avogadro2/scripts/inputGenerators/apbs.py")), m_loadStructureFile(false), m_loadCubeFile(false) { m_ui->setupUi(this); connect(m_ui->closeButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_ui->openPdbFileButton, SIGNAL(clicked()), this, SLOT(openPdbFile())); connect(m_ui->openPqrFileButton, SIGNAL(clicked()), this, SLOT(openPqrFile())); connect(m_ui->runApbsButton, SIGNAL(clicked()), this, SLOT(runApbs())); connect(m_ui->runPdb2PqrButton, SIGNAL(clicked()), this, SLOT(runPdb2Pqr())); connect(m_ui->saveInputFileButton, SIGNAL(clicked()), this, SLOT(saveInputFile())); } ApbsDialog::~ApbsDialog() { delete m_ui; delete m_inputGenerator; } void ApbsDialog::setMolecule(QtGui::Molecule* molecule) { if (molecule != m_molecule) { m_molecule = molecule; // clear values from previous runs m_ui->pdbFileLineEdit->clear(); m_ui->pqrFileLineEdit->clear(); m_ui->textEdit->clear(); } } QString ApbsDialog::pqrFileName() const { if (m_ui->generateFromPdbButton->isChecked()) return m_generatedPqrFileName; else return m_ui->pqrFileLineEdit->text(); } QString ApbsDialog::cubeFileName() const { return m_cubeFileName; } void ApbsDialog::openPdbFile() { QString fileName = QFileDialog::getOpenFileName( qobject_cast(parent()), tr("Open PDB File"), QString(), tr("PDB Files (*.pdb)")); if (!fileName.isEmpty()) { m_ui->pdbFileLineEdit->setText(fileName); m_ui->generateFromPdbButton->setChecked(true); } } void ApbsDialog::openPqrFile() { QString fileName = QFileDialog::getOpenFileName( qobject_cast(parent()), tr("Open PQR File"), QString(), tr("PQR Files (*.pqr)")); if (!fileName.isEmpty()) { m_ui->pqrFileLineEdit->setText(fileName); m_ui->loadFromPqrButton->setChecked(true); } updatePreviewTextImmediately(); } void ApbsDialog::runApbs() { saveInputFile("apbs.in"); // run apbs QStringList arguments; arguments.append("apbs.in"); QProcess process; process.start("apbs", arguments); // FIXME: show progress dialog process.waitForFinished(); if (process.exitStatus() == QProcess::NormalExit) { m_cubeFileName = "pot-PE0.dx"; ApbsOutputDialog dialog(this); connect(&dialog, SIGNAL(accepted()), this, SLOT(accept())); int code = dialog.exec(); if (code == QDialog::Accepted) { m_loadStructureFile = dialog.loadStructureFile(); m_loadCubeFile = dialog.loadCubeFile(); } else { m_loadStructureFile = false; m_loadCubeFile = false; } } else { m_loadStructureFile = false; m_loadCubeFile = false; QMessageBox::critical(this, tr("Error"), tr("Error running APBS: %1") .arg(process.readAllStandardError().constData())); } } void ApbsDialog::runPdb2Pqr() { QString pdbFileName = m_ui->pdbFileLineEdit->text(); if (pdbFileName.isEmpty()) { QMessageBox::critical(this, tr("Error"), tr("Please specify PDB file")); return; } QString pqrFileName_ = QFileInfo(pdbFileName).baseName() + ".pqr"; QString forceFieldName = m_ui->forceFieldComboBox->currentText(); // run pdb2pqr QStringList arguments; arguments.append("--ff"); arguments.append(forceFieldName); arguments.append(pdbFileName); arguments.append(pqrFileName_); QProcess process; process.start("pdb2pqr", arguments); process.waitForFinished(); if (process.exitStatus() == QProcess::NormalExit) { QMessageBox::information(this, "Success", QString("Generated %1").arg(pqrFileName_)); m_generatedPqrFileName = pqrFileName_; updatePreviewTextImmediately(); } else { QMessageBox::critical(this, "Error", QString("Error running PDB2PQR")); m_generatedPqrFileName.clear(); } } void ApbsDialog::saveInputFile() { QString fileName = QFileDialog::getSaveFileName( this, tr("Save APBS Input File"), "apbs.in", tr("APBS Input (*.in)")); if (!fileName.isEmpty()) { saveInputFile(fileName); QMessageBox::information(this, tr("Success"), tr("Input file written to '%1'").arg(fileName)); } } void ApbsDialog::saveInputFile(const QString& fileName) { QString contents = m_inputGenerator->fileContents("apbs.in"); QFile file(fileName); file.open(QFile::WriteOnly); file.write(contents.toLocal8Bit()); file.close(); } void ApbsDialog::updatePreviewTextImmediately() { QString pqrFileName_; if (m_ui->generateFromPdbButton->isChecked()) pqrFileName_ = m_generatedPqrFileName; else pqrFileName_ = m_ui->pqrFileLineEdit->text(); QJsonObject inputOptions; QJsonObject options; options["mainFile"] = pqrFileName_; options["Input File"] = pqrFileName_; options["Calculation"] = QString("mg-auto"); inputOptions["options"] = options; QJsonObject settings; inputOptions["settings"] = settings; bool success = m_inputGenerator->generateInput(inputOptions, *m_molecule); if (!success) { // FIXME: show in a dialog std::cerr << "errors: " << std::endl; foreach (const QString& error, m_inputGenerator->errorList()) { std::cerr << " " << error.toStdString() << std::endl; } } m_ui->textEdit->setText(m_inputGenerator->fileContents("apbs.in")); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbsdialog.h000066400000000000000000000031501506155467400233750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_APBS_APBSDIALOG_H #define AVOGADRO_QTPLUGINS_APBS_APBSDIALOG_H #include #include namespace Ui { class ApbsDialog; } namespace Avogadro { namespace QtGui { class Molecule; } namespace MoleQueue { class InputGenerator; } namespace QtPlugins { /** * @brief Dialog for running APBS. */ class ApbsDialog : public QDialog { Q_OBJECT public: /** * Constructor for ApbsDialog. */ ApbsDialog(QWidget* parent_ = nullptr); /** * Destructor for ApbsDialog. */ ~ApbsDialog() override; void setMolecule(QtGui::Molecule* molecule); /** * Returns the file name for the input .pqr file. */ QString pqrFileName() const; /** * Returns the file name for the output .dx file. */ QString cubeFileName() const; private slots: void openPdbFile(); void openPqrFile(); void runApbs(); void runPdb2Pqr(); void saveInputFile(); void saveInputFile(const QString& fileName); private: void updatePreviewTextImmediately(); private: Ui::ApbsDialog* m_ui; QString m_generatedPqrFileName; QtGui::Molecule* m_molecule; MoleQueue::InputGenerator* m_inputGenerator; QString m_cubeFileName; bool m_loadStructureFile; bool m_loadCubeFile; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_APBS_APBSDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbsdialog.ui000066400000000000000000000126301506155467400235660ustar00rootroot00000000000000 ApbsDialog 0 0 794 644 APBS Structure Input File Generate Input From PDB true PDB File: 0 0 Force Field: AMBER CHARMM PARSE TYL06 PEOEPB SWANSON 0 0 Run PDB2PQR Load Existing PQR File PQR File: APBS Input File Save Input File Run APBS Close avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbsoutputdialog.cpp000066400000000000000000000014411506155467400252120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "apbsoutputdialog.h" #include "ui_apbsoutputdialog.h" namespace Avogadro::QtPlugins { ApbsOutputDialog::ApbsOutputDialog(QWidget* parent_) : QDialog(parent_), m_ui(new Ui::ApbsOutputDialog) { m_ui->setupUi(this); } ApbsOutputDialog::~ApbsOutputDialog() {} bool ApbsOutputDialog::loadStructureFile() const { return m_ui->loadStructureCheckBox->isChecked(); } bool ApbsOutputDialog::loadCubeFile() const { return m_ui->loadCubeCheckBox->isChecked(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbsoutputdialog.h000066400000000000000000000024601506155467400246610ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_APBS_APBSOUTPUTDIALOG_H #define AVOGADRO_QTPLUGINS_APBS_APBSOUTPUTDIALOG_H #include namespace Ui { class ApbsOutputDialog; } namespace Avogadro::QtPlugins { /** * @brief Dialog indicating a successful run of APBS. * * The ApbsOutputDialog class is used to tell the user that the run of APBS * was successful. It allows the user to select which of the input and output * files to load. */ class ApbsOutputDialog : public QDialog { Q_OBJECT public: /** * Constructor for ApbsOutputDialog. */ ApbsOutputDialog(QWidget* parent_ = nullptr); /** * Destructor for ApbsOutputDialog. */ ~ApbsOutputDialog() override; /** * Returns true if the user checked the 'Load Structure' check box. */ bool loadStructureFile() const; /** * Returns true if the user checked the 'Load Cube' check box. */ bool loadCubeFile() const; private: Ui::ApbsOutputDialog* m_ui; }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_APBS_APBSOUTPUTDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/apbs/apbsoutputdialog.ui000066400000000000000000000040571506155467400250530ustar00rootroot00000000000000 ApbsOutputDialog 0 0 325 99 Success Success! Load Structure File true Load Cube File true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ApbsOutputDialog accept() 248 254 157 274 buttonBox rejected() ApbsOutputDialog reject() 316 260 286 274 avogadrolibs-1.101.0/avogadro/qtplugins/apbs/opendxreader.cpp000066400000000000000000000043611506155467400243100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "opendxreader.h" #include #include #include #include namespace Avogadro::QtPlugins { using Core::Cube; bool OpenDxReader::readFile(const QString& fileName) { QFile file(fileName); if (!file.open(QFile::ReadOnly)) { m_errorString = "Failed to open file for reading"; return false; } delete m_cube; Vector3i dim(0, 0, 0); Vector3 origin(0, 0, 0); QVector spacings; std::vector values; while (!file.atEnd()) { QByteArray line = file.readLine(); QTextStream stream(line); if (line.isEmpty()) { // skip empty line continue; } else if (line[0] == '#') { // skip comment line continue; } else if (line.startsWith("object")) { if (dim[0] != 0) continue; QString unused; stream >> unused >> unused >> unused >> unused >> unused; stream >> dim[0] >> dim[1] >> dim[2]; } else if (line.startsWith("origin")) { QString unused; stream >> unused >> origin[0] >> origin[1] >> origin[2]; } else if (line.startsWith("delta")) { QString unused; Vector3 delta; stream >> unused >> delta[0] >> delta[1] >> delta[2]; spacings.append(delta); } else if (line.startsWith("attribute")) { continue; } else if (line.startsWith("component")) { continue; } else { // data line while (!stream.atEnd()) { double value; stream >> value; values.push_back(value); stream.skipWhiteSpace(); } } } Vector3 spacing(spacings[0][0], spacings[1][1], spacings[2][2]); // create potential cube m_cube = new Cube; m_cube->setCubeType(Cube::ESP); m_cube->setLimits(origin, dim, spacing); m_cube->setData(values); return true; } QString OpenDxReader::errorString() const { return m_errorString; } Cube* OpenDxReader::cube() const { return m_cube; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/apbs/opendxreader.h000066400000000000000000000024151506155467400237530ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_APBS_OPENDXREADER_H #define AVOGADRO_QTPLUGINS_APBS_OPENDXREADER_H #include namespace Avogadro { namespace Core { class Cube; } namespace QtPlugins { /** * @brief Provide a reader for OpenDX files. */ class OpenDxReader { public: /** * Constructor for OpenDxReader. */ OpenDxReader() = default; /** * Destructor for OpenDxReader. */ ~OpenDxReader() = default; /** * Reads the file with the given @fileName. Returns false if an error * occurs. */ bool readFile(const QString& fileName); /** * @return String describing the last error that occurred. */ QString errorString() const; /** * Returns the potential energy cube read from the file. Returns 0 if no file * has been successfully read. */ Core::Cube* cube() const; private: Core::Cube* m_cube = nullptr; QString m_errorString; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_APBS_OPENDXREADER_H avogadrolibs-1.101.0/avogadro/qtplugins/applycolors/000077500000000000000000000000001506155467400225425ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/applycolors/CMakeLists.txt000066400000000000000000000003701506155467400253020ustar00rootroot00000000000000avogadro_plugin(ApplyColors "Extension to apply color schemes to atoms and residues." ExtensionPlugin applycolors.h ApplyColors "applycolors.cpp" chargedialog.ui ) target_link_libraries(ApplyColors PRIVATE Avogadro::Calc tinycolormap) avogadrolibs-1.101.0/avogadro/qtplugins/applycolors/applycolors.cpp000066400000000000000000000414771506155467400256320ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "applycolors.h" #include "ui_chargedialog.h" #include #include #include #include #include #include #include #include #include #include using namespace tinycolormap; namespace Avogadro::QtPlugins { const int atomColors = 0; const int bondColors = 1; const int residueColors = 2; class ChargeColorDialog : public QDialog, public Ui::ChargeDialog { public: ChargeColorDialog(QWidget* parent = nullptr) : QDialog(parent) { setWindowFlags(Qt::Dialog | Qt::Tool); setupUi(this); } }; ApplyColors::ApplyColors(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_dialog(nullptr) { QAction* action; action = new QAction(tr("By Custom Color…"), this); action->setData(atomColors); connect(action, SIGNAL(triggered()), SLOT(openColorDialog())); m_actions.append(action); action = new QAction(tr("By Atomic Index…"), this); action->setData(atomColors); connect(action, SIGNAL(triggered()), SLOT(applyIndexColors())); m_actions.append(action); action = new QAction(tr("By Distance…"), this); action->setData(atomColors); connect(action, SIGNAL(triggered()), SLOT(applyDistanceColors())); m_actions.append(action); action = new QAction(tr("By Element"), this); action->setData(atomColors); connect(action, SIGNAL(triggered()), SLOT(resetColors())); m_actions.append(action); // not sure if we want to color atoms by residue or not... action = new QAction(tr("By Custom Color…"), this); action->setData(residueColors); connect(action, SIGNAL(triggered()), SLOT(openColorDialogResidue())); m_actions.append(action); action = new QAction(tr("By Chain"), this); action->setData(residueColors); connect(action, SIGNAL(triggered()), SLOT(resetColorsResidue())); m_actions.append(action); action = new QAction(tr("By Partial Charge…"), this); action->setData(atomColors); connect(action, SIGNAL(triggered()), SLOT(applyChargeColors())); m_actions.append(action); action = new QAction(tr("By Secondary Structure"), this); action->setData(residueColors); connect(action, SIGNAL(triggered()), SLOT(applySecondaryStructureColors())); m_actions.append(action); action = new QAction(tr("By Amino Acid"), this); action->setData(residueColors); connect(action, SIGNAL(triggered()), SLOT(applyAminoColors())); m_actions.append(action); action = new QAction(tr("By Shapely Scheme"), this); action->setData(residueColors); connect(action, SIGNAL(triggered()), SLOT(applyShapelyColors())); m_actions.append(action); } ApplyColors::~ApplyColors() { if (m_dialog) m_dialog->deleteLater(); } ColormapType ApplyColors::getColormapFromString(const QString& name) const { // Just do all of them, even though we won't use them all if (name == tr("Parula", "colormap")) return ColormapType::Parula; else if (name == tr("Heat", "colormap")) return ColormapType::Heat; else if (name == tr("Hot", "colormap")) return ColormapType::Hot; else if (name == tr("Gray", "colormap")) return ColormapType::Gray; else if (name == tr("Magma", "colormap")) return ColormapType::Magma; else if (name == tr("Inferno", "colormap")) return ColormapType::Inferno; else if (name == tr("Plasma", "colormap")) return ColormapType::Plasma; else if (name == tr("Viridis", "colormap")) return ColormapType::Viridis; else if (name == tr("Cividis", "colormap")) return ColormapType::Cividis; else if (name == tr("Spectral", "colormap")) return ColormapType::Spectral; else if (name == tr("Coolwarm", "colormap")) return ColormapType::Coolwarm; else if (name == tr("Balance", "colormap")) return ColormapType::Balance; else if (name == tr("Blue-DarkRed", "colormap")) return ColormapType::BlueDkRed; else if (name == tr("Turbo", "colormap")) return ColormapType::Turbo; return ColormapType::Turbo; } QString ApplyColors::description() const { return tr("Apply color schemes to atoms and residues."); } QList ApplyColors::actions() const { return m_actions; } QStringList ApplyColors::menuPath(QAction* action) const { if (action->data() == atomColors) return QStringList() << tr("&View") << tr("Color Atoms"); else if (action->data() == residueColors) return QStringList() << tr("&View") << tr("Color Residues"); else return QStringList(); } void ApplyColors::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void ApplyColors::openColorDialog() { if (m_dialog == nullptr) { m_dialog = new QColorDialog(qobject_cast(parent())); connect(m_dialog, SIGNAL(currentColorChanged(const QColor&)), SLOT(applyCustomColor(const QColor&))); } m_dialog->exec(); } // TODO - read colormap gradients (e.g., turbo) Vector3ub rainbowGradient(const float value, const ColormapType type = ColormapType::Turbo) { auto color = tinycolormap::GetColor(value, type); Vector3ub ci(color.ri(), color.gi(), color.bi()); return ci; } Vector3ub chargeGradient(const float value, const float clamp, const ColormapType type = ColormapType::Coolwarm) { // okay, typically color scales have blue at the bottom, red at the top. // so we need to invert, so blue is positive charge, red is negative charge. // we also need to scale the color to the range of the charge. float scaledValue = value / clamp; // from -1 to 1.0 float scaledValue2 = 1.0 - ((scaledValue + 1.0) / 2.0); // from 0 to 1.0 red to blue auto color = tinycolormap::GetColor(scaledValue2, type); Vector3ub ci(color.ri(), color.gi(), color.bi()); return ci; } void ApplyColors::applyIndexColors() { if (m_molecule == nullptr) return; // check on colormap ColormapType type = ColormapType::Turbo; QStringList colormaps; bool ok; colormaps << tr("Parula", "colormap") << tr("Magma", "colormap") << tr("Inferno", "colormap") << tr("Plasma", "colormap") << tr("Viridis", "colormap") << tr("Cividis", "colormap") << tr("Spectral", "colormap") << tr("Turbo", "colormap"); QString item = QInputDialog::getItem( nullptr, tr("Select Colormap"), tr("Colormap:"), colormaps, 7, false, &ok); if (ok) { type = getColormapFromString(item); } bool isSelection = !m_molecule->isSelectionEmpty(); // probably better to get color scales, but for now do it manually auto numAtoms = m_molecule->atomCount(); for (Index i = 0; i < numAtoms; ++i) { // if there's a selection and this atom isn't selected, skip it if (isSelection && !m_molecule->atomSelected(i)) continue; float indexFraction = float(i) / float(numAtoms); m_molecule->atom(i).setColor(rainbowGradient(indexFraction, type)); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::applyChargeColors() { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); // get the list of possible models const auto identifiers = Calc::ChargeManager::instance().identifiersForMolecule(*m_molecule); if (identifiers.empty()) return; // populate the dialog to choose the model and colormap ChargeColorDialog dialog; for (const auto& model : identifiers) { auto name = Calc::ChargeManager::instance().nameForModel(model); dialog.modelCombo->addItem(name.c_str(), model.c_str()); } dialog.exec(); if (dialog.result() != QDialog::Accepted) return; // get the model and colormap const auto model = dialog.modelCombo->currentData().toString().toStdString(); const auto colormapName = dialog.colorMapCombo->currentText(); const auto type = getColormapFromString(colormapName); // first off, get the range of partial charges auto numAtoms = m_molecule->atomCount(); float minCharge = 0.0f; float maxCharge = 0.0f; auto charges = Calc::ChargeManager::instance().partialCharges(model, *m_molecule); // check if the model string is already a partial charge type if (m_molecule->partialChargeTypes().find(model) != m_molecule->partialChargeTypes().end()) { charges = m_molecule->partialCharges(model); } for (Index i = 0; i < numAtoms; ++i) { float charge = charges(i, 0); minCharge = std::min(minCharge, charge); maxCharge = std::max(maxCharge, charge); } // now apply the colors float clamp = std::max(std::abs(minCharge), std::abs(maxCharge)); for (Index i = 0; i < numAtoms; ++i) { // if there's a selection and this atom isn't selected, skip it if (isSelection && !m_molecule->atomSelected(i)) continue; m_molecule->atom(i).setColor(chargeGradient(charges(i, 0), clamp, type)); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::applyDistanceColors() { if (m_molecule == nullptr && m_molecule->atomCount() == 0) return; bool isSelection = !m_molecule->isSelectionEmpty(); // check on colormap ColormapType type = ColormapType::Turbo; QStringList colormaps; bool ok; colormaps << tr("Parula", "colormap") << tr("Magma", "colormap") << tr("Inferno", "colormap") << tr("Plasma", "colormap") << tr("Viridis", "colormap") << tr("Cividis", "colormap") << tr("Spectral", "colormap") << tr("Turbo", "colormap"); QString item = QInputDialog::getItem( nullptr, tr("Select Colormap"), tr("Colormap:"), colormaps, 7, false, &ok); if (ok) { type = getColormapFromString(item); } Vector3 firstPos = m_molecule->atomPosition3d(0); Real size = 2.0 * m_molecule->radius(); // probably better to get color scales, but for now do it manually auto numAtoms = m_molecule->atomCount(); for (Index i = 0; i < numAtoms; ++i) { // if there's a selection and this atom isn't selected, skip it if (isSelection && !m_molecule->atomSelected(i)) continue; Vector3 currPos = m_molecule->atomPosition3d(i); Vector3 diff = currPos - firstPos; Real distance = diff.norm(); Real distanceFraction = distance / size; m_molecule->atom(i).setColor(rainbowGradient(distanceFraction, type)); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::resetColors() { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); for (Index i = 0; i < m_molecule->atomCount(); ++i) { // if there's a selection and this atom isn't selected, skip it if (isSelection && !m_molecule->atomSelected(i)) continue; Vector3ub color(Core::Elements::color(m_molecule->atomicNumber(i))); m_molecule->atom(i).setColor(color); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::applyCustomColor(const QColor& new_color) { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); Vector3ub color; // map to our types color[0] = static_cast(new_color.red()); color[1] = static_cast(new_color.green()); color[2] = static_cast(new_color.blue()); for (Index i = 0; i < m_molecule->atomCount(); ++i) { // if there's a selection and this atom isn't selected, skip it if (isSelection && !m_molecule->atomSelected(i)) continue; m_molecule->atom(i).setColor(color); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::openColorDialogResidue() { if (m_dialog == nullptr) { m_dialog = new QColorDialog(qobject_cast(parent())); } m_dialog->disconnect(); connect(m_dialog, SIGNAL(currentColorChanged(const QColor&)), SLOT(applyCustomColorResidue(const QColor&))); m_dialog->exec(); } void ApplyColors::applyCustomColorResidue(const QColor& new_color) { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); Vector3ub color; // map to our types color[0] = static_cast(new_color.red()); color[1] = static_cast(new_color.green()); color[2] = static_cast(new_color.blue()); for (Index i = 0; i < m_molecule->residueCount(); ++i) { // if there's a selection and this residue isn't selected, skip it auto& residue = m_molecule->residue(i); if (isSelection && !m_molecule->atomSelected(residue.getAtomByName("CA").index())) continue; residue.setColor(color); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::resetColorsResidue() { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); for (Index i = 0; i < m_molecule->residueCount(); ++i) { // if there's a selection and this residue isn't selected, skip it auto& residue = m_molecule->residue(i); if (isSelection && !m_molecule->atomSelected(residue.getAtomByName("CA").index())) continue; int offset = 0; char chainId = residue.chainId(); if (chainId >= 'A' && chainId <= 'Z') offset = chainId - 'A'; else if (chainId >= 'a' && chainId <= 'z') offset = chainId - 'a'; else if (chainId >= '0' && chainId <= '9') offset = chainId - '0' + 15; // starts at 'P' Vector3ub color(Core::chain_color[offset]); residue.setColor(color); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::applySecondaryStructureColors() { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); for (Index i = 0; i < m_molecule->residueCount(); ++i) { // if there's a selection and this residue isn't selected, skip it auto& residue = m_molecule->residue(i); if (isSelection && !m_molecule->atomSelected(residue.getAtomByName("CA").index())) continue; Core::Residue::SecondaryStructure type = residue.secondaryStructure(); if (type < 0 || type > 7) { type = Core::Residue::SecondaryStructure::coil; } Vector3ub color(Core::secondary_color[type]); residue.setColor(color); } // end loop m_molecule->emitChanged(QtGui::Molecule::Atoms); } int residueNameToOffset(const std::string& name) { std::string residueName(name); // ensure it's always in uppercase for (auto& c : residueName) c = (unsigned char)toupper(c); // used for "amino" and "shapely" color schemes int offset = 22; // other if (residueName == "ALA") offset = 0; else if (residueName == "ARG") offset = 1; else if (residueName == "ASN") offset = 2; else if (residueName == "ASP") offset = 3; else if (residueName == "CYS") offset = 4; else if (residueName == "GLN") offset = 5; else if (residueName == "GLU") offset = 6; else if (residueName == "GLY") offset = 7; else if (residueName == "HIS") offset = 8; else if (residueName == "ILE") offset = 9; else if (residueName == "LEU") offset = 10; else if (residueName == "LYS") offset = 11; else if (residueName == "MET") offset = 12; else if (residueName == "PHE") offset = 13; else if (residueName == "PRO") offset = 14; else if (residueName == "SER") offset = 15; else if (residueName == "THR") offset = 16; else if (residueName == "TRP") offset = 17; else if (residueName == "TYR") offset = 18; else if (residueName == "VAL") offset = 19; else if (residueName == "ASX") offset = 20; else if (residueName == "GLX") offset = 21; else offset = 22; // default return offset; } void ApplyColors::applyAminoColors() { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); for (Index i = 0; i < m_molecule->residueCount(); ++i) { // if there's a selection and this residue isn't selected, skip it auto& residue = m_molecule->residue(i); if (isSelection && !m_molecule->atomSelected(residue.getAtomByName("CA").index())) continue; int offset = residueNameToOffset(residue.residueName()); Vector3ub color(Core::amino_color[offset]); residue.setColor(color); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } void ApplyColors::applyShapelyColors() { if (m_molecule == nullptr) return; bool isSelection = !m_molecule->isSelectionEmpty(); for (Index i = 0; i < m_molecule->residueCount(); ++i) { // if there's a selection and this residue isn't selected, skip it auto& residue = m_molecule->residue(i); if (isSelection && !m_molecule->atomSelected(residue.getAtomByName("CA").index())) continue; int offset = residueNameToOffset(residue.residueName()); Vector3ub color(Core::shapely_color[offset]); residue.setColor(color); } m_molecule->emitChanged(QtGui::Molecule::Atoms); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/applycolors/applycolors.h000066400000000000000000000032521506155467400252640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_APPLYCOLORS_H #define AVOGADRO_QTPLUGINS_APPLYCOLORS_H #include #include #include class QColorDialog; namespace Avogadro::QtPlugins { /** * @brief The ApplyColors class is an extension to modify apply custom colors. */ class ApplyColors : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit ApplyColors(QObject* parent_ = nullptr); ~ApplyColors() override; QString name() const override { return tr("ApplyColors"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void openColorDialog(); void applyCustomColor(const QColor& color); void applyDistanceColors(); void applyIndexColors(); void applyChargeColors(); void resetColors(); void openColorDialogResidue(); void applyCustomColorResidue(const QColor& color); void applyAminoColors(); void applyShapelyColors(); void applySecondaryStructureColors(); void resetColorsResidue(); private: QList m_actions; QtGui::Molecule* m_molecule; QColorDialog* m_dialog; tinycolormap::ColormapType getColormapFromString(const QString& name) const; }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_APPLYCOLORS_H avogadrolibs-1.101.0/avogadro/qtplugins/applycolors/chargedialog.ui000066400000000000000000000056071506155467400255220ustar00rootroot00000000000000 ChargeDialog 0 0 293 126 Partial Charges Colormap: QDialogButtonBox::Cancel|QDialogButtonBox::Ok Balance Blue-DarkRed Coolwarm Spectral Turbo Charge Model: Qt::Vertical 20 40 modelCombo colorMapCombo buttonBox rejected() ChargeDialog reject() 194 105 146 62 buttonBox accepted() ChargeDialog accept() 194 105 146 62 avogadrolibs-1.101.0/avogadro/qtplugins/ballandstick/000077500000000000000000000000001506155467400226265ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/ballandstick/CMakeLists.txt000066400000000000000000000003031506155467400253620ustar00rootroot00000000000000avogadro_plugin(BallStick "Ball and stick rendering scheme" ScenePlugin ballandstick.h BallAndStick ballandstick.cpp "") target_link_libraries(BallStick PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/ballandstick/ballandstick.cpp000066400000000000000000000313321506155467400257670ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "ballandstick.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Elements; using QtGui::PluginLayerManager; using Rendering::CylinderGeometry; using Rendering::GeometryNode; using Rendering::SphereGeometry; struct LayerBallAndStick : Core::LayerData { QWidget* widget; bool multiBonds; bool showHydrogens; float atomScale; float bondRadius; float opacity; LayerBallAndStick() { widget = nullptr; QSettings settings; atomScale = settings.value("ballandstick/atomScale", 0.3).toDouble(); bondRadius = settings.value("ballandstick/bondRadius", 0.1).toDouble(); multiBonds = settings.value("ballandstick/multiBonds", true).toBool(); showHydrogens = settings.value("ballandstick/showHydrogens", true).toBool(); opacity = settings.value("ballandstick/opacity", 1.0).toDouble(); } LayerBallAndStick(std::string settings) { widget = nullptr; deserialize(settings); } ~LayerBallAndStick() override { if (widget) widget->deleteLater(); } std::string serialize() final { return boolToString(multiBonds) + " " + boolToString(showHydrogens) + " " + std::to_string(atomScale) + " " + std::to_string(bondRadius) + " " + std::to_string(opacity); } void deserialize(std::string text) final { std::stringstream ss(text); std::string aux; ss >> aux; multiBonds = stringToBool(aux); ss >> aux; showHydrogens = stringToBool(aux); ss >> aux; atomScale = std::stof(aux); ss >> aux; bondRadius = std::stof(aux); ss >> aux; if (!aux.empty()) opacity = std::stof(aux); // backwards compatibility } LayerData* clone() final { return new LayerBallAndStick(serialize()); } void setupWidget(BallAndStick* slot) { if (!widget) { widget = new QWidget(qobject_cast(slot->parent())); auto* v = new QVBoxLayout; auto* f = new QFormLayout; auto* atomRadiusSlider = new QSlider(Qt::Horizontal); atomRadiusSlider->setMinimum(1); atomRadiusSlider->setMaximum(9); atomRadiusSlider->setTickInterval(1); atomRadiusSlider->setValue(atomScale * 10); QObject::connect(atomRadiusSlider, &QSlider::valueChanged, slot, &BallAndStick::atomRadiusChanged); f->addRow(QObject::tr("Atom scale"), atomRadiusSlider); auto* bondRadiusSlider = new QSlider(Qt::Horizontal); bondRadiusSlider->setMinimum(1); bondRadiusSlider->setMaximum(8); bondRadiusSlider->setTickInterval(1); bondRadiusSlider->setValue(bondRadius * 10); QObject::connect(bondRadiusSlider, &QSlider::valueChanged, slot, &BallAndStick::bondRadiusChanged); f->addRow(QObject::tr("Bond scale"), bondRadiusSlider); auto* opacitySlider = new QSlider(Qt::Horizontal); opacitySlider->setMinimum(0); opacitySlider->setMaximum(100); opacitySlider->setTickInterval(1); opacitySlider->setValue(static_cast(opacity * 100)); QObject::connect(opacitySlider, &QSlider::valueChanged, slot, &BallAndStick::opacityChanged); f->addRow(QObject::tr("Opacity"), opacitySlider); v->addLayout(f); auto* check = new QCheckBox(QObject::tr("Show multiple bonds")); check->setChecked(multiBonds); QObject::connect(check, &QCheckBox::clicked, slot, &BallAndStick::multiBonds); v->addWidget(check); check = new QCheckBox(QObject::tr("Show hydrogens")); check->setChecked(showHydrogens); QObject::connect(check, &QCheckBox::clicked, slot, &BallAndStick::showHydrogens); v->addWidget(check); v->addStretch(1); widget->setLayout(v); } } }; BallAndStick::BallAndStick(QObject* p) : ScenePlugin(p), m_group(nullptr) { m_layerManager = PluginLayerManager(m_name); } BallAndStick::~BallAndStick() {} void BallAndStick::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { m_layerManager.load(); // Add a sphere node to contain all of the spheres. m_group = &node; auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; spheres->identifier().molecule = reinterpret_cast(&molecule); spheres->identifier().type = Rendering::AtomType; geometry->addDrawable(spheres); // if we have to draw any translucent spheres, we need to add a separate // geometry node for them auto translucentSpheres = new SphereGeometry; translucentSpheres->setRenderPass(Rendering::TranslucentPass); translucentSpheres->identifier().molecule = reinterpret_cast(&molecule); translucentSpheres->identifier().type = Rendering::AtomType; geometry->addDrawable(translucentSpheres); // for the selected atoms auto selectedSpheres = new SphereGeometry; selectedSpheres->setOpacity(0.42); geometry->addDrawable(selectedSpheres); for (Index i = 0; i < molecule.atomCount(); ++i) { Core::Atom atom = molecule.atom(i); if (!m_layerManager.atomEnabled(i)) { continue; } unsigned char atomicNumber = atom.atomicNumber(); auto* interface = m_layerManager.getSetting( m_layerManager.getLayerID(i)); if (atomicNumber == 1 && !interface->showHydrogens) continue; Vector3ub color = atom.color(); auto radius = static_cast(Elements::radiusVDW(atomicNumber)); float scale = interface->atomScale; if (interface->opacity < 1.0f) { translucentSpheres->addSphere(atom.position3d().cast(), color, radius * scale, i); translucentSpheres->setOpacity(interface->opacity); } else spheres->addSphere(atom.position3d().cast(), color, radius * scale, i); if (atom.selected()) { // add the selected indicator color = Vector3ub(0, 0, 255); radius *= 1.2; selectedSpheres->addSphere(atom.position3d().cast(), color, radius * scale, i); } } auto* cylinders = new CylinderGeometry; cylinders->identifier().molecule = &molecule; cylinders->identifier().type = Rendering::BondType; geometry->addDrawable(cylinders); auto* translucentBonds = new CylinderGeometry; translucentBonds->setRenderPass(Rendering::TranslucentPass); translucentBonds->identifier().molecule = &molecule; translucentBonds->identifier().type = Rendering::BondType; float opacity = 1.0f; // for any translucent bonds geometry->addDrawable(translucentBonds); for (Index i = 0; i < molecule.bondCount(); ++i) { Core::Bond bond = molecule.bond(i); if (!m_layerManager.bondEnabled(bond.atom1().index(), bond.atom2().index())) { continue; } auto* interface1 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom1().index())); auto* interface2 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom2().index())); if (!interface1->showHydrogens && !interface2->showHydrogens && (bond.atom1().atomicNumber() == 1 || bond.atom2().atomicNumber() == 1)) { continue; } bool doOpaque = true; if (interface1->opacity < 1.0f || interface2->opacity < 1.0f) { opacity = std::min(interface1->opacity, interface2->opacity); translucentBonds->setOpacity(opacity); doOpaque = false; } float bondRadius = (interface1->bondRadius + interface2->bondRadius) * 0.5f; Vector3f pos1 = bond.atom1().position3d().cast(); Vector3f pos2 = bond.atom2().position3d().cast(); Vector3ub color1 = bond.atom1().color(); Vector3ub color2 = bond.atom2().color(); Vector3f bondVector = pos2 - pos1; float bondLength = bondVector.norm(); bondVector /= bondLength; switch (interface1->multiBonds || interface2->multiBonds ? bond.order() : 1) { case 3: { Vector3f delta = bondVector.unitOrthogonal(); // Rotate 45 degrees around the bond vector. Eigen::Quaternionf q; q = Eigen::AngleAxisf(45.0f * DEG_TO_RAD_F, bondVector); delta = q * delta * 2.0f * bondRadius; if (doOpaque) { cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.15, color1, color2, i); cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.15, color1, color2, i); } else { translucentBonds->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.15, color1, color2, i); translucentBonds->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.15, color1, color2, i); } // This relies upon the single bond case below for the third cylinder. [[fallthrough]]; } default: case 1: if (doOpaque) { cylinders->addCylinder(pos1, pos2, m_bondRadius, color1, color2, i); } else { translucentBonds->addCylinder(pos1, pos2, m_bondRadius, color1, color2, i); } break; case 2: { Vector3f delta = bondVector.unitOrthogonal(); // Rotate 45 degrees around the bond vector. Eigen::Quaternionf q; q = Eigen::AngleAxisf(45.0f * DEG_TO_RAD_F, bondVector); delta = q * delta * bondRadius; if (doOpaque) { cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.3, color1, color2, i); cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.3, color1, color2, i); } else { translucentBonds->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.3, color1, color2, i); translucentBonds->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.3, color1, color2, i); } } } } } QWidget* BallAndStick::setupWidget() { auto* interface = m_layerManager.getSetting(); interface->setupWidget(this); return interface->widget; } void BallAndStick::opacityChanged(int opacity) { m_opacity = static_cast(opacity) / 100.0f; auto* interface = m_layerManager.getSetting(); if (m_opacity != interface->opacity) { interface->opacity = m_opacity; emit drawablesChanged(); } QSettings settings; settings.setValue("ballandstick/opacity", m_opacity); } void BallAndStick::atomRadiusChanged(int value) { m_atomScale = static_cast(value) / 10.0f; auto* interface = m_layerManager.getSetting(); if (m_atomScale != interface->atomScale) { interface->atomScale = m_atomScale; emit drawablesChanged(); } QSettings settings; settings.setValue("ballandstick/atomScale", m_atomScale); } void BallAndStick::bondRadiusChanged(int value) { m_bondRadius = static_cast(value) / 10.0f; auto* interface = m_layerManager.getSetting(); if (m_bondRadius != interface->bondRadius) { interface->bondRadius = m_bondRadius; emit drawablesChanged(); } QSettings settings; settings.setValue("ballandstick/bondRadius", m_bondRadius); } void BallAndStick::multiBonds(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->multiBonds) { interface->multiBonds = show; emit drawablesChanged(); } QSettings settings; settings.setValue("ballandstick/multiBonds", show); } void BallAndStick::showHydrogens(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showHydrogens) { interface->showHydrogens = show; emit drawablesChanged(); } QSettings settings; settings.setValue("ballandstick/showHydrogens", show); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/ballandstick/ballandstick.h000066400000000000000000000031021506155467400254260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_BALLANDSTICK_H #define AVOGADRO_QTPLUGINS_BALLANDSTICK_H #include namespace Avogadro::QtPlugins { /** * @brief Render a molecule in the ball and stick style. * @author Allison Vacanti */ class BallAndStick : public QtGui::ScenePlugin { Q_OBJECT public: explicit BallAndStick(QObject* parent = nullptr); ~BallAndStick() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Ball and Stick"); } QString description() const override { return tr("Render atoms as spheres and bonds as cylinders."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::True; } public slots: void atomRadiusChanged(int value); void bondRadiusChanged(int value); void multiBonds(bool show); void showHydrogens(bool show); void opacityChanged(int value); private: Rendering::GroupNode* m_group; std::string m_name = "Ball and Stick"; float m_atomScale = 0.3f; float m_bondRadius = 0.1f; float m_opacity = 1.0f; }; } // end namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_BALLANDSTICK_H avogadrolibs-1.101.0/avogadro/qtplugins/bondcentrictool/000077500000000000000000000000001506155467400233635ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/bondcentrictool/CMakeLists.txt000066400000000000000000000005611506155467400261250ustar00rootroot00000000000000set(bondcentrictool_srcs bondcentrictool.cpp ) set(bondcentrictool_uis ) set(bondcentrictool_rcs bondcentrictool.qrc ) avogadro_plugin(BondCentric "Bond-centric" ToolPlugin bondcentrictool.h BondCentricTool "${bondcentrictool_srcs}" "${bondcentrictool_uis}" "${bondcentrictool_rcs}" ) target_link_libraries(BondCentric PRIVATE Avogadro::QtOpenGL) avogadrolibs-1.101.0/avogadro/qtplugins/bondcentrictool/bondcentric_dark.svg000066400000000000000000000013421506155467400273770ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/bondcentrictool/bondcentric_light.svg000066400000000000000000000013421506155467400275650ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/bondcentrictool/bondcentrictool.cpp000066400000000000000000001041211506155467400272560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Adapted from Avogadro 1.x with the following authors' permission: Copyright (C) 2007 by Shahzad Ali Copyright (C) 2007 by Ross Braithwaite Copyright (C) 2007 by James Bunt Copyright (C) 2007,2008 by Marcus D. Hanwell Copyright (C) 2006,2007 by Benoit Jacob This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "bondcentrictool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif namespace Avogadro::QtPlugins { using Core::Array; using QtGui::Molecule; using QtGui::RWAtom; using QtGui::RWBond; using QtGui::RWMolecule; using Rendering::ArcSector; using Rendering::ArcStrip; using Rendering::GeometryNode; using Rendering::Quad; using Rendering::QuadOutline; namespace { const std::string degreeString("°"); /// @todo Add wide character support to text renderer. const std::string angstromString("Å"); // Lookup for coloring bond angles: const Vector3ub& getColor(size_t i) { static std::vector colors; if (colors.empty()) { colors.emplace_back(255, 64, 32); colors.emplace_back(64, 255, 32); colors.emplace_back(32, 64, 255); colors.emplace_back(255, 255, 32); colors.emplace_back(255, 32, 255); colors.emplace_back(32, 255, 255); colors.emplace_back(255, 128, 0); colors.emplace_back(128, 255, 0); colors.emplace_back(0, 255, 128); colors.emplace_back(0, 128, 255); colors.emplace_back(255, 0, 128); colors.emplace_back(128, 0, 255); } return colors[i % colors.size()]; } // Returns unsigned, smallest angle between v1 and v2 inline float vectorAngleDegrees(const Vector3f& v1, const Vector3f& v2) { const float crossProductNorm(v1.cross(v2).norm()); const float dotProduct(v1.dot(v2)); return std::atan2(crossProductNorm, dotProduct) * RAD_TO_DEG_F; } // Returns signed, smallest angle between v1 and v2. Sign is determined from a // right hand rule around axis. inline float vectorAngleDegrees(const Vector3f& v1, const Vector3f& v2, const Vector3f& axis) { const Vector3f crossProduct(v1.cross(v2)); const float crossProductNorm(crossProduct.norm()); const float dotProduct(v1.dot(v2)); const float signDet(crossProduct.dot(axis)); const float angle(std::atan2(crossProductNorm, dotProduct) * RAD_TO_DEG_F); return signDet > 0.f ? angle : -angle; } } // namespace BondCentricTool::BondCentricTool(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_renderer(nullptr), m_moveState(IgnoreMove), m_planeSnapIncr(10.f), m_snapPlaneToBonds(true) { QString shortcut = tr("Ctrl+7", "control-key 7"); m_activateAction->setText(tr("Bond-Centric Manipulation")); m_activateAction->setToolTip( tr("Bond Centric Manipulation Tool\t(%1)\n\n" "Left Mouse:\tClick and drag to rotate the view.\n" "Middle Mouse:\tClick and drag to zoom in or out.\n" "Right Mouse:\tClick and drag to move the view.\n" "Double-Click:\tReset the view.\n\n" "Left Click & Drag on a Bond to set the Manipulation Plane\n" "Left Click & Drag one of the Atoms in the Bond to change the angle\n" "Right Click & Drag one of the Atoms in the Bond to change the length") .arg(shortcut)); setIcon(); } BondCentricTool::~BondCentricTool() {} void BondCentricTool::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/bondcentric_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/bondcentric_light.svg")); } QWidget* BondCentricTool::toolWidget() const { return nullptr; } void BondCentricTool::setMolecule(QtGui::Molecule* mol) { if (mol && mol->undoMolecule() != m_molecule) { m_molecule = mol->undoMolecule(); reset(); } } void BondCentricTool::setEditMolecule(QtGui::RWMolecule* mol) { if (m_molecule != mol) { m_molecule = mol; reset(); } } void BondCentricTool::setGLWidget(QtOpenGL::GLWidget*) {} void BondCentricTool::setGLRenderer(Rendering::GLRenderer* ren) { m_renderer = ren; } QUndoCommand* BondCentricTool::mousePressEvent(QMouseEvent* e) { // Don't start a new operation if we're already working: if (m_moveState != IgnoreMove) return nullptr; Rendering::Identifier ident = m_renderer->hit(e->pos().x(), e->pos().y()); // If no hits, return. Also ensure that the hit molecule is the one we expect. if (!ident.isValid() || ident.molecule != &m_molecule->molecule()) return nullptr; // If the hit is a left click on a bond, make it the selected bond and map // mouse movements to the bond plane rotation. if (ident.type == Rendering::BondType && e->button() == Qt::LeftButton) return initRotatePlane(e, ident); // Return if selectedBond is not valid or the hit is not on a bond: if (!m_selectedBond.isValid() || ident.type != Rendering::AtomType) return nullptr; // Test if the atom is in the selected bond, or one bond removed. RWAtom clickedAtom = m_molecule->atom(ident.index); RWBond selectedBond = m_selectedBond.bond(); bool atomIsInBond = bondContainsAtom(selectedBond, clickedAtom); bool atomIsNearBond = false; RWAtom anchorAtom; if (!atomIsInBond) { Array bonds = m_molecule->bonds(clickedAtom); for (auto& bond : bonds) { RWAtom atom = bond.getOtherAtom(clickedAtom); if (bondContainsAtom(selectedBond, atom)) { anchorAtom = atom; atomIsNearBond = true; break; } } } if (!atomIsInBond && !atomIsNearBond) return nullptr; if (m_molecule) { m_molecule->setInteractive(true); } // If the hit is a left click on an atom in the selected bond, prepare to // rotate the clicked bond around the other atom in the bond. if (atomIsInBond && e->button() == Qt::LeftButton) return initRotateBondedAtom(e, clickedAtom); // If the hit is a right click on an atom in the selected bond, prepare to // change the bond length. if (atomIsInBond && e->button() == Qt::RightButton) return initAdjustBondLength(e, clickedAtom); // Is the hit a left click on an atom bonded to an atom in selectedBond? if (atomIsNearBond && (e->button() == Qt::LeftButton || e->button() == Qt::RightButton)) { return initRotateNeighborAtom(e, clickedAtom, anchorAtom); } return nullptr; } QUndoCommand* BondCentricTool::mouseDoubleClickEvent(QMouseEvent* e) { if (m_selectedBond.isValid() && e->button() == Qt::LeftButton) { reset(); emit drawablesChanged(); } return nullptr; } QUndoCommand* BondCentricTool::mouseMoveEvent(QMouseEvent* e) { if (m_moveState == IgnoreMove) return nullptr; QUndoCommand* result = nullptr; switch (m_moveState) { case RotatePlane: result = rotatePlane(e); break; case RotateBondedAtom: result = rotateBondedAtom(e); break; case AdjustBondLength: result = adjustBondLength(e); break; case RotateNeighborAtom: result = rotateNeighborAtom(e); break; default: break; } return result; } QUndoCommand* BondCentricTool::mouseReleaseEvent(QMouseEvent*) { if (m_moveState != IgnoreMove) { reset(KeepBond); emit drawablesChanged(); if (m_molecule) { m_molecule->setInteractive(false); // allow an undo now } } return nullptr; } void BondCentricTool::draw(Rendering::GroupNode& node) { RWBond selectedBond = m_selectedBond.bond(); if (!selectedBond.isValid()) return; auto* geo = new GeometryNode; node.addChild(geo); switch (m_moveState) { default: case IgnoreMove: case RotatePlane: drawBondQuad(*geo, selectedBond); drawAtomBondAngles(*geo, selectedBond.atom1(), selectedBond); drawAtomBondAngles(*geo, selectedBond.atom2(), selectedBond); break; case RotateBondedAtom: { drawBondQuad(*geo, selectedBond); RWAtom otherAtom = selectedBond.getOtherAtom(m_clickedAtom.atom()); if (otherAtom.isValid()) { drawAtomBondAngles(*geo, otherAtom, selectedBond); } break; } case AdjustBondLength: drawBondQuad(*geo, selectedBond); drawBondLengthLabel(*geo, selectedBond); break; case RotateNeighborAtom: { RWAtom clickedAtom = m_clickedAtom.atom(); RWAtom anchorAtom = m_anchorAtom.atom(); RWBond otherBond = m_molecule->bond(clickedAtom, anchorAtom); if (otherBond.isValid()) drawBondAngle(*geo, selectedBond, otherBond); break; } } } void BondCentricTool::reset(BondCentricTool::ResetBondBehavior bond) { if (bond == ResetBond) m_selectedBond.reset(); m_clickedAtom.reset(); m_anchorAtom.reset(); m_moveState = IgnoreMove; m_clickedPoint = QPoint(); } void BondCentricTool::initializeBondVectors() { RWBond bond = m_selectedBond.bond(); if (bond.isValid()) { m_bondVector = (bond.atom2().position3d().cast() - bond.atom1().position3d().cast()) .normalized(); m_planeNormalMouse = m_bondVector.unitOrthogonal(); } } void BondCentricTool::updateBondVector() { RWBond bond = m_selectedBond.bond(); if (bond.isValid()) { m_bondVector = (bond.atom2().position3d().cast() - bond.atom1().position3d().cast()) .normalized(); } } QUndoCommand* BondCentricTool::initRotatePlane( QMouseEvent* e, const Rendering::Identifier& ident) { RWBond selectedBond = m_molecule->bond(ident.index); // Get unique id: Index bondUniqueId = m_molecule->bondUniqueId(selectedBond); if (bondUniqueId == MaxIndex) return nullptr; // Something went horribly wrong. // Reset the bond vector/plane normal if the bond changed if (bondUniqueId != m_selectedBond.uniqueIdentifier()) { m_selectedBond = QtGui::RWMolecule::PersistentBondType(m_molecule, bondUniqueId); initializeBondVectors(); } updatePlaneSnapAngles(); updateSnappedPlaneNormal(); if (!m_selectedBond.isValid()) return nullptr; e->accept(); m_moveState = RotatePlane; m_clickedPoint = e->pos(); m_lastDragPoint = e->pos(); emit drawablesChanged(); return nullptr; } QUndoCommand* BondCentricTool::initRotateBondedAtom( QMouseEvent* e, const QtGui::RWAtom& clickedAtom) { m_clickedAtom = RWMolecule::PersistentAtomType(clickedAtom); if (!m_clickedAtom.isValid()) return nullptr; e->accept(); m_moveState = RotateBondedAtom; m_clickedPoint = e->pos(); m_lastDragPoint = e->pos(); resetFragment(); emit drawablesChanged(); return nullptr; } QUndoCommand* BondCentricTool::initAdjustBondLength( QMouseEvent* e, const QtGui::RWAtom& clickedAtom) { m_clickedAtom = RWMolecule::PersistentAtomType(clickedAtom); if (!m_clickedAtom.isValid()) return nullptr; e->accept(); m_moveState = AdjustBondLength; m_clickedPoint = e->pos(); m_lastDragPoint = e->pos(); resetFragment(); emit drawablesChanged(); return nullptr; } QUndoCommand* BondCentricTool::initRotateNeighborAtom( QMouseEvent* e, const QtGui::RWAtom& clickedAtom, const QtGui::RWAtom& anchorAtom) { m_clickedAtom = RWMolecule::PersistentAtomType(clickedAtom); m_anchorAtom = RWMolecule::PersistentAtomType(anchorAtom); if (!m_clickedAtom.isValid() || !m_anchorAtom.isValid()) return nullptr; e->accept(); m_moveState = RotateNeighborAtom; m_clickedPoint = e->pos(); m_lastDragPoint = e->pos(); resetFragment(); emit drawablesChanged(); return nullptr; } QUndoCommand* BondCentricTool::rotatePlane(QMouseEvent* e) { // The bond should be valid. const RWBond selectedBond = m_selectedBond.bond(); if (!selectedBond.isValid()) return nullptr; const QPoint deltaDrag = e->pos() - m_lastDragPoint; const Rendering::Camera& camera(m_renderer->camera()); // Atomic position in world coordinates const Vector3 beginPos(selectedBond.atom1().position3d()); const Vector3 endPos(selectedBond.atom2().position3d()); // Various quantities in window coordinates. const Vector3f beginWin(camera.project(beginPos.cast())); const Vector3f endWin(camera.project(endPos.cast())); Vector3f bondVecWin(endWin - beginWin); bondVecWin.z() = 0.f; // Points into the viewing volume from camera: const Vector3f zAxisWin(0.f, 0.f, 1.f); // In plane of screen, orthogonal to bond: const Vector3f orthoWin(zAxisWin.cross(bondVecWin).normalized()); const Vector3f dragWin(static_cast(deltaDrag.x()), static_cast(deltaDrag.y()), 0.f); // Compute the rotation. Not quite sure what's going on here, this is just // ported from Avogadro 1. It doesn't seem right that rotation would be in // degrees (it's the result of a dot product) and I think the fact that the // DEG_TO_RAD conversion results in a useful angle is just a happy // coincidence. But it works quite well. const float rotation = dragWin.dot(orthoWin) / orthoWin.norm(); const Eigen::AngleAxisf rotator(rotation * DEG_TO_RAD_F, m_bondVector); // Rotate m_planeNormalMouse = rotator * m_planeNormalMouse; updateSnappedPlaneNormal(); emit drawablesChanged(); m_lastDragPoint = e->pos(); return nullptr; } QUndoCommand* BondCentricTool::rotateBondedAtom(QMouseEvent* e) { // Ensure that the mouse has moved a reasonable amount: if ((m_lastDragPoint - e->pos()).manhattanLength() < 2) return nullptr; RWBond bond = m_selectedBond.bond(); RWAtom clickedAtom = m_clickedAtom.atom(); RWAtom centerAtom = bond.getOtherAtom(clickedAtom); // Sanity check: if (!bond.isValid() || !clickedAtom.isValid() || !centerAtom.isValid()) return nullptr; // Compute the transformation: // - Rotation axis is m_planeNormal // - Rotation angle is: // - magnitude is angle between initial click and current pos around // center atom (performed in 2D). // - sign is based on whether m_planeNormal is pointing into/out of the // screen. const Rendering::Camera& camera(m_renderer->camera()); // Get the window coordinates of the relevant points const Vector3f centerPos(centerAtom.position3d().cast()); const Vector3f centerWin(camera.project(centerPos)); const Vector2f centerWin2(centerWin.head<2>()); const Vector2f lastDragWin( static_cast(m_lastDragPoint.x()), static_cast(camera.height() - m_lastDragPoint.y())); const Vector2f dragWin(static_cast(e->pos().x()), static_cast(camera.height() - e->pos().y())); // Compute the angle between last drag and current drag positions const Vector2f lastDragWinVec((lastDragWin - centerWin2).normalized()); const Vector2f dragWinVec((dragWin - centerWin2).normalized()); const float crossProductNorm(lastDragWinVec.x() * dragWinVec.y() - lastDragWinVec.y() * dragWinVec.x()); const float dotProduct(lastDragWinVec.dot(dragWinVec)); const float angle(std::atan2(crossProductNorm, dotProduct)); // Figure out if the sign needs to be reversed: const Vector3f centerPlusNormal(centerPos + m_planeNormal); const Vector3f centerPlusNormalWin(camera.project(centerPlusNormal)); bool reverseSign = (centerPlusNormalWin.z() - centerWin.z()) >= 0; // Build transform m_transform.setIdentity(); m_transform.translate(centerPos); m_transform.rotate( Eigen::AngleAxisf(reverseSign ? -angle : angle, m_planeNormal)); m_transform.translate(-centerPos); // Build the fragment if needed: if (m_fragment.empty()) buildFragment(bond, clickedAtom); // Perform transformation transformFragment(); updateBondVector(); m_molecule->emitChanged(Molecule::Modified | Molecule::Atoms); emit drawablesChanged(); m_lastDragPoint = e->pos(); return nullptr; } QUndoCommand* BondCentricTool::adjustBondLength(QMouseEvent* e) { // Ensure that the mouse has moved a reasonable amount: if ((m_lastDragPoint - e->pos()).manhattanLength() < 2) return nullptr; RWBond selectedBond = m_selectedBond.bond(); RWAtom clickedAtom = m_clickedAtom.atom(); // Sanity check: if (!selectedBond.isValid() || !clickedAtom.isValid()) return nullptr; const Rendering::Camera& camera(m_renderer->camera()); RWAtom otherAtom = selectedBond.getOtherAtom(clickedAtom); const Vector2f curPosWin(static_cast(e->pos().x()), static_cast(e->pos().y())); const Vector2f lastPosWin(static_cast(m_lastDragPoint.x()), static_cast(m_lastDragPoint.y())); const Vector3f bond(clickedAtom.position3d().cast() - otherAtom.position3d().cast()); const Vector3f mouse(camera.unProject(curPosWin) - camera.unProject(lastPosWin)); const Vector3f displacement((mouse.dot(bond) / bond.squaredNorm()) * bond); // Build transform m_transform.setIdentity(); m_transform.translate(displacement); // Build the fragment if needed: if (m_fragment.empty()) buildFragment(selectedBond, clickedAtom); // Perform transformation transformFragment(); m_molecule->emitChanged(QtGui::Molecule::Modified | QtGui::Molecule::Atoms); emit drawablesChanged(); m_lastDragPoint = e->pos(); return nullptr; } QUndoCommand* BondCentricTool::rotateNeighborAtom(QMouseEvent* e) { // Ensure that the mouse has moved a reasonable amount: if ((m_lastDragPoint - e->pos()).manhattanLength() < 2) return nullptr; RWBond selectedBond = m_selectedBond.bond(); // Atom that was clicked RWAtom clickedAtom = m_clickedAtom.atom(); // Atom in selected bond also attached to clickedAtom RWAtom anchorAtom = m_anchorAtom.atom(); // The "other" atom in selected bond RWAtom otherAtom = selectedBond.getOtherAtom(anchorAtom); // Sanity check: if (!selectedBond.isValid() || !anchorAtom.isValid() || !otherAtom.isValid() || !clickedAtom.isValid()) { return nullptr; } const Rendering::Camera& camera(m_renderer->camera()); // Compute the angle between last drag and current drag positions const Vector3f center(anchorAtom.position3d().cast()); const Vector3f centerProj(camera.project(center)); const Vector2f centerWin(centerProj.head<2>()); const Vector2f curWin(static_cast(e->pos().x()), static_cast(camera.height() - e->pos().y())); const Vector2f lastWin( static_cast(m_lastDragPoint.x()), static_cast(camera.height() - m_lastDragPoint.y())); const Vector2f curVecWin((curWin - centerWin).normalized()); const Vector2f lastVecWin((lastWin - centerWin).normalized()); const float crossProductNorm(lastVecWin.x() * curVecWin.y() - lastVecWin.y() * curVecWin.x()); const float dotProduct(lastVecWin.dot(curVecWin)); const float angle(std::atan2(crossProductNorm, dotProduct)); // Figure out if the sign needs to be reversed: const Vector3f other(otherAtom.position3d().cast()); const Vector3f otherProj(camera.project(other)); const bool reverseSign = otherProj.z() <= centerProj.z(); // Axis of rotation const Vector3f axis((center - other).normalized()); // Build transform m_transform.setIdentity(); m_transform.translate(center); m_transform.rotate(Eigen::AngleAxisf(reverseSign ? -angle : angle, axis)); m_transform.translate(-center); // Build the fragment if needed: if (m_fragment.empty()) buildFragment(selectedBond, anchorAtom); // Perform transformation transformFragment(); updateBondVector(); m_molecule->emitChanged(QtGui::Molecule::Modified | QtGui::Molecule::Atoms); emit drawablesChanged(); m_lastDragPoint = e->pos(); return nullptr; } void BondCentricTool::drawBondQuad(Rendering::GeometryNode& node, const RWBond& bond) const { const Vector3f atom1Pos(bond.atom1().position3d().cast()); const Vector3f atom2Pos(bond.atom2().position3d().cast()); Vector3f offset(m_bondVector.cross(m_planeNormal)); const Vector3f v1(atom1Pos + offset); const Vector3f v2(atom2Pos + offset); const Vector3f v3(atom1Pos - offset); const Vector3f v4(atom2Pos - offset); Quad* quad = new Quad; node.addDrawable(quad); quad->setColor(Vector3ub(63, 127, 255)); quad->setOpacity(127); quad->setRenderPass(Rendering::TranslucentPass); quad->setQuad(v1, v2, v3, v4); auto* quadOutline = new QuadOutline; node.addDrawable(quadOutline); quadOutline->setColor(Vector3ub(63, 127, 255)); quadOutline->setRenderPass(Rendering::OpaquePass); quadOutline->setQuad(v1, v2, v3, v4, 1.f); // If the plane is rotating, show a hint for the unsnapped plane. if (m_moveState == RotatePlane) { Vector3f moffset(m_bondVector.cross(m_planeNormalMouse)); const Vector3f mv1(atom1Pos + moffset); const Vector3f mv2(atom2Pos + moffset); const Vector3f mv3(atom1Pos - moffset); const Vector3f mv4(atom2Pos - moffset); auto* mouseQuadOutline = new QuadOutline; node.addDrawable(mouseQuadOutline); mouseQuadOutline->setColor(Vector3ub(255, 255, 255)); mouseQuadOutline->setOpacity(127); mouseQuadOutline->setRenderPass(Rendering::TranslucentPass); mouseQuadOutline->setQuad(mv1, mv2, mv3, mv4, 1.f); } } void BondCentricTool::drawBondAngle(Rendering::GeometryNode& node, const QtGui::RWBond& selectedBond, const QtGui::RWBond& movingBond) const { // Draw the selected bond quad as usual drawBondQuad(node, selectedBond); // Determine the atom shared between the bonds (atom1). RWAtom atom1; RWAtom atom2; if (selectedBond.atom1() == movingBond.atom1() || selectedBond.atom2() == movingBond.atom1()) { atom1 = movingBond.atom1(); atom2 = movingBond.atom2(); } else if (selectedBond.atom1() == movingBond.atom2() || selectedBond.atom2() == movingBond.atom2()) { atom1 = movingBond.atom2(); atom2 = movingBond.atom1(); } if (!atom1.isValid()) return; // Add another quad in the plane normal to // m_bondVector.cross(movingBondVector) const Vector3f a1(atom1.position3d().cast()); const Vector3f a2(atom2.position3d().cast()); const Vector3f movingBondVector(a2 - a1); const Vector3f movingBondUnitVector(movingBondVector.normalized()); // calculate a vector in the plane spanned by movingBondVector and // m_bondVector that is orthogonal to m_bondVector, then project // movingBondVector onto it. This is used to calculate the 'new' a2. const Vector3f movingBondNormal(m_bondVector.cross(movingBondUnitVector)); const Vector3f newA2Direction(movingBondNormal.cross(m_bondVector)); const Vector3f movingBondVectorProj(movingBondVector.dot(newA2Direction) * newA2Direction); const Vector3f newA2(a1 + movingBondVectorProj); const Vector3f& movingBondOffset(m_bondVector); const Vector3f v1(a1 + movingBondOffset); const Vector3f v2(newA2 + movingBondOffset); const Vector3f v3(a1 - movingBondOffset); const Vector3f v4(newA2 - movingBondOffset); Quad* quad = new Quad; node.addDrawable(quad); quad->setColor(Vector3ub(63, 127, 255)); quad->setOpacity(127); quad->setRenderPass(Rendering::TranslucentPass); quad->setQuad(v1, v2, v3, v4); auto* quadOutline = new QuadOutline; node.addDrawable(quadOutline); quadOutline->setColor(Vector3ub(63, 127, 255)); quadOutline->setRenderPass(Rendering::OpaquePass); quadOutline->setQuad(v1, v2, v3, v4, 1.f); // Add an arc and label to show a bit more info: const Vector3f selectedBondOffset(m_planeNormal.cross(m_bondVector)); const float radius(movingBondVector.norm() * 0.75f); Vector3f startEdge(newA2Direction * radius); Vector3f normal(m_bondVector); float angle = vectorAngleDegrees(startEdge, selectedBondOffset, normal); float displayAngle = std::fabs(angle); auto* sect = new ArcSector; node.addDrawable(sect); sect->setColor(Vector3ub(255, 127, 63)); sect->setOpacity(127); sect->setRenderPass(Rendering::TranslucentPass); sect->setArcSector(a1, startEdge, normal, angle, 5.f); auto* arc = new ArcStrip; node.addDrawable(arc); arc->setColor(Vector3ub(255, 127, 63)); arc->setRenderPass(Rendering::OpaquePass); arc->setArc(a1, startEdge, normal, angle, 5.f, 1.f); const Vector3f& textPos(a1); auto* label = new Rendering::TextLabel3D; label->setText(tr("%L1°").arg(displayAngle, 5, 'f', 1).toStdString()); label->setRenderPass(Rendering::Overlay3DPass); label->setAnchor(textPos); node.addDrawable(label); Rendering::TextProperties tprop; tprop.setAlign(Rendering::TextProperties::HCenter, Rendering::TextProperties::VCenter); tprop.setFontFamily(Rendering::TextProperties::SansSerif); tprop.setColorRgb(255, 200, 64); label->setTextProperties(tprop); } void BondCentricTool::drawBondLengthLabel(Rendering::GeometryNode& node, const QtGui::RWBond& bond) { const Vector3f startPos(bond.atom1().position3d().cast()); const Vector3f endPos(bond.atom2().position3d().cast()); const Vector3f bondCenter((startPos + endPos) * 0.5f); const Vector3f bondVector(endPos - startPos); auto* label = new Rendering::TextLabel3D; label->setText(tr("%L1 Å").arg(bondVector.norm(), 4, 'f', 2).toStdString()); label->setRenderPass(Rendering::Overlay3DPass); label->setAnchor(bondCenter); node.addDrawable(label); Rendering::TextProperties tprop; tprop.setAlign(Rendering::TextProperties::HCenter, Rendering::TextProperties::VCenter); tprop.setFontFamily(Rendering::TextProperties::SansSerif); tprop.setColorRgb(255, 200, 64); label->setTextProperties(tprop); } void BondCentricTool::drawAtomBondAngles(Rendering::GeometryNode& node, const RWAtom& atom, const RWBond& anchorBond) { const Array bonds = m_molecule->bonds(atom); auto bondIter(bonds.begin()); auto bondEnd(bonds.end()); size_t count = 0; while (bondIter != bondEnd) { if (*bondIter != anchorBond) drawAtomBondAngle(node, atom, anchorBond, *bondIter, getColor(count++)); ++bondIter; } } void BondCentricTool::drawAtomBondAngle(Rendering::GeometryNode& node, const QtGui::RWAtom& atom, const QtGui::RWBond& anchorBond, const QtGui::RWBond& otherBond, const Vector3ub& color) { const RWAtom otherAtom = otherBond.getOtherAtom(atom); const RWAtom otherAnchorAtom = anchorBond.getOtherAtom(atom); const Vector3f atomPos(atom.position3d().cast()); const Vector3f otherAtomPos(otherAtom.position3d().cast()); const Vector3f otherAnchorAtomPos(otherAnchorAtom.position3d().cast()); const Vector3f otherVector(otherAtomPos - atomPos); const Vector3f anchorVector(otherAnchorAtomPos - atomPos); const Vector3f anchorUnitVector(anchorVector.normalized()); const float radius(otherVector.norm() * 0.75f); const Vector3f& origin(atomPos); const Vector3f start(anchorUnitVector * radius); const Vector3f axis(anchorVector.cross(otherVector).normalized()); const float angle = vectorAngleDegrees(otherVector, anchorVector); const Vector3f& labelPos(otherAtomPos); auto* sect = new ArcSector; node.addDrawable(sect); sect->setColor(color); sect->setOpacity(127); sect->setRenderPass(Rendering::TranslucentPass); sect->setArcSector(origin, start, axis, angle, 5.f); auto* arc = new ArcStrip; node.addDrawable(arc); arc->setColor(color); arc->setRenderPass(Rendering::OpaquePass); arc->setArc(origin, start, axis, angle, 5.f, 1.f); auto* label = new Rendering::TextLabel3D; label->setText(tr("%L1°").arg(angle, 6, 'f', 1).toStdString()); label->setRenderPass(Rendering::Overlay3DPass); label->setAnchor(labelPos); node.addDrawable(label); Rendering::TextProperties tprop; tprop.setAlign(Rendering::TextProperties::HCenter, Rendering::TextProperties::VCenter); tprop.setFontFamily(Rendering::TextProperties::SansSerif); tprop.setColorRgb(color); label->setTextProperties(tprop); } inline bool BondCentricTool::bondContainsAtom(const QtGui::RWBond& bond, const QtGui::RWAtom& atom) const { return atom == bond.atom1() || atom == bond.atom2(); } inline void BondCentricTool::transformFragment() const { // Convert the internal float matrix to use the same precision as the atomic // coordinates. Eigen::Transform transform(m_transform.cast()); for (int it : m_fragment) { RWAtom atom = m_molecule->atomByUniqueId(it); if (atom.isValid()) { Vector3 pos = atom.position3d(); pos = transform * pos; atom.setPosition3d(pos); } } } void BondCentricTool::updatePlaneSnapAngles() { m_planeSnapRef = m_bondVector.unitOrthogonal(); m_planeSnapAngles.clear(); // Add bond angles if requested: RWBond selectedBond = m_selectedBond.bond(); if (m_snapPlaneToBonds && selectedBond.isValid()) { const RWAtom atom1 = selectedBond.atom1(); const RWAtom atom2 = selectedBond.atom2(); for (int i = 0; i < 2; ++i) { const RWAtom& atom = i == 0 ? atom1 : atom2; const Vector3f atomPos(atom.position3d().cast()); const Array bonds = m_molecule->bonds(atom); for (auto bond : bonds) { if (bond != selectedBond) { const RWAtom otherAtom(bond.getOtherAtom(atom)); const Vector3f otherAtomPos(otherAtom.position3d().cast()); const Vector3f otherBondVector(otherAtomPos - atomPos); // Project otherBondVector into the plane normal to m_bondVector // (e.g. the rejection of otherBondVector onto m_bondVector) const Vector3f rej( otherBondVector - (otherBondVector.dot(m_bondVector) * m_bondVector)); float angle(vectorAngleDegrees(m_planeSnapRef, rej, m_bondVector)); m_planeSnapAngles.insert(angle); angle += 180.f; if (angle > 180.f) angle -= 360.f; m_planeSnapAngles.insert(angle); } } } } // Add default increments only if they are more than 5 degrees away // from a bond angle. const float minDist(5.f); for (float angle = -180.f; angle < 180.f; angle += m_planeSnapIncr) { auto upper(m_planeSnapAngles.lower_bound(angle)); if (upper != m_planeSnapAngles.end()) { if (*upper - minDist < angle) continue; if (upper != m_planeSnapAngles.begin()) { auto lower(upper); std::advance(lower, -1); if (*lower + minDist > angle) continue; } m_planeSnapAngles.insert(angle); } } } // There may be some weirdness around +/-180 since we don't check for // wrapping, but it should be fine for this use case. void BondCentricTool::updateSnappedPlaneNormal() { const Vector3f mousePlaneVector(m_planeNormalMouse.cross(m_bondVector)); const float angle( vectorAngleDegrees(m_planeSnapRef, mousePlaneVector, m_bondVector)); float snappedAngle(angle); auto upper(m_planeSnapAngles.lower_bound(angle)); if (upper != m_planeSnapAngles.end()) { if (upper != m_planeSnapAngles.begin()) { auto lower(upper); std::advance(lower, -1); float upperDist = std::fabs(angle - *upper); float lowerDist = std::fabs(angle - *lower); snappedAngle = upperDist < lowerDist ? *upper : *lower; } else { snappedAngle = *upper; } } if (angle == snappedAngle) { // If the angle didn't change, keep on keepin' on: m_planeNormal = m_planeNormalMouse; } else { // Otherwise, update the vector. const Vector3f planeVector = Eigen::AngleAxisf(snappedAngle * DEG_TO_RAD_F, m_bondVector) * m_planeSnapRef; m_planeNormal = planeVector.cross(m_bondVector); } } inline bool BondCentricTool::fragmentHasAtom(int uid) const { return std::find(m_fragment.begin(), m_fragment.end(), uid) != m_fragment.end(); } void BondCentricTool::buildFragment(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom) { m_fragment.clear(); if (!buildFragmentRecurse(bond, startAtom, startAtom)) { // If this returns false, then a cycle has been found. Only move startAtom // in this case. m_fragment.clear(); } m_fragment.push_back(m_molecule->atomUniqueId(startAtom)); } bool BondCentricTool::buildFragmentRecurse(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom, const QtGui::RWAtom& currentAtom) { // does our cycle include both bonded atoms? const RWAtom bondedAtom(bond.getOtherAtom(startAtom)); Array bonds = m_molecule->bonds(currentAtom); for (auto& it : bonds) { if (it != bond) { // Skip the current bond const RWAtom nextAtom = it.getOtherAtom(currentAtom); if (nextAtom != startAtom && nextAtom != bondedAtom) { // Skip atoms that have already been added. This prevents infinite // recursion on cycles in the fragments int uid = m_molecule->atomUniqueId(nextAtom); if (!fragmentHasAtom(uid)) { m_fragment.push_back(uid); if (!buildFragmentRecurse(it, startAtom, nextAtom)) return false; } } else if (nextAtom == bondedAtom) { // If we've found the bonded atom, the bond is in a cycle return false; } } // *it != bond } // foreach bond return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/bondcentrictool/bondcentrictool.h000066400000000000000000000141601506155467400267260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_BONDCENTRICTOOL_H #define AVOGADRO_QTPLUGINS_BONDCENTRICTOOL_H #include #include #include #include #include #include #include #include #include namespace Avogadro { namespace Rendering { class GeometryNode; } namespace QtPlugins { /** * @brief BondCentricTool manipulates molecular geometry by adjusting bond * angles/lengths. * * @note This class is adapted from the class of the same name in Avogadro 1.x, * written by Shahzad Ali, Ross Braithwaite, James Bunt, Marcus D. Hanwell, and * Benoit Jacob. */ class BondCentricTool : public QtGui::ToolPlugin { Q_OBJECT public: explicit BondCentricTool(QObject* parent_ = nullptr); ~BondCentricTool() override; QString name() const override; QString description() const override; unsigned char priority() const override { return 40; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule*) override; void setEditMolecule(QtGui::RWMolecule*) override; void setGLWidget(QtOpenGL::GLWidget* widget) override; void setGLRenderer(Rendering::GLRenderer* ren) override; QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseDoubleClickEvent(QMouseEvent* e) override; QUndoCommand* mouseMoveEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; void draw(Rendering::GroupNode& node) override; private: enum MoveState { IgnoreMove = 0, RotatePlane, RotateBondedAtom, AdjustBondLength, RotateNeighborAtom }; enum ResetBondBehavior { KeepBond = 0, ResetBond }; void reset(ResetBondBehavior bond = ResetBond); void initializeBondVectors(); void updateBondVector(); // Mouse press event handlers: QUndoCommand* initRotatePlane(QMouseEvent* e, const Rendering::Identifier& ident); QUndoCommand* initRotateBondedAtom(QMouseEvent* e, const QtGui::RWAtom& clickedAtom); QUndoCommand* initAdjustBondLength(QMouseEvent* e, const QtGui::RWAtom& clickedAtom); QUndoCommand* initRotateNeighborAtom(QMouseEvent* e, const QtGui::RWAtom& clickedAtom, const QtGui::RWAtom& anchorAtom); // Mouse move event handlers: QUndoCommand* rotatePlane(QMouseEvent* e); QUndoCommand* rotateBondedAtom(QMouseEvent* e); QUndoCommand* adjustBondLength(QMouseEvent* e); QUndoCommand* rotateNeighborAtom(QMouseEvent* e); // Drawing helpers: void drawBondQuad(Rendering::GeometryNode& node, const QtGui::RWBond& bond) const; void drawBondAngle(Rendering::GeometryNode& node, const QtGui::RWBond& selectedBond, const QtGui::RWBond& movingBond) const; void drawBondLengthLabel(Rendering::GeometryNode& node, const QtGui::RWBond& bond); void drawAtomBondAngles(Rendering::GeometryNode& node, const QtGui::RWAtom& atom, const QtGui::RWBond& anchorBond); void drawAtomBondAngle(Rendering::GeometryNode& node, const QtGui::RWAtom& atom, const QtGui::RWBond& anchorBond, const QtGui::RWBond& otherBond, const Vector3ub& color); // Bond utilities bool bondContainsAtom(const QtGui::RWBond& bond, const QtGui::RWAtom& atom) const; // The 'fragment' is the SkeletonTree of the 1.x implementation. It is a list // of atoms created by buildFragment(bond, startAtom), which walks the bonds // connected to startAtom (not including the passed-in bond), adding each // atom it encounters to the list, and then walking that atom's bonds. If a // cycle is detected, only startAtom is added to m_fragment. void resetFragment() { m_fragment.clear(); } bool fragmentHasAtom(int uid) const; void buildFragment(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom); bool buildFragmentRecurse(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom, const QtGui::RWAtom& currentAtom); // Use transformFragment to transform the position of each atom in the // fragment by m_transform. void transformFragment() const; QAction* m_activateAction; QtGui::RWMolecule* m_molecule; Rendering::GLRenderer* m_renderer; MoveState m_moveState; QPoint m_clickedPoint; QPoint m_lastDragPoint; Vector3f m_bondVector; Vector3f m_planeNormalMouse; Vector3f m_planeNormal; // unique ids of atoms that will need to be moved: std::vector m_fragment; Eigen::Affine3f m_transform; // Snap angles for RotatePlane. Angles are relative to m_planeSnapRef and // follow a right-hand rule around m_bondVector. Range is [-180, 180). std::set m_planeSnapAngles; float m_planeSnapIncr; Vector3f m_planeSnapRef; bool m_snapPlaneToBonds; void updatePlaneSnapAngles(); void updateSnappedPlaneNormal(); QtGui::RWMolecule::PersistentBondType m_selectedBond; QtGui::RWMolecule::PersistentAtomType m_anchorAtom; QtGui::RWMolecule::PersistentAtomType m_clickedAtom; }; inline QString BondCentricTool::name() const { return tr("Bond centric manipulation tool."); } inline QString BondCentricTool::description() const { return tr("Tool used to edit molecular geometry by changing bond lengths and " "angles."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_BONDCENTRICTOOL_H avogadrolibs-1.101.0/avogadro/qtplugins/bondcentrictool/bondcentrictool.qrc000066400000000000000000000002231506155467400272570ustar00rootroot00000000000000 bondcentric_light.svg bondcentric_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/bonding/000077500000000000000000000000001506155467400216135ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/bonding/CMakeLists.txt000066400000000000000000000002071506155467400243520ustar00rootroot00000000000000avogadro_plugin(Bonding "Perform bonding operations." ExtensionPlugin bonding.h Bonding "bonding.cpp" "bondingdialog.ui" ) avogadrolibs-1.101.0/avogadro/qtplugins/bonding/bonding.cpp000066400000000000000000000160021506155467400237360ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "bonding.h" #include #include #include #include #include #include #include "ui_bondingdialog.h" namespace Avogadro::QtPlugins { using Core::Array; using Core::Elements; using NeighborListType = Avogadro::Core::Array; Bonding::Bonding(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_action(new QAction(tr("Bond Atoms"), this)), m_orderAction(new QAction(tr("Perceive Bond Orders"), this)), m_clearAction(new QAction(tr("Remove Bonds"), this)), m_configAction(new QAction(tr("Configure Bonding…"), this)), m_createBondsAction(new QAction(tr("Bond Selected Atoms"), this)), m_dialog(nullptr), m_ui(nullptr) { QSettings settings; m_tolerance = settings.value("bonding/tolerance", 0.45).toDouble(); m_minDistance = settings.value("bonding/minDistance", 0.32).toDouble(); m_action->setShortcut(QKeySequence("Ctrl+B")); m_action->setProperty("menu priority", 750); m_createBondsAction->setProperty("menu priority", 740); m_orderAction->setProperty("menu priority", 735); m_clearAction->setShortcut(QKeySequence("Ctrl+Shift+B")); m_clearAction->setProperty("menu priority", 720); connect(m_action, SIGNAL(triggered()), SLOT(bond())); connect(m_createBondsAction, SIGNAL(triggered()), SLOT(createBond())); connect(m_orderAction, SIGNAL(triggered()), SLOT(bondOrders())); connect(m_clearAction, SIGNAL(triggered()), SLOT(clearBonds())); connect(m_configAction, SIGNAL(triggered()), SLOT(configure())); } QList Bonding::actions() const { QList result; return result << m_action << m_createBondsAction << m_orderAction << m_clearAction << m_configAction; } QStringList Bonding::menuPath(QAction*) const { return QStringList() << tr("&Build") << tr("Bond"); } void Bonding::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void Bonding::registerCommands() { emit registerCommand("removeBonds", tr("Remove bonds from all or selected atoms.")); emit registerCommand("createBonds", tr("Create bonds between all or selected atoms.")); emit registerCommand("addBondOrders", tr("Perceive bond orders.")); } bool Bonding::handleCommand(const QString& command, [[maybe_unused]] const QVariantMap& options) { if (m_molecule == nullptr) return false; // No molecule to handle the command. if (command == "removeBonds") { clearBonds(); return true; } else if (command == "createBonds") { bond(); return true; } else if (command == "addBondOrders") { bondOrders(); return true; } return false; } void Bonding::configure() { if (!m_ui) { m_dialog = new QDialog(qobject_cast(parent())); m_ui = new Ui::BondingDialog; m_ui->setupUi(m_dialog); m_ui->toleranceSpinBox->setValue(m_tolerance); m_ui->minimumSpinBox->setValue(m_minDistance); connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(setValues())); connect(m_ui->buttonBox, SIGNAL(rejected()), m_dialog, SLOT(close())); } m_dialog->show(); m_dialog->activateWindow(); } void Bonding::setValues() { if (m_dialog == nullptr || m_ui == nullptr) return; m_dialog->close(); m_tolerance = m_ui->toleranceSpinBox->value(); m_minDistance = m_ui->minimumSpinBox->value(); QSettings settings; settings.setValue("bonding/tolerance", m_tolerance); settings.setValue("bonding/minDistance", m_minDistance); } void Bonding::createBond() { // Create bond between selected atoms no matter the distance if (!m_molecule) return; if (m_molecule->isSelectionEmpty()) return; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!m_molecule->atomSelected(i)) continue; for (Index j = i + 1; j < m_molecule->atomCount(); ++j) { if (!m_molecule->atomSelected(j)) continue; m_molecule->addBond(i, j, 1); } } m_molecule->emitChanged(QtGui::Molecule::Bonds); } void Bonding::bond() { // Yes, this is largely reproduced from Core::Molecule::perceiveBondsSimple // .. but that class doesn't know about selections if (!m_molecule) return; // Check for 3D coordinates, can't do bond perception without this. if (m_molecule->atomPositions3d().size() != m_molecule->atomCount()) return; // cache atomic radii std::vector radii(m_molecule->atomCount()); for (size_t i = 0; i < radii.size(); i++) { radii[i] = Elements::radiusCovalent(m_molecule->atomicNumbers()[i]); if (radii[i] <= 0.0) radii[i] = 0.0; } bool emptySelection = m_molecule->isSelectionEmpty(); double minSq = m_minDistance * m_minDistance; // Main bond perception loop based on a simple distance metric. for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!emptySelection && !m_molecule->atomSelected(i)) continue; Vector3 ipos = m_molecule->atomPositions3d()[i]; for (Index j = i + 1; j < m_molecule->atomCount(); ++j) { if (!emptySelection && !m_molecule->atomSelected(j)) continue; double cutoff = radii[i] + radii[j] + m_tolerance; Vector3 jpos = m_molecule->atomPositions3d()[j]; Vector3 diff = jpos - ipos; if (std::fabs(diff[0]) > cutoff || std::fabs(diff[1]) > cutoff || std::fabs(diff[2]) > cutoff || (m_molecule->atomicNumbers()[i] == 1 && m_molecule->atomicNumbers()[j] == 1)) { continue; } // check radius and add bond if needed double cutoffSq = cutoff * cutoff; double diffsq = diff.squaredNorm(); if (diffsq < cutoffSq && diffsq > minSq) m_molecule->addBond(m_molecule->atom(i), m_molecule->atom(j), 1); } } m_molecule->emitChanged(QtGui::Molecule::Bonds); } void Bonding::bondOrders() { m_molecule->perceiveBondOrders(); m_molecule->emitChanged(QtGui::Molecule::Bonds); } void Bonding::clearBonds() { // remove any bonds connected to the selected atoms // Array bonds(Index a); if (m_molecule->isSelectionEmpty()) m_molecule->clearBonds(); else { std::vector bondIndices; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!m_molecule->atomSelected(i)) continue; // OK, the atom is selected, get the bonds to delete const NeighborListType bonds = m_molecule->bonds(i); for (auto bond : bonds) { bondIndices.push_back(bond.index()); } } // end looping through atoms // now delete the bonds for (auto it = bondIndices.rbegin(), itEnd = bondIndices.rend(); it != itEnd; ++it) { m_molecule->removeBond(*it); } } // end else(selected atoms) m_molecule->emitChanged(QtGui::Molecule::Bonds); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/bonding/bonding.h000066400000000000000000000032741506155467400234120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_BONDING_H #define AVOGADRO_QTPLUGINS_BONDING_H #include #include #include namespace Ui { class BondingDialog; } namespace Avogadro::QtPlugins { /** * @brief The Bonding class performs bonding operations on demand. */ class Bonding : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Bonding(QObject* parent_ = nullptr); ~Bonding() override = default; QString name() const override { return tr("Bonding"); } QString description() const override { return tr("Perform bonding operations."); } QList actions() const override; QStringList menuPath(QAction* action) const override; bool handleCommand(const QString& command, const QVariantMap& options) override; void registerCommands() override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void bond(); void createBond(); void bondOrders(); void clearBonds(); void configure(); void setValues(); private: QtGui::Molecule* m_molecule; double m_tolerance; double m_minDistance; QAction* m_action; QAction* m_orderAction; QAction* m_clearAction; QAction* m_configAction; QAction* m_createBondsAction; QDialog* m_dialog; Ui::BondingDialog* m_ui; }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_BONDING_H avogadrolibs-1.101.0/avogadro/qtplugins/bonding/bondingdialog.ui000066400000000000000000000040701506155467400247530ustar00rootroot00000000000000 BondingDialog 0 0 305 122 Form Distance Tolerance: Minimum Distance: QDialogButtonBox::Cancel|QDialogButtonBox::Ok Å 3 2.000000000000000 0.050000000000000 0.450000000000000 Å 3 1.000000000000000 0.100000000000000 0.100000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/cartoons/000077500000000000000000000000001506155467400220235ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/cartoons/CMakeLists.txt000066400000000000000000000002721506155467400245640ustar00rootroot00000000000000avogadro_plugin(Cartoons "Cartoon family rendering scheme" ScenePlugin cartoons.h Cartoons cartoons.cpp "") target_link_libraries(Cartoons LINK_PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/cartoons/cartoons.cpp000066400000000000000000000371601506155467400243660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cartoons.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Atom; using Core::AtomicNumber; using Core::Residue; using QtGui::Molecule; using QtGui::PluginLayerManager; using Rendering::BezierGeometry; using Rendering::BSplineGeometry; using Rendering::Cartoon; using Rendering::CylinderGeometry; using Rendering::GeometryNode; using Rendering::SphereGeometry; using std::list; using std::map; using std::reference_wrapper; using std::vector; struct LayerCartoon : Core::LayerData { QWidget* widget; bool showBackbone; bool showTrace; bool showTube; bool showRibbon; bool showSimpleCartoon; bool showCartoon; bool showRope; using JumpTable = void (Cartoons::*)(bool); JumpTable jumpTable[7]; std::string serialize() final { return boolToString(showBackbone) + " " + boolToString(showTrace) + " " + boolToString(showTube) + " " + boolToString(showRibbon) + " " + boolToString(showSimpleCartoon) + " " + boolToString(showCartoon) + " " + boolToString(showRope); } void deserialize(std::string text) final { std::stringstream ss(text); std::string aux; ss >> aux; showBackbone = stringToBool(aux); ss >> aux; showTrace = stringToBool(aux); ss >> aux; showTube = stringToBool(aux); ss >> aux; showRibbon = stringToBool(aux); ss >> aux; showSimpleCartoon = stringToBool(aux); ss >> aux; showCartoon = stringToBool(aux); ss >> aux; showRope = stringToBool(aux); } void setupWidget(Cartoons* slot) { if (!widget) { widget = new QWidget(qobject_cast(slot->parent())); auto* v = new QVBoxLayout; QStringList boxesText; boxesText << QObject::tr("Backbone", "protein rendering style") << QObject::tr("Trace", "protein rendering style") << QObject::tr("Tube", "protein rendering style") << QObject::tr("Ribbon", "protein rendering style") << QObject::tr("Simple Cartoon", "protein rendering style") << QObject::tr("Cartoon", "protein rendering style") << QObject::tr("Rope", "protein rendering style"); vector> boxesBools = { showBackbone, showTrace, showTube, showRibbon, showSimpleCartoon, showCartoon, showRope }; jumpTable[0] = &Cartoons::showBackbone; jumpTable[1] = &Cartoons::showTrace; jumpTable[2] = &Cartoons::showTube; jumpTable[3] = &Cartoons::showRibbon; jumpTable[4] = &Cartoons::showSimpleCartoon; jumpTable[5] = &Cartoons::showCartoon; jumpTable[6] = &Cartoons::showRope; for (int i = 0; i < boxesText.size(); ++i) { auto* check = new QCheckBox(boxesText[i]); check->setChecked(boxesBools[i]); QObject::connect(check, &QCheckBox::clicked, slot, jumpTable[i]); v->addWidget(check); } // make sure there's empty space at the bottom,s otherwise the v->addStretch(1); widget->setLayout(v); } } LayerCartoon() : widget(nullptr) { QSettings settings; showBackbone = settings.value("cartoon/backbone", false).toBool(); showCartoon = settings.value("cartoon/cartoon", true).toBool(); showTrace = settings.value("cartoon/trace", false).toBool(); showTube = settings.value("cartoon/tube", false).toBool(); showRibbon = settings.value("cartoon/ribbon", false).toBool(); showRope = settings.value("cartoon/rope", false).toBool(); showSimpleCartoon = settings.value("cartoon/simplecartoon", false).toBool(); } LayerCartoon(std::string settings) { widget = nullptr; deserialize(settings); } ~LayerCartoon() override { if (widget) widget->deleteLater(); } LayerData* clone() final { return new LayerCartoon(serialize()); } }; struct BackboneResidue { BackboneResidue() = default; BackboneResidue(const Vector3f p, const Vector3ub& c1, const Vector3ub& c2, const size_t& g, size_t id, bool sel, Residue::SecondaryStructure sec) : pos(p), color1(c1), color2(c2), group(g), residueID(id), selected(sel), secondaryStructure(sec) { } Vector3f pos; Vector3ub color1; Vector3ub color2; size_t group; size_t residueID; bool selected; Residue::SecondaryStructure secondaryStructure; }; using AtomsPairList = list; Cartoons::Cartoons(QObject* parent) : ScenePlugin(parent), m_group(nullptr) { m_layerManager = PluginLayerManager(m_name); } void addBackBone(map& result, map& previousCA, const Atom& caAtom, const Vector3ub& color, Index group, Residue::SecondaryStructure sec) { Vector3ub color1; if (result.find(group) == result.end()) { result[group] = AtomsPairList(); color1 = Vector3ub(0, 0, 0); } else { color1 = previousCA[group].color2; } Vector3f ca = caAtom.position3d().cast(); BackboneResidue backBone = BackboneResidue( ca, color1, color, group, caAtom.index(), caAtom.selected(), sec); // the 1º insertion will always be incompleated, so fix it if (result[group].size() == 1) { result[group].front().color1 = color1; } previousCA[group] = backBone; result[group].push_back(backBone); } map Cartoons::getBackboneByResidues( const Molecule& molecule, size_t layer) { const auto& graph = molecule.graph(); map result; map previousAtom; for (const auto& residue : molecule.residues()) { if (!residue.isHeterogen()) { Atom caAtom = residue.getAtomByName("CA"); Atom oAtom = residue.getAtomByName("O"); if (caAtom.isValid() && oAtom.isValid() && m_layerManager.atomEnabled(layer, caAtom.index()) && m_layerManager.atomEnabled(layer, oAtom.index())) { // get the group ID and check if it's initialized in the map size_t group = graph.getConnectedID(caAtom.index()); addBackBone(result, previousAtom, caAtom, residue.color(), group, residue.secondaryStructure()); } else { // maybe DNA Atom c3Atom = residue.getAtomByName("C3'"); Atom o3Atom = residue.getAtomByName("O3'"); if (c3Atom.isValid() && o3Atom.isValid() && m_layerManager.atomEnabled(layer, c3Atom.index()) && m_layerManager.atomEnabled(layer, o3Atom.index())) { size_t group = graph.getConnectedID(c3Atom.index()); addBackBone(result, previousAtom, c3Atom, residue.color(), group, residue.secondaryStructure()); } } } } return result; } map Cartoons::getBackboneManually( const Molecule& molecule, size_t layer) { // manual filter const auto& graph = molecule.graph(); map result; map previousCA; for (size_t i = 0; i < molecule.atomCount(); ++i) { const auto atom = molecule.atom(i); if (atom.atomicNumber() == AtomicNumber::Carbon && m_layerManager.atomEnabled(layer, atom.index())) { size_t group = graph.getConnectedID(atom.index()); addBackBone(result, previousCA, atom, atom.color(), group, Residue::SecondaryStructure::undefined); } } return result; } void renderBackbone(const AtomsPairList& backbone, const Molecule& molecule, Rendering::GroupNode& node, float radius) { auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; spheres->identifier().molecule = reinterpret_cast(&molecule); spheres->identifier().type = Rendering::AtomType; geometry->addDrawable(spheres); auto* cylinders = new CylinderGeometry; cylinders->identifier().molecule = &molecule; cylinders->identifier().type = Rendering::AtomType; geometry->addDrawable(cylinders); Index i = 0; for (auto it = backbone.begin(); it != backbone.end(); ++it) { const auto& bone = *it; auto color1 = bone.color1; auto color2 = bone.color2; if (bone.selected) { color1 += Vector3ub(155, 0, 0); color2 += Vector3ub(155, 0, 0); } const Vector3f& pos = bone.pos; spheres->addSphere(pos, color1, radius, bone.residueID); if (std::next(it) != backbone.end()) { const auto& nextBone = *std::next(it); const Vector3f& pos2 = nextBone.pos; cylinders->addCylinder(pos, pos2, radius, color1, color2, bone.residueID); } ++i; } } void renderRope(const AtomsPairList& backbone, const Molecule& molecule, Rendering::GroupNode& node, float radius) { auto* geometry = new GeometryNode; node.addChild(geometry); auto* bezier = new BezierGeometry; bezier->identifier().molecule = &molecule; bezier->identifier().type = Rendering::AtomType; geometry->addDrawable(bezier); Vector3ub color = Vector3ub::Zero(); for (const auto& bone : backbone) { bezier->addPoint(bone.pos, bone.color1 + color, radius, bone.group, bone.residueID); color = bone.selected ? Vector3ub(155, 0, 0) : Vector3ub::Zero(); } } void renderTube(const AtomsPairList& backbone, const Molecule& molecule, Rendering::GroupNode& node, float radius) { auto* geometry = new GeometryNode; node.addChild(geometry); auto* bezier = new BSplineGeometry; bezier->identifier().molecule = &molecule; bezier->identifier().type = Rendering::AtomType; geometry->addDrawable(bezier); Vector3ub color = Vector3ub::Zero(); for (const auto& bone : backbone) { bezier->addPoint(bone.pos, bone.color1 + color, radius, bone.group, bone.residueID); color = bone.selected ? Vector3ub(155, 0, 0) : Vector3ub::Zero(); } } void renderSimpleCartoon(const AtomsPairList& backbone, const Molecule& molecule, Rendering::GroupNode& node, float radius) { auto* geometry = new GeometryNode; node.addChild(geometry); auto* cartoon = new Cartoon; cartoon->identifier().molecule = &molecule; cartoon->identifier().type = Rendering::AtomType; geometry->addDrawable(cartoon); Vector3ub color = Vector3ub::Zero(); for (const auto& bone : backbone) { cartoon->CurveGeometry::addPoint(bone.pos, bone.color1 + color, radius, bone.group, bone.residueID); color = bone.selected ? Vector3ub(155, 0, 0) : Vector3ub::Zero(); } } void renderCartoon(const AtomsPairList& backbone, const Molecule& molecule, Rendering::GroupNode& node, float radius) { auto* geometry = new GeometryNode; node.addChild(geometry); auto* cartoon = new Cartoon(radius * 0.2f, radius * 1.5f); cartoon->identifier().molecule = &molecule; cartoon->identifier().type = Rendering::AtomType; geometry->addDrawable(cartoon); Vector3ub color = Vector3ub::Zero(); for (const auto& bone : backbone) { cartoon->addPoint(bone.pos, bone.color1 + color, bone.group, bone.residueID, bone.secondaryStructure); color = bone.selected ? Vector3ub(155, 0, 0) : Vector3ub::Zero(); } } void Cartoons::process(const Molecule& molecule, Rendering::GroupNode& node) { m_layerManager.load(); m_group = &node; for (size_t layer = 0; layer < m_layerManager.layerCount(); ++layer) { auto* interface = m_layerManager.getSetting(layer); if (interface->showBackbone || interface->showTrace || interface->showTube || interface->showRibbon || interface->showSimpleCartoon || interface->showCartoon || interface->showRope) { map backbones; if (molecule.residues().size() > 0) { backbones = getBackboneByResidues(molecule, layer); } if (backbones.size() == 0) { continue; // maybe something in a different layer } size_t i = 0; for (const auto& group : backbones) { const auto& backbone = group.second; if (interface->showBackbone) { renderBackbone(backbone, molecule, node, 0.1f); } if (interface->showTrace) { renderTube(backbone, molecule, node, -0.15f); } if (interface->showTube) { renderTube(backbone, molecule, node, 0.15f); } if (interface->showRibbon) { renderCartoon(backbone, molecule, node, -1.0f * Cartoon::ELIPSE_RATIO); } if (interface->showSimpleCartoon) { renderSimpleCartoon(backbone, molecule, node, 1.0f); } if (interface->showCartoon) { renderCartoon(backbone, molecule, node, 1.0f); } if (interface->showRope) { renderRope(backbone, molecule, node, 1.0f); } ++i; } } } } QWidget* Cartoons::setupWidget() { auto* interface = m_layerManager.getSetting(); interface->setupWidget(this); return interface->widget; } void Cartoons::showBackbone(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showBackbone) { interface->showBackbone = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/backbone", show); } void Cartoons::showTrace(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showTrace) { interface->showTrace = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/trace", show); } void Cartoons::showTube(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showTube) { interface->showTube = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/tube", show); } void Cartoons::showRibbon(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showRibbon) { interface->showRibbon = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/ribbon", show); } void Cartoons::showSimpleCartoon(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showSimpleCartoon) { interface->showSimpleCartoon = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/simpleCartoon", show); } void Cartoons::showCartoon(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showCartoon) { interface->showCartoon = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/cartoon", show); } void Cartoons::showRope(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showRope) { interface->showRope = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/rope", show); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/cartoons/cartoons.h000066400000000000000000000041741506155467400240320ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CARTOONS_H #define AVOGADRO_QTPLUGINS_CARTOONS_H #include #include #include namespace Avogadro::QtPlugins { struct BackboneResidue; using AtomsPairList = std::list; class Cartoons : public QtGui::ScenePlugin { Q_OBJECT public: explicit Cartoons(QObject* parent = 0); ~Cartoons() override = default; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Cartoons", "protein ribbon / cartoon rendering"); } QString description() const override { return tr("Display of biomolecule ribbons / cartoons."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::True; } public slots: // straights line between alpha carbons void showBackbone(bool show); // A flat line is displayed along the main backbone trace. void showTrace(bool show); // same as trace but with a circle volume (like a pipeline) void showTube(bool show); // same as trace but a flat plane void showRibbon(bool show); // the classic void showSimpleCartoon(bool show); // the classic + sheets and helix void showCartoon(bool show); // same as tube but instead of creating a b-spline (cuadratic bezier, segments // of 3) we crea a big N-bezier line void showRope(bool show); private: Rendering::GroupNode* m_group; std::string m_name = "Cartoons"; std::map getBackboneByResidues( const QtGui::Molecule& molecule, size_t layer); std::map getBackboneManually( const QtGui::Molecule& molecule, size_t layer); }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_CARTOONS_H avogadrolibs-1.101.0/avogadro/qtplugins/centroid/000077500000000000000000000000001506155467400220025ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/centroid/CMakeLists.txt000066400000000000000000000002001506155467400245320ustar00rootroot00000000000000avogadro_plugin(Centroid "Add centroid and center-of-mass." ExtensionPlugin centroid.h Centroid "centroid.cpp" "" ) avogadrolibs-1.101.0/avogadro/qtplugins/centroid/centroid.cpp000066400000000000000000000072041506155467400243200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "centroid.h" #include #include #include #include namespace Avogadro::QtPlugins { using Core::Array; using Core::Elements; Centroid::Centroid(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_centroidAction(new QAction(tr("Add Centroid"), this)), m_comAction(new QAction(tr("Add Center of Mass"), this)), m_normalAction( new QAction(tr("Add Perpendicular", "add a point normal to the plane of the molecule"), this)) { m_centroidAction->setProperty("menu priority", 190); m_comAction->setProperty("menu priority", 180); m_normalAction->setProperty("menu priority", 170); connect(m_centroidAction, SIGNAL(triggered()), SLOT(addCentroid())); connect(m_comAction, SIGNAL(triggered()), SLOT(addCenterOfMass())); connect(m_normalAction, SIGNAL(triggered()), SLOT(normal())); } QList Centroid::actions() const { QList result; return result << m_centroidAction << m_comAction << m_normalAction; } QStringList Centroid::menuPath(QAction*) const { return QStringList() << tr("&Build"); } void Centroid::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void Centroid::addCentroid() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; Vector3 center; Index count = 0; bool hasSelection = !m_molecule->isSelectionEmpty(); for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (hasSelection && !m_molecule->atomSelected(i)) continue; // don't count dummy atoms if (m_molecule->atomicNumber(i) == 0) continue; center += m_molecule->atomPosition3d(i); ++count; } center /= count; m_molecule->undoMolecule()->addAtom(0, center); m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } void Centroid::addCenterOfMass() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; Vector3 center; Real totalMass = 0.0; Index count = 0; bool hasSelection = !m_molecule->isSelectionEmpty(); // we have to first find the centroid for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (hasSelection && !m_molecule->atomSelected(i)) continue; // skip it if it's a dummy atom if (m_molecule->atomicNumber(i) == 0) continue; Real mass = Elements::mass(m_molecule->atomicNumber(i)); center += m_molecule->atomPosition3d(i) * mass; totalMass += mass; ++count; } center /= totalMass; m_molecule->undoMolecule()->addAtom(0, center); m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } void Centroid::normal() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; if (m_molecule->isSelectionEmpty()) { auto pair = m_molecule->bestFitPlane(); m_molecule->addAtom(0.0, pair.second * 2.0); } else { Array selectedAtoms; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!m_molecule->atomSelected(i)) continue; selectedAtoms.push_back(m_molecule->atomPosition3d(i)); } auto pair = m_molecule->bestFitPlane(selectedAtoms); Vector3 newPos = pair.second * 2.0 + pair.first; m_molecule->addAtom(0, newPos); } m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/centroid/centroid.h000066400000000000000000000024501506155467400237630ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CENTROID_H #define AVOGADRO_QTPLUGINS_CENTROID_H #include #include namespace Avogadro::QtPlugins { /** * @brief The Centroid class adds centroids and center-of-mass */ class Centroid : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Centroid(QObject* parent_ = nullptr); ~Centroid() override = default; QString name() const override { return tr("Centroid"); } QString description() const override { return tr("Add centroids and center-of-mass."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void addCentroid(); void addCenterOfMass(); void normal(); private: QtGui::Molecule* m_molecule; QAction* m_centroidAction; QAction* m_comAction; QAction* m_normalAction; }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_BONDING_H avogadrolibs-1.101.0/avogadro/qtplugins/closecontacts/000077500000000000000000000000001506155467400230375ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/closecontacts/CMakeLists.txt000066400000000000000000000003211506155467400255730ustar00rootroot00000000000000avogadro_plugin(CloseContacts "Predictive close-contact rendering" ScenePlugin closecontacts.h CloseContacts closecontacts.cpp "") target_link_libraries(CloseContacts PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/closecontacts/closecontacts.cpp000066400000000000000000000237011506155467400264120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "closecontacts.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Array; using Core::Atom; using Core::Bond; using Core::NeighborPerceiver; using QtGui::Molecule; using QtGui::PluginLayerManager; using Rendering::DashedLineGeometry; using Rendering::GeometryNode; CloseContacts::CloseContacts(QObject* p) : ScenePlugin(p) { m_layerManager = PluginLayerManager(m_name); QSettings settings; m_maximumDistances = { settings.value("closeContacts/maximumDistance0", 2.0).toDouble(), settings.value("closeContacts/maximumDistance1", 4.0).toDouble(), settings.value("closeContacts/maximumDistance2", 4.0).toDouble() }; auto contactColor = settings.value("closeContacts/lineColor0", QColor(128, 128, 128)) .value(); auto saltBColor = settings.value("closeContacts/lineColor1", QColor(192, 0, 255)) .value(); auto repulsiveColor = settings.value("closeContacts/lineColor2", QColor(255, 64, 64)) .value(); m_lineColors = { Vector3ub(contactColor.red(), contactColor.green(), contactColor.blue()), Vector3ub(saltBColor.red(), saltBColor.green(), saltBColor.blue()), Vector3ub(repulsiveColor.red(), repulsiveColor.green(), repulsiveColor.blue()) }; m_lineWidths = { settings.value("closeContacts/lineWidth0", 2.0).toFloat(), settings.value("closeContacts/lineWidth1", 5.0).toFloat(), settings.value("closeContacts/lineWidth2", 5.0).toFloat() }; } static bool checkPairNot1213(const Molecule& molecule, Index i, Index n) { static Array bondedCache; static Index lastIndex; static bool lastIndexValid = false; if (!lastIndexValid || lastIndex != i) { bondedCache.clear(); for (const Bond* b : molecule.bonds(i)) bondedCache.push_back(b->atom1().index() == i ? b->atom2().index() : b->atom1().index()); lastIndex = i; lastIndexValid = true; } for (const Bond* b : molecule.bonds(n)) { Index m = (b->atom1().index() == n ? b->atom2() : b->atom1()).index(); if (m == i) // exclude 1-2 pairs return false; for (Index bn : bondedCache) if (bn == m) // exclude 1-3 pairs return false; } return true; } void addChargedAtom(Array& positions, Array& charges, Array& residues, const Molecule& molecule, Index residueId, Atom atom, double charge) { auto pos = molecule.atomPosition3d(atom.index()); if (molecule.formalCharge(atom.index()) != 0) { for (Index i = 0; i < positions.size(); i++) { if ((positions[i] - pos).norm() < 0.00001) { residues[i] = residueId; return; } } } positions.push_back(pos); charges.push_back(charge); residues.push_back(residueId); } void CloseContacts::process(const Molecule& molecule, Rendering::GroupNode& node) { // Add general contacts NeighborPerceiver perceiver(molecule.atomPositions3d(), m_maximumDistances[0]); std::vector isAtomEnabled(molecule.atomCount()); for (Index i = 0; i < molecule.atomCount(); ++i) isAtomEnabled[i] = m_layerManager.atomEnabled(i); auto* geometry = new GeometryNode; node.addChild(geometry); std::array lineGroups; for (Index type = 0; type < 3; type++) { lineGroups[type] = new DashedLineGeometry; lineGroups[type]->identifier().molecule = &molecule; lineGroups[type]->identifier().type = Rendering::BondType; lineGroups[type]->setLineWidth(m_lineWidths[type]); geometry->addDrawable(lineGroups[type]); } Array neighbors; for (Index i = 0; i < molecule.atomCount(); ++i) { if (!isAtomEnabled[i]) continue; Vector3 pos = molecule.atomPosition3d(i); perceiver.getNeighborsInclusiveInPlace(neighbors, pos); for (Index n : neighbors) { if (n <= i) // check each pair only once continue; if (!isAtomEnabled[n]) continue; if (!checkPairNot1213(molecule, i, n)) continue; Vector3 npos = molecule.atomPosition3d(n); double distance = (npos - pos).norm(); if (distance < m_maximumDistances[0]) lineGroups[0]->addDashedLine(pos.cast(), npos.cast(), m_lineColors[0], 8); } } // Add charged atoms Array positions; Array charges; Array residues; for (Index i = 0; i < molecule.atomCount(); ++i) { if (molecule.formalCharge(i) != 0) { if (!isAtomEnabled[i]) continue; positions.push_back(molecule.atomPosition3d(i)); charges.push_back(molecule.formalCharge(i)); residues.push_back(Index(0) - 1); } } // Add predicted charged atoms from residues for (const auto& r : molecule.residues()) { if (!r.residueName().compare("LYS")) { addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("NZ"), 1.0); } else if (!r.residueName().compare("ARG")) { addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("NE"), 1.0); addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("NH1"), 1.0); addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("NH2"), 1.0); } else if (!r.residueName().compare("HIS")) { addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("ND1"), 1.0); } else if (!r.residueName().compare("ASP")) { addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("OD1"), -1.0); addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("OD2"), -1.0); } else if (!r.residueName().compare("GLU")) { addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("OE1"), -1.0); addChargedAtom(positions, charges, residues, molecule, r.residueId(), r.getAtomByName("OE2"), -1.0); } } // detect contacts among them NeighborPerceiver ionPerceiver( positions, std::max(m_maximumDistances[1], m_maximumDistances[2])); for (Index i = 0; i < positions.size(); ++i) { const Vector3& pos = positions[i]; ionPerceiver.getNeighborsInclusiveInPlace(neighbors, pos); for (Index n : neighbors) { if (n <= i) // check each pair only once continue; if (residues[n] == residues[i] && residues[i] != Index(0) - 1) continue; // ignore intra-residue interactions Vector3 npos = positions[n]; double distance = (npos - pos).norm(); if (charges[i] * charges[n] > 0.0 && distance < m_maximumDistances[2]) lineGroups[2]->addDashedLine(pos.cast(), npos.cast(), m_lineColors[2], 8); else if (distance < m_maximumDistances[1]) lineGroups[1]->addDashedLine(pos.cast(), npos.cast(), m_lineColors[1], 8); } } } QWidget* CloseContacts::setupWidget() { auto* widget = new QWidget(qobject_cast(this->parent())); auto* v = new QVBoxLayout; auto* tabs = new QTabWidget; for (Index i = 0; i < 3; i++) { // maximum distance auto* distance_spin = new QDoubleSpinBox; distance_spin->setRange(1.5, 10.0); distance_spin->setSingleStep(0.1); distance_spin->setDecimals(1); distance_spin->setSuffix(tr(" Å")); distance_spin->setValue(m_maximumDistances[i]); QObject::connect( distance_spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this, i](float distance) { return setMaximumDistance(distance, i); }); // line width auto* lineWidth_spin = new QDoubleSpinBox; lineWidth_spin->setRange(1.0, 10.0); lineWidth_spin->setSingleStep(0.5); lineWidth_spin->setDecimals(1); lineWidth_spin->setValue(m_lineWidths[i]); QObject::connect(lineWidth_spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this, i](float width) { return setLineWidth(width, i); }); auto* form = new QFormLayout; form->addRow(QObject::tr("Maximum distance:"), distance_spin); form->addRow(QObject::tr("Line width:"), lineWidth_spin); auto* page = new QWidget; page->setLayout(form); tabs->addTab(page, INTERACTION_NAMES[i]); } v->addWidget(tabs); v->addStretch(1); widget->setLayout(v); return widget; } void CloseContacts::setMaximumDistance(float maximumDistance, Index index) { m_maximumDistances[index] = maximumDistance; emit drawablesChanged(); QSettings settings; settings.setValue(QString("closeContacts/maximumDistance%1").arg(index), maximumDistance); } void CloseContacts::setLineWidth(float width, Index index) { m_lineWidths[index] = width; emit drawablesChanged(); QSettings settings; settings.setValue(QString("closeContacts/lineWidth%1").arg(index), width); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/closecontacts/closecontacts.h000066400000000000000000000034741506155467400260640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CLOSECONTACTS_H #define AVOGADRO_QTPLUGINS_CLOSECONTACTS_H #include #include namespace Avogadro::QtPlugins { /** * @brief Detect and render close contacts between atoms. * @author Aritz Erkiaga */ class CloseContacts : public QtGui::ScenePlugin { Q_OBJECT public: explicit CloseContacts(QObject* parent = nullptr); ~CloseContacts() override = default; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Close Contacts", "rendering of non-covalent close contacts"); } QString description() const override { return tr("Render close contacts between atoms."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } public slots: void setMaximumDistance(float maximumDistance, Index index); void setLineWidth(float width, Index index); private: std::string m_name = "Close Contacts"; const std::array INTERACTION_NAMES = { tr("Contact"), tr("Salt Bridge"), tr("Repulsive") }; std::array m_maximumDistances; std::array m_lineColors; std::array m_lineWidths; }; } // end namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_CLOSECONTACTS_H avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/000077500000000000000000000000001506155467400234005ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/CMakeLists.txt000066400000000000000000000006761506155467400261510ustar00rootroot00000000000000set(_srcs coloropacitymap.cpp histogramwidget.cpp vtkChartHistogram.cpp vtkChartHistogramColorOpacityEditor.cpp vtkCustomPiecewiseControlPointsItem.cpp comdialog.cpp ) set(_uis comdialog.ui ) avogadro_plugin(ColorOpacityMap "Edit the color opacity map." ExtensionPlugin coloropacitymap.h ColorOpacityMap "${_srcs}" "${_uis}" ) target_link_libraries(ColorOpacityMap PRIVATE Avogadro::QtOpenGL Avogadro::Vtk) avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/coloropacitymap.cpp000066400000000000000000000121671506155467400273200ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "coloropacitymap.h" #include "comdialog.h" #include "computehistogram.h" #include "histogramwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::QtGui::Molecule; using Avogadro::QtOpenGL::ActiveObjects; namespace Avogadro::QtPlugins { vtkImageData* cubeImageData(Core::Cube* cube) { auto data = vtkImageData::New(); // data->SetNumberOfScalarComponents(1, nullptr); Eigen::Vector3i dim = cube->dimensions(); data->SetExtent(0, dim.x() - 1, 0, dim.y() - 1, 0, dim.z() - 1); // Translate origin, spacing, and types from Avogadro to VTK. data->SetOrigin(cube->min().x(), cube->min().y(), cube->min().z()); data->SetSpacing(cube->spacing().data()); data->AllocateScalars(VTK_FLOAT, 1); auto* dataPtr = static_cast(data->GetScalarPointer()); std::vector* cubePtr = cube->data(); // Reorder our cube for VTK's Fortran ordering in vtkImageData. for (int i = 0; i < dim.x(); ++i) { for (int j = 0; j < dim.y(); ++j) { for (int k = 0; k < dim.z(); ++k) { dataPtr[(k * dim.y() + j) * dim.x() + i] = (*cubePtr)[(i * dim.y() + j) * dim.z() + k]; } } } return data; } ColorOpacityMap::ColorOpacityMap(QObject* p) : Avogadro::QtGui::ExtensionPlugin(p), m_actions(QList()), m_displayDialogAction(new QAction(this)) { m_displayDialogAction->setText(tr("Edit Color Opacity Map…")); connect(m_displayDialogAction.data(), &QAction::triggered, this, &ColorOpacityMap::displayDialog); m_actions.push_back(m_displayDialogAction.data()); m_displayDialogAction->setProperty("menu priority", 70); updateActions(); } ColorOpacityMap::~ColorOpacityMap() = default; QList ColorOpacityMap::actions() const { return m_actions; } QStringList ColorOpacityMap::menuPath(QAction*) const { return QStringList() << tr("&Extensions"); } void ColorOpacityMap::setActiveWidget(QWidget* widget) { auto vtkWidget = qobject_cast(widget); if (vtkWidget) { m_vtkWidget = true; updateActions(); } else { m_vtkWidget = false; updateActions(); } } void ColorOpacityMap::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); } void ColorOpacityMap::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); // I think we need to look at adding cubes to changes, flaky right now. auto changes = static_cast(c); if (changes & Molecule::Added || changes & Molecule::Removed) { updateActions(); if (m_comDialog) updateHistogram(); } } void ColorOpacityMap::updateActions() { foreach (QAction* action, m_actions) action->setEnabled(m_vtkWidget); } void ColorOpacityMap::updateHistogram() { auto widget = ActiveObjects::instance().activeWidget(); auto vtkWidget = qobject_cast(widget); if (vtkWidget == nullptr) m_vtkWidget = false; if (widget && vtkWidget && widget != m_activeWidget) { if (m_activeWidget) disconnect(widget, nullptr, this, nullptr); connect(widget, SIGNAL(imageDataUpdated()), SLOT(updateHistogram())); m_activeWidget = widget; } if (vtkWidget && m_molecule && m_molecule->cubeCount()) { m_vtkWidget = true; vtkNew table; auto imageData = vtkWidget->imageData(); auto lut = vtkWidget->lut(); auto opacity = vtkWidget->opacityFunction(); m_histogramWidget->setLUT(lut); m_histogramWidget->setOpacityFunction(opacity); if (imageData) { PopulateHistogram(imageData, table); m_histogramWidget->setInputData(table, "image_extents", "image_pops"); } } } void ColorOpacityMap::displayDialog() { if (!m_comDialog) { auto p = qobject_cast(parent()); m_comDialog = new ComDialog(p); m_comDialog->setMolecule(m_molecule); m_histogramWidget = m_comDialog->histogramWidget(); // m_c->resize(800, 600); connect(m_histogramWidget, SIGNAL(colorMapUpdated()), SLOT(render())); connect(m_histogramWidget, SIGNAL(opacityChanged()), SLOT(render())); connect(m_comDialog, SIGNAL(renderNeeded()), SLOT(render())); } updateHistogram(); m_comDialog->show(); } void ColorOpacityMap::render() { auto widget = ActiveObjects::instance().activeWidget(); auto vtkWidget = qobject_cast(widget); if (vtkWidget) { vtkWidget->renderWindow()->Render(); vtkWidget->update(); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/coloropacitymap.h000066400000000000000000000030611506155467400267560ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef AVOGADRO_QTPLUGINS_COLOROPACITYMAP_H #define AVOGADRO_QTPLUGINS_COLOROPACITYMAP_H #include namespace Avogadro { class HistogramWidget; namespace QtPlugins { class ComDialog; /** * @brief An interactive color opacity map editor with a value population * histogram. */ class ColorOpacityMap : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit ColorOpacityMap(QObject* parent_ = nullptr); ~ColorOpacityMap(); QString name() const override { return tr("ColorOpacityMap"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); void setActiveWidget(QWidget* widget) override; private slots: void updateActions(); void updateHistogram(); void displayDialog(); void render(); private: QList m_actions; QtGui::Molecule* m_molecule = nullptr; ComDialog* m_comDialog = nullptr; HistogramWidget* m_histogramWidget = nullptr; QScopedPointer m_displayDialogAction; bool m_vtkWidget = false; QWidget* m_activeWidget = nullptr; }; inline QString ColorOpacityMap::description() const { return tr("Edit color opacity maps, primarily for volume rendering."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_COLOROPACITYMAP_H avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/comdialog.cpp000066400000000000000000000044451506155467400260510ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "comdialog.h" #include "ui_comdialog.h" #include #include #include #include namespace Avogadro::QtPlugins { using QtOpenGL::ActiveObjects; using VTK::vtkGLWidget; ComDialog::ComDialog(QWidget* p, Qt::WindowFlags f) : QDialog(p, f), m_ui(new Ui::ComDialog) { m_ui->setupUi(this); connect(m_ui->enableVolumeRendering, SIGNAL(stateChanged(int)), SLOT(enableVolume(int))); connect(m_ui->enableIsosurface, SIGNAL(stateChanged(int)), SLOT(enableIsosurface(int))); connect(m_ui->isoValue, SIGNAL(valueChanged(double)), SLOT(setIsoValue(double))); connect(m_ui->opacity, SIGNAL(valueChanged(double)), SLOT(setOpacity(double))); } ComDialog::~ComDialog() { delete m_ui; } HistogramWidget* ComDialog::histogramWidget() { return m_ui->histogramWidget; } void ComDialog::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol || !mol) return; m_molecule = mol; // Figure out which cubes are available. m_ui->cubesComboBox->clear(); for (Index i = 0; i < mol->cubeCount(); ++i) { m_ui->cubesComboBox->addItem(QString(mol->cube(i)->name().c_str())); } } void ComDialog::enableVolume(int enable) { auto w = ActiveObjects::instance().activeWidget(); auto vtkgl = qobject_cast(w); if (vtkgl) { vtkgl->renderVolume(enable == 0 ? false : true); emit renderNeeded(); } } void ComDialog::enableIsosurface(int enable) { auto w = ActiveObjects::instance().activeWidget(); auto vtkgl = qobject_cast(w); if (vtkgl) { vtkgl->renderIsosurface(enable == 0 ? false : true); emit renderNeeded(); } } void ComDialog::setIsoValue(double value) { auto w = ActiveObjects::instance().activeWidget(); auto vtkgl = qobject_cast(w); if (vtkgl) { vtkgl->setIsoValue(value); emit renderNeeded(); } } void ComDialog::setOpacity(double value) { auto w = ActiveObjects::instance().activeWidget(); auto vtkgl = qobject_cast(w); if (vtkgl) { vtkgl->setOpacity(value); emit renderNeeded(); } } } // namespace Avogadro::QtPluginsavogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/comdialog.h000066400000000000000000000017261506155467400255150ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef AVOGADRO_QTPLUGINS_COMDIALOG_H #define AVOGADRO_QTPLUGINS_COMDIALOG_H #include namespace Ui { class ComDialog; } namespace Avogadro { class HistogramWidget; namespace QtGui { class Molecule; } namespace QtPlugins { class ComDialog : public QDialog { Q_OBJECT public: explicit ComDialog(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~ComDialog() override; HistogramWidget* histogramWidget(); void setMolecule(QtGui::Molecule* mol); protected slots: void enableVolume(int enable); void enableIsosurface(int enable); void setIsoValue(double value); void setOpacity(double value); signals: void renderNeeded(); private: Ui::ComDialog* m_ui = nullptr; QtGui::Molecule* m_molecule = nullptr; }; } // End namespace QtPlugins } // End namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/comdialog.ui000066400000000000000000000102131506155467400256720ustar00rootroot00000000000000 ComDialog 0 0 776 549 Volume Rendering 1 1 Cube Enable volume rendering true Enable surface rendering Isovalue false 3 0.010000000000000 0.050000000000000 Opacity false 1.000000000000000 0.100000000000000 0.500000000000000 Avogadro::HistogramWidget QWidget
histogramwidget.h
1
enableIsosurface toggled(bool) isoValue setEnabled(bool) 472 465 472 493 enableIsosurface toggled(bool) opacity setEnabled(bool) 472 465 472 525
avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/computehistogram.h000066400000000000000000000136701506155467400271520ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef AVOGADRO_COMPUTE_HISTOGRAM_H #define AVOGADRO_COMPUTE_HISTOGRAM_H #include #include #include #include #include #include #include #include namespace Avogadro { /** Single component integral type specialization. */ template ::value>::type* = nullptr> void calcHistogram(T* values, const vtkIdType numTuples, const float min, const float inv, int* pops, int&) { for (vtkIdType j = 0; j < numTuples; ++j) { ++pops[static_cast((*values++ - min) * inv)]; } } /** Needs to be present, should never be compiled. */ template void calcHistogram(T*, const vtkIdType, int*) { static_assert(!std::is_same::value, "Invalid type"); } /** Single component unsigned char covering 0 -> 255 range. */ void calcHistogram(unsigned char* values, const vtkIdType numTuples, int* pops) { for (vtkIdType j = 0; j < numTuples; ++j) { ++pops[*values++]; } } /** Single component floating point type specialization. */ template ::value>::type* = nullptr> void calcHistogram(T* values, const vtkIdType numTuples, const float min, const float inv, int* pops, int& invalid) { for (vtkIdType j = 0; j < numTuples; ++j) { T value = *(values++); if (std::isfinite(value)) { ++pops[static_cast((value - min) * inv)]; } else { ++invalid; } } } /** * Computes a histogram from an array of values. * \param values The array from which to compute the histogram. * \param numTuples Number of tuples in the array. * \param numComponents Number of components in each tuple. * \param min Minimum value in range * \param max Maximum value in range * \param inv Inverse of bin size, numBins is the number of bins * in the histogram (or length of the pops array), and invalid is a return * parameter indicating how many values in the array had a non-finite value. */ template void CalculateHistogram(T* values, const vtkIdType numTuples, const vtkIdType numComponents, const float min, const float max, int* pops, const float inv, int& invalid) { // Single component is a simpler/faster path, let's dispatch separately. if (numComponents == 1) { // Very fast path for unsigned char in 0 -> 255 range, or fast path. if (std::is_same::value && min == 0.f && max == 255.f) { calcHistogram(values, numTuples, pops); } else { calcHistogram(values, numTuples, min, inv, pops, invalid); } } else { // Multicomponent magnitude for (vtkIdType j = 0; j < numTuples; ++j) { // Check that all components are valid. bool valid = true; double squaredSum = 0.0; for (vtkIdType c = 0; c < numComponents; ++c) { T value = *(values + c); if (!vtkMath::IsFinite(value)) { valid = false; break; } squaredSum += (value * value); } if (valid) { int index = static_cast((sqrt(squaredSum) - min) * inv); ++pops[index]; } else { ++invalid; } values += numComponents; } } } void PopulateHistogram(vtkImageData* input, vtkTable* output) { // The output table will have the twice the number of columns, they will be // the x and y for input column. This is the bin centers, and the population. double minmax[2] = { 0.0, 0.0 }; // This number of bins in the 2D histogram will also be used as the number of // bins in the 2D transfer function for X (scalar value) and Y (gradient mag.) const int numberOfBins = 256; // Keep the array we are working on around even if the user shallow copies // over the input image data by incrementing the reference count here. vtkSmartPointer arrayPtr = input->GetPointData()->GetScalars(); if (!arrayPtr) { return; } // The bin values are the centers, extending +/- half an inc either side arrayPtr->GetFiniteRange(minmax, -1); if (minmax[0] == minmax[1]) { minmax[1] = minmax[0] + 1.0; } double inc = (minmax[1] - minmax[0]) / (numberOfBins - 1); double halfInc = inc / 2.0; vtkSmartPointer extents = vtkFloatArray::SafeDownCast(output->GetColumnByName("image_extents")); if (!extents) { extents = vtkSmartPointer::New(); extents->SetName("image_extents"); } extents->SetNumberOfTuples(numberOfBins); double min = minmax[0] + halfInc; for (int j = 0; j < numberOfBins; ++j) { extents->SetValue(j, min + j * inc); } vtkSmartPointer populations = vtkIntArray::SafeDownCast(output->GetColumnByName("image_pops")); if (!populations) { populations = vtkSmartPointer::New(); populations->SetName("image_pops"); } populations->SetNumberOfTuples(numberOfBins); auto pops = static_cast(populations->GetVoidPointer(0)); for (int k = 0; k < numberOfBins; ++k) { pops[k] = 0; } int invalid = 0; switch (arrayPtr->GetDataType()) { vtkTemplateMacro(CalculateHistogram( reinterpret_cast(arrayPtr->GetVoidPointer(0)), arrayPtr->GetNumberOfTuples(), arrayPtr->GetNumberOfComponents(), minmax[0], minmax[1], pops, 1.0 / inc, invalid)); default: cout << "UpdateFromFile: Unknown data type" << endl; } #ifndef NDEBUG vtkIdType total = invalid; for (int i = 0; i < numberOfBins; ++i) total += pops[i]; assert(total == arrayPtr->GetNumberOfTuples()); #endif if (invalid) { cout << "Warning: NaN or infinite value in dataset" << endl; } output->AddColumn(extents); output->AddColumn(populations); } } // namespace Avogadro #endif // AVOGADRO_COMPUTE_HISTOGRAM_H avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/histogramwidget.cpp000066400000000000000000000105131506155467400273050ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "histogramwidget.h" #include #include #include #include #include "vtkChartHistogramColorOpacityEditor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro { HistogramWidget::HistogramWidget(QWidget* parent) : QWidget(parent), m_qvtk(new VTK::QVTKWidget(this)) { // Set up our little chart. m_histogramView->SetRenderWindow(m_qvtk->renderWindow()); m_histogramView->SetInteractor(m_qvtk->interactor()); m_histogramView->GetScene()->AddItem(m_histogramColorOpacityEditor); // Connect events from the histogram color/opacity editor. m_eventLink->Connect(m_histogramColorOpacityEditor, vtkCommand::CursorChangedEvent, this, SLOT(histogramClicked(vtkObject*))); m_eventLink->Connect(m_histogramColorOpacityEditor, vtkCommand::EndEvent, this, SLOT(onScalarOpacityFunctionChanged())); m_eventLink->Connect(m_histogramColorOpacityEditor, vtkControlPointsItem::CurrentPointEditEvent, this, SLOT(onCurrentPointEditEvent())); auto hLayout = new QHBoxLayout(this); hLayout->addWidget(m_qvtk); setLayout(hLayout); } HistogramWidget::~HistogramWidget() = default; void HistogramWidget::setLUT(vtkColorTransferFunction* lut) { if (m_LUT != lut) { m_LUT = lut; m_histogramColorOpacityEditor->SetColorTransferFunction(lut); emit colorMapUpdated(); } } void HistogramWidget::setOpacityFunction(vtkPiecewiseFunction* opacity) { if (m_opacityFunction) { m_eventLink->Disconnect(m_opacityFunction, vtkCommand::ModifiedEvent, this, SLOT(onScalarOpacityFunctionChanged())); } m_opacityFunction = opacity; m_histogramColorOpacityEditor->SetOpacityFunction(opacity); m_eventLink->Connect(m_opacityFunction, vtkCommand::ModifiedEvent, this, SLOT(onScalarOpacityFunctionChanged())); } vtkColorTransferFunction* HistogramWidget::LUT() { return m_LUT; } vtkPiecewiseFunction* HistogramWidget::opacityFunction() { return m_opacityFunction; } void HistogramWidget::setInputData(vtkTable* table, const char* x, const char* y) { m_inputData = table; m_histogramColorOpacityEditor->SetHistogramInputData(table, x, y); m_histogramColorOpacityEditor->SetOpacityFunction(m_opacityFunction); if (m_LUT && table) { m_histogramColorOpacityEditor->SetScalarVisibility(true); m_histogramColorOpacityEditor->SetColorTransferFunction(m_LUT); m_histogramColorOpacityEditor->SelectColorArray("image_extents"); } m_histogramView->Render(); } void HistogramWidget::onScalarOpacityFunctionChanged() { // Update the histogram m_histogramView->GetRenderWindow()->Render(); emit opacityChanged(); } void HistogramWidget::onCurrentPointEditEvent() { double rgb[3]; if (m_histogramColorOpacityEditor->GetCurrentControlPointColor(rgb)) { QColor color = QColorDialog::getColor(QColor::fromRgbF(rgb[0], rgb[1], rgb[2]), this, "Select Color for Control Point"); if (color.isValid()) { rgb[0] = color.redF(); rgb[1] = color.greenF(); rgb[2] = color.blueF(); m_histogramColorOpacityEditor->SetCurrentControlPointColor(rgb); onScalarOpacityFunctionChanged(); } } } void HistogramWidget::histogramClicked(vtkObject*) {} void HistogramWidget::updateUI() {} void HistogramWidget::renderViews() { // pqView* view = // tomviz::convert(ActiveObjects::instance().activeView()); // if (view) { // view->render(); // } } void HistogramWidget::showEvent(QShowEvent* event) { QWidget::showEvent(event); renderViews(); } } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/histogramwidget.h000066400000000000000000000032011506155467400267460ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef AVOGADRO_QTPLUGINS_HISTOGRAMWIDGET_H #define AVOGADRO_QTPLUGINS_HISTOGRAMWIDGET_H #include #include #include class vtkChartHistogramColorOpacityEditor; class vtkContextView; class vtkEventQtSlotConnect; class vtkPiecewiseFunction; class vtkObject; class vtkTable; class QToolButton; class vtkColorTransferFunction; namespace Avogadro { namespace VTK { class QVTKWidget; } class HistogramWidget : public QWidget { Q_OBJECT public: explicit HistogramWidget(QWidget* parent_ = nullptr); ~HistogramWidget() override; void setLUT(vtkColorTransferFunction* lut); vtkColorTransferFunction* LUT(); void setOpacityFunction(vtkPiecewiseFunction* opacity); vtkPiecewiseFunction* opacityFunction(); void setInputData(vtkTable* table, const char* x, const char* y); signals: void colorMapUpdated(); void opacityChanged(); public slots: void onScalarOpacityFunctionChanged(); void onCurrentPointEditEvent(); void histogramClicked(vtkObject*); void updateUI(); protected: void showEvent(QShowEvent* event) override; private: void renderViews(); vtkNew m_histogramColorOpacityEditor; vtkNew m_histogramView; vtkNew m_eventLink; vtkWeakPointer m_LUT; vtkWeakPointer m_opacityFunction; vtkWeakPointer m_inputData; VTK::QVTKWidget* m_qvtk; }; } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_HISTOGRAMWIDGET_H avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/vtkChartHistogram.cpp000066400000000000000000000164251506155467400275600ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "vtkChartHistogram.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtkCustomPiecewiseControlPointsItem.h" class vtkHistogramMarker : public vtkPlot { public: static vtkHistogramMarker* New(); double PositionX; bool Paint(vtkContext2D* painter) override { vtkNew pen; pen->SetColor(255, 0, 0, 255); pen->SetWidth(2.0); painter->ApplyPen(pen.Get()); painter->DrawLine(PositionX, 0, PositionX, 1e9); return true; } }; vtkStandardNewMacro(vtkHistogramMarker) vtkStandardNewMacro(vtkChartHistogram) vtkChartHistogram::vtkChartHistogram() { this->SetBarWidthFraction(1.0); this->SetRenderEmpty(true); this->SetAutoAxes(false); this->ZoomWithMouseWheelOff(); this->GetAxis(vtkAxis::LEFT)->SetTitle(""); this->GetAxis(vtkAxis::BOTTOM)->SetTitle(""); this->GetAxis(vtkAxis::BOTTOM)->SetBehavior(vtkAxis::FIXED); this->GetAxis(vtkAxis::BOTTOM)->SetRange(0, 255); this->GetAxis(vtkAxis::LEFT)->SetBehavior(vtkAxis::FIXED); this->GetAxis(vtkAxis::LEFT)->SetRange(0.0001, 10); this->GetAxis(vtkAxis::LEFT)->SetMinimumLimit(1); this->GetAxis(vtkAxis::LEFT)->SetLogScale(true); this->GetAxis(vtkAxis::LEFT)->SetNotation(vtkAxis::SCIENTIFIC_NOTATION); this->GetAxis(vtkAxis::LEFT)->SetPrecision(1); this->GetAxis(vtkAxis::RIGHT)->SetBehavior(vtkAxis::FIXED); this->GetAxis(vtkAxis::RIGHT)->SetRange(0.0, 1.0); this->GetAxis(vtkAxis::RIGHT)->SetVisible(false); int fontSize = 8; this->GetAxis(vtkAxis::LEFT)->GetLabelProperties()->SetFontSize(fontSize); this->GetAxis(vtkAxis::BOTTOM)->GetLabelProperties()->SetFontSize(fontSize); this->GetAxis(vtkAxis::RIGHT)->GetLabelProperties()->SetFontSize(fontSize); this->GetTooltip()->GetTextProperties()->SetFontSize(fontSize); // Set up the plot bar this->AddPlot(this->HistogramPlotBar.Get()); this->HistogramPlotBar->SetColor(0, 0, 255, 255); this->HistogramPlotBar->GetPen()->SetLineType(vtkPen::NO_PEN); this->HistogramPlotBar->SetSelectable(false); // Set up and add the opacity editor chart items this->OpacityFunctionItem->SetOpacity( 0.0); // don't show the transfer function this->AddPlot(this->OpacityFunctionItem.Get()); this->SetPlotCorner(this->OpacityFunctionItem.Get(), 1); this->OpacityControlPointsItem->SetEndPointsXMovable(false); this->OpacityControlPointsItem->SetEndPointsYMovable(true); this->OpacityControlPointsItem->SetEndPointsRemovable(false); vtkPen* pen = this->OpacityControlPointsItem->GetPen(); pen->SetLineType(vtkPen::SOLID_LINE); pen->SetColor(0, 0, 0); pen->SetOpacity(255); pen->SetWidth(2.0); this->AddPlot(this->OpacityControlPointsItem.Get()); this->SetPlotCorner(this->OpacityControlPointsItem.Get(), 1); } vtkChartHistogram::~vtkChartHistogram() {} bool vtkChartHistogram::MouseDoubleClickEvent(const vtkContextMouseEvent& m) { // Determine the location of the click, and emit something we can listen to! vtkPlotBar* histo = nullptr; if (this->GetNumberOfPlots() > 0) { histo = vtkPlotBar::SafeDownCast(this->GetPlot(0)); } if (!histo) { return false; } this->CalculateUnscaledPlotTransform(histo->GetXAxis(), histo->GetYAxis(), this->Transform.Get()); vtkVector2f pos; this->Transform->InverseTransformPoints(m.GetScenePos().GetData(), pos.GetData(), 1); this->ContourValue = pos.GetX(); this->Marker->PositionX = this->ContourValue; this->Marker->Modified(); this->Scene->SetDirty(true); if (this->GetNumberOfPlots() > 0) { // Work around a bug in the charts - ensure corner is invalid for the plot. this->Marker->SetXAxis(nullptr); this->Marker->SetYAxis(nullptr); this->AddPlot(this->Marker.Get()); } this->InvokeEvent(vtkCommand::CursorChangedEvent); return true; } void vtkChartHistogram::SetHistogramInputData(vtkTable* table, const char* xAxisColumn, const char* yAxisColumn) { this->HistogramPlotBar->SetInputData(table, xAxisColumn, yAxisColumn); // vtkPlotBar doesn't seem to behave well when given a null table, // so we just hide the components. auto setItemsVisible = [this](bool vis) { this->HistogramPlotBar->SetVisible(vis); this->OpacityFunctionItem->SetVisible(vis); this->OpacityControlPointsItem->SetVisible(vis); }; if (!table) { // Set axis this->GetAxis(vtkAxis::LEFT)->SetRange(0, 1.0); this->GetAxis(vtkAxis::BOTTOM)->SetRange(0, 255); // Set visibility of items setItemsVisible(false); return; } if (!this->HistogramPlotBar->GetVisible()) { setItemsVisible(true); } // Set the range of the axes vtkDataArray* yArray = vtkDataArray::SafeDownCast(table->GetColumnByName(yAxisColumn)); if (!yArray) { return; } double max = log10(yArray->GetRange()[1]); vtkAxis* leftAxis = this->GetAxis(vtkAxis::LEFT); leftAxis->SetUnscaledMinimum(1.0); leftAxis->SetMaximumLimit(max + 2.0); leftAxis->SetMaximum(static_cast(max) + 1.0); vtkDataArray* xArray = vtkDataArray::SafeDownCast(table->GetColumnByName(xAxisColumn)); if (xArray && xArray->GetNumberOfTuples() > 2) { double range[2]; xArray->GetRange(range); double halfInc = (xArray->GetTuple1(1) - xArray->GetTuple1(0)) / 2.0; vtkAxis* bottomAxis = this->GetAxis(vtkAxis::BOTTOM); bottomAxis->SetBehavior(vtkAxis::FIXED); bottomAxis->SetRange(range[0] - halfInc, range[1] + halfInc); } // reset the right axis vtkAxis* rightAxis = this->GetAxis(vtkAxis::RIGHT); rightAxis->SetBehavior(vtkAxis::FIXED); rightAxis->SetRange(0.0, 1.0); } void vtkChartHistogram::SetScalarVisibility(bool visible) { this->HistogramPlotBar->SetScalarVisibility(visible); } void vtkChartHistogram::SetHistogramVisible(bool visible) { this->HistogramPlotBar->SetVisible(visible); } void vtkChartHistogram::SetMarkerVisible(bool visible) { this->Marker->SetVisible(visible); } void vtkChartHistogram::ScalarVisibilityOn() { this->HistogramPlotBar->ScalarVisibilityOn(); } void vtkChartHistogram::SetLookupTable(vtkScalarsToColors* lut) { this->HistogramPlotBar->SetLookupTable(lut); } void vtkChartHistogram::SelectColorArray(const char* arrayName) { this->HistogramPlotBar->SelectColorArray(arrayName); } void vtkChartHistogram::SetOpacityFunction( vtkPiecewiseFunction* opacityFunction) { this->OpacityFunctionItem->SetPiecewiseFunction(opacityFunction); this->OpacityControlPointsItem->SetPiecewiseFunction(opacityFunction); } void vtkChartHistogram::SetDPI(int dpi) { if (this->GetScene()) { vtkRenderer* renderer = this->GetScene()->GetRenderer(); if (renderer && renderer->GetRenderWindow()) { renderer->GetRenderWindow()->SetDPI(dpi); } } } avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/vtkChartHistogram.h000066400000000000000000000035521506155467400272220ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef tomvizvtkChartHistogram_h #define tomvizvtkChartHistogram_h #include #include #include class vtkContextMouseEvent; class vtkCustomPiecewiseControlPointsItem; class vtkHistogramMarker; class vtkPiecewiseFunction; class vtkPiecewiseFunctionItem; class vtkPlotBar; class vtkScalarsToColors; class vtkTable; class vtkChartHistogram : public vtkChartXY { public: static vtkChartHistogram* New(); bool MouseDoubleClickEvent(const vtkContextMouseEvent& mouse) override; // Set input for histogram virtual void SetHistogramInputData(vtkTable* table, const char* xAxisColumn, const char* yAxisColumn); // Set scalar visibility in the histogram plot bar virtual void SetScalarVisibility(bool visible); virtual void ScalarVisibilityOn(); void SetHistogramVisible(bool visible); void SetMarkerVisible(bool visible); // Set lookup table virtual void SetLookupTable(vtkScalarsToColors* lut); // Set the color array name virtual void SelectColorArray(const char* arrayName); // Set opacity function from a transfer function virtual void SetOpacityFunction(vtkPiecewiseFunction* opacityFunction); // Set the contour value from the contour marker vtkSetMacro(ContourValue, double) vtkGetMacro(ContourValue, double) // Set the DPI of the chart. void SetDPI(int dpi); protected: vtkNew Transform; double ContourValue; vtkNew Marker; vtkNew HistogramPlotBar; vtkNew OpacityFunctionItem; vtkNew OpacityControlPointsItem; private: vtkChartHistogram(); virtual ~vtkChartHistogram(); }; #endif // tomvizvtkChartHistogram_h avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/vtkChartHistogramColorOpacityEditor.cpp000066400000000000000000000212011506155467400332430ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "vtkChartHistogramColorOpacityEditor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtkChartHistogram.h" class vtkChartHistogramColorOpacityEditor::PIMPL { public: PIMPL() : Geometry(0, 0), NeedsUpdate(true) {} ~PIMPL() {} void ForwardEvent(vtkObject* vtkNotUsed(object), unsigned long eventId, void* vtkNotUsed(data)) { this->Self->InvokeEvent(eventId); } // Cached geometry of the chart vtkVector2i Geometry; // Dirty bit bool NeedsUpdate; // Reference to owner of the PIMPL vtkChartHistogramColorOpacityEditor* Self; }; vtkStandardNewMacro(vtkChartHistogramColorOpacityEditor) vtkChartHistogramColorOpacityEditor::vtkChartHistogramColorOpacityEditor() { this->Private = new PIMPL(); this->Private->Self = this; this->Borders[vtkAxis::LEFT] = 8; this->Borders[vtkAxis::BOTTOM] = 8; this->Borders[vtkAxis::RIGHT] = 8; this->Borders[vtkAxis::TOP] = 20; this->HistogramChart->SetHiddenAxisBorder(10); this->HistogramChart->SetLayoutStrategy(vtkChart::AXES_TO_RECT); this->ColorTransferFunctionChart->SetBarWidthFraction(1.0); this->ColorTransferFunctionChart->SetHiddenAxisBorder(8); this->ColorTransferFunctionChart->SetRenderEmpty(true); this->ColorTransferFunctionChart->SetAutoAxes(false); this->ColorTransferFunctionChart->ZoomWithMouseWheelOff(); this->ColorTransferFunctionChart->SetLayoutStrategy(vtkChart::AXES_TO_RECT); this->ColorTransferFunctionItem->SelectableOff(); this->ColorTransferControlPointsItem->SetEndPointsXMovable(false); this->ColorTransferControlPointsItem->SetEndPointsYMovable(true); this->ColorTransferControlPointsItem->SetEndPointsRemovable(false); this->ColorTransferControlPointsItem->SelectableOff(); this->ColorTransferFunctionChart->AddPlot( this->ColorTransferFunctionItem.Get()); this->ColorTransferFunctionChart->SetPlotCorner( this->ColorTransferFunctionItem.Get(), 1); this->ColorTransferFunctionChart->AddPlot( this->ColorTransferControlPointsItem.Get()); this->ColorTransferFunctionChart->SetPlotCorner( this->ColorTransferControlPointsItem.Get(), 1); vtkAxis* bottomAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::BOTTOM); bottomAxis->SetTitle(""); bottomAxis->SetBehavior(vtkAxis::FIXED); bottomAxis->SetVisible(false); bottomAxis->SetRange(0, 255); vtkAxis* leftAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::LEFT); leftAxis->SetTitle(""); leftAxis->SetBehavior(vtkAxis::FIXED); leftAxis->SetVisible(false); vtkAxis* topAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::TOP); topAxis->SetVisible(false); this->AddItem(this->HistogramChart.Get()); this->AddItem(this->ColorTransferFunctionChart.Get()); // Forward events from internal charts to observers of this object this->HistogramChart->AddObserver(vtkCommand::CursorChangedEvent, this->Private, &PIMPL::ForwardEvent); this->ColorTransferControlPointsItem->AddObserver( vtkCommand::EndEvent, this->Private, &PIMPL::ForwardEvent); this->ColorTransferControlPointsItem->AddObserver( vtkControlPointsItem::CurrentPointEditEvent, this->Private, &PIMPL::ForwardEvent); } vtkChartHistogramColorOpacityEditor::~vtkChartHistogramColorOpacityEditor() { delete this->Private; } void vtkChartHistogramColorOpacityEditor::SetHistogramInputData( vtkTable* table, const char* xAxisColumn, const char* yAxisColumn) { this->HistogramChart->SetHistogramInputData(table, xAxisColumn, yAxisColumn); if (!table) { this->ColorTransferFunctionChart->SetVisible(false); return; } if (!this->ColorTransferFunctionChart->GetVisible()) { this->ColorTransferFunctionChart->SetVisible(true); this->ColorTransferFunctionChart->RecalculateBounds(); } // The histogram chart bottom axis range was updated in the call above. // Set the same range for the color bar bottom axis here. vtkAxis* histogramBottomAxis = this->HistogramChart->GetAxis(vtkAxis::BOTTOM); double axisRange[2]; histogramBottomAxis->GetRange(axisRange); vtkAxis* bottomAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::BOTTOM); bottomAxis->SetRange(axisRange); // The data range may change and cause the labels to change. Hence, update // the geometry. this->Private->NeedsUpdate = true; } void vtkChartHistogramColorOpacityEditor::SetColorTransferFunction( vtkColorTransferFunction* ctf) { this->HistogramChart->SetLookupTable(ctf); this->ColorTransferFunctionItem->SetColorTransferFunction(ctf); this->ColorTransferControlPointsItem->SetColorTransferFunction(ctf); this->ColorTransferFunctionChart->RecalculateBounds(); } void vtkChartHistogramColorOpacityEditor::SetScalarVisibility(bool visible) { this->HistogramChart->SetScalarVisibility(visible); } void vtkChartHistogramColorOpacityEditor::SelectColorArray( const char* arrayName) { this->HistogramChart->SelectColorArray(arrayName); } void vtkChartHistogramColorOpacityEditor::SetOpacityFunction( vtkPiecewiseFunction* opacityFunction) { this->HistogramChart->SetOpacityFunction(opacityFunction); } vtkAxis* vtkChartHistogramColorOpacityEditor::GetHistogramAxis(int axis) { return this->HistogramChart->GetAxis(axis); } bool vtkChartHistogramColorOpacityEditor::GetCurrentControlPointColor( double rgb[3]) { vtkColorTransferFunction* ctf = this->ColorTransferControlPointsItem->GetColorTransferFunction(); if (!ctf) { return false; } vtkIdType currentIdx = this->ColorTransferControlPointsItem->GetCurrentPoint(); if (currentIdx < 0) { return false; } double xrgbms[6]; ctf->GetNodeValue(currentIdx, xrgbms); rgb[0] = xrgbms[1]; rgb[1] = xrgbms[2]; rgb[2] = xrgbms[3]; return true; } void vtkChartHistogramColorOpacityEditor::SetCurrentControlPointColor( const double rgb[3]) { vtkColorTransferFunction* ctf = this->ColorTransferControlPointsItem->GetColorTransferFunction(); if (!ctf) { return; } vtkIdType currentIdx = this->ColorTransferControlPointsItem->GetCurrentPoint(); if (currentIdx < 0) { return; } double xrgbms[6]; ctf->GetNodeValue(currentIdx, xrgbms); xrgbms[1] = rgb[0]; xrgbms[2] = rgb[1]; xrgbms[3] = rgb[2]; ctf->SetNodeValue(currentIdx, xrgbms); } double vtkChartHistogramColorOpacityEditor::GetContourValue() { return this->HistogramChart->GetContourValue(); } void vtkChartHistogramColorOpacityEditor::SetDPI(int dpi) { if (this->HistogramChart.Get()) { this->HistogramChart->SetDPI(dpi); } } bool vtkChartHistogramColorOpacityEditor::Paint(vtkContext2D* painter) { vtkContextScene* scene = this->GetScene(); int sceneWidth = scene->GetSceneWidth(); int sceneHeight = scene->GetSceneHeight(); if (this->Private->NeedsUpdate || sceneWidth != this->Private->Geometry.GetX() || sceneHeight != this->Private->Geometry.GetY()) { this->Private->NeedsUpdate = false; // Update the geometry size cache this->Private->Geometry.Set(sceneWidth, sceneHeight); // Upper chart (histogram) expands, lower chart (color bar) is fixed height. float x = this->Borders[vtkAxis::LEFT]; float y = this->Borders[vtkAxis::BOTTOM]; // Add the width of the left axis to x to make room for y labels this->GetHistogramAxis(vtkAxis::LEFT)->Update(); float leftAxisWidth = this->GetHistogramAxis(vtkAxis::LEFT) ->GetBoundingRect(painter) .GetWidth(); x += leftAxisWidth; float colorBarThickness = 20; float plotWidth = sceneWidth - x - this->Borders[vtkAxis::RIGHT]; vtkRectf colorTransferFunctionChartSize(x, y, plotWidth, colorBarThickness); this->ColorTransferFunctionChart->SetSize(colorTransferFunctionChartSize); this->ColorTransferFunctionChart->RecalculateBounds(); float bottomAxisHeight = this->GetHistogramAxis(vtkAxis::BOTTOM) ->GetBoundingRect(painter) .GetHeight(); float verticalMargin = bottomAxisHeight; y += colorBarThickness + verticalMargin - 5; vtkRectf histogramChart(x, y, plotWidth, sceneHeight - y - this->Borders[vtkAxis::TOP]); this->HistogramChart->SetSize(histogramChart); } return this->Superclass::Paint(painter); } avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/vtkChartHistogramColorOpacityEditor.h000066400000000000000000000052061506155467400327170ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef tomvizvtkChartHistogramColorOpacityEditor_h #define tomvizvtkChartHistogramColorOpacityEditor_h #include #include class vtkAxis; class vtkChartHistogram; class vtkChartXY; class vtkColorTransferControlPointsItem; class vtkColorTransferFunction; class vtkColorTransferFunctionItem; class vtkPiecewiseFunction; class vtkScalarsToColors; class vtkTable; // This class is a chart that combines a histogram from a data set // a color bar editor, and an opacity editor. class vtkChartHistogramColorOpacityEditor : public vtkAbstractContextItem { public: vtkTypeMacro( vtkChartHistogramColorOpacityEditor, vtkAbstractContextItem) static vtkChartHistogramColorOpacityEditor* New(); // Set the input data. void SetHistogramInputData(vtkTable* table, const char* xAxisColumn, const char* yAxisColumn); // Set the lookup table. void SetColorTransferFunction(vtkColorTransferFunction* lut); // Enable or disable scalar visibility. virtual void SetScalarVisibility(bool visible); // Set the name of the array by which the histogram should be colored. virtual void SelectColorArray(const char* arrayName); // Set the opacity function. virtual void SetOpacityFunction(vtkPiecewiseFunction* opacityFunction); // Get an axis from the histogram chart. vtkAxis* GetHistogramAxis(int axis); // Get the color of the current color control point. Returns true if there // is a currently selected control point, false otherwise. bool GetCurrentControlPointColor(double rgb[3]); // Set the color of the current color control point. void SetCurrentControlPointColor(const double rgb[3]); // Get the current contour value double GetContourValue(); // Set the DPI void SetDPI(int); // Paint event for the editor. virtual bool Paint(vtkContext2D* painter) override; protected: // This provides the histogram, contour value marker, and opacity editor. vtkNew HistogramChart; // This is used for the color transfer function editor. vtkNew ColorTransferFunctionChart; // Controls for color transfer function editor. vtkNew ColorTransferControlPointsItem; // Display of color transfer function. vtkNew ColorTransferFunctionItem; private: vtkChartHistogramColorOpacityEditor(); ~vtkChartHistogramColorOpacityEditor() override; class PIMPL; PIMPL* Private; float Borders[4]; }; #endif // tomvizvtkChartHistogramColorOpacityEditor_h avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/vtkCustomPiecewiseControlPointsItem.cpp000066400000000000000000000036301506155467400333200ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "vtkCustomPiecewiseControlPointsItem.h" #include #include #include #include vtkStandardNewMacro(vtkCustomPiecewiseControlPointsItem) vtkCustomPiecewiseControlPointsItem::vtkCustomPiecewiseControlPointsItem() { } vtkCustomPiecewiseControlPointsItem::~vtkCustomPiecewiseControlPointsItem() {} bool vtkCustomPiecewiseControlPointsItem::MouseButtonPressEvent( const vtkContextMouseEvent& mouse) { // Ignore middle- and right-click events if (mouse.GetButton() != vtkContextMouseEvent::LEFT_BUTTON) { return false; } vtkVector2f vpos = mouse.GetPos(); this->TransformScreenToData(vpos, vpos); double pos[2]; pos[0] = vpos.GetX(); pos[1] = vpos.GetY(); bool pointOnFunction = this->PointNearPiecewiseFunction(pos); if (!pointOnFunction) { this->SetCurrentPoint(-1); return false; } return this->Superclass::MouseButtonPressEvent(mouse); } bool vtkCustomPiecewiseControlPointsItem::MouseDoubleClickEvent( const vtkContextMouseEvent& mouse) { // Ignore middle- and right-click events if (mouse.GetButton() != vtkContextMouseEvent::LEFT_BUTTON) { return false; } return this->Superclass::MouseDoubleClickEvent(mouse); } bool vtkCustomPiecewiseControlPointsItem::PointNearPiecewiseFunction( const double position[2]) { double x = position[0]; double y = 0.0; vtkPiecewiseFunction* pwf = this->GetPiecewiseFunction(); if (!pwf) { return false; } // Evaluate the piewewise function at the given point and get the y position. // If we are within a small distance of the piecewise function, return true. // Otherwise, we are too far away from the line, and return false. pwf->GetTable(x, x, 1, &y, 1); return (fabs(y - position[1]) < 0.05); } avogadrolibs-1.101.0/avogadro/qtplugins/coloropacitymap/vtkCustomPiecewiseControlPointsItem.h000066400000000000000000000026411506155467400327660ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #ifndef tomvizvtkCustomPiecewiseControlPointsItem_h #define tomvizvtkCustomPiecewiseControlPointsItem_h #include class vtkContextMouseEvent; // Special control points item class that overrides the MouseDoubleClickEvent() // event handler to do nothing. class vtkCustomPiecewiseControlPointsItem : public vtkPiecewiseControlPointsItem { public: vtkTypeMacro( vtkCustomPiecewiseControlPointsItem, vtkPiecewiseControlPointsItem) static vtkCustomPiecewiseControlPointsItem* New(); // Override to ignore button presses if the control modifier key is pressed. bool MouseButtonPressEvent(const vtkContextMouseEvent& mouse) override; // Override to avoid catching double-click events bool MouseDoubleClickEvent(const vtkContextMouseEvent& mouse) override; protected: vtkCustomPiecewiseControlPointsItem(); virtual ~vtkCustomPiecewiseControlPointsItem(); // Utility function to determine whether a position is near the piecewise // function. bool PointNearPiecewiseFunction(const double pos[2]); private: vtkCustomPiecewiseControlPointsItem( const vtkCustomPiecewiseControlPointsItem&); // Not implemented. void operator=( const vtkCustomPiecewiseControlPointsItem&); // Not implemented. }; #endif // tomvizvtkCustomPiecewiseControlPointsItem_h avogadrolibs-1.101.0/avogadro/qtplugins/commandscripts/000077500000000000000000000000001506155467400232215ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/commandscripts/CMakeLists.txt000066400000000000000000000005001506155467400257540ustar00rootroot00000000000000# Extension set(command_srcs command.cpp ) avogadro_plugin(commands "Script commands" ExtensionPlugin command.h Command "${command_srcs}" ) target_link_libraries(commands PRIVATE Avogadro::IO) # We no longer bundle scripts because they should be downloaded separately. # This also helps base translations.avogadrolibs-1.101.0/avogadro/qtplugins/commandscripts/command.cpp000066400000000000000000000234531506155467400253520ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "command.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Avogadro::QtGui::InterfaceScript; using Avogadro::QtGui::InterfaceWidget; Command::Command(QObject* parent_) : ExtensionPlugin(parent_), m_molecule(nullptr), m_currentDialog(nullptr), m_currentInterface(nullptr), m_currentScript(nullptr), m_progress(nullptr), m_outputFormat(nullptr) { refreshScripts(); } Command::~Command() { qDeleteAll(m_dialogs.values()); m_dialogs.clear(); } QList Command::actions() const { return m_actions; } QStringList Command::menuPath(QAction* action) const { QString scriptFileName = action->data().toString(); QStringList path; // if we're passed the "Set Python" action if (scriptFileName.isEmpty()) { path << tr("&Extensions") << tr("Scripts"); return path; } // cache the menu paths QSettings settings; QFileInfo info(scriptFileName); // check if the script matches the hash QString hash = settings.value("scripts/" + scriptFileName + "/hash").toString(); if (hash == QString::number(info.size()) + info.lastModified().toString()) { path = settings.value("scripts/" + scriptFileName + "/menu").toStringList(); if (!path.isEmpty()) return path; } // otherwise, we have a script name, so ask it InterfaceScript gen(scriptFileName); path = gen.menuPath().split('|'); if (gen.hasErrors()) { path << tr("&Extensions") << tr("Scripts"); qWarning() << "Command: Unable to retrieve menu " "name for: " << scriptFileName << "." << gen.errorList().join("\n\n"); return path; } // look for {number} in the last part of the path // (this is a priority integer) QString lastPart = path.takeLast(); int priority = 0; int braceIndex = lastPart.indexOf('{'); int endBraceIndex = lastPart.indexOf('}'); if (braceIndex >= 0 && endBraceIndex >= 0 && endBraceIndex > braceIndex) { bool ok = false; size_t len = endBraceIndex - braceIndex - 1; priority = lastPart.mid(braceIndex + 1, len).toInt(&ok); if (ok) { lastPart = lastPart.left(braceIndex); } } // add it back to the path path << lastPart; // cache the path settings.setValue("scripts/" + scriptFileName + "/menu", path); if (priority != 0) { action->setProperty("menu priority", priority); } // try to translate each part of the path // not ideal, but most menus should already be in the translation file QStringList translatedPath; foreach (QString part, path) translatedPath << tr(part.toUtf8()); return translatedPath; } void Command::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; m_molecule = mol; foreach (InterfaceWidget* dlg, m_dialogs.values()) dlg->setMolecule(mol); } bool Command::readMolecule(QtGui::Molecule& mol) { Io::FileFormat* reader = m_outputFormat->newInstance(); bool success = reader->readFile(m_outputFileName.toStdString(), mol); if (!success) { QMessageBox::information(qobject_cast(parent()), tr("Error"), tr("Error reading output file '%1':\n%2") .arg(m_outputFileName) .arg(QString::fromStdString(reader->error()))); } m_outputFormat = nullptr; m_outputFileName.clear(); return success; } void Command::refreshScripts() { updateScripts(); updateActions(); } void Command::menuActivated() { auto* theSender = qobject_cast(sender()); if (!theSender) return; QString scriptFileName = theSender->data().toString(); QWidget* theParent = qobject_cast(parent()); if (m_currentDialog) { delete m_currentDialog->layout(); if (m_currentInterface) m_currentInterface->hide(); } // check if there are any options before this song-and-dance InterfaceWidget* widget = m_dialogs.value(scriptFileName, nullptr); if (!widget) { widget = new InterfaceWidget(scriptFileName, theParent); m_dialogs.insert(scriptFileName, widget); } widget->setMolecule(m_molecule); m_currentInterface = widget; // remember this when we get the run() signal if (widget->isEmpty()) { run(); // no options, do it immediately return; } m_currentDialog = new QDialog(theParent); QString title; QtGui::ScriptLoader::queryProgramName(scriptFileName, title); m_currentDialog->setWindowTitle(title); auto* vbox = new QVBoxLayout(); widget->show(); vbox->addWidget(widget); auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(run())); connect(buttonBox, SIGNAL(rejected()), m_currentDialog, SLOT(reject())); vbox->addWidget(buttonBox); m_currentDialog->setLayout(vbox); m_currentDialog->exec(); } void Command::run() { if (m_currentDialog) m_currentDialog->accept(); if (m_progress) m_progress->deleteLater(); if (m_currentScript) { disconnect(m_currentScript, SIGNAL(finished()), this, SLOT(processFinished())); m_currentScript->deleteLater(); } if (m_currentInterface) { QJsonObject options = m_currentInterface->collectOptions(); // @todo - need a cleaner way to get a script pointer from the widget QString scriptFilePath = m_currentInterface->interfaceScript().scriptFilePath(); m_currentScript = new InterfaceScript(scriptFilePath, parent()); connect(m_currentScript, SIGNAL(finished()), this, SLOT(processFinished())); // no cancel button - just an indication we're waiting... QString title = tr("Processing %1").arg(m_currentScript->displayName()); m_progress = new QProgressDialog(title, QString(), 0, 0, qobject_cast(parent())); m_progress->setMinimumDuration(1000); // 1 second m_currentScript->runCommand(options, m_molecule); } } void Command::processFinished() { if (m_currentScript == nullptr) return; if (m_progress) { m_progress->close(); m_progress->deleteLater(); m_progress = nullptr; } m_currentScript->processCommand(m_molecule); // collect errors if (m_currentScript->hasErrors()) { qWarning() << m_currentScript->errorList(); } } void Command::configurePython() { // Create objects QSettings settings; QDialog dlg(qobject_cast(parent())); auto* label = new QLabel; auto* layout = new QVBoxLayout; auto* browser = new QtGui::FileBrowseWidget; auto* buttonBox = new QDialogButtonBox; // Configure objects // Check for python interpreter in env var QString pythonInterp = QString::fromLocal8Bit(qgetenv("AVO_PYTHON_INTERPRETER")); if (pythonInterp.isEmpty()) { // Check settings pythonInterp = settings.value("interpreters/python", QString()).toString(); } // Use compile-time default if still not found. if (pythonInterp.isEmpty()) pythonInterp = QString(pythonInterpreterPath); browser->setMode(QtGui::FileBrowseWidget::ExecutableFile); browser->setFileName(pythonInterp); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); dlg.setWindowTitle(tr("Set path to Python interpreter:")); label->setText( tr("Select the python interpreter to run external scripts.\n" "Avogadro must be restarted for any changes to take effect.")); // Build layout layout->addWidget(label); layout->addWidget(browser); layout->addWidget(buttonBox); dlg.setLayout(layout); // Connect connect(buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject())); // Show dialog auto response = static_cast(dlg.exec()); if (response != QDialog::Accepted) return; // Handle response settings.setValue("interpreters/python", browser->fileName()); } void Command::updateScripts() { m_commandScripts = QtGui::ScriptLoader::scriptList("commands"); } void Command::updateActions() { m_actions.clear(); // QAction* action = new QAction(tr("Set Python Path…"), this); // connect(action, SIGNAL(triggered()), SLOT(configurePython())); // m_actions << action; foreach (const QString& programName, m_commandScripts.uniqueKeys()) { QStringList scripts = m_commandScripts.values(programName); // Include the full path if there are multiple generators with the same // name. if (scripts.size() == 1) { addAction(programName, scripts.first()); } else { foreach (const QString& filePath, scripts) { addAction(QString("%1 (%2)").arg(programName, filePath), filePath); } } } } void Command::addAction(const QString& label, const QString& scriptFilePath) { auto* action = new QAction(tr(label.toUtf8()), this); action->setData(scriptFilePath); action->setEnabled(true); connect(action, SIGNAL(triggered()), SLOT(menuActivated())); m_actions << action; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/commandscripts/command.h000066400000000000000000000042461506155467400250160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_COMMAND_H #define AVOGADRO_QTPLUGINS_COMMAND_H #include #include #include class QAction; class QDialog; class QProgressDialog; namespace Avogadro { namespace Io { class FileFormat; } namespace QtGui { class InterfaceScript; class InterfaceWidget; } // namespace QtGui namespace QtPlugins { /** * @brief The Command class implements the extension interface for * external (script) Commands * @author Geoffrey R. Hutchison */ class Command : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Command(QObject* parent = nullptr); ~Command() override; QString name() const override { return tr("Command scripts"); } QString description() const override { return tr("Run external script commands"); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; public slots: /** * Scan for new scripts in the command directories. */ void refreshScripts(); void run(); bool readMolecule(QtGui::Molecule& mol) override; void processFinished(); private slots: void menuActivated(); void configurePython(); private: void updateScripts(); void updateActions(); void addAction(const QString& label, const QString& scriptFilePath); QList m_actions; QtGui::Molecule* m_molecule; // keyed on script file path QMap m_dialogs; QDialog* m_currentDialog; QtGui::InterfaceWidget* m_currentInterface; QtGui::InterfaceScript* m_currentScript; QProgressDialog* m_progress; // maps program name --> script file path QMultiMap m_commandScripts; const Io::FileFormat* m_outputFormat; QString m_outputFileName; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_COMMAND_H avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/000077500000000000000000000000001506155467400234165ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/CMakeLists.txt000066400000000000000000000003531506155467400261570ustar00rootroot00000000000000avogadro_plugin(ConfigurePython "Configure Python environments." ExtensionPlugin configurepython.h ConfigurePython "condadialog.cpp;configurepython.cpp;configurepythondialog.cpp" "condadialog.ui;configurepythondialog.ui" ) avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/condadialog.cpp000066400000000000000000000012401506155467400263630ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "condadialog.h" #include "ui_condadialog.h" namespace Avogadro::QtPlugins { CondaDialog::CondaDialog(QWidget* aParent) : QDialog(aParent), m_ui(new Ui::CondaDialog) { m_ui->setupUi(this); } CondaDialog::~CondaDialog() { delete m_ui; } QString CondaDialog::environmentName() const { return m_ui->environmentName->text(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/condadialog.h000066400000000000000000000015311506155467400260330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CONDADIALOG_H #define AVOGADRO_QTPLUGINS_CONDADIALOG_H #include namespace Avogadro { namespace QtPlugins { namespace Ui { class CondaDialog; } /** * @brief Dialog to prompt a format and descriptor string. */ class CondaDialog : public QDialog { Q_OBJECT public: explicit CondaDialog(QWidget* parent = nullptr); ~CondaDialog() override; QString environmentName() const; private: Ui::CondaDialog* m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CONDADIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/condadialog.ui000066400000000000000000000052771506155467400262340ustar00rootroot00000000000000 Avogadro::QtPlugins::CondaDialog 0 0 376 169 0 0 Python Settings… Only the “base” conda environment exists. Would you like to create a new environment for Avogadro? This will make a copy of your base environment. Environment name: avogadro Qt::Vertical 20 0 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() Avogadro::QtPlugins::CondaDialog accept() 187 148 187 84 buttonBox rejected() Avogadro::QtPlugins::CondaDialog reject() 187 148 187 84 avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/configurepython.cpp000066400000000000000000000170031506155467400273460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "configurepython.h" #include "configurepythondialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using QtGui::FileBrowseWidget; using QtGui::Utilities::findExecutablePaths; ConfigurePython::ConfigurePython(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_action(new QAction(this)), m_dialog(nullptr) { m_action->setEnabled(true); m_action->setText(tr("Python Settings…")); m_action->setProperty("menu priority", 510); connect(m_action, SIGNAL(triggered()), SLOT(showDialog())); // check for Python on first launch QStringList paths = pythonPaths(); QSettings settings; if (paths.isEmpty()) { // show a warning if (settings.contains("interpreters/firstlaunch")) return; // the user ignored the warning // suggest the user install Python auto option = QMessageBox::information( qobject_cast(parent()), tr("Install Python"), tr("Python is used for many Avogadro " "features. Do you want to download Python?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (option == QMessageBox::Yes) { // QUrl miniforge; #ifdef Q_OS_WIN // TODO: ARM or Intel? .. but conda-forge doesn't have ARM builds yet miniforge = QUrl("https://github.com/conda-forge/miniforge/releases/" "latest/download/Miniforge3-Windows-x86_64.exe"); #elif defined(Q_OS_MACOS) // ARM or Intel? if (QSysInfo::currentCpuArchitecture().contains("arm")) miniforge = QUrl("https://github.com/conda-forge/miniforge/releases/" "latest/download/Miniforge3-MacOSX-arm64.sh"); else miniforge = QUrl("https://github.com/conda-forge/miniforge/releases/" "latest/download/Miniforge3-MacOSX-x86_64.sh"); #else QString arch = QSysInfo::currentCpuArchitecture(); if (arch.contains("arm")) miniforge = QUrl("https://github.com/conda-forge/miniforge/releases/" "latest/download/Miniforge3-Linux-aarch64.sh"); else if (arch.contains("ppc")) miniforge = QUrl("https://github.com/conda-forge/miniforge/releases/" "latest/download/Miniforge3-Linux-ppc64le.sh"); else miniforge = QUrl("https://github.com/conda-forge/miniforge/releases/" "latest/download/Miniforge3-Linux-x86_64.sh"); #endif if (miniforge.isValid()) { QDesktopServices::openUrl(miniforge); // open install instructions QDesktopServices::openUrl(QUrl("https://github.com/conda-forge/" "miniforge?tab=readme-ov-file#install")); } } settings.setValue("interpreters/firstlaunch", true); } } ConfigurePython::~ConfigurePython() { delete m_action; } QList ConfigurePython::actions() const { return QList() << m_action; } QStringList ConfigurePython::menuPath(QAction*) const { return QStringList() << tr("&Extensions"); } void ConfigurePython::accept() { if (m_dialog == nullptr) return; // Save the settings QSettings settings; settings.setValue("interpreters/python", m_dialog->currentOption()); // check if the dialog has a conda environment selected if (!m_dialog->condaEnvironment().isEmpty()) { settings.setValue("interpreters/condaEnvironment", m_dialog->condaEnvironment()); // get the path to conda QString condaPath = m_dialog->condaPath(); if (!condaPath.isEmpty()) { settings.setValue("interpreters/condaPath", condaPath); } } // TODO: reload the python interpreters } QStringList ConfigurePython::pythonPaths() const { // Check for python interpreter in env var QString pythonInterp = QString::fromLocal8Bit(qgetenv("AVO_PYTHON_INTERPRETER")); if (pythonInterp.isEmpty()) { // Check settings QSettings settings; pythonInterp = settings.value("interpreters/python", QString()).toString(); } // Use compile-time default if still not found. if (pythonInterp.isEmpty()) pythonInterp = QString(pythonInterpreterPath); // get the list from the system path QStringList names; #ifdef Q_OS_WIN names << "python3.exe" << "python.exe"; #else names << "python3" << "python"; #endif QStringList paths = findExecutablePaths(names); // Add the current interpreter to the list // it may be filtered out by the loop below if (!paths.contains(pythonInterp)) { paths.prepend(pythonInterp); } #ifdef Q_OS_WIN // on Windows, check for a few possible locations // if they exist, add them to the list // e.g. C:\Program Files\Python* // C:\Program Files (x86)\Python* QStringList programDirs; programDirs << "C:/Program Files" << "C:/Program Files (x86)"; // might also be in the APPDATA folder QString homePath = QDir::homePath(); if (!homePath.isEmpty()) { programDirs << homePath + "/AppData/Local/Programs"; programDirs << homePath + "/AppData/Local/Programs/Python"; } foreach (const QString& dir, programDirs) { QDir programFiles(dir); QStringList pythonDirs = programFiles.entryList( QStringList() << "Python*", QDir::Dirs | QDir::NoDotAndDotDot); // check if there's a python3.exe or python.exe foreach (const QString& pythonDir, pythonDirs) { QDir pythonDirInfo(dir + "/" + pythonDir); if (pythonDirInfo.exists("python3.exe")) paths << pythonDirInfo.absolutePath() + "/python3.exe"; else if (pythonDirInfo.exists("python.exe")) paths << pythonDirInfo.absolutePath() + "/python.exe"; } } #endif // check to make sure each of the items are valid or remove them // (i.e., the python should return a version flag) QStringList validPaths; QStringList arguments; arguments << "-V"; foreach (const QString& path, paths) { QFileInfo info(path); if (info.exists() && info.isExecutable()) { // try to run it to get the version QProcess process; process.start(path, arguments); if (process.waitForFinished()) { QString output = process.readAllStandardOutput(); // should be like Python 3.10.14 if (output.startsWith("Python")) { QString version = output.split(" ").at(1).simplified(); // make sure it's at least Python 3 // in the future, we can ensure particular releases if (version.startsWith("3")) validPaths << path; } } // if we didn't get results, it's not valid } } return validPaths; } void ConfigurePython::showDialog() { if (m_dialog == nullptr) { m_dialog = new ConfigurePythonDialog(qobject_cast(parent())); connect(m_dialog, SIGNAL(accepted()), SLOT(accept())); } // Populate the dialog with the current settings QStringList pythonInterps = pythonPaths(); m_dialog->setOptions(pythonInterps); m_dialog->show(); m_dialog->raise(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/configurepython.h000066400000000000000000000024151506155467400270140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CONFIGUREPYTHON_H #define AVOGADRO_QTPLUGINS_CONFIGUREPYTHON_H #include namespace Avogadro { namespace QtPlugins { class ConfigurePythonDialog; /** * @brief Configure Python environment through a dialog. */ class ConfigurePython : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit ConfigurePython(QObject* parent_ = nullptr); ~ConfigurePython() override; QString name() const override { return tr("ConfigurePython"); } QString description() const override { return tr("Configure Python environments."); } QList actions() const override; QStringList menuPath(QAction*) const override; QStringList pythonPaths() const; void setMolecule(QtGui::Molecule*) override {} private slots: void showDialog(); void accept(); private: QAction* m_action; ConfigurePythonDialog* m_dialog; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CONFIGUREPYTHON_H avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/configurepythondialog.cpp000066400000000000000000000204111506155467400305230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "configurepythondialog.h" #include "condadialog.h" #include "ui_configurepythondialog.h" #include #include #include #include #include using Avogadro::QtGui::Utilities::findExecutablePaths; namespace Avogadro::QtPlugins { ConfigurePythonDialog::ConfigurePythonDialog(QWidget* aParent) : QDialog(aParent), m_ui(new Ui::ConfigurePythonDialog), m_condaUi(nullptr) { m_ui->setupUi(this); m_ui->browseWidget->hide(); connect(m_ui->environmentCombo, SIGNAL(currentIndexChanged(int)), SLOT(optionChanged(int))); connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(accept())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); // look for conda environments QProcess condaProcess; QSettings settings; QString condaPath = settings.value("interpreters/condaPath", "conda").toString(); // check if conda is executable if (!QFileInfo(condaPath).isExecutable()) { // see if we can find any related executables in the path QStringList names; names << "micromamba" << "mamba" << "conda"; #ifdef Q_OS_WIN names << "micromamba.exe" << "mamba.exe" << "conda.exe"; #endif QStringList paths = findExecutablePaths(names); if (!paths.isEmpty()) { condaPath = paths.first(); } else return; // nothing more to do } // set the path to conda settings.setValue("interpreters/condaPath", condaPath); // get the list of environments condaProcess.start(condaPath, QStringList() << "env" << "list"); if (condaProcess.waitForFinished()) { QString output = condaProcess.readAllStandardOutput(); QStringList lines = output.split("\n"); foreach (const QString& line, lines) { if (line.startsWith("#")) continue; QStringList parts = line.split(" "); if (parts.size() > 1) m_condaEnvironments << parts.at(0); } } if (m_condaEnvironments.size() < 2) { // no environment or only the base found setupCondaEnvironment(); } } ConfigurePythonDialog::~ConfigurePythonDialog() { delete m_ui; } void ConfigurePythonDialog::setupCondaEnvironment() { // suggest the user create a new environment through a dialog if (m_condaUi == nullptr) { m_condaUi = new CondaDialog(qobject_cast(parent())); } int choice = m_condaUi->exec(); if (choice == QDialog::Rejected) return; QString newEnvironment = m_condaUi->environmentName(); if (newEnvironment.isEmpty()) return; // create the environment QProcess condaProcess; QSettings settings; QString condaPath = settings.value("interpreters/condaPath", "conda").toString(); // check if conda is executable if (!QFileInfo(condaPath).isExecutable()) { // see if we can find any related executables in the path QStringList names; names << "micromamba" << "mamba" << "conda"; #ifdef Q_OS_WIN names << "micromamba.exe" << "mamba.exe" << "conda.exe"; #endif QStringList paths = findExecutablePaths(names); if (!paths.isEmpty()) { condaPath = paths.first(); } else return; // nothing more to do } QStringList arguments; arguments << "create" << "-n" << newEnvironment << "--clone" << "base"; condaProcess.start(condaPath, arguments); if (condaProcess.waitForFinished()) { QString output = condaProcess.readAllStandardOutput(); if (output.contains("done")) { // environment created m_condaEnvironments << newEnvironment; settings.setValue("interpreters/condaEnvironment", newEnvironment); } } } QString ConfigurePythonDialog::condaPath() const { QSettings settings; QString path = settings.value("interpreters/condaPath").toString(); return path; } QString ConfigurePythonDialog::condaEnvironment() const { QSettings settings; QString environment = settings.value("interpreters/condaEnvironment").toString(); return environment; } void ConfigurePythonDialog::setOptions(const QStringList& options) { m_ui->environmentCombo->clear(); // check the current choice from QSettings QSettings settings; QString currentInterpreter = settings.value("interpreters/python", QString()).toString(); QString currentConda = settings.value("interpreters/condaEnvironment", QString()).toString(); int index = -1; // add all conda environments foreach (const QString& environment, m_condaEnvironments) { if (environment.isEmpty()) continue; // shouldn't happen, but just in case m_ui->environmentCombo->addItem(QString("%1 (conda)").arg(environment)); if (environment == currentConda) index = m_ui->environmentCombo->count() - 1; } // get the Python version from each interpreter QStringList versions, arguments; QProcess process; arguments << "-V"; foreach (const QString& option, options) { process.start(option, arguments); if (process.waitForFinished()) { QString output = process.readAllStandardOutput(); if (output.startsWith("Python")) { versions << output.split(" ").at(1).simplified(); } else { versions << tr("Unknown"); } } else { versions << tr("Unknown"); } } for (int i = 0; i < options.size(); ++i) { m_ui->environmentCombo->addItem( QString("%1 (%2)").arg(options.at(i)).arg(versions.at(i))); // if the conda environment isn't the current, check the python interpreter if (options.at(i) == currentInterpreter && index == -1) index = m_ui->environmentCombo->count() - 1; } m_ui->environmentCombo->addItem(tr("Other…")); // set the current choice if (index >= 0) m_ui->environmentCombo->setCurrentIndex(index); m_ui->browseWidget->hide(); } void ConfigurePythonDialog::optionChanged(int index) { // if there's only one choice, check if it's "Other…" if ((index == m_ui->environmentCombo->count() - 1) || m_ui->environmentCombo->currentText() == tr("Other…")) { // "Other…" selected m_ui->browseWidget->setEnabled(true); m_ui->browseWidget->show(); } else { m_ui->browseWidget->setEnabled(false); m_ui->browseWidget->hide(); } } QString ConfigurePythonDialog::currentOption() const { if (m_ui->environmentCombo->currentIndex() == m_ui->environmentCombo->count() - 1) return m_ui->browseWidget->fileName(); QString path = m_ui->environmentCombo->currentText(); // check if this is a conda choice int index = path.indexOf(" (conda)"); if (index >= 0) { // get the environment name QString environment = path.left(index); QSettings settings; settings.setValue("interpreters/condaEnvironment", environment); // activate the environment and get the path to the python interpreter QProcess condaProcess; QString condaPath = settings.value("interpreters/condaPath", "conda").toString(); // check if conda is executable if (!QFileInfo(condaPath).isExecutable()) return QString(); condaProcess.start(condaPath, QStringList() << "run" << "-n" << environment << "which" << "python"); if (condaProcess.waitForFinished()) { QString output = condaProcess.readAllStandardOutput(); qDebug() << " output: " << output << "\n"; if (output.contains("python")) { // remove the newline output.remove("\n"); return output; } } } // remove the Python version to get the path index = path.indexOf(" ("); if (index >= 0) return path.left(index); return path; } void ConfigurePythonDialog::reject() { QDialog::reject(); emit rejected(); } void ConfigurePythonDialog::accept() { QDialog::accept(); emit accepted(); } void ConfigurePythonDialog::setCurrentOption(const QString& option) { int index = m_ui->environmentCombo->findText(option); if (index >= 0) m_ui->environmentCombo->setCurrentIndex(index); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/configurepythondialog.h000066400000000000000000000025211506155467400301720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CONFIGUREPYTHONDIALOG_H #define AVOGADRO_QTPLUGINS_CONFIGUREPYTHONDIALOG_H #include namespace Avogadro { namespace QtPlugins { class CondaDialog; namespace Ui { class ConfigurePythonDialog; } /** * @brief Dialog to prompt a format and descriptor string. */ class ConfigurePythonDialog : public QDialog { Q_OBJECT public: explicit ConfigurePythonDialog(QWidget* parent = nullptr); ~ConfigurePythonDialog() override; void setupCondaEnvironment(); void setOptions(const QStringList& options); void setCurrentOption(const QString& option); QString currentOption() const; QString condaPath() const; QString condaEnvironment() const; signals: void accepted(); void rejected(); protected slots: void optionChanged(int index); void accept() override; void reject() override; private: Ui::ConfigurePythonDialog* m_ui; CondaDialog* m_condaUi; QStringList m_condaEnvironments; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CONFIGUREPYTHONDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/configurepython/configurepythondialog.ui000066400000000000000000000064721506155467400303710ustar00rootroot00000000000000 Avogadro::QtPlugins::ConfigurePythonDialog 0 0 376 166 0 0 Python Settings… Select the Python version used to run scripts. Avogadro must be restarted for any changes to take effect. Environment: Qt::Horizontal 40 20 false Qt::Vertical 20 0 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok QtGui::FileBrowseWidget QWidget
avogadro/qtgui/filebrowsewidget.h
1
buttonBox accepted() Avogadro::QtPlugins::ConfigurePythonDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::QtPlugins::ConfigurePythonDialog reject() 316 260 286 274
avogadrolibs-1.101.0/avogadro/qtplugins/constraints/000077500000000000000000000000001506155467400225425ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/constraints/CMakeLists.txt000066400000000000000000000005461506155467400253070ustar00rootroot00000000000000 set(constraints constraintsextension.cpp constraintsdialog.cpp constraintsmodel.cpp ) set(constraints_uis constraintsdialog.ui ) avogadro_plugin(ConstraintsExtension "Constraints extension" ExtensionPlugin constraintsextension.h ConstraintsExtension "${constraints}" "${constraints_uis}" ) target_link_libraries(ConstraintsExtension) avogadrolibs-1.101.0/avogadro/qtplugins/constraints/constraintsdialog.cpp000066400000000000000000000152461506155467400270050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "constraintsdialog.h" #include "ui_constraintsdialog.h" #include #include #include using Avogadro::Core::Constraint; using Avogadro::QtGui::Molecule; namespace Avogadro { namespace QtPlugins { ConstraintsDialog::ConstraintsDialog(QWidget* parent_, Qt::WindowFlags f) : QDialog(parent_, f), ui(new Ui::ConstraintsDialog), m_model(new ConstraintsModel) { ui->setupUi(this); connect(ui->comboType, SIGNAL(currentIndexChanged(int)), this, SLOT(changeType(int))); connect(ui->okButton, SIGNAL(clicked()), this, SLOT(acceptConstraints())); connect(ui->addConstraint, SIGNAL(clicked()), this, SLOT(addConstraint())); connect(ui->deleteConstraint, SIGNAL(clicked()), this, SLOT(deleteConstraint())); connect(ui->deleteAllConstraints, SIGNAL(clicked()), this, SLOT(deleteAllConstraints())); changeType(0); // TODO use sort model auto* proxyModel = new QSortFilterProxyModel(this); proxyModel->setSourceModel(m_model); proxyModel->setDynamicSortFilter(true); proxyModel->setSortLocaleAware(true); // this role will received direct floating-point numbers from the model proxyModel->setSortRole(Qt::UserRole); auto* view = ui->constraintsTableView; view->setModel(proxyModel); view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); view->resizeColumnsToContents(); view->setSelectionBehavior(QAbstractItemView::SelectRows); view->setAlternatingRowColors(true); // TODO: Allow sorting the table // requires remapping connect(view->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(highlightSelected(const QModelIndex&, const QModelIndex&))); } ConstraintsDialog::~ConstraintsDialog() { delete ui; m_model->deleteLater(); } void ConstraintsDialog::setMolecule(QtGui::Molecule* molecule) { m_molecule = molecule; m_model->setConstraints(molecule->constraints()); connect(m_molecule, SIGNAL(changed(unsigned int)), this, SLOT(updateConstraints())); } void ConstraintsDialog::highlightSelected(const QModelIndex& newIndex, const QModelIndex& oldIndex) { // get the selected row in the table auto row = ui->constraintsTableView->currentIndex().row(); if (row < 0 || row >= m_model->rowCount()) return; // get the constraint auto constraint = m_model->constraint(row); if (constraint.type() == Constraint::None) return; // unselect everything else in the molecule for (Index i = 0; i < m_molecule->atomCount(); ++i) m_molecule->undoMolecule()->setAtomSelected(i, false); // select the atoms in the constraint m_molecule->undoMolecule()->setAtomSelected(constraint.aIndex(), true); m_molecule->undoMolecule()->setAtomSelected(constraint.bIndex(), true); if (constraint.cIndex() != MaxIndex) m_molecule->undoMolecule()->setAtomSelected(constraint.cIndex(), true); if (constraint.dIndex() != MaxIndex) m_molecule->undoMolecule()->setAtomSelected(constraint.dIndex(), true); m_molecule->emitChanged(Molecule::Selection); } void ConstraintsDialog::updateConstraints() { if (m_molecule == nullptr || m_model == nullptr) return; m_model->setConstraints(m_molecule->constraints()); ui->constraintsTableView->resizeColumnsToContents(); // update the maximum atoms on the spin boxes Index maxAtom = m_molecule->atomCount() - 1; ui->editA->setMaximum(maxAtom); ui->editB->setMaximum(maxAtom); ui->editC->setMaximum(maxAtom); ui->editD->setMaximum(maxAtom); } void ConstraintsDialog::changeType(int newType) { // disable B-C-D and enable what we need ui->editB->setEnabled(false); ui->editC->setEnabled(false); ui->editD->setEnabled(false); ui->editB->setMinimum(0); ui->editC->setMinimum(0); ui->editD->setMinimum(0); switch (newType) { case 2: // torsion ui->editD->setEnabled(true); ui->editD->setMinimum(1); case 1: // angle ui->editC->setEnabled(true); ui->editC->setMinimum(1); case 0: // distance ui->editB->setEnabled(true); ui->editB->setMinimum(1); } if (newType == 0) ui->editValue->setSuffix("Å"); else ui->editValue->setSuffix("°"); } void ConstraintsDialog::acceptConstraints() { hide(); } void ConstraintsDialog::deleteConstraint() { if (m_molecule == nullptr || m_molecule == nullptr) return; auto row = ui->constraintsTableView->currentIndex().row(); m_model->deleteConstraint(row); // get the new constraints m_molecule->setConstraints(m_model->constraints()); m_molecule->emitChanged(Molecule::Constraints); } void ConstraintsDialog::addConstraint() { if (m_molecule == nullptr) return; // TODO: Check user input for sanity Constraint::Type type; switch (ui->comboType->currentIndex()) { case 1: type = Constraint::AngleConstraint; break; case 2: type = Constraint::TorsionConstraint; break; case 0: default: type = Constraint::DistanceConstraint; break; } double value = ui->editValue->value(); int atomIdA = ui->editA->value(); int atomIdB = ui->editB->value(); int atomIdC = ui->editC->value(); int atomIdD = ui->editD->value(); Index a, b, c, d; if (atomIdA < 1 || atomIdA > m_molecule->atomCount()) return; else a = atomIdA - 1; if (atomIdB < 1 || atomIdB > m_molecule->atomCount()) return; else b = atomIdB - 1; if (atomIdC < 1 || atomIdC > m_molecule->atomCount()) c = MaxIndex; else c = atomIdC - 1; if (atomIdD < 1 || atomIdD > m_molecule->atomCount()) d = MaxIndex; else d = atomIdD - 1; if (type == Constraint::DistanceConstraint) { if (a == b || value == 0.0) return; } else if (type == Constraint::AngleConstraint) { if (a == b || b == c) return; } else if (type == Constraint::TorsionConstraint) if (a == b || a == c || a == d || b == c || b == d || c == d) return; Constraint newConstraint(a, b, c, d, value); newConstraint.setType(type); m_molecule->addConstraint(newConstraint); m_model->addConstraint(type, a, b, c, d, value); m_molecule->emitChanged(Molecule::Constraints); } void ConstraintsDialog::deleteAllConstraints() { if (m_molecule == nullptr) return; m_molecule->clearConstraints(); m_molecule->emitChanged(Molecule::Constraints); // update the model m_model->clear(); } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/constraints/constraintsdialog.h000066400000000000000000000024161506155467400264450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CONSTRAINTSDIALOG_H #define AVOGADRO_QTPLUGINS_CONSTRAINTSDIALOG_H #include "constraintsmodel.h" #include namespace Avogadro { namespace QtPlugins { namespace Ui { class ConstraintsDialog; } class ConstraintsDialog : public QDialog { Q_OBJECT public: explicit ConstraintsDialog(QWidget* parent_ = 0, Qt::WindowFlags f = Qt::WindowFlags()); ~ConstraintsDialog() override; void setMolecule(QtGui::Molecule* molecule); public slots: void acceptConstraints(); void addConstraint(); void deleteConstraint(); void deleteAllConstraints(); void highlightSelected(const QModelIndex& newIndex, const QModelIndex& oldIndex); void changeType(int type); void updateConstraints(); private: Ui::ConstraintsDialog* ui; ConstraintsModel* m_model; QtGui::Molecule* m_molecule = nullptr; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CONSTRAINTSDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/constraints/constraintsdialog.ui000066400000000000000000000141721506155467400266350ustar00rootroot00000000000000 Avogadro::QtPlugins::ConstraintsDialog 0 0 441 446 Constraints Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustIgnored QAbstractItemView::SelectRows Add Constraints 40 16777215 Type: 0 Distance Angle Torsion Angle Atom Indices: 999 999 999 999 Value: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Å 3 105.989999999999995 Add Qt::Vertical 20 40 Options Qt::Horizontal 40 20 0 0 100 0 Delete Selected Delete All OK avogadrolibs-1.101.0/avogadro/qtplugins/constraints/constraintsextension.cpp000066400000000000000000000032061506155467400275530ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "constraintsextension.h" #include "constraintsdialog.h" #include "constraintsmodel.h" #include #include #include #include namespace Avogadro { namespace QtPlugins { ConstraintsExtension::ConstraintsExtension(QObject* p) : ExtensionPlugin(p) { QAction* action = new QAction(this); action->setEnabled(true); action->setText(tr("Constraints…")); connect(action, SIGNAL(triggered()), SLOT(openDialog())); m_actions.push_back(action); } ConstraintsExtension::~ConstraintsExtension() { if (m_dialog) m_dialog->deleteLater(); } QList ConstraintsExtension::actions() const { return m_actions; } QStringList ConstraintsExtension::menuPath(QAction*) const { return QStringList() << tr("&Extensions") << tr("&Calculate"); } void ConstraintsExtension::openDialog() { if (m_dialog == nullptr) { m_dialog = new ConstraintsDialog(qobject_cast(parent())); } // update the constraints before we show the dialog if (m_molecule != nullptr) m_dialog->setMolecule(m_molecule); m_dialog->updateConstraints(); m_dialog->show(); m_dialog->raise(); } void ConstraintsExtension::setMolecule(QtGui::Molecule* mol) { if (mol != m_molecule) { m_molecule = mol; } if (m_dialog != nullptr) m_dialog->setMolecule(mol); } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/constraints/constraintsextension.h000066400000000000000000000024361506155467400272240ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CONSTRAINTS_H #define AVOGADRO_QTPLUGINS_CONSTRAINTS_H #include #include #include class QAction; namespace Avogadro { namespace QtPlugins { class ConstraintsDialog; class ConstraintsExtension : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit ConstraintsExtension(QObject* parent = 0); ~ConstraintsExtension() override; QString name() const override { return tr("Constraints"); } QString description() const override { return tr("Set constraints for geometry optimizations"); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; private slots: void openDialog(); private: QList m_actions; QtGui::Molecule* m_molecule = nullptr; ConstraintsDialog* m_dialog = nullptr; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CONSTRAINTS_H avogadrolibs-1.101.0/avogadro/qtplugins/constraints/constraintsmodel.cpp000066400000000000000000000116731506155467400266460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "constraintsmodel.h" #include #include using namespace std; namespace Avogadro { using Core::Constraint; namespace QtPlugins { void ConstraintsModel::emitDataChanged() { emit dataChanged(QModelIndex(), QModelIndex()); } void ConstraintsModel::setConstraints( const std::vector& constraints) { beginResetModel(); m_constraints = constraints; endResetModel(); } int ConstraintsModel::rowCount(const QModelIndex&) const { return m_constraints.size(); } int ConstraintsModel::columnCount(const QModelIndex&) const { // Type, value, atom 1, 2, 3, 4 return 6; } QVariant ConstraintsModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= m_constraints.size()) return QVariant(); Constraint currentConstraint = m_constraints[index.row()]; Index aIndex = currentConstraint.aIndex(); Index bIndex = currentConstraint.bIndex(); Index cIndex = currentConstraint.cIndex(); Index dIndex = currentConstraint.dIndex(); if (role == Qt::DisplayRole || role == Qt::UserRole) switch (index.column()) { case 0: if (currentConstraint.type() == 1) return tr("Distance"); else if (currentConstraint.type() == 2) return tr("Angle"); else if (currentConstraint.type() == 3) return tr("Torsion Angle"); // these aren't really implemented in the UI yet // but we're saving the strings for translation else if (currentConstraint.type() == 4) return tr("Freeze Atom", "fix / remain constant"); else if (currentConstraint.type() == 5) return tr("Freeze X Axis", "fix / remain constant"); else if (currentConstraint.type() == 6) return tr("Freeze Y Axis", "fix / remain constant"); else if (currentConstraint.type() == 7) return tr("Freeze Z Axis", "fix / remain constant"); break; case 1: // TODO handle fixed-length number and sorting if (role == Qt::UserRole) return currentConstraint.value(); if (currentConstraint.type() == 1) return QString("%1 Å").arg(currentConstraint.value(), 0, 'f', 3); else if (currentConstraint.type() == 2 || currentConstraint.type() == 3) return QString("%1 °").arg(currentConstraint.value(), 0, 'f', 3); else return "--"; break; case 2: if (aIndex != MaxIndex) return QVariant(static_cast(aIndex)); else return "--"; break; case 3: if (bIndex != MaxIndex) return QVariant(static_cast(bIndex)); else return "--"; break; case 4: if (cIndex != MaxIndex) return QVariant(static_cast(cIndex)); else return "--"; break; case 5: if (dIndex != MaxIndex) return QVariant(static_cast(dIndex)); else return "--"; break; } return QVariant(); } QVariant ConstraintsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Type"); break; case 1: return tr("Value"); break; case 2: return tr("Atom 1"); break; case 3: return tr("Atom 2"); break; case 4: return tr("Atom 3"); break; case 5: return tr("Atom 4"); break; } } return section + 1; } void ConstraintsModel::addConstraint(int type, int a, int b, int c, int d, double value) { beginInsertRows(QModelIndex(), m_constraints.size(), m_constraints.size()); m_constraints.push_back(Constraint(a, b, c, d, value)); endInsertRows(); } void ConstraintsModel::clear() { if (m_constraints.size()) { beginRemoveRows(QModelIndex(), 0, m_constraints.size() - 1); m_constraints.clear(); endRemoveRows(); } } void ConstraintsModel::deleteConstraint(int index) { if (m_constraints.size() && (index >= 0)) { beginRemoveRows(QModelIndex(), index, index); auto position = m_constraints.begin() + index; m_constraints.erase(position); endRemoveRows(); } } Core::Constraint ConstraintsModel::constraint(int index) { if (index < 0 || index >= m_constraints.size()) return Constraint(MaxIndex, MaxIndex, MaxIndex, MaxIndex, 0.0); else return m_constraints[index]; } } // namespace QtPlugins } // end namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/constraints/constraintsmodel.h000066400000000000000000000027561506155467400263150ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef CONSTRAINTSMODEL_H #define CONSTRAINTSMODEL_H #include #include #include #include #include #include namespace Avogadro { namespace QtPlugins { class ConstraintsModel : public QAbstractTableModel { Q_OBJECT public: ConstraintsModel() : QAbstractTableModel() {} int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void clear(); Core::Constraint constraint(int index); std::vector constraints() { return m_constraints; } void addConstraint(int type, int a, int b, int c, int d, double value); void deleteConstraint(int index); void setConstraints(const std::vector& constraints); public slots: void emitDataChanged(); private: std::vector m_constraints; }; // ConstraintsModel } // namespace QtPlugins } // end namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/000077500000000000000000000000001506155467400235315ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/CMakeLists.txt000066400000000000000000000003741506155467400262750ustar00rootroot00000000000000avogadro_plugin(CoordinateEditor "Show a window with a free-text coordinate editor." ExtensionPlugin coordinateeditor.h CoordinateEditor "coordinateeditor.cpp;coordinateeditordialog.cpp;coordinatetextedit.cpp" "coordinateeditordialog.ui" ) avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/coordinateeditor.cpp000066400000000000000000000030401506155467400275700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "coordinateeditor.h" #include "coordinateeditordialog.h" #include namespace Avogadro::QtPlugins { CoordinateEditor::CoordinateEditor(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_dialog(nullptr), m_molecule(nullptr), m_action(new QAction(tr("Atomic &Coordinate Editor…"), this)) { m_action->setProperty("menu priority", 900); connect(m_action, SIGNAL(triggered()), SLOT(triggered())); } CoordinateEditor::~CoordinateEditor() {} QList CoordinateEditor::actions() const { return QList() << m_action; } QStringList CoordinateEditor::menuPath(QAction*) const { return QStringList() << tr("&Build"); } void CoordinateEditor::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; if (m_dialog) m_dialog->setMolecule(mol); } void CoordinateEditor::triggered() { if (!m_dialog) { m_dialog = new CoordinateEditorDialog(qobject_cast(parent())); m_dialog->setMolecule(m_molecule); connect(m_dialog, SIGNAL(pastedMolecule()), SLOT(pastedMolecule())); } m_dialog->show(); } void CoordinateEditor::pastedMolecule() { // We went from no atoms to something, don't edit by default // PR#394 requestActiveTool("Navigator"); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/coordinateeditor.h000066400000000000000000000026241506155467400272440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_COORDINATEEDITOR_H #define AVOGADRO_QTPLUGINS_COORDINATEEDITOR_H #include #include namespace Avogadro { namespace QtPlugins { class CoordinateEditorDialog; /** * @brief CoordinateEditor implements the plugin interface for the coordinate * editor extension. */ class CoordinateEditor : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit CoordinateEditor(QObject* parent_ = nullptr); ~CoordinateEditor() override; QString name() const override { return tr("Coordinate editor"); } QString description() const override { return tr("Text editing of atomic coordinates."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void triggered(); void pastedMolecule(); private: CoordinateEditorDialog* m_dialog; QtGui::Molecule* m_molecule; QAction* m_action; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_COORDINATEEDITOR_H avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/coordinateeditordialog.cpp000066400000000000000000000651201506155467400307570ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "coordinateeditordialog.h" #include "coordinatetextedit.h" #include "ui_coordinateeditordialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Define this to print out details of the format detection algorithm. #undef ENABLE_FORMAT_DEBUG #ifdef ENABLE_FORMAT_DEBUG #define FORMAT_DEBUG(x) x #else // ENABLE_FORMAT_DEBUG #define FORMAT_DEBUG(x) #endif // ENABLE_FORMAT_DEBUG using Avogadro::Vector3; using Avogadro::Core::Elements; using Avogadro::QtGui::Molecule; namespace { // Ensure a cross-platform monospaced font #if defined(Q_OS_WIN) || defined(Q_OS_OSX) static const QString EDITOR_FONT = "Courier"; #else // Linux and other OSes static const QString EDITOR_FONT = "Monospace"; #endif // Various integer constants. enum { CustomPreset = 0 }; // Distance unit indices -- keep in sync with the .ui file. enum DistanceUnitIndex { Angstrom = 0, Bohr }; // Types of tokens, used while parsing. enum TokenType { Integer = 0, Double, String }; // Some frequently used regexes: static const QRegularExpression TOKEN_SEPARATOR("[\\s,;]+"); static const QRegularExpression VALID_TOKEN("[^\\s,;]+"); // These two need to be exact static const QRegularExpression INT_CHECKER( QRegularExpression::anchoredPattern("(:?[+-])?\\d+")); static const QRegularExpression DOUBLE_CHECKER( QRegularExpression::anchoredPattern( "(:?[+-])?" // Leading sign "(:?" // Must match one of the following: "\\d*\\.\\d*" // Fractional part "|" // or "\\d+[Ee](:?[+-])?\\d+" // Exponential part "|" // or "\\d*\\.\\d*" // Fractional part and "[Ee](:?[+-])?\\d+" // Exponential part ")")); struct AtomStruct { unsigned char atomicNumber; Vector3 pos; }; } // namespace namespace Avogadro::QtPlugins { // Storage class used to hold state while validating input. class CoordinateEditorDialog::ValidateStorage { public: ValidateStorage() : isValidating(false), restartWhenFinished(false), collectAtoms(false), convertDistance(false), latticePositions(false), distanceConversion(1.f) { } bool isValidating; bool restartWhenFinished; bool collectAtoms; bool convertDistance; bool latticePositions; float distanceConversion; // Format specification QString spec; // Text cursors QTextCursor lineCursor; QTextCursor tokenCursor; // Accumulate atom data QVector atoms; }; CoordinateEditorDialog::CoordinateEditorDialog(QWidget* parent_) : QDialog(parent_), m_ui(new Ui::CoordinateEditorDialog), m_molecule(nullptr), m_validate(new ValidateStorage), m_defaultSpec("SZxyz#N") { m_ui->setupUi(this); // Set up text editor m_ui->text->setFont(QFont(EDITOR_FONT, qApp->font().pointSize())); connect(m_ui->text->document(), SIGNAL(modificationChanged(bool)), SLOT(textModified(bool))); // Setup spec edit QRegularExpression specRegExp("[#ZGSLNabcxyz01_]*"); auto* specValidator = new QRegularExpressionValidator(specRegExp, this); m_ui->spec->setValidator(specValidator); connect(m_ui->presets, SIGNAL(currentIndexChanged(int)), SLOT(presetChanged(int))); connect(m_ui->spec, SIGNAL(textChanged(QString)), SLOT(specChanged())); connect(m_ui->spec, SIGNAL(textEdited(QString)), SLOT(specEdited())); connect(m_ui->distanceUnit, SIGNAL(currentIndexChanged(int)), SLOT(updateText())); connect(m_ui->help, SIGNAL(clicked()), SLOT(helpClicked())); connect(m_ui->cut, SIGNAL(clicked()), SLOT(cutClicked())); connect(m_ui->copy, SIGNAL(clicked()), SLOT(copyClicked())); connect(m_ui->paste, SIGNAL(clicked()), SLOT(pasteClicked())); connect(m_ui->revert, SIGNAL(clicked()), SLOT(revertClicked())); connect(m_ui->clear, SIGNAL(clicked()), SLOT(clearClicked())); connect(m_ui->apply, SIGNAL(clicked()), SLOT(applyClicked())); m_ui->cut->setIcon(QIcon::fromTheme("edit-cut")); m_ui->copy->setIcon(QIcon::fromTheme("edit-copy")); m_ui->paste->setIcon(QIcon::fromTheme("edit-paste")); buildPresets(); listenForTextEditChanges(true); } CoordinateEditorDialog::~CoordinateEditorDialog() { delete m_ui; } void CoordinateEditorDialog::setMolecule(QtGui::Molecule* mol) { if (mol != m_molecule) { if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateText(); } } void CoordinateEditorDialog::moleculeChanged(uint change) { if (static_cast(change) & Molecule::Atoms || static_cast(change) & Molecule::UnitCell) { updateText(); } } void CoordinateEditorDialog::presetChanged(int ind) { QVariant itemData(m_ui->presets->itemData(ind)); bool isCustom(itemData.type() != QVariant::String); // Changing the spec text will update the editor text. m_ui->spec->setText(isCustom ? m_defaultSpec : itemData.toString()); } void CoordinateEditorDialog::specChanged() { // Store the spec if custom preset is selected. if (m_ui->presets->currentIndex() == CustomPreset) m_defaultSpec = m_ui->spec->text(); updateText(); } void CoordinateEditorDialog::specEdited() { // Editing the spec switches to and updates the custom preset. if (m_ui->presets->currentIndex() != CustomPreset) { m_defaultSpec = m_ui->spec->text(); m_ui->presets->setCurrentIndex(CustomPreset); } } void CoordinateEditorDialog::updateText() { if (m_ui->text->document()->isModified()) { int reply = QMessageBox::question( this, tr("Overwrite changes?"), tr("The text document has been modified. Would " "you like to discard your changes and revert " "to the current molecule?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (reply != QMessageBox::Yes) return; } Core::CoordinateBlockGenerator gen; gen.setMolecule(m_molecule); gen.setSpecification(m_ui->spec->text().toStdString()); switch (m_ui->distanceUnit->currentIndex()) { default: case Angstrom: gen.setDistanceUnit(Core::CoordinateBlockGenerator::Angstrom); break; case Bohr: gen.setDistanceUnit(Core::CoordinateBlockGenerator::Bohr); break; } // Disable markup for the generated text. listenForTextEditChanges(false); m_ui->text->document()->setPlainText( QString::fromStdString(gen.generateCoordinateBlock())); listenForTextEditChanges(true); m_ui->text->document()->setModified(false); } void CoordinateEditorDialog::helpClicked() { // Give the spec lineedit focus and show its tooltip. m_ui->spec->setFocus(Qt::MouseFocusReason); QPoint point(m_ui->spec->pos() + pos()); point.setY(point.y() + m_ui->spec->frameGeometry().height() + 5); QToolTip::showText(point, m_ui->spec->toolTip(), m_ui->spec); } void CoordinateEditorDialog::validateInput() { if (m_validate->isValidating) { m_validate->restartWhenFinished = true; return; } // Reset formatting. Stop listening for changes since format changes will // retrigger validation. listenForTextEditChanges(false); m_ui->text->resetMarks(); listenForTextEditChanges(true); // No text, nothing to do! Trim the plain text - this fixes a crashing // bug if the user accidentally presses return in a blank document. if (m_ui->text->document()->toPlainText().trimmed().isEmpty()) { emit validationFinished(true); return; } // Try to detect the input format QString inputFormat(detectInputFormat()); if (inputFormat.isEmpty()) { emit validationFinished(false); return; } // Initialize m_validate->latticePositions = inputFormat.contains('a'); m_validate->isValidating = true; m_validate->spec = inputFormat; m_validate->lineCursor = QTextCursor(m_ui->text->document()); // Start the worker validateInputWorker(); } void CoordinateEditorDialog::validateInputWorker() { if (!m_validate->isValidating) return; // Disable revalidation due to formatting changes. listenForTextEditChanges(false); // Setup some aliases to keep code concise: const QString& spec(m_validate->spec); QTextCursor& lineCursor(m_validate->lineCursor); QTextCursor& tokenCursor(m_validate->tokenCursor); QTextDocument* doc(m_ui->text->document()); QString::const_iterator begin(spec.constBegin()); QString::const_iterator end(spec.constEnd()); QString::const_iterator iter; // Only do a few lines at a time, then return control to the event loop. int lineThisIteration = 0; while (++lineThisIteration <= 10 && !lineCursor.atEnd()) { // Place the entire line in the line cursor's selection. lineCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); lineCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); // Skip empty lines - this is nice if the user accidentally left some // empty lines at the end of the input if (lineCursor.selectedText().trimmed().isEmpty()) { // So that we don't have an infinite loop... if (!lineCursor.atEnd()) { lineCursor.movePosition(QTextCursor::Down); lineCursor.movePosition(QTextCursor::StartOfLine); } continue; } // Start the token cursor at the beginning of the current line. tokenCursor.setPosition(lineCursor.anchor(), QTextCursor::MoveAnchor); // This is used when applying changes to store the atom specifications. AtomStruct atom; // Iterate through spec characters for (iter = begin; iter != end; ++iter) { // Place the next valid token in tokenCursor's selection: tokenCursor = doc->find(VALID_TOKEN, tokenCursor); // If the token cursor has moved off of the current line, mark the entire // line as invalid and move on. if (tokenCursor.isNull() || tokenCursor.position() > lineCursor.position()) { m_ui->text->markInvalid(lineCursor, tr("Too few entries on line.")); break; } switch (iter->toLatin1()) { case '?': // Nothing to validate other than that this is a valid token. break; case 'N': { // Validate name: QString cleanToken(tokenCursor.selectedText().toLower()); if (!cleanToken.isEmpty()) cleanToken.replace(0, 1, cleanToken[0].toUpper()); std::string tokenStd(cleanToken.toStdString()); atom.atomicNumber = Elements::atomicNumberFromName(tokenStd); if (atom.atomicNumber == Avogadro::InvalidElement) m_ui->text->markInvalid(tokenCursor, tr("Invalid element name.")); else m_ui->text->markValid(tokenCursor, tr("Element name.")); break; } case 'S': { // Validate symbol: QString cleanToken(tokenCursor.selectedText().toLower()); if (!cleanToken.isEmpty()) cleanToken.replace(0, 1, cleanToken[0].toUpper()); std::string tokenStd(cleanToken.toStdString()); atom.atomicNumber = Elements::atomicNumberFromSymbol(tokenStd); if (atom.atomicNumber == Avogadro::InvalidElement) m_ui->text->markInvalid(tokenCursor, tr("Invalid element symbol.")); else m_ui->text->markValid(tokenCursor, tr("Element symbol.")); break; } case 'L': { // Validate label (symbol + number) QString cleanToken(tokenCursor.selectedText().toLower()); if (!cleanToken.isEmpty()) cleanToken.replace(0, 1, cleanToken[0].toUpper()); // Split the label into symbol and number QRegularExpression labelSplitter("([A-Z][a-z]?)(\\d+)"); QRegularExpressionMatch match = labelSplitter.match(cleanToken); if (match.hasMatch()) { m_ui->text->markInvalid(tokenCursor, tr("Invalid atom label.")); break; } // check the symbol std::string tokenStd(match.captured(1).toStdString()); atom.atomicNumber = Elements::atomicNumberFromSymbol(tokenStd); if (atom.atomicNumber == Avogadro::InvalidElement) m_ui->text->markInvalid(tokenCursor, tr("Invalid element symbol.")); else m_ui->text->markValid(tokenCursor, tr("Element symbol.")); break; } case '#': { // Validate integer: bool isInt; [[maybe_unused]] int index = tokenCursor.selectedText().toInt(&isInt); if (!isInt) m_ui->text->markInvalid(tokenCursor, tr("Invalid atomic index.")); else m_ui->text->markValid(tokenCursor, tr("Atomic index.")); break; } case 'Z': { // Validate integer: bool isInt; atom.atomicNumber = static_cast( tokenCursor.selectedText().toInt(&isInt)); if (!isInt) m_ui->text->markInvalid(tokenCursor, tr("Invalid atomic number.")); else m_ui->text->markValid(tokenCursor, tr("Atomic number.")); break; } case 'x': { // Validate real: bool isReal; atom.pos.x() = tokenCursor.selectedText().toDouble(&isReal); if (!isReal) m_ui->text->markInvalid(tokenCursor, tr("Invalid coordinate.")); else m_ui->text->markValid(tokenCursor, tr("X coordinate.")); break; } case 'y': { // Validate real: bool isReal; atom.pos.y() = tokenCursor.selectedText().toDouble(&isReal); if (!isReal) m_ui->text->markInvalid(tokenCursor, tr("Invalid coordinate.")); else m_ui->text->markValid(tokenCursor, tr("Y coordinate.")); break; } case 'z': { // Validate real: bool isReal; atom.pos.z() = tokenCursor.selectedText().toDouble(&isReal); if (!isReal) m_ui->text->markInvalid(tokenCursor, tr("Invalid coordinate.")); else m_ui->text->markValid(tokenCursor, tr("Z coordinate.")); break; } case 'a': { // Validate real: bool isReal; atom.pos.x() = tokenCursor.selectedText().toDouble(&isReal); if (!isReal) m_ui->text->markInvalid(tokenCursor, tr("Invalid coordinate.")); else m_ui->text->markValid(tokenCursor, tr("'a' lattice coordinate.")); break; } case 'b': { // Validate real: bool isReal; atom.pos.y() = tokenCursor.selectedText().toDouble(&isReal); if (!isReal) m_ui->text->markInvalid(tokenCursor, tr("Invalid coordinate.")); else m_ui->text->markValid(tokenCursor, tr("'b' lattice coordinate.")); break; } case 'c': { // Validate real: bool isReal; atom.pos.z() = tokenCursor.selectedText().toDouble(&isReal); if (!isReal) m_ui->text->markInvalid(tokenCursor, tr("Invalid coordinate.")); else m_ui->text->markValid(tokenCursor, tr("'c' lattice coordinate.")); break; } default: qWarning() << "Unhandled character in detected spec: " << *iter; break; } } // Store this atom info if collecting. if (m_validate->collectAtoms) { if (m_validate->convertDistance && !m_validate->latticePositions) atom.pos *= m_validate->distanceConversion; m_validate->atoms << atom; } // Move down to the next line if we are not at the end. Moving to the // start of the line after moving down is necessary in case moving down // puts us at the end of the document. if (!lineCursor.atEnd()) { lineCursor.movePosition(QTextCursor::Down); lineCursor.movePosition(QTextCursor::StartOfLine); } } // Reenable validation. listenForTextEditChanges(true); // If we're not at the end, post this method back into the event loop. if (!lineCursor.atEnd()) { QTimer::singleShot(0, this, SLOT(validateInputWorker())); } else { // Otherwise emit the finished signal. emit validationFinished(!m_ui->text->hasInvalidMarks()); m_validate->isValidating = false; // If a validation request came in while already validating, revalidate. if (m_validate->restartWhenFinished) { m_validate->restartWhenFinished = false; validateInput(); } } } void CoordinateEditorDialog::applyClicked() { if (!m_molecule) return; // If we're in the middle of a validation, abort it if (m_validate->isValidating) { m_validate->isValidating = false; qApp->processEvents(); } m_validate->collectAtoms = true; m_validate->atoms.clear(); switch (m_ui->distanceUnit->currentIndex()) { case Bohr: m_validate->convertDistance = true; m_validate->distanceConversion = BOHR_TO_ANGSTROM_F; break; default: m_validate->convertDistance = false; m_validate->distanceConversion = 1.f; break; } connect(this, SIGNAL(validationFinished(bool)), SLOT(applyFinish(bool))); validateInput(); } void CoordinateEditorDialog::applyFinish(bool valid) { // Clean up m_validate->collectAtoms = false; QVector atoms(m_validate->atoms); m_validate->atoms.clear(); disconnect(this, SIGNAL(validationFinished(bool)), this, SLOT(applyFinish(bool))); if (!valid) { QMessageBox::critical(this, tr("Error applying geometry"), tr("Could not parse geometry specification. Fix the " "highlighted errors and try again.\n\n" "(Hint: Hold the mouse over red text for a " "description of the error.)")); return; } bool hadAtoms(m_molecule->atomCount() > 0); bool hadBonds(m_molecule->bondCount() > 0); // Create a new molecule so we can eventually store both in the undo command Molecule newMolecule = *m_molecule; newMolecule.clearAtoms(); foreach (const AtomStruct& atom, atoms) newMolecule.addAtom(atom.atomicNumber).setPosition3d(atom.pos); if (m_validate->latticePositions) { Core::CrystalTools::setFractionalCoordinates(newMolecule, newMolecule.atomPositions3d()); } else { newMolecule.perceiveBondsSimple(); newMolecule.perceiveBondOrders(); } m_ui->text->document()->setModified(false); Molecule::MoleculeChanges change = Molecule::NoChange; if (hadAtoms) change |= Molecule::Atoms | Molecule::Removed; if (hadBonds) change |= Molecule::Bonds | Molecule::Removed; if (newMolecule.atomCount() > 0) change |= Molecule::Atoms | Molecule::Added; if (newMolecule.bondCount() > 0) change |= Molecule::Bonds | Molecule::Added; QString undoText = tr("Edit Atomic Coordinates"); m_molecule->undoMolecule()->modifyMolecule(newMolecule, change, undoText); // We went from no atoms to something, don't edit by default // PR#394 if (!hadAtoms) emit pastedMolecule(); } void CoordinateEditorDialog::textModified(bool modified) { m_ui->apply->setEnabled(modified); m_ui->revert->setEnabled(modified); } void CoordinateEditorDialog::buildPresets() { // Custom must be first: m_ui->presets->addItem(tr("Custom"), QVariant()); m_ui->presets->addItem(tr("XYZ format (symbols)"), QVariant("Sxyz")); m_ui->presets->addItem(tr("XYZ format (names)"), QVariant("Nxyz")); m_ui->presets->addItem(tr("XYZ format (atomic numbers)"), QVariant("Zxyz")); m_ui->presets->addItem(tr("Lattice coordinates (symbols)"), QVariant("Sabc")); m_ui->presets->addItem(tr("Lattice coordinates (names)"), QVariant("Nabc")); m_ui->presets->addItem(tr("Lattice coordinates (atomic numbers)"), QVariant("Zabc")); m_ui->presets->addItem(tr("GAMESS format (symbols)"), QVariant("SGxyz")); m_ui->presets->addItem(tr("GAMESS format (names)"), QVariant("NGxyz")); m_ui->presets->addItem(tr("Turbomole format"), QVariant("xyzS")); // show Sxyz initially m_ui->presets->setCurrentIndex(1); } void CoordinateEditorDialog::listenForTextEditChanges(bool enable) { if (enable) connect(m_ui->text, SIGNAL(textChanged()), this, SLOT(validateInput())); else disconnect(m_ui->text, SIGNAL(textChanged()), this, SLOT(validateInput())); } QString CoordinateEditorDialog::detectInputFormat() const { if (m_ui->text->document()->isEmpty()) return QString(); if (!m_ui->spec->text().isEmpty()) return m_ui->spec->text(); // Extract the first non-empty line of text from the document. QTextCursor cur(m_ui->text->document()); QString sample; while (sample.isEmpty()) { cur.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); cur.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); sample = cur.selectedText(); cur.movePosition(QTextCursor::Down); } FORMAT_DEBUG(qDebug() << "\n\nExamining sample:" << sample;) // Split the string into tokens, and identify the type of each. QList tokens(sample.split(TOKEN_SEPARATOR, Qt::SkipEmptyParts)); QList tokenTypes; tokenTypes.reserve(tokens.size()); size_t tokenTypeCounts[3] = { 0, 0, 0 }; foreach (const QString& token, tokens) { TokenType tokenType = String; if (INT_CHECKER.match(token).hasMatch()) tokenType = Integer; else if (DOUBLE_CHECKER.match(token).hasMatch()) tokenType = Double; ++tokenTypeCounts[tokenType]; tokenTypes << tokenType; } FORMAT_DEBUG(qDebug() << "\nDetected types:"; qDebug() << tokens; qDebug() << tokenTypes;); // If less than three doubles are present, promote some integers to doubles. if (tokenTypeCounts[Double] < 3 && tokenTypeCounts[Double] + tokenTypeCounts[Integer] >= 3) { // If numInts + numDoubles is greater than 3, leave the first integer as is, // we'll assume it's the atomic number. bool skipNextInt(tokenTypeCounts[Integer] + tokenTypeCounts[Double] > 3); size_t intsToPromote = 3 - tokenTypeCounts[Double]; QMutableListIterator tokenTypeIter(tokenTypes); while (intsToPromote > 0 && tokenTypeIter.hasNext()) { if (tokenTypeIter.next() == Integer) { if (!skipNextInt) { tokenTypeIter.setValue(Double); --intsToPromote; --tokenTypeCounts[Integer]; ++tokenTypeCounts[Double]; } else { skipNextInt = false; } } } } FORMAT_DEBUG(qDebug() << "\nAfter promotion:"; qDebug() << tokens; qDebug() << tokenTypes;) // If there are no strings or integers, bail out -- we can't determine the // atom types. Likewise if there are less than 3 doubles, the coordinates // are incomplete. if ((tokenTypeCounts[Integer] == 0 && tokenTypeCounts[String] == 0) || tokenTypeCounts[Double] < 3) { return ""; } // Start assigning meaning to the values: QString resultSpec; bool atomTypeSet(false); int numCoordsSet(0); const int numberOfElements(static_cast(Core::Elements::elementCount())); for (int i = 0; i < tokens.size() && (!atomTypeSet || numCoordsSet < 3); ++i) { QChar current = '?'; switch (tokenTypes[i]) { case Integer: if (!atomTypeSet) { int tokenAsInt = tokens[i].toInt(); if (tokenAsInt >= 0 && tokenAsInt <= numberOfElements) { current = 'Z'; atomTypeSet = true; } } break; case Double: switch (numCoordsSet) { case 0: current = 'x'; ++numCoordsSet; break; case 1: current = 'y'; ++numCoordsSet; break; case 2: current = 'z'; ++numCoordsSet; break; default: break; } break; case String: if (!atomTypeSet) { QString cleanToken(tokens[i].toLower()); if (!cleanToken.isEmpty()) cleanToken.replace(0, 1, cleanToken[0].toUpper()); if (cleanToken.size() <= 3) { if (Elements::atomicNumberFromSymbol(cleanToken.toStdString()) != Avogadro::InvalidElement) { current = 'S'; atomTypeSet = true; } } else { if (Elements::atomicNumberFromName(cleanToken.toStdString()) != Avogadro::InvalidElement) { current = 'N'; atomTypeSet = true; } } } break; } FORMAT_DEBUG(qDebug() << current << tokens[i];) resultSpec += current; } // Check the current specification -- if a|b|c appears before x|y|z, assume // that the specified coordinates are lattice coords static QRegularExpression cartesianSniffer("x|y|z"); static QRegularExpression fractionalSniffer("a|b|c"); const QString currentSpec(m_ui->spec->text()); int cartesianIndex = currentSpec.indexOf(cartesianSniffer); int fractionalIndex = currentSpec.indexOf(fractionalSniffer); if (fractionalIndex != -1 && (cartesianIndex == -1 || fractionalIndex < cartesianIndex)) { resultSpec.replace('x', 'a'); resultSpec.replace('y', 'b'); resultSpec.replace('z', 'c'); FORMAT_DEBUG(qDebug() << "Interpreting positions as lattice coordinates.";) } FORMAT_DEBUG(qDebug() << "Detected format:" << resultSpec); return (!atomTypeSet || numCoordsSet < 3) ? QString() : resultSpec; } void CoordinateEditorDialog::cutClicked() { copyClicked(); clearClicked(); } void CoordinateEditorDialog::copyClicked() { qApp->clipboard()->setText(m_ui->text->document()->toPlainText()); } void CoordinateEditorDialog::pasteClicked() { const QMimeData* mimeData = qApp->clipboard()->mimeData(); m_ui->text->document()->setPlainText( (mimeData && mimeData->hasText()) ? mimeData->text() : ""); } void CoordinateEditorDialog::revertClicked() { updateText(); } void CoordinateEditorDialog::clearClicked() { m_ui->text->document()->clear(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/coordinateeditordialog.h000066400000000000000000000035261506155467400304260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_COORDINATEEDITORDIALOG_H #define AVOGADRO_QTPLUGINS_COORDINATEEDITORDIALOG_H #include namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { namespace Ui { class CoordinateEditorDialog; } /** * @brief The CoordinateEditorDialog class implements a free-text coordinate * editor. */ class CoordinateEditorDialog : public QDialog { Q_OBJECT public: explicit CoordinateEditorDialog(QWidget* parent_ = nullptr); ~CoordinateEditorDialog() override; void setMolecule(QtGui::Molecule* mol); signals: void validationFinished(bool valid); void pastedMolecule(); private slots: void moleculeChanged(uint); void presetChanged(int); void specChanged(); void specEdited(); void updateText(); void helpClicked(); void validateInput(); void validateInputWorker(); void cutClicked(); void copyClicked(); void pasteClicked(); void revertClicked(); void clearClicked(); void applyClicked(); void applyFinish(bool valid); void textModified(bool modified); private: void buildPresets(); // Enable/disable input validation when the text edit is modified. void listenForTextEditChanges(bool enable); QString detectInputFormat() const; Ui::CoordinateEditorDialog* m_ui; QtGui::Molecule* m_molecule; // State storage for validateInput methods. PIMPL'd for organization. class ValidateStorage; ValidateStorage* m_validate; QString m_defaultSpec; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_COORDINATEEDITORDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/coordinateeditordialog.ui000066400000000000000000000150211506155467400306050ustar00rootroot00000000000000 Avogadro::QtPlugins::CoordinateEditorDialog 0 0 500 400 Coordinate Editor Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Preset: presets Distance Unit: distanceUnit Angstrom Bohr Format: spec <html><head/><body><p>Specification of format. Each character indicates a value to write per atom:</p><p><span style=" font-weight:600;">#</span> - Atom index (1, 2, ..., numAtoms)<br/><span style=" font-weight:600;">Z</span> - Atomic number (e.g. &quot;6&quot; for carbon)<br/><span style=" font-weight:600;">G</span> - GAMESS-style atomic number (e.g. &quot;6.0&quot; for carbon)<br/><span style=" font-weight:600;">N</span> - Element name (e.g. &quot;Carbon&quot;)<br/><span style=" font-weight:600;">S</span> - Element symbol (e.g. &quot;C&quot; for carbon)<br/><span style=" font-weight:700;">L</span> - Atom label (e.g., &quot;C2&quot; for second carbon atom, &quot;H1&quot; for first hydrogen) <br/><span style=" font-weight:600;">x</span> - X position coordinate<br/><span style=" font-weight:600;">y</span> - Y position coordinate<br/><span style=" font-weight:600;">z</span> - Z position coordinate<br/><span style=" font-weight:600;">a</span> - 'a' lattice coordinate (crystals only)<br/><span style=" font-weight:600;">b</span> - 'b' lattice coordinate (crystals only)<br/><span style=" font-weight:600;">c</span> - 'c' lattice coordinate (crystals only)<br/><span style=" font-weight:600;">_</span> - A literal space (&quot; &quot;), useful for alignment<br/><span style=" font-weight:600;">0</span> - A literal 0 (&quot;0&quot;), useful for optimization flags<br/><span style=" font-weight:600;">1</span> - A literal 1 (&quot;1&quot;), useful for optimization flags<br/></p></body></html> Help… QTextEdit::NoWrap Cut Copy Paste Qt::Horizontal 40 20 Revert Clear Apply Avogadro::QtPlugins::CoordinateTextEdit QTextEdit
coordinatetextedit.h
presets spec help distanceUnit text cut copy paste revert clear apply
avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/coordinatetextedit.cpp000066400000000000000000000052331506155467400301420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "coordinatetextedit.h" #include #include #include #include #include namespace Avogadro::QtPlugins { CoordinateTextEdit::CoordinateTextEdit(QWidget* p) : QTextEdit(p), m_hasInvalidMarks(false) { setMouseTracking(true); m_unmarkedFormat.setUnderlineStyle(QTextCharFormat::NoUnderline); m_unmarkedFormat.setForeground(qApp->palette().color(QPalette::WindowText)); m_unmarkedFormat.setBackground(qApp->palette().color(QPalette::Base)); m_invalidFormat.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); m_invalidFormat.setForeground(Qt::darkRed); m_invalidFormat.setBackground(Qt::lightGray); m_validFormat.setUnderlineStyle(QTextCharFormat::NoUnderline); m_validFormat.setForeground(Qt::darkGreen); } void CoordinateTextEdit::resetMarks() { m_hasInvalidMarks = false; m_marks.clear(); if (!document()->isEmpty()) { QTextCursor cur(document()); cur.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cur.mergeCharFormat(m_unmarkedFormat); } } void CoordinateTextEdit::markInvalid(QTextCursor& cur, const QString& tooltip) { m_hasInvalidMarks = true; cur.mergeCharFormat(m_invalidFormat); m_marks.append(Mark(cur.anchor(), cur.position(), tooltip)); } void CoordinateTextEdit::markValid(QTextCursor& cur, const QString& tooltip) { cur.mergeCharFormat(m_validFormat); m_marks.append(Mark(cur.anchor(), cur.position(), tooltip)); } bool CoordinateTextEdit::event(QEvent* e) { if (e->type() == QEvent::ToolTip) { auto* helpEvent = static_cast(e); showToolTip(helpEvent); return true; } return QTextEdit::event(e); } void CoordinateTextEdit::showToolTip(QHelpEvent* e) const { int position(cursorForPosition(e->pos()).position()); bool handled(false); if (position >= 0) { // Iterate backwards -- this ensures that "line too short" errors are shown // instead of the token-specific messages in that line. QListIterator iter(m_marks); iter.toBack(); while (iter.hasPrevious()) { const Mark& mark = iter.previous(); if (mark.contains(position)) { QToolTip::showText(e->globalPos(), mark.tooltip); handled = true; break; } } } if (!handled) { QToolTip::hideText(); e->ignore(); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/coordinateeditor/coordinatetextedit.h000066400000000000000000000030261506155467400276050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_COORDINATETEXTEDIT_H #define AVOGADRO_QTPLUGINS_COORDINATETEXTEDIT_H #include #include class QHelpEvent; namespace Avogadro { namespace QtPlugins { /** * @brief The CoordinateTextEdit class extends QTextEdit to provide context * tooltips and highlighting for syntax errors. */ class CoordinateTextEdit : public QTextEdit { Q_OBJECT public: explicit CoordinateTextEdit(QWidget* p = nullptr); bool hasInvalidMarks() const { return m_hasInvalidMarks; } public slots: void resetMarks(); void markInvalid(QTextCursor& cur, const QString& tooltip); void markValid(QTextCursor& cur, const QString& tooltip); protected: bool event(QEvent* e) override; private: void showToolTip(QHelpEvent* e) const; struct Mark { int start; int end; QString tooltip; Mark(int s, int e, const QString& t) : start(s), end(e), tooltip(t) {} bool contains(int i) const { return i >= start && i <= end; } }; QList m_marks; bool m_hasInvalidMarks; QTextCharFormat m_unmarkedFormat; QTextCharFormat m_invalidFormat; QTextCharFormat m_validFormat; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_COORDINATETEXTEDIT_H avogadrolibs-1.101.0/avogadro/qtplugins/copypaste/000077500000000000000000000000001506155467400222025ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/copypaste/CMakeLists.txt000066400000000000000000000002671506155467400247470ustar00rootroot00000000000000avogadro_plugin(CopyPaste "Interact with the clipboard." ExtensionPlugin copypaste.h CopyPaste "copypaste.cpp" "" ) target_link_libraries(CopyPaste PRIVATE Avogadro::IO) avogadrolibs-1.101.0/avogadro/qtplugins/copypaste/copypaste.cpp000066400000000000000000000226151506155467400247230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "copypaste.h" #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using namespace Avogadro::QtGui; CopyPaste::CopyPaste(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_pastedFormat(nullptr), m_copyAction(new QAction(tr("Copy"), this)), // don't translate SMILES, InChI, or XYZ m_copySMILES(new QAction("SMILES", this)), m_copyInChI(new QAction("InChI", this)), m_copyXYZ(new QAction("XYZ", this)), m_cutAction(new QAction(tr("Cut"), this)), m_clearAction(new QAction(tr("Clear"), this)), m_pasteAction(new QAction(tr("&Paste"), this)) { m_cutAction->setShortcut(QKeySequence::Cut); m_cutAction->setIcon(QIcon::fromTheme("edit-cut")); m_cutAction->setProperty("menu priority", 560); connect(m_cutAction, SIGNAL(triggered()), SLOT(cut())); m_copyAction->setShortcut(QKeySequence::Copy); m_copyAction->setIcon(QIcon::fromTheme("edit-copy")); m_copyAction->setProperty("menu priority", 550); connect(m_copyAction, SIGNAL(triggered()), SLOT(copyCJSON())); m_copySMILES->setProperty("menu priority", 540); connect(m_copySMILES, SIGNAL(triggered()), SLOT(copySMILES())); m_copyInChI->setProperty("menu priority", 530); connect(m_copyInChI, SIGNAL(triggered()), SLOT(copyInChI())); m_copyXYZ->setProperty("menu priority", 520); connect(m_copyXYZ, SIGNAL(triggered()), SLOT(copyXYZ())); m_pasteAction->setShortcut(QKeySequence::Paste); m_pasteAction->setIcon(QIcon::fromTheme("edit-paste")); m_pasteAction->setProperty("menu priority", 510); connect(m_pasteAction, SIGNAL(triggered()), SLOT(paste())); m_clearAction->setShortcut(QKeySequence::Delete); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) m_clearAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditClear)); #else m_clearAction->setIcon(QIcon::fromTheme("edit-clear")); #endif m_clearAction->setProperty("menu priority", 500); connect(m_clearAction, SIGNAL(triggered()), SLOT(clear())); } CopyPaste::~CopyPaste() { delete m_pastedFormat; } QList CopyPaste::actions() const { QList result; return result << m_copyAction << m_copySMILES << m_copyInChI << m_copyXYZ << m_cutAction << m_pasteAction << m_clearAction; } QStringList CopyPaste::menuPath(QAction* action) const { if (action->text() != tr("SMILES") && action->text() != tr("InChI") && action->text() != tr("XYZ")) return QStringList() << tr("&Edit"); else return QStringList() << tr("&Edit") << tr("Copy As"); } void CopyPaste::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } bool CopyPaste::copyCJSON() { Io::CjsonFormat cjson; return copy(&cjson); } void CopyPaste::copySMILES() { Io::FileFormatManager& formats = Io::FileFormatManager::instance(); Io::FileFormat* format(formats.newFormatFromFileExtension("smi")); copy(format); delete format; } void CopyPaste::copyInChI() { Io::FileFormatManager& formats = Io::FileFormatManager::instance(); Io::FileFormat* format(formats.newFormatFromFileExtension("inchi")); copy(format); delete format; } void CopyPaste::copyXYZ() { Io::FileFormatManager& formats = Io::FileFormatManager::instance(); Io::FileFormat* format(formats.newFormatFromFileExtension("xyz")); copy(format); delete format; } bool CopyPaste::copy(Io::FileFormat* format) { if (m_molecule == nullptr || m_molecule->atomCount() == 0 || format == nullptr) return false; std::string output; QtGui::Molecule* copy = m_molecule; if (!m_molecule->isSelectionEmpty()) { // create a copy of the selected atoms only copy = new QtGui::Molecule(m_molecule->parent()); // go through the selected atoms and add them // make sure to track the new index std::vector atomIndex(m_molecule->atomCount(), 0); for (Index i = 0; i < m_molecule->atomCount(); ++i) if (m_molecule->atomSelected(i)) { auto a = copy->addAtom(m_molecule->atomicNumber(i)); a.setPosition3d(m_molecule->atomPosition3d(i)); // track the index atomIndex[i] = a.index(); } for (Index i = 0; i < m_molecule->bondCount(); ++i) { Core::Bond bond = m_molecule->bond(i); Index start = bond.atom1().index(); Index end = bond.atom2().index(); if (m_molecule->atomSelected(start) && m_molecule->atomSelected(start)) { copy->addBond(atomIndex[start], atomIndex[end], bond.order()); } } } if (!format->writeString(output, *copy)) { QMessageBox::warning( qobject_cast(this->parent()), tr("Error Clipping Molecule"), tr("Error generating clipboard data.") + "\n" + tr("Output format: %1\n%2", "file format") .arg(QString::fromStdString(m_pastedFormat->name())) .arg(QString::fromStdString(m_pastedFormat->description())) + "\n\n" + tr("Reader error:\n%1") .arg(QString::fromStdString(m_pastedFormat->error()))); return false; } QByteArray outputBA(output.c_str(), static_cast(output.length())); auto* mimeData(new QMimeData); std::vector mimeTypes(format->mimeTypes()); for (auto& mimeType : mimeTypes) mimeData->setData(QString::fromStdString(mimeType), outputBA); mimeData->setData("text/plain", outputBA); QApplication::clipboard()->setMimeData(mimeData); if (!m_molecule->isSelectionEmpty()) copy->deleteLater(); // don't leak our copy return true; } void CopyPaste::cut() { if (!copyCJSON()) return; if (m_molecule->isSelectionEmpty()) m_molecule->undoMolecule()->clearAtoms(); else { // Remove atoms from the largest to the smallest index // (that way, the index doesn't change) for (Index i = m_molecule->atomCount(); i > 0; --i) // atoms go from 0 to atomCount()-1 if (m_molecule->atomSelected(i - 1)) m_molecule->undoMolecule()->removeAtom(i - 1); } m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Bonds | QtGui::Molecule::Removed); } void CopyPaste::clear() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; if (m_molecule->isSelectionEmpty()) m_molecule->undoMolecule()->clearAtoms(); else { // Remove atoms from the largest to the smallest index // (that way, the index doesn't change) for (Index i = m_molecule->atomCount(); i > 0; --i) { // atoms go from 0 to atomCount()-1 if (m_molecule->atomSelected(i - 1)) m_molecule->undoMolecule()->removeAtom(i - 1); } } m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Bonds | QtGui::Molecule::Removed); } void CopyPaste::paste() { // Delete any old clipboard data. if (m_pastedFormat) { delete m_pastedFormat; m_pastedFormat = nullptr; m_pastedData.clear(); } if (!m_molecule) return; // nothing to do // make sure we clear the current selection for (Index i = 0; i < m_molecule->atomCount(); ++i) m_molecule->setAtomSelected(i, false); const QMimeData* mimeData(QApplication::clipboard()->mimeData()); if (!mimeData) { QMessageBox::warning(qobject_cast(this->parent()), tr("Error Pasting Molecule"), tr("Cannot paste molecule: Clipboard empty!")); return; } // Try to find a reader that can handle the available mime-types. Io::FileFormatManager& mgr = Io::FileFormatManager::instance(); QStringList mimeTypes(mimeData->formats()); Io::FileFormat::Operations ops(Io::FileFormat::Read | Io::FileFormat::String); foreach (const QString& mimeType, mimeTypes) { if ((m_pastedFormat = mgr.newFormatFromMimeType(mimeType.toStdString(), ops))) { m_pastedData = mimeData->data(mimeType); break; } } // No mime-type match, default to cjson. if (!m_pastedFormat && mimeData->hasText()) { m_pastedFormat = new Io::CjsonFormat; m_pastedData = mimeData->text().toLatin1(); } if (!m_pastedFormat) return; // we have a format, so try to insert the new bits into m_molecule Avogadro::QtGui::Molecule mol(m_molecule->parent()); bool success = m_pastedFormat->readString( std::string(m_pastedData.constData(), m_pastedData.size()), mol); if (!success) { QMessageBox::warning( qobject_cast(this->parent()), tr("Error Pasting Molecule"), tr("Error reading clipboard data.") + "\n" + tr("Detected format: %1\n%2", "file format description") .arg(QString::fromStdString(m_pastedFormat->name())) .arg(QString::fromStdString(m_pastedFormat->description())) + "\n\n" + tr("Reader error:\n%1") .arg(QString::fromStdString(m_pastedFormat->error()))); } // insert mol into m_molecule m_molecule->undoMolecule()->appendMolecule(mol, "Paste Molecule"); emit requestActiveTool("Manipulator"); delete m_pastedFormat; m_pastedFormat = nullptr; m_pastedData.clear(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/copypaste/copypaste.h000066400000000000000000000034021506155467400243610ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_COPYPASTE_H #define AVOGADRO_QTPLUGINS_COPYPASTE_H #include #include #include namespace Avogadro { namespace Io { class FileFormat; } namespace QtPlugins { /** * @brief The CopyPaste class allows interaction with the system clipboard. */ class CopyPaste : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit CopyPaste(QObject* parent_ = nullptr); ~CopyPaste() override; QString name() const override { return tr("Copy and paste"); } QString description() const override { return tr("Interact with the clipboard."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: bool copy( Io::FileFormat* format); // returns bool so cut can reuse implementation. bool copyCJSON(); void copySMILES(); void copyInChI(); void copyXYZ(); void cut(); void paste(); void clear(); private: // Cached between emitting moleculeReady() and calling readMolecule(). QByteArray m_pastedData; Io::FileFormat* m_pastedFormat; QtGui::Molecule* m_molecule; QAction* m_copyAction; QAction* m_copySMILES; QAction* m_copyInChI; QAction* m_copyXYZ; QAction* m_cutAction; QAction* m_clearAction; QAction* m_pasteAction; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_COPYPASTE_H avogadrolibs-1.101.0/avogadro/qtplugins/cp2kinput/000077500000000000000000000000001506155467400221125ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/cp2kinput/CMakeLists.txt000066400000000000000000000004301506155467400246470ustar00rootroot00000000000000# Extension set(cp2kinput_srcs cp2kinputdialog.cpp cp2kinput.cpp ) avogadro_plugin(Cp2kInput "CP2K input file generation" ExtensionPlugin cp2kinput.h Cp2kInput "${cp2kinput_srcs}" cp2kinputdialog.ui ) target_link_libraries(Cp2kInput PRIVATE Avogadro::MoleQueue) avogadrolibs-1.101.0/avogadro/qtplugins/cp2kinput/cp2kinput.cpp000066400000000000000000000054641506155467400245460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cp2kinput.h" #include "cp2kinputdialog.h" #include #include #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { using Avogadro::MoleQueue::JobObject; Cp2kInput::Cp2kInput(QObject* parent_) : ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr), m_dialog(nullptr), m_outputFormat(nullptr) { m_action->setEnabled(true); m_action->setText(tr("&CP2K…")); connect(m_action, SIGNAL(triggered()), SLOT(menuActivated())); } Cp2kInput::~Cp2kInput() {} QList Cp2kInput::actions() const { QList actions_; actions_.append(m_action); return actions_; } QStringList Cp2kInput::menuPath(QAction*) const { QStringList path; path << tr("&Input"); return path; } void Cp2kInput::setMolecule(QtGui::Molecule* mol) { if (m_dialog) m_dialog->setMolecule(mol); m_molecule = mol; } void Cp2kInput::openJobOutput(const JobObject& job) { m_outputFormat = nullptr; m_outputFileName.clear(); QString outputPath(job.value("outputDirectory").toString()); using QtGui::FileFormatDialog; FileFormatDialog::FormatFilePair result = FileFormatDialog::fileToRead( qobject_cast(parent()), tr("Open Output File"), outputPath); if (result.first == nullptr) // User canceled return; m_outputFormat = result.first; m_outputFileName = result.second; emit moleculeReady(1); } bool Cp2kInput::readMolecule(QtGui::Molecule& mol) { Io::FileFormat* reader = m_outputFormat->newInstance(); bool success = reader->readFile(m_outputFileName.toStdString(), mol); if (!success) { QMessageBox::information(qobject_cast(parent()), tr("Error"), tr("Error reading output file '%1':\n%2") .arg(m_outputFileName) .arg(QString::fromStdString(reader->error()))); } m_outputFormat = nullptr; m_outputFileName.clear(); return success; } void Cp2kInput::menuActivated() { if (!m_dialog) { m_dialog = new Cp2kInputDialog(qobject_cast(parent())); connect(m_dialog, SIGNAL(openJobOutput(Avogadro::MoleQueue::JobObject)), this, SLOT(openJobOutput(Avogadro::MoleQueue::JobObject))); } m_dialog->setMolecule(m_molecule); m_dialog->show(); } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/cp2kinput/cp2kinput.h000066400000000000000000000030231506155467400242000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CP2KINPUT_H #define AVOGADRO_QTPLUGINS_CP2KINPUT_H #include class QAction; class QDialog; namespace Avogadro { namespace Io { class FileFormat; } namespace MoleQueue { class JobObject; } namespace QtPlugins { class Cp2kInputDialog; class Cp2kInput : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Cp2kInput(QObject* parent = nullptr); ~Cp2kInput() override; QString name() const override { return tr("CP2K input"); } QString description() const override { return tr("Generate input for CP2K."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; public slots: /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ void openJobOutput(const Avogadro::MoleQueue::JobObject& job); bool readMolecule(QtGui::Molecule& mol) override; private slots: void menuActivated(); private: QAction* m_action; QtGui::Molecule* m_molecule; Cp2kInputDialog* m_dialog; const Io::FileFormat* m_outputFormat; QString m_outputFileName; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CP2KINPUT_H avogadrolibs-1.101.0/avogadro/qtplugins/cp2kinput/cp2kinputdialog.cpp000066400000000000000000000663431506155467400257310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cp2kinputdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::MoleQueue::JobObject; using Avogadro::MoleQueue::MoleQueueDialog; using Avogadro::MoleQueue::MoleQueueManager; namespace Avogadro::QtPlugins { enum FunctionalOption { FunctionalBLYP = 0, FunctionalBP, FunctionalHCTH120, FunctionalPADE, FunctionalPBE, FunctionalCount }; enum MethodOption { DFT = 0, MolecularMechanics, HybridQuantumClassical, MethodCount }; // MM tab enum EWALDTypeOption { EWALD = 0, ewaldNONE, PME, SPME, EWALDTypeCount }; // QM tab enum SCFGuessOption { ATOMIC = 0, CORE, DENSITIES, HISTORY_RESTART, MOPAC, scfNONE, RANDOM, RESTART, SPARSE, SCFGuessCount }; enum OTMinimizerOption { CG = 0, BROYDEN, DIIS, SD, OTMinimizerCount }; Cp2kInputDialog::Cp2kInputDialog(QWidget* parent_, Qt::WindowFlags f) : QDialog(parent_, f), m_molecule(nullptr), // m_highlighter(NULL), m_updatePending(false) { ui.setupUi(this); // m_highlighter = new Cp2kHighlighter(ui.previewText->document()); buildOptions(); connectBasic(); connectPreview(); connectButtons(); setBasicDefaults(); updatePreviewText(); } Cp2kInputDialog::~Cp2kInputDialog() {} void Cp2kInputDialog::setMolecule(QtGui::Molecule* mol) { if (mol == m_molecule) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; connect(mol, SIGNAL(changed(unsigned int)), SLOT(updatePreviewText())); connect(mol, SIGNAL(changed(unsigned int)), SLOT(updateTitlePlaceholder())); updateTitlePlaceholder(); updatePreviewText(); } void Cp2kInputDialog::showEvent(QShowEvent* e) { QWidget::showEvent(e); // Update the preview text if an update was requested while hidden. Use a // single shot to allow the dialog to show before popping up any warnings. if (m_updatePending) QTimer::singleShot(0, this, SLOT(updatePreviewText())); } void Cp2kInputDialog::connectBasic() { connect(ui.titleEdit, SIGNAL(textChanged(QString)), this, SLOT(updatePreviewText())); connect(ui.calculateCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.calculateCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTitlePlaceholder())); connect(ui.functionalCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.functionalCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTitlePlaceholder())); connect(ui.basisCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.basisCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTitlePlaceholder())); connect(ui.methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.ewaldtypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.emaxSplineSpin, SIGNAL(valueChanged(double)), this, SLOT(updatePreviewText())); connect(ui.rcutnbSplineSpin, SIGNAL(valueChanged(double)), this, SLOT(updatePreviewText())); connect(ui.ewaldalphaSpin, SIGNAL(valueChanged(double)), this, SLOT(updatePreviewText())); connect(ui.ewaldgmaxSpin, SIGNAL(valueChanged(double)), this, SLOT(updatePreviewText())); connect(ui.lsdcheckBox, SIGNAL(stateChanged(bool)), this, SLOT(updatePreviewText())); connect(ui.maxscfspinBox, SIGNAL(valueChanged(int)), this, SLOT(updatePreviewText())); connect(ui.epsscfSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updatePreviewText())); connect(ui.outerMaxscfSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updatePreviewText())); connect(ui.outerEpsscfSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updatePreviewText())); connect(ui.scfguessComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.otminimizerComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); } void Cp2kInputDialog::connectPreview() {} void Cp2kInputDialog::connectButtons() { connect(ui.resetAllButton, SIGNAL(clicked()), SLOT(resetClicked())); connect(ui.defaultsButton, SIGNAL(clicked()), SLOT(defaultsClicked())); connect(ui.generateButton, SIGNAL(clicked()), SLOT(generateClicked())); connect(ui.computeButton, SIGNAL(clicked()), SLOT(computeClicked())); connect(ui.closeButton, SIGNAL(clicked()), SLOT(close())); } void Cp2kInputDialog::buildOptions() { buildCalculateOptions(); buildFunctionalOptions(); buildBasisOptions(); buildMethodOptions(); buildEWALDTypeOptions(); buildSCFGuessOptions(); buildOTMinimizerOptions(); } void Cp2kInputDialog::updateOptionCache() { m_optionCache.clear(); m_optionCache.insert(ui.calculateCombo, ui.calculateCombo->currentIndex()); m_optionCache.insert(ui.functionalCombo, ui.functionalCombo->currentIndex()); m_optionCache.insert(ui.basisCombo, ui.basisCombo->currentIndex()); m_optionCache.insert(ui.methodCombo, ui.methodCombo->currentIndex()); m_optionCache.insert(ui.ewaldtypeCombo, ui.ewaldtypeCombo->currentIndex()); m_optionCache.insert(ui.scfguessComboBox, ui.scfguessComboBox->currentIndex()); m_optionCache.insert(ui.otminimizerComboBox, ui.otminimizerComboBox->currentIndex()); } void Cp2kInputDialog::restoreOptionCache() { foreach (QComboBox* combo, m_optionCache.keys()) { combo->blockSignals(true); combo->setCurrentIndex(m_optionCache.value(combo, 0)); combo->blockSignals(false); } } QString Cp2kInputDialog::fromCalcEnum(CalculateOption option) { switch (option) { case CalculateEnergy: return "ENERGY"; case CalculateEnergyAndForces: return "ENERGY_FORCE"; case CalculateMolecularDynamics: return "MOLECULAR_DYNAMICS"; case CalculateGeometryOptimization: return "GEO_OPT"; default: throw std::invalid_argument{ __func__ }; // unreachable } } void Cp2kInputDialog::buildCalculateOptions() { for (int i = 0; i < static_cast(CalculateCount); ++i) { QString text = ""; switch (static_cast(i)) { case CalculateEnergy: text = tr("Energy"); break; case CalculateEnergyAndForces: text = tr("Energy and Forces"); break; case CalculateMolecularDynamics: text = tr("Molecular Dynamics"); break; case CalculateGeometryOptimization: text = tr("Geometry Optimization"); break; default: break; } ui.calculateCombo->addItem(text); } } void Cp2kInputDialog::buildFunctionalOptions() { for (int i = 0; i < static_cast(FunctionalCount); ++i) { QString text = ""; switch (static_cast(i)) { case FunctionalBLYP: text = "BLYP"; break; case FunctionalBP: text = "BP"; break; case FunctionalHCTH120: text = "HCTH120"; break; case FunctionalPADE: text = "PADE"; break; case FunctionalPBE: text = "PBE"; break; default: break; } ui.functionalCombo->addItem(text); } } void Cp2kInputDialog::buildBasisOptions() { for (int i = 0; i < static_cast(BasisCount); ++i) { QString text = ""; switch (static_cast(i)) { case BasisSZVGTH: text = "SZV-GTH"; break; case BasisDZVGTH: text = "DZV-GTH"; break; case BasisDZVPGTH: text = "DZVP-GTH"; break; case BasisTZVPGTH: text = "TZVP-GTH"; break; case BasisTZV2PGTH: text = "TZV2P-GTH"; break; default: break; } ui.basisCombo->addItem(text); } } void Cp2kInputDialog::buildMethodOptions() { for (int i = 0; i < static_cast(MethodCount); ++i) { QString text = ""; switch (static_cast(i)) { case DFT: text = tr("Electronic structure methods (DFT)"); break; case MolecularMechanics: text = tr("Molecular Mechanics"); break; case HybridQuantumClassical: text = tr("Hybrid quantum classical (Not yet supported)"); break; default: break; } ui.methodCombo->addItem(text); } } void Cp2kInputDialog::buildEWALDTypeOptions() { for (int i = 0; i < static_cast(EWALDTypeCount); ++i) { QString text = ""; switch (static_cast(i)) { case EWALD: text = tr("EWALD"); break; case ewaldNONE: text = tr("NONE"); break; case PME: text = tr("PME"); break; case SPME: text = tr("SPME"); break; default: break; } ui.ewaldtypeCombo->addItem(text); } } void Cp2kInputDialog::buildSCFGuessOptions() { for (int i = 0; i < static_cast(SCFGuessCount); ++i) { QString text = ""; switch (static_cast(i)) { case ATOMIC: text = tr("ATOMIC"); break; case CORE: text = tr("CORE"); break; case DENSITIES: text = tr("DENSITIES"); break; case HISTORY_RESTART: text = tr("HISTORY_RESTART"); break; case MOPAC: text = tr("MOPAC"); break; case scfNONE: text = tr("NONE"); break; case RANDOM: text = tr("RANDOM"); break; case RESTART: text = tr("RESTART"); break; case SPARSE: text = tr("SPARSE"); break; default: break; } ui.scfguessComboBox->addItem(text); } } void Cp2kInputDialog::buildOTMinimizerOptions() { for (int i = 0; i < static_cast(OTMinimizerCount); ++i) { QString text = ""; switch (static_cast(i)) { case BROYDEN: text = tr("BROYDEN"); break; case CG: text = tr("Conjugate Gradients"); break; case DIIS: text = tr("DIIS"); break; case SD: text = tr("Steepest descent"); break; default: break; } ui.otminimizerComboBox->addItem(text); } } void Cp2kInputDialog::setBasicDefaults() { ui.titleEdit->setText(QString()); ui.calculateCombo->setCurrentIndex(CalculateEnergy); ui.functionalCombo->setCurrentIndex(FunctionalBLYP); ui.basisCombo->setCurrentIndex(BasisSZVGTH); ui.methodCombo->setCurrentIndex(DFT); ui.ewaldtypeCombo->setCurrentIndex(SPME); ui.scfguessComboBox->setCurrentIndex(ATOMIC); ui.otminimizerComboBox->setCurrentIndex(CG); } QString Cp2kInputDialog::generateJobTitle() const { QString calculation(fromCalcEnum( static_cast(ui.calculateCombo->currentIndex()))); QString basis(ui.basisCombo->currentText()); QString formula(m_molecule ? QString::fromStdString(m_molecule->formula()) : tr("[no molecule]")); // Merge theory/basis into theory // replace(QRegExp("\\s+"), ""); return QString("%1_%2_%3").arg(formula, calculation, basis); } void Cp2kInputDialog::updatePreviewText() { std::map valencee; valencee["H"] = 1; valencee["He"] = 2; // Z=2 valencee["Li"] = 3; valencee["Be"] = 4; valencee["B"] = 3; valencee["C"] = 4; valencee["N"] = 5; valencee["O"] = 6; valencee["F"] = 7; valencee["Ne"] = 8; // Z=10 valencee["Na"] = 9; valencee["Mg"] = 10; valencee["Al"] = 3; valencee["Si"] = 4; valencee["P"] = 5; valencee["S"] = 6; valencee["Cl"] = 7; valencee["Ar"] = 8; // Z=18 valencee["K"] = 9; valencee["Ca"] = 10; valencee["Sc"] = 11; valencee["Ti"] = 12; valencee["V"] = 13; valencee["Cr"] = 14; valencee["Mn"] = 15; valencee["Fe"] = 16; valencee["Co"] = 17; valencee["Ni"] = 18; valencee["Cu"] = 11; valencee["Zn"] = 12; valencee["Ga"] = 13; valencee["Ge"] = 4; valencee["As"] = 5; valencee["Se"] = 6; valencee["Br"] = 7; valencee["Kr"] = 8; // Z=36 valencee["Rb"] = 9; valencee["Sr"] = 10; valencee["Y"] = 11; valencee["Zr"] = 12; valencee["Nb"] = 13; valencee["Mo"] = 14; valencee["Tc"] = 15; valencee["Ru"] = 16; valencee["Rh"] = 17; valencee["Pd"] = 18; valencee["Ag"] = 11; valencee["Cd"] = 12; valencee["In"] = 13; valencee["Sn"] = 4; valencee["Sb"] = 5; valencee["Te"] = 6; valencee["I"] = 7; valencee["Xe"] = 8; // Z=54 valencee["Cs"] = 9; valencee["Ba"] = 10; valencee["W"] = 14; valencee["Pt"] = 10; valencee["Au"] = 11; valencee["Hg"] = 12; valencee["Pb"] = 14; valencee["Bi"] = 15; valencee["Rn"] = 8; // Z=86 // If the dialog is not shown, delay the update in case we need to prompt the // user to overwrite changes. Set the m_updatePending flag to true so we'll // know to update in the show event. if (!isVisible()) { m_updatePending = true; return; } m_updatePending = false; // Has the preview text been modified? if (ui.previewText->document()->isModified()) { QString message = tr("The input file has been modified. " "Would you like to overwrite your changes to reflect " "the new geometry or job options?"); int response = QMessageBox::question( this, tr("Overwrite modified input file?"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (static_cast(response) != QMessageBox::Yes) { restoreOptionCache(); return; } } // Gather options: QString title(ui.titleEdit->text()); if (title.isEmpty()) title = generateJobTitle(); auto calculate( static_cast(ui.calculateCombo->currentIndex())); auto functional( static_cast(ui.functionalCombo->currentIndex())); auto basis(static_cast(ui.basisCombo->currentIndex())); auto method(static_cast(ui.methodCombo->currentIndex())); auto EWALDType( static_cast(ui.ewaldtypeCombo->currentIndex())); auto SCFGuess( static_cast(ui.scfguessComboBox->currentIndex())); auto OTMinimizer( static_cast(ui.otminimizerComboBox->currentIndex())); QString emaxSpline = QString::number(ui.emaxSplineSpin->value()); QString rcutnb = QString::number(ui.rcutnbSplineSpin->value()); QString ewaldalpha = QString::number(ui.ewaldalphaSpin->value()); QString ewaldgmax = QString::number(ui.ewaldgmaxSpin->value()); QString maxSCF = QString::number(ui.maxscfspinBox->value()); QString epsSCF = QString::number(ui.epsscfSpinBox->value()); QString outermaxSCF = QString::number(ui.outerMaxscfSpinBox->value()); QString outerepsSCF = QString::number(ui.outerEpsscfSpinBox->value()); // Generate text. // Variables: QString runTyp; QString scfTyp; QString gbasis; QString gfunc; QString gmethod; QString mult; QString ewaldtype; QString lsd; QString scfGuess; QString otMinimizer; // Extra options for lines QString extraBasis; QString extraContrl; // Optional lines QString statPt; QString force; QString pcm; runTyp = fromCalcEnum(calculate); switch (functional) { case FunctionalBLYP: gfunc = "BLYP"; break; case FunctionalBP: gfunc = "BP"; break; case FunctionalHCTH120: gfunc = "HCTH120"; break; case FunctionalPADE: gfunc = "PADE"; break; case FunctionalPBE: gfunc = "PBE"; break; default: break; } switch (basis) { case BasisSZVGTH: gbasis = "SZV-GTH"; break; case BasisDZVGTH: gbasis = "DZV-GTH"; break; case BasisDZVPGTH: gbasis = "DZVP-GTH"; break; case BasisTZVPGTH: gbasis = "TZVP-GTH"; break; case BasisTZV2PGTH: gbasis = "TZV2P-GTH"; break; default: break; } switch (method) { case DFT: gmethod = "QS"; break; case MolecularMechanics: gmethod = "FIST"; break; case HybridQuantumClassical: gmethod = "QMMM"; break; default: break; } switch (EWALDType) { case EWALD: ewaldtype = "EWALD"; break; case ewaldNONE: ewaldtype = "NONE"; break; case PME: ewaldtype = "PME"; break; case SPME: ewaldtype = "SPME"; break; default: break; } switch (SCFGuess) { case ATOMIC: scfGuess = "ATOMIC"; break; case CORE: scfGuess = "CORE"; break; case DENSITIES: scfGuess = "DENSITIES"; break; case HISTORY_RESTART: scfGuess = "HISTORY_RESTART"; break; case MOPAC: scfGuess = "MOPAC"; break; case scfNONE: scfGuess = "NONE"; break; case RANDOM: scfGuess = "RANDOM"; break; case RESTART: scfGuess = "RESTART"; break; case SPARSE: scfGuess = "SPARSE"; break; default: break; } switch (OTMinimizer) { case BROYDEN: otMinimizer = "BROYDEN"; break; case CG: otMinimizer = "CG"; break; case DIIS: otMinimizer = "DIIS"; break; case SD: otMinimizer = "SD"; break; default: break; } /* switch (lsdbool) { case true: lsd = "TRUE"; break; case false: lsd = "FALSE"; break; }*/ // build up the input file: QString file; file += "&GLOBAL\n"; file += QString(" PROJECT %1\n").arg(title); file += QString(" RUN_TYPE %1\n").arg(runTyp); file += " PRINT_LEVEL LOW\n"; file += "&END GLOBAL\n\n"; file += "&FORCE_EVAL\n"; file += QString(" METHOD %1\n").arg(gmethod); file += " &SUBSYS\n"; if (m_molecule) { std::vector atomList; bool inlist = true; for (size_t i = 0; i < m_molecule->atomCount(); ++i) { Core::Atom atom = m_molecule->atom(i); for (unsigned iat : atomList) { if (iat == atom.atomicNumber()) { inlist = false; break; } else { inlist = true; } } if (inlist) { atomList.push_back(atom.atomicNumber()); QString symbol = Core::Elements::symbol(atom.atomicNumber()); file += QString(" &KIND %1\n").arg(symbol); file += QString(" ELEMENT %1\n") .arg(Core::Elements::symbol(atom.atomicNumber())); file += QString(" BASIS_SET %1\n").arg(gbasis); const auto e = valencee.find(symbol); const auto suffix = e != valencee.cend() ? QString("-q%1").arg(e->second) : ""; file += QString( " ! XXX you may have to manually modify/append '-qn' suffix, " "where n is the number of valence electrons\n"); file += QString(" POTENTIAL GTH-%1%2\n").arg(gfunc).arg(suffix); file += QString(" &END KIND\n"); } } } // unit cell file += " &CELL\n"; if (const Core::UnitCell* cell = m_molecule->unitCell()) { Vector3f a = cell->aVector().cast(); Vector3f b = cell->bVector().cast(); Vector3f c = cell->cVector().cast(); file += QStringLiteral(" A%1%2%3\n") .arg(a.x(), 16, 'f', 7) .arg(a.y(), 16, 'f', 7) .arg(a.z(), 16, 'f', 7); file += QStringLiteral(" B%1%2%3\n") .arg(b.x(), 16, 'f', 7) .arg(b.y(), 16, 'f', 7) .arg(b.z(), 16, 'f', 7); file += QStringLiteral(" C%1%2%3\n") .arg(c.x(), 16, 'f', 7) .arg(c.y(), 16, 'f', 7) .arg(c.z(), 16, 'f', 7); } else { file += " ! XXX please manually adjust cell size\n"; file += " A 10.00000000 0.000000000 0.000000000\n"; file += " B 0.000000000 10.00000000 0.000000000\n"; file += " C 0.000000000 0.000000000 10.00000000\n"; file += " PERIODIC NONE\n"; } file += " &END CELL\n"; if (m_molecule) { file += " &COORD\n"; for (size_t i = 0; i < m_molecule->atomCount(); ++i) { Core::Atom atom = m_molecule->atom(i); file += QString(" %1 %2 %3 %4\n") .arg(Core::Elements::symbol(atom.atomicNumber()), -3) .arg(atom.position3d().x(), 9, 'f', 5) .arg(atom.position3d().y(), 9, 'f', 5) .arg(atom.position3d().z(), 9, 'f', 5); } file += " &END COORD\n"; } file += " &END SUBSYS\n"; if (gmethod == "QS") { file += " &DFT\n"; file += " ! XXX: you may have to manually modify the file names, " "depending on the system and calculation level\n"; file += " BASIS_SET_FILE_NAME BASIS_SET\n"; file += " POTENTIAL_FILE_NAME GTH_POTENTIALS\n"; if (ui.lsdcheckBox->isChecked()) file += " LSD TRUE\n"; file += " &QS\n"; file += " EPS_DEFAULT 1.0E-10\n"; file += " &END QS\n"; file += QString(" LSD %1\n").arg(lsd); file += " &MGRID\n"; file += " CUTOFF 280\n"; file += " COMMENSURATE\n"; file += " &END MGRID\n"; file += " &SCF\n"; file += QString(" MAX_SCF %1\n").arg(maxSCF); file += QString(" EPS_SCF %1\n").arg(epsSCF); file += QString(" SCF_GUESS %1\n").arg(scfGuess); file += " &OUTER_SCF\n"; file += QString(" MAX_SCF %1\n").arg(outermaxSCF); file += QString(" EPS_SCF %1\n").arg(outerepsSCF); file += " &END\n"; file += " &OT T\n"; file += QString(" MINIMIZER %1\n").arg(otMinimizer); file += " N_DIIS 7\n"; file += " &END OT\n"; file += " &END SCF\n"; file += " &XC\n"; file += QString(" &XC_FUNCTIONAL %1\n").arg(gfunc); file += " &END XC_FUNCTIONAL\n"; file += " &END XC\n"; file += " &END DFT\n"; file += " &PRINT\n"; file += " &FORCES ON\n"; file += " &END FORCES\n"; file += " &END PRINT\n"; } else if (gmethod == "FIST") { file += " &TOPOLOGY\n"; file += " CHARGE_BETA\n"; file += " CONNECTIVITY AMBER\n"; file += " CONN_FILE_NAME ! Add file name that contains connectivity data\n"; file += " &END TOPOLOGY \n"; file += " &PRINT\n"; file += " &TOPOLOGY_INFO\n"; file += " AMBER_INFO\n"; file += " &END\n"; file += " &END \n"; file += " &END SUBSYS\n"; file += " &MM\n"; file += " &FORCEFIELD\n"; file += " PARM_FILE_NAME ! Add file name that contains force field " "parameters\n"; file += " PARMTYPE AMBER\n"; file += " &SPLINE\n"; file += QString(" EMAX_SPLINE %1\n").arg(emaxSpline); file += QString(" RCUT_NB %1\n").arg(rcutnb); file += " &END SPLINE\n"; file += " &END FORCEFIELD\n"; file += " &POISSON\n"; file += " &EWALD\n"; file += QString(" EWALD_TYPE %1\n").arg(ewaldtype); file += QString(" ALPHA %1\n").arg(ewaldalpha); file += QString(" GMAX %1\n").arg(ewaldgmax); file += " &END EWALD\n"; file += " &END POISSON\n"; file += " &PRINT\n"; file += " &FF_INFO\n"; file += " $END\n"; file += " &FF_PARAMETER_FILE\n"; file += " &END\n"; file += " &END PRINT\n"; file += " &END MM\n"; } file += "&END FORCE_EVAL\n"; ui.previewText->setText(file); ui.previewText->document()->setModified(false); updateOptionCache(); } void Cp2kInputDialog::resetClicked() { setBasicDefaults(); updatePreviewText(); } void Cp2kInputDialog::defaultsClicked() { setBasicDefaults(); updatePreviewText(); } void Cp2kInputDialog::generateClicked() { QSettings settings; QString fileName = (ui.baseNameEdit->text().isEmpty() ? ui.baseNameEdit->placeholderText() : ui.baseNameEdit->text()) + ".inp"; QString targetFile = settings.value("cp2kInput/outputDirectory", QDir::homePath()).toString(); targetFile = QDir(QFileInfo(targetFile).absoluteDir()).absoluteFilePath(fileName); fileName = QFileDialog::getSaveFileName(this, tr("Save CP2K input file"), targetFile); // User cancel: if (fileName.isNull()) return; settings.setValue("cp2kInput/outputDirectory", fileName); QFile file(fileName); bool success = false; if (file.open(QFile::WriteOnly | QFile::Text)) { if (file.write(ui.previewText->toPlainText().toLatin1()) > 0) { success = true; } file.close(); } if (!success) { QMessageBox::critical(this, tr("Output Error"), tr("Failed to write to file %1.").arg(fileName)); } } void Cp2kInputDialog::computeClicked() { // Verify that molequeue is running: MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) { QMessageBox::information(this, tr("Cannot connect to MoleQueue"), tr("Cannot connect to MoleQueue server. Please " "ensure that it is running and try again.")); return; } QString description(ui.titleEdit->text()); if (description.isEmpty()) description = generateJobTitle(); QString fileNameBase = ui.baseNameEdit->text().isEmpty() ? ui.baseNameEdit->placeholderText() : ui.baseNameEdit->text(); JobObject job; job.setProgram("CP2K"); job.setDescription(description); job.setInputFile(QString("%1.inp").arg(fileNameBase), ui.previewText->toPlainText()); MoleQueueDialog::SubmitStatus submitStatus = MoleQueueDialog::submitJob(this, tr("Submit CP2K Calculation"), job, MoleQueueDialog::WaitForSubmissionResponse | MoleQueueDialog::SelectProgramFromTemplate); switch (submitStatus) { default: case MoleQueueDialog::SubmissionSuccessful: case MoleQueueDialog::SubmissionFailed: case MoleQueueDialog::SubmissionAttempted: case MoleQueueDialog::SubmissionAborted: // The dialog handles these cases adequately, we don't need to do // anything. break; case MoleQueueDialog::JobFailed: // Inform the user: QMessageBox::information(this, tr("Job Failed"), tr("The job did not complete successfully."), QMessageBox::Ok); break; case MoleQueueDialog::JobFinished: // Let the world know that the job is ready to open. job has been // overwritten with the final job details. emit openJobOutput(job); hide(); break; } } void Cp2kInputDialog::updateTitlePlaceholder() { ui.titleEdit->setPlaceholderText(generateJobTitle()); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/cp2kinput/cp2kinputdialog.h000066400000000000000000000047141506155467400253700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef CP2KINPUTDIALOG_H #define CP2KINPUTDIALOG_H #include #include #include #include "ui_cp2kinputdialog.h" #include class QJsonObject; namespace Avogadro { namespace MoleQueue { class JobObject; } namespace QtGui { class Molecule; } namespace QtPlugins { // class GamessHighlighter; class Cp2kInputDialog : public QDialog { Q_OBJECT enum CalculateOption { CalculateEnergy = 0, CalculateEnergyAndForces, CalculateMolecularDynamics, CalculateGeometryOptimization, CalculateCount }; enum BasisOption { BasisSZVGTH = 0, BasisDZVGTH, BasisDZVPGTH, BasisTZVPGTH, BasisTZV2PGTH, BasisCount }; public: explicit Cp2kInputDialog(QWidget* parent_ = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~Cp2kInputDialog() override; void setMolecule(QtGui::Molecule* mol); signals: /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ void openJobOutput(const Avogadro::MoleQueue::JobObject& job); protected: void showEvent(QShowEvent* e) override; private slots: void updatePreviewText(); void defaultsClicked(); void resetClicked(); void generateClicked(); void computeClicked(); void updateTitlePlaceholder(); private: void connectBasic(); void connectPreview(); void connectButtons(); void buildOptions(); void updateOptionCache(); void restoreOptionCache(); void buildCalculateOptions(); void buildFunctionalOptions(); void buildMethodOptions(); void buildBasisOptions(); void buildStateOptions(); void buildMultiplicityOptions(); void buildChargeOptions(); void buildEWALDTypeOptions(); void buildSCFGuessOptions(); void buildOTMinimizerOptions(); void setBasicDefaults(); /// @return valid values for CP2K RUN_TYPE static QString fromCalcEnum(CalculateOption option); QString generateJobTitle() const; Ui::Cp2kInputDialog ui; QtGui::Molecule* m_molecule; // GamessHighlighter *m_highlighter; bool m_updatePending; QMap m_optionCache; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // CP2KINPUTDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/cp2kinput/cp2kinputdialog.ui000066400000000000000000000470321506155467400255560ustar00rootroot00000000000000 Cp2kInputDialog 0 0 651 566 CP2K Input Reset All Defaults Qt::Horizontal QSizePolicy::MinimumExpanding 10 20 Submit Calculation… Save File… Close 0 1 0 300 0 &Basic Setup Title: titleEdit Filename Base: job Calculate: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter calculateCombo Qt::Horizontal 40 20 Method: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter methodCombo Qt::Horizontal 40 20 Basis set: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter basisCombo Functional Qt::Horizontal 16 20 Qt::Vertical 20 0 MM FF Emax Spline Spin 6 -99.000000000000000 0.000010000000000 0.000000000000000 Qt::Horizontal 40 20 FF RCUT NB 6 -99.000000000000000 -1.000000000000000 Qt::Horizontal 40 20 Poisson EWALD type Qt::Horizontal 40 20 Poisson EWALD Alpha -99.000000000000000 Qt::Horizontal 40 20 Poisson EWALD GMAX 1000 10 Qt::Horizontal 40 20 QM LSD TRUE Qt::Horizontal 40 20 MAX SCF 999 50 Qt::Horizontal 40 20 EPS SCF 8 0.000010000000000 0.000010000000000 Qt::Horizontal 40 20 SCF GUESS Qt::Horizontal 40 20 OUTER MAX SCF 999 50 Qt::Horizontal 40 20 OUTER EPS SCF 8 0.000010000000000 0.000010000000000 Qt::Horizontal 40 20 OT T MINIMIZER Qt::Horizontal 40 20 calculateCombo basisCombo functionalCombo methodCombo previewText resetAllButton defaultsButton generateButton closeButton avogadrolibs-1.101.0/avogadro/qtplugins/crystal/000077500000000000000000000000001506155467400216545ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/crystal/CMakeLists.txt000066400000000000000000000011061506155467400244120ustar00rootroot00000000000000set(crystal_srcs crystal.cpp importcrystaldialog.cpp supercelldialog.cpp unitcelldialog.cpp volumescalingdialog.cpp ) set(crystal_uis importcrystaldialog.ui supercelldialog.ui unitcelldialog.ui volumescalingdialog.ui ) avogadro_plugin(Crystal "Provide crystal-specific editing/analysis." ExtensionPlugin crystal.h Crystal "${crystal_srcs}" "${crystal_uis}" ) avogadro_plugin(CrystalScene "Render unit cell lattice." ScenePlugin crystalscene.h CrystalScene crystalscene.cpp) target_link_libraries(CrystalScene PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/crystal/crystal.cpp000066400000000000000000000165271506155467400240540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "crystal.h" #include "importcrystaldialog.h" #include "supercelldialog.h" #include "unitcelldialog.h" #include "volumescalingdialog.h" #include #include #include #include #include #include #include using Avogadro::Core::CrystalTools; using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { Crystal::Crystal(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_unitCellDialog(nullptr), m_importCrystalClipboardAction(new QAction(this)), m_editUnitCellAction(new QAction(this)), m_buildSupercellAction(new QAction(this)), m_niggliReduceAction(new QAction(this)), m_scaleVolumeAction(new QAction(this)), m_standardOrientationAction(new QAction(this)), m_toggleUnitCellAction(new QAction(this)), m_wrapAtomsToCellAction(new QAction(this)) { m_importCrystalClipboardAction->setText(tr("Import Crystal from Clipboard…")); connect(m_importCrystalClipboardAction, SIGNAL(triggered()), SLOT(importCrystalClipboard())); m_actions.push_back(m_importCrystalClipboardAction); m_importCrystalClipboardAction->setProperty("menu priority", 220); // this will be changed when the molecule is set: m_toggleUnitCellAction->setText(tr("Toggle Unit Cell")); connect(m_toggleUnitCellAction, SIGNAL(triggered()), SLOT(toggleUnitCell())); m_actions.push_back(m_toggleUnitCellAction); m_toggleUnitCellAction->setProperty("menu priority", 210); m_editUnitCellAction->setText(tr("Edit Unit Cell…")); connect(m_editUnitCellAction, SIGNAL(triggered()), SLOT(editUnitCell())); m_actions.push_back(m_editUnitCellAction); m_editUnitCellAction->setProperty("menu priority", 190); m_wrapAtomsToCellAction->setText(tr("&Wrap Atoms to Unit Cell")); connect(m_wrapAtomsToCellAction, SIGNAL(triggered()), SLOT(wrapAtomsToCell())); m_actions.push_back(m_wrapAtomsToCellAction); m_wrapAtomsToCellAction->setProperty("menu priority", 180); m_standardOrientationAction->setText(tr("Rotate to Standard &Orientation")); connect(m_standardOrientationAction, SIGNAL(triggered()), SLOT(standardOrientation())); m_actions.push_back(m_standardOrientationAction); m_standardOrientationAction->setProperty("menu priority", 170); m_scaleVolumeAction->setText(tr("Scale Cell &Volume…")); connect(m_scaleVolumeAction, SIGNAL(triggered()), SLOT(scaleVolume())); m_actions.push_back(m_scaleVolumeAction); m_scaleVolumeAction->setProperty("menu priority", 160); m_buildSupercellAction->setText(tr("Build &Supercell…")); connect(m_buildSupercellAction, SIGNAL(triggered()), SLOT(buildSupercell())); m_actions.push_back(m_buildSupercellAction); m_buildSupercellAction->setProperty("menu priority", 150); m_niggliReduceAction->setText(tr("Reduce Cell (&Niggli)")); connect(m_niggliReduceAction, SIGNAL(triggered()), SLOT(niggliReduce())); m_actions.push_back(m_niggliReduceAction); m_niggliReduceAction->setProperty("menu priority", 140); updateActions(); } Crystal::~Crystal() { if (m_unitCellDialog) m_unitCellDialog->deleteLater(); qDeleteAll(m_actions); m_actions.clear(); } QList Crystal::actions() const { return m_actions; } QStringList Crystal::menuPath(QAction*) const { return QStringList() << tr("&Crystal"); } void Crystal::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_unitCellDialog) m_unitCellDialog->setMolecule(m_molecule); if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); } void Crystal::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); auto changes = static_cast(c); if (changes & Molecule::UnitCell) { if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); } } void Crystal::updateActions() { // Disable everything for nullptr molecules. if (!m_molecule) { foreach (QAction* action, m_actions) action->setEnabled(false); return; } if (m_molecule->unitCell()) { foreach (QAction* action, m_actions) action->setEnabled(true); m_toggleUnitCellAction->setText(tr("Remove &Unit Cell")); } else { foreach (QAction* action, m_actions) action->setEnabled(false); m_importCrystalClipboardAction->setEnabled(true); m_toggleUnitCellAction->setEnabled(true); m_toggleUnitCellAction->setText(tr("Add &Unit Cell")); } } void Crystal::importCrystalClipboard() { ImportCrystalDialog d; Core::Molecule m; if (d.importCrystalClipboard(m)) { // If we succeeded, update m_molecule Molecule::MoleculeChanges changes = Molecule::Added | Molecule::Atoms | Molecule::UnitCell; QString undoText = tr("Import Crystal from Clipboard"); m_molecule->undoMolecule()->modifyMolecule(m, changes, undoText); } } void Crystal::registerCommands() { emit registerCommand("wrapUnitCell", tr("Wrap atoms into the unit cell.")); emit registerCommand("standardCrystalOrientation", tr("Rotate the unit cell to the standard orientation.")); } bool Crystal::handleCommand(const QString& command, [[maybe_unused]] const QVariantMap& options) { if (m_molecule == nullptr) return false; // No molecule to handle the command. if (command == "wrapUnitCell") { wrapAtomsToCell(); return true; } else if (command == "standardCrystalOrientation") { standardOrientation(); return true; } return false; } void Crystal::editUnitCell() { if (!m_unitCellDialog) { m_unitCellDialog = new UnitCellDialog(qobject_cast(parent())); m_unitCellDialog->setMolecule(m_molecule); } m_unitCellDialog->show(); } void Crystal::buildSupercell() { SupercellDialog d; d.buildSupercell(*m_molecule); } void Crystal::niggliReduce() { if (CrystalTools::isNiggliReduced(*m_molecule)) { QMessageBox::information( qobject_cast(parent()), tr("Niggli Reduce Crystal"), tr("The unit cell is already reduced."), QMessageBox::Ok); return; } m_molecule->undoMolecule()->niggliReduceCell(); } void Crystal::scaleVolume() { if (!m_molecule->unitCell()) return; VolumeScalingDialog dlg; dlg.setCurrentVolume(m_molecule->unitCell()->volume()); int reply = dlg.exec(); if (reply != QDialog::Accepted) return; m_molecule->undoMolecule()->setCellVolume( dlg.newVolume(), dlg.transformAtoms() ? CrystalTools::TransformAtoms : CrystalTools::None); } void Crystal::standardOrientation() { m_molecule->undoMolecule()->rotateCellToStandardOrientation(); } void Crystal::toggleUnitCell() { if (m_molecule->unitCell()) { m_molecule->undoMolecule()->removeUnitCell(); } else { m_molecule->undoMolecule()->addUnitCell(); editUnitCell(); } } void Crystal::wrapAtomsToCell() { m_molecule->undoMolecule()->wrapAtomsToCell(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/crystal/crystal.h000066400000000000000000000037001506155467400235060ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CRYSTAL_H #define AVOGADRO_QTPLUGINS_CRYSTAL_H #include namespace Avogadro { namespace QtPlugins { class UnitCellDialog; /** * @brief Tools for crystal-specific editing/analysis. */ class Crystal : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit Crystal(QObject* parent_ = nullptr); ~Crystal() override; QString name() const override { return tr("Crystal"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; bool handleCommand(const QString& command, const QVariantMap& options) override; void registerCommands() override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); private slots: void updateActions(); void importCrystalClipboard(); void editUnitCell(); void buildSupercell(); void niggliReduce(); void scaleVolume(); void standardOrientation(); void toggleUnitCell(); void wrapAtomsToCell(); private: QList m_actions; QtGui::Molecule* m_molecule; UnitCellDialog* m_unitCellDialog; QAction* m_importCrystalClipboardAction; QAction* m_editUnitCellAction; QAction* m_buildSupercellAction; QAction* m_niggliReduceAction; QAction* m_scaleVolumeAction; QAction* m_standardOrientationAction; QAction* m_toggleUnitCellAction; QAction* m_wrapAtomsToCellAction; }; inline QString Crystal::description() const { return tr("Tools for crystal-specific editing/analysis."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CRYSTAL_H avogadrolibs-1.101.0/avogadro/qtplugins/crystal/crystalscene.cpp000066400000000000000000000125431506155467400250640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "crystalscene.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Array; using Core::UnitCell; using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::LineStripGeometry; const Vector3ub red = { 255, 0, 0 }; const Vector3ub green = { 0, 255, 0 }; const Vector3ub blue = { 0, 0, 255 }; CrystalScene::CrystalScene(QObject* p) : ScenePlugin(p), m_setupWidget(nullptr) { m_layerManager = QtGui::PluginLayerManager(m_name); QSettings settings; m_lineWidth = settings.value("crystal/lineWidth", 2.0).toDouble(); auto color = settings.value("crystal/color", QColor(Qt::white)).value(); m_color[0] = static_cast(color.red()); m_color[1] = static_cast(color.green()); m_color[2] = static_cast(color.blue()); m_multiColor = settings.value("crystal/multiColor", true).toBool(); } CrystalScene::~CrystalScene() {} void CrystalScene::process(const QtGui::Molecule& molecule, GroupNode& node) { if (const UnitCell* cell = molecule.unitCell()) { auto* geometry = new GeometryNode; node.addChild(geometry); auto* lines = new LineStripGeometry; geometry->addDrawable(lines); lines->setColor(m_color); auto color = m_color; float width = m_lineWidth; Vector3f a = cell->aVector().cast(); Vector3f b = cell->bVector().cast(); Vector3f c = cell->cVector().cast(); Vector3f vertex(Vector3f::Zero()); Array strip; // draw the a axis strip.reserve(5); strip.push_back(vertex); strip.push_back(vertex + a); if (!m_multiColor) lines->addLineStrip(strip, color, width); else // a axis is R-G-B lines->addLineStrip(strip, red, width); // now the b-axis strip.clear(); strip.push_back(vertex); strip.push_back(vertex + b); if (!m_multiColor) lines->addLineStrip(strip, color, width); else // b axis is R-G-B lines->addLineStrip(strip, green, width); // now the rest of the ab plane strip.clear(); strip.push_back(vertex + a); strip.push_back(vertex + a + b); strip.push_back(vertex + b); lines->addLineStrip(strip, width); // now the ab plane "up" by axis c strip.clear(); strip.push_back(vertex + c); strip.push_back(vertex + a + c); strip.push_back(vertex + a + b + c); strip.push_back(vertex + b + c); strip.push_back(vertex + c); lines->addLineStrip(strip, width); // now the c axis strip.resize(2); strip[0] = Vector3f::Zero(); strip[1] = c; if (!m_multiColor) lines->addLineStrip(strip, color, width); else // c axis is R-G-B lines->addLineStrip(strip, blue, width); // now the remaining "struts" from ab plane along c axis strip[0] += a; strip[1] += a; lines->addLineStrip(strip, width); strip[0] += b; strip[1] += b; lines->addLineStrip(strip, width); strip[0] -= a; strip[1] -= a; lines->addLineStrip(strip, width); } } void CrystalScene::setLineWidth(double width) { m_lineWidth = width; emit drawablesChanged(); QSettings settings; settings.setValue("crystal/lineWidth", width); } void CrystalScene::setColor(const QColor& color) { m_color[0] = static_cast(color.red()); m_color[1] = static_cast(color.green()); m_color[2] = static_cast(color.blue()); emit drawablesChanged(); QSettings settings; settings.setValue("crystal/color", color); } void CrystalScene::setMultiColor(bool multiColor) { m_multiColor = multiColor; emit drawablesChanged(); QSettings settings; settings.setValue("crystal/multiColor", multiColor); } QWidget* CrystalScene::setupWidget() { if (!m_setupWidget) { m_setupWidget = new QWidget(qobject_cast(parent())); auto* v = new QVBoxLayout; // line width auto* spin = new QDoubleSpinBox; spin->setRange(0.5, 5.0); spin->setSingleStep(0.25); spin->setDecimals(2); spin->setValue(m_lineWidth); connect(spin, SIGNAL(valueChanged(double)), SLOT(setLineWidth(double))); auto* form = new QFormLayout; form->addRow(tr("Line width:"), spin); auto* multiColor = new QCheckBox; multiColor->setChecked(m_multiColor); form->addRow(tr("Color axes:"), multiColor); connect(multiColor, SIGNAL(toggled(bool)), SLOT(setMultiColor(bool))); auto* color = new QtGui::ColorButton; connect(color, SIGNAL(colorChanged(const QColor&)), SLOT(setColor(const QColor&))); form->addRow(tr("Line color:"), color); v->addLayout(form); v->addStretch(1); m_setupWidget->setLayout(v); } return m_setupWidget; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/crystal/crystalscene.h000066400000000000000000000026621506155467400245320ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CRYSTALSCENE_H #define AVOGADRO_QTPLUGINS_CRYSTALSCENE_H #include #include #include namespace Avogadro { namespace QtPlugins { /** * @brief Render the unit cell boundaries. */ class CrystalScene : public QtGui::ScenePlugin { Q_OBJECT public: explicit CrystalScene(QObject* parent = nullptr); ~CrystalScene() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Crystal Lattice"); } QString description() const override { return tr("Render the unit cell boundaries."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } private slots: void setColor(const QColor& color); void setLineWidth(double width); void setMultiColor(bool multiColor); private: std::string m_name = "Crystal Lattice"; QWidget* m_setupWidget; float m_lineWidth; Vector3ub m_color; bool m_multiColor; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CRYSTALSCENE_H avogadrolibs-1.101.0/avogadro/qtplugins/crystal/importcrystaldialog.cpp000066400000000000000000000037551506155467400264660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "importcrystaldialog.h" #include "ui_importcrystaldialog.h" #include #include #include #include #include #include using std::string; namespace Avogadro::QtPlugins { ImportCrystalDialog::ImportCrystalDialog(QWidget* p) : QDialog(p), m_ui(new Ui::ImportCrystalDialog) { m_ui->setupUi(this); } ImportCrystalDialog::~ImportCrystalDialog() { delete m_ui; } bool ImportCrystalDialog::importCrystalClipboard(Avogadro::Core::Molecule& mol) { QString text = QApplication::clipboard()->text(); m_ui->edit_text->setText(text); // If the user rejected, just return false if (this->exec() == QDialog::Rejected) return false; // Use POSCAR format by default. If the extension was set, use that instead std::string ext = m_ui->edit_extension->text().toStdString(); if (ext.empty()) ext = "POSCAR"; // Update the text text = m_ui->edit_text->toPlainText(); std::stringstream s(text.toStdString()); if (Io::FileFormatManager::instance().readString(mol, s.str(), ext)) return true; // Print out the error messages from the read if we failed if (!Io::FileFormatManager::instance().error().empty()) { qDebug() << "FileFormatManager error message:" << QString::fromStdString( Io::FileFormatManager::instance().error()); } displayInvalidFormatMessage(); return false; } void ImportCrystalDialog::displayInvalidFormatMessage() { QMessageBox::critical( this, tr("Cannot Parse Text"), tr("Failed to read the data with the supplied format.")); reject(); close(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/crystal/importcrystaldialog.h000066400000000000000000000022631506155467400261240ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_IMPORTCRYSTALDIALOG_H #define AVOGADRO_QTPLUGINS_IMPORTCRYSTALDIALOG_H #include #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { namespace Ui { class ImportCrystalDialog; } /** * @brief The ImportCrystalDialog class provides a dialog for importing * a crystal from the clipboard. */ class ImportCrystalDialog : public QDialog { Q_OBJECT public: ImportCrystalDialog(QWidget* p = nullptr); ~ImportCrystalDialog() override; // Avogadro::Core::Molecule is required for the format function bool importCrystalClipboard(Avogadro::Core::Molecule& mol); void displayInvalidFormatMessage(); private: AVO_DISABLE_COPY(ImportCrystalDialog) Ui::ImportCrystalDialog* m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_IMPORTCRYSTALDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/crystal/importcrystaldialog.ui000066400000000000000000000040501506155467400263060ustar00rootroot00000000000000 Avogadro::QtPlugins::ImportCrystalDialog 0 0 400 300 Import Crystal File extension for Open Babel conversion (default - Avogadro::POSCAR): edit_extension Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() Avogadro::QtPlugins::ImportCrystalDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::QtPlugins::ImportCrystalDialog reject() 316 260 286 274 avogadrolibs-1.101.0/avogadro/qtplugins/crystal/supercelldialog.cpp000066400000000000000000000024261506155467400255420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "supercelldialog.h" #include "ui_supercelldialog.h" #include #include #include #include namespace Avogadro::QtPlugins { SupercellDialog::SupercellDialog(QWidget* p) : QDialog(p), m_ui(new Ui::SupercellDialog) { m_ui->setupUi(this); } SupercellDialog::~SupercellDialog() { delete m_ui; } bool SupercellDialog::buildSupercell(Avogadro::QtGui::Molecule& mol) { // If the user rejected, just return false if (this->exec() == QDialog::Rejected) return false; // Read the values unsigned int a = m_ui->aCellSpinBox->value(); unsigned int b = m_ui->bCellSpinBox->value(); unsigned int c = m_ui->cCellSpinBox->value(); // No need to do anything if all the values are one if (a == 1 && b == 1 && c == 1) return true; // Run the supercell-building tool mol.undoMolecule()->buildSupercell(a, b, c); return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/crystal/supercelldialog.h000066400000000000000000000021001506155467400251740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SUPERCELLDIALOG_H #define AVOGADRO_QTPLUGINS_SUPERCELLDIALOG_H #include #include namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { namespace Ui { class SupercellDialog; } /** * @brief The SupercellDialog class provides a dialog for * building a supercell from a crystal. */ class SupercellDialog : public QDialog { Q_OBJECT public: SupercellDialog(QWidget* p = nullptr); ~SupercellDialog() override; bool buildSupercell(Avogadro::QtGui::Molecule& mol); void displayInvalidFormatMessage(); private: AVO_DISABLE_COPY(SupercellDialog) Ui::SupercellDialog* m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SUPERCELLDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/crystal/supercelldialog.ui000066400000000000000000000064721506155467400254020ustar00rootroot00000000000000 Avogadro::QtPlugins::SupercellDialog 0 0 324 188 Supercell Parameters Super Cell Options A repeat: 1 B repeat: 1 C repeat: 1 QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Vertical 20 0 ok_cancel_bb accepted() Avogadro::QtPlugins::SupercellDialog accept() 161 159 161 93 ok_cancel_bb rejected() Avogadro::QtPlugins::SupercellDialog reject() 161 159 161 93 avogadrolibs-1.101.0/avogadro/qtplugins/crystal/unitcelldialog.cpp000066400000000000000000000245501506155467400253650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "unitcelldialog.h" #include "ui_unitcelldialog.h" #include #include #include #include #include #include #include using Avogadro::QtGui::Molecule; namespace { // Matrix formatting: const int MATRIX_WIDTH = 9; const int MATRIX_PREC = 5; const char MATRIX_FMT = 'f'; // Valid value separators in matrix editors: const static QRegularExpression MATRIX_SEP( R"(\s|,|;|\||\[|\]|\{|\}|\(|\)|\&|/|<|>)"); } // namespace namespace Avogadro::QtPlugins { UnitCellDialog::UnitCellDialog(QWidget* p) : QDialog(p), m_ui(new Ui::UnitCellDialog), m_molecule(nullptr), m_mode(Invalid) { m_ui->setupUi(this); connect(m_ui->a, SIGNAL(valueChanged(double)), SLOT(parametersEdited())); connect(m_ui->b, SIGNAL(valueChanged(double)), SLOT(parametersEdited())); connect(m_ui->c, SIGNAL(valueChanged(double)), SLOT(parametersEdited())); connect(m_ui->alpha, SIGNAL(valueChanged(double)), SLOT(parametersEdited())); connect(m_ui->beta, SIGNAL(valueChanged(double)), SLOT(parametersEdited())); connect(m_ui->gamma, SIGNAL(valueChanged(double)), SLOT(parametersEdited())); connect(m_ui->cellMatrix, SIGNAL(textChanged()), SLOT(cellMatrixEdited())); connect(m_ui->fractionalMatrix, SIGNAL(textChanged()), SLOT(fractionalMatrixEdited())); connect(m_ui->apply, SIGNAL(clicked()), SLOT(apply())); connect(m_ui->revert, SIGNAL(clicked()), SLOT(revert())); } UnitCellDialog::~UnitCellDialog() { delete m_ui; } void UnitCellDialog::setMolecule(QtGui::Molecule* molecule) { if (molecule != m_molecule) { if (m_molecule) m_molecule->disconnect(this); m_molecule = molecule; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); revert(); } } void UnitCellDialog::moleculeChanged(unsigned int changes) { if (changes & Molecule::UnitCell) revert(); } void UnitCellDialog::parametersEdited() { setMode(Parameters); updateParameters(); revertCellMatrix(); revertFractionalMatrix(); } void UnitCellDialog::cellMatrixEdited() { setMode(CellMatrix); if (validateCellMatrix()) { updateCellMatrix(); revertParameters(); revertFractionalMatrix(); enableApply(true); } else { enableApply(false); } } void UnitCellDialog::fractionalMatrixEdited() { setMode(FractionalMatrix); if (validateFractionalMatrix()) { updateFractionalMatrix(); revertParameters(); revertCellMatrix(); enableApply(true); } else { enableApply(false); } } void UnitCellDialog::apply() { if (!isCrystal()) { revert(); return; } switch (m_mode) { case Invalid: case Clean: revert(); break; default: { Core::CrystalTools::Options options = Core::CrystalTools::None; if (m_ui->transformAtoms->isChecked()) options |= Core::CrystalTools::TransformAtoms; m_molecule->undoMolecule()->editUnitCell(m_tempCell.cellMatrix(), options); break; } } } void UnitCellDialog::revert() { if (isCrystal()) m_tempCell = *m_molecule->unitCell(); revertParameters(); revertCellMatrix(); validateCellMatrix(); revertFractionalMatrix(); validateFractionalMatrix(); setMode(isCrystal() ? Clean : Invalid); } bool UnitCellDialog::isCrystal() const { return m_molecule && m_molecule->unitCell(); } void UnitCellDialog::setMode(UnitCellDialog::Mode m) { if (m != m_mode) { m_mode = m; enableParameters(m == Clean || m == Parameters); enableCellMatrix(m == Clean || m == CellMatrix); enableFractionalMatrix(m == Clean || m == FractionalMatrix); enableApply(m != Clean && m != Invalid); enableRevert(m != Clean && m != Invalid); } } void UnitCellDialog::enableParameters(bool e) { m_ui->a->setEnabled(e); m_ui->b->setEnabled(e); m_ui->c->setEnabled(e); m_ui->alpha->setEnabled(e); m_ui->beta->setEnabled(e); m_ui->gamma->setEnabled(e); } void UnitCellDialog::enableCellMatrix(bool e) { m_ui->cellMatrix->setEnabled(e); } void UnitCellDialog::enableFractionalMatrix(bool e) { m_ui->fractionalMatrix->setEnabled(e); } void UnitCellDialog::enableApply(bool e) { m_ui->apply->setEnabled(e); } void UnitCellDialog::enableRevert(bool e) { m_ui->revert->setEnabled(e); } void UnitCellDialog::blockParametersSignals(bool e) { m_ui->a->blockSignals(e); m_ui->b->blockSignals(e); m_ui->c->blockSignals(e); m_ui->alpha->blockSignals(e); m_ui->beta->blockSignals(e); m_ui->gamma->blockSignals(e); } void UnitCellDialog::blockCellMatrixSignals(bool e) { m_ui->cellMatrix->blockSignals(e); } void UnitCellDialog::blockFractionalMatrixSignals(bool e) { m_ui->fractionalMatrix->blockSignals(e); } void UnitCellDialog::revertParameters() { blockParametersSignals(true); if (isCrystal()) { m_ui->a->setValue(static_cast(m_tempCell.a())); m_ui->b->setValue(static_cast(m_tempCell.b())); m_ui->c->setValue(static_cast(m_tempCell.c())); m_ui->alpha->setValue(static_cast(m_tempCell.alpha() * RAD_TO_DEG)); m_ui->beta->setValue(static_cast(m_tempCell.beta() * RAD_TO_DEG)); m_ui->gamma->setValue(static_cast(m_tempCell.gamma() * RAD_TO_DEG)); } else { enableParameters(false); m_ui->a->setValue(3.); m_ui->b->setValue(3.); m_ui->c->setValue(3.); m_ui->alpha->setValue(90.); m_ui->beta->setValue(90.); m_ui->gamma->setValue(90.); } blockParametersSignals(false); } void UnitCellDialog::revertCellMatrix() { blockCellMatrixSignals(true); if (isCrystal()) { m_ui->cellMatrix->setPlainText(matrixToString(m_tempCell.cellMatrix())); } else { enableCellMatrix(false); m_ui->cellMatrix->setPlainText(tr("No unit cell present.")); } blockCellMatrixSignals(false); } void UnitCellDialog::revertFractionalMatrix() { blockFractionalMatrixSignals(true); if (isCrystal()) { m_ui->fractionalMatrix->setPlainText( matrixToString(m_tempCell.fractionalMatrix())); } else { enableFractionalMatrix(false); m_ui->fractionalMatrix->setPlainText(tr("No unit cell present.")); } blockFractionalMatrixSignals(false); } void UnitCellDialog::updateParameters() { const auto a = static_cast(m_ui->a->value()); const auto b = static_cast(m_ui->b->value()); const auto c = static_cast(m_ui->c->value()); assert(a > 0.0 && b > 0.0 && c > 0.0); Core::UnitCell tmp; tmp.setCellParameters(a, b, c, static_cast(m_ui->alpha->value()) * DEG_TO_RAD, static_cast(m_ui->beta->value()) * DEG_TO_RAD, static_cast(m_ui->gamma->value()) * DEG_TO_RAD); if (!tmp.isRegular()) { QMessageBox::warning(nullptr, tr("Unit Cell Editor"), tr("Ignoring singular cell matrix")); return; } m_tempCell = tmp; } void UnitCellDialog::updateCellMatrix() { const Matrix3 tmp = stringToMatrix(m_ui->cellMatrix->toPlainText()); if (!Core::UnitCell::isRegular(tmp)) { QMessageBox::warning(nullptr, tr("Unit Cell Editor"), tr("Ignoring singular cell matrix")); return; } m_tempCell.setCellMatrix(tmp); } void UnitCellDialog::updateFractionalMatrix() { const Matrix3 tmp = stringToMatrix(m_ui->fractionalMatrix->toPlainText()); if (!Core::UnitCell::isRegular(tmp)) { QMessageBox::warning(nullptr, tr("Unit Cell Editor"), tr("Ignoring singular fractional cell matrix")); return; } m_tempCell.setFractionalMatrix(tmp); } bool UnitCellDialog::validateCellMatrix() { return validateMatrixEditor(m_ui->cellMatrix); } bool UnitCellDialog::validateFractionalMatrix() { return validateMatrixEditor(m_ui->fractionalMatrix); } void UnitCellDialog::initializeMatrixEditor(QPlainTextEdit* edit) { #if defined(Q_OS_WIN) || defined(Q_OS_OSX) QFont font("Courier"); #else QFont font("Monospace"); #endif edit->setFont(font); QFontMetrics metrics(font); int minWidth = 3 * metrics.horizontalAdvance('0') * (MATRIX_WIDTH + 1); int minHeight = metrics.lineSpacing() * 3; edit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); edit->setMinimumSize(minWidth, minHeight); } bool UnitCellDialog::validateMatrixEditor(QPlainTextEdit* edit) { bool valid = stringToMatrix(edit->toPlainText()) != Matrix3::Zero(); QPalette pal = edit->palette(); pal.setColor(QPalette::Text, valid ? Qt::black : Qt::red); edit->setPalette(pal); return valid; } QString UnitCellDialog::matrixToString(const Matrix3& mat) { // Transpose into the more intuitive row-vector format. return QString("%1 %2 %3\n%4 %5 %6\n%7 %8 %9") .arg(static_cast(mat(0, 0)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(1, 0)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(2, 0)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(0, 1)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(1, 1)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(2, 1)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(0, 2)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(1, 2)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC) .arg(static_cast(mat(2, 2)), MATRIX_WIDTH, MATRIX_FMT, MATRIX_PREC); } Matrix3 UnitCellDialog::stringToMatrix(const QString& str) { Matrix3 result; QStringList lines = str.split('\n'); if (lines.size() != 3) return Matrix3::Zero(); bool ok; int row = 0; int col = 0; foreach (const QString& line, lines) { QStringList values = line.split(MATRIX_SEP, Qt::SkipEmptyParts); if (values.size() != 3) return Matrix3::Zero(); foreach (const QString& value, values) { Real val = static_cast(value.toDouble(&ok)); if (!ok) return Matrix3::Zero(); // Transpose from the more intuitive row-vector format. result(col++, row) = val; } row++; col = 0; } return result; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/crystal/unitcelldialog.h000066400000000000000000000044121506155467400250250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_UNITCELLDIALOG_H #define AVOGADRO_QTPLUGINS_UNITCELLDIALOG_H #include #include #include #include class QPlainTextEdit; namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { namespace Ui { class UnitCellDialog; } /** * @brief The UnitCellDialog class provides a dialog for editing a molecule's * unit cell. */ class UnitCellDialog : public QDialog { Q_OBJECT public: enum Mode { Clean, Invalid, Parameters, CellMatrix, FractionalMatrix }; explicit UnitCellDialog(QWidget* parent = nullptr); ~UnitCellDialog() override; void setMolecule(QtGui::Molecule* molecule); public slots: void moleculeChanged(unsigned int changes); void parametersEdited(); void cellMatrixEdited(); void fractionalMatrixEdited(); void apply(); void revert(); private: bool isCrystal() const; void setMode(Mode m); void enableParameters(bool e); void enableCellMatrix(bool e); void enableFractionalMatrix(bool e); void enableApply(bool e); void enableRevert(bool e); void blockParametersSignals(bool e); void blockCellMatrixSignals(bool e); void blockFractionalMatrixSignals(bool e); // m_tempCell --> ui void revertParameters(); void revertCellMatrix(); void revertFractionalMatrix(); // ui --> m_tempCell void updateParameters(); void updateCellMatrix(); void updateFractionalMatrix(); bool validateCellMatrix(); bool validateFractionalMatrix(); static void initializeMatrixEditor(QPlainTextEdit* edit); static bool validateMatrixEditor(QPlainTextEdit* edit); static QString matrixToString(const Matrix3& mat); static Matrix3 stringToMatrix(const QString& str); private: Ui::UnitCellDialog* m_ui; QtGui::Molecule* m_molecule; Core::UnitCell m_tempCell; Mode m_mode; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_UNITCELLDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/crystal/unitcelldialog.ui000066400000000000000000000233661506155467400252240ustar00rootroot00000000000000 Avogadro::QtPlugins::UnitCellDialog 0 0 334 431 Unit Cell Editor QFormLayout::AllNonFixedFieldsGrow A: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 5 0.010000000000000 1000000.000000000000000 0.100000000000000 3.000000000000000 B: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 5 0.010000000000000 1000000.000000000000000 0.100000000000000 3.000000000000000 C: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 5 0.010000000000000 1000000.000000000000000 0.100000000000000 3.000000000000000 QFormLayout::AllNonFixedFieldsGrow α: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter β: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter γ: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter ° 5 5.000000000000000 175.000000000000000 90.000000000000000 ° 5 5.000000000000000 175.000000000000000 90.000000000000000 ° 5 5.000000000000000 175.000000000000000 90.000000000000000 Qt::Horizontal Cell Matrix: Qt::Horizontal Fractional Matrix: Qt::Vertical 20 54 Qt::Horizontal &Transform Atoms Qt::Horizontal 40 20 &Apply &Revert &Hide a b c alpha beta gamma cellMatrix fractionalMatrix apply revert pushButton clicked() Avogadro::QtPlugins::UnitCellDialog hide() 309 404 325 372 avogadrolibs-1.101.0/avogadro/qtplugins/crystal/volumescalingdialog.cpp000066400000000000000000000031371506155467400264140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the MoleQueue project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "volumescalingdialog.h" #include "ui_volumescalingdialog.h" namespace Avogadro::QtPlugins { VolumeScalingDialog::VolumeScalingDialog(QWidget* p) : QDialog(p), m_ui(new Ui::VolumeScalingDialog), m_currentVolume(0.) { m_ui->setupUi(this); connect(m_ui->newVolume, SIGNAL(valueChanged(double)), SLOT(volumeEdited())); connect(m_ui->scalingFactor, SIGNAL(valueChanged(double)), SLOT(factorEdited())); } VolumeScalingDialog::~VolumeScalingDialog() { delete m_ui; } void VolumeScalingDialog::setCurrentVolume(double vol) { m_currentVolume = vol; m_ui->currentVolume->setText(QString::number(vol, 'f', 5)); m_ui->newVolume->setValue(vol); } double VolumeScalingDialog::newVolume() const { return m_ui->newVolume->value(); } bool VolumeScalingDialog::transformAtoms() const { return m_ui->transformAtoms->isChecked(); } void VolumeScalingDialog::volumeEdited() { double v = m_ui->newVolume->value(); m_ui->scalingFactor->blockSignals(true); m_ui->scalingFactor->setValue(v / m_currentVolume); m_ui->scalingFactor->blockSignals(false); } void VolumeScalingDialog::factorEdited() { double f = m_ui->scalingFactor->value(); m_ui->newVolume->blockSignals(true); m_ui->newVolume->setValue(m_currentVolume * f); m_ui->newVolume->blockSignals(false); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/crystal/volumescalingdialog.h000066400000000000000000000021611506155467400260550ustar00rootroot00000000000000/****************************************************************************** This source file is part of the MoleQueue project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_VOLUMESCALINGDIALOG_H #define AVOGADRO_QTPLUGINS_VOLUMESCALINGDIALOG_H #include namespace Avogadro { namespace QtPlugins { namespace Ui { class VolumeScalingDialog; } /** * @brief The VolumeScalingDialog class provides a dialog with options for * adjusting the volume of a Molecule's UnitCell. */ class VolumeScalingDialog : public QDialog { Q_OBJECT public: explicit VolumeScalingDialog(QWidget* parent = nullptr); ~VolumeScalingDialog() override; void setCurrentVolume(double vol); double newVolume() const; bool transformAtoms() const; private slots: void volumeEdited(); void factorEdited(); private: Ui::VolumeScalingDialog* m_ui; double m_currentVolume; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_VOLUMESCALINGDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/crystal/volumescalingdialog.ui000066400000000000000000000116371506155467400262530ustar00rootroot00000000000000 Avogadro::QtPlugins::VolumeScalingDialog 0 0 348 151 Scale Unit Cell Volume Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter New &Volume: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter newVolume &Scaling Factor: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter scalingFactor Current Volume: TextLabel 5 0.010000000000000 9999999.000000000000000 5 0.000010000000000 999999.999990000040270 0.100000000000000 1.000000000000000 Qt::Vertical 20 40 &Transform Atoms Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok newVolume scalingFactor transformAtoms buttonBox buttonBox accepted() Avogadro::QtPlugins::VolumeScalingDialog accept() 374 222 157 274 buttonBox rejected() Avogadro::QtPlugins::VolumeScalingDialog reject() 385 228 286 274 avogadrolibs-1.101.0/avogadro/qtplugins/customelements/000077500000000000000000000000001506155467400232425ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/customelements/CMakeLists.txt000066400000000000000000000002531506155467400260020ustar00rootroot00000000000000avogadro_plugin(CustomElements "Manipulate custom element types in the current molecule." ExtensionPlugin customelements.h CustomElements "customelements.cpp" ) avogadrolibs-1.101.0/avogadro/qtplugins/customelements/customelements.cpp000066400000000000000000000037731506155467400270270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "customelements.h" #include #include #include using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { CustomElements::CustomElements(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_reassignAction(new QAction(tr("Reassign &Custom Elements…"), this)) { connect(m_reassignAction, SIGNAL(triggered()), SLOT(reassign())); updateReassignAction(); } CustomElements::~CustomElements() {} QString CustomElements::description() const { return tr("Manipulate custom element types in the current molecule."); } QList CustomElements::actions() const { return QList() << m_reassignAction; } QStringList CustomElements::menuPath(QAction*) const { return QStringList() << tr("&Build"); } void CustomElements::setMolecule(QtGui::Molecule* mol) { if (m_molecule != mol) { if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateReassignAction(); } } void CustomElements::moleculeChanged(unsigned int c) { auto changes = static_cast(c); if (changes & Molecule::Atoms && (changes & (Molecule::Added | Molecule::Modified))) { updateReassignAction(); } } void CustomElements::reassign() { if (m_molecule) { QtGui::CustomElementDialog::resolve(qobject_cast(parent()), *m_molecule); } } void CustomElements::updateReassignAction() { m_reassignAction->setEnabled(m_molecule && m_molecule->hasCustomElements()); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/customelements/customelements.h000066400000000000000000000023461506155467400264670ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_CUSTOMELEMENTS_H #define AVOGADRO_QTPLUGINS_CUSTOMELEMENTS_H #include namespace Avogadro { namespace QtPlugins { /** * @brief Manipulate custom element types in the current molecule. */ class CustomElements : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit CustomElements(QObject* parent_ = nullptr); ~CustomElements() override; QString name() const override { return tr("Custom Elements"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void moleculeChanged(unsigned int changes); void reassign(); private: QtGui::Molecule* m_molecule; QAction* m_reassignAction; void updateReassignAction(); }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_CUSTOMELEMENTS_H avogadrolibs-1.101.0/avogadro/qtplugins/dipole/000077500000000000000000000000001506155467400214475ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/dipole/CMakeLists.txt000066400000000000000000000002621506155467400242070ustar00rootroot00000000000000avogadro_plugin(Dipole "Dipole rendering scheme" ScenePlugin dipole.h Dipole dipole.cpp "") target_link_libraries(Dipole PRIVATE Avogadro::Calc Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/dipole/dipole.cpp000066400000000000000000000052741506155467400234370ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "dipole.h" #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using QtGui::Molecule; using Rendering::ArrowGeometry; using Rendering::GeometryNode; using Rendering::GroupNode; Dipole::Dipole(QObject* p) : ScenePlugin(p) { m_layerManager = QtGui::PluginLayerManager(m_name); } Dipole::~Dipole() {} void Dipole::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { // check if the molecule is empty // (single atoms don't have a dipole moment) if (molecule.atomCount() < 2) { return; } // check if the molecule has the dipole set if (!m_customDipole) { if (molecule.hasData("dipoleMoment")) { m_dipoleVector = molecule.data("dipoleMoment").toVector3(); } else { // connect to molecule changes connect(&molecule, &QtGui::Molecule::update, this, &Dipole::updateDipole); connect(&molecule, SIGNAL(changed(unsigned int)), SLOT(updateDipole())); } } else { // custom dipole moment set m_dipoleVector = m_customDipoleVector; } // okay if we have all that, set up the arrow auto* geometry = new GeometryNode; node.addChild(geometry); auto* arrow = new ArrowGeometry; arrow->identifier().molecule = &molecule; arrow->setColor(Vector3ub(255, 0, 0)); geometry->addDrawable(arrow); Vector3f origin = Vector3f::Zero(); arrow->addSingleArrow(m_dipoleVector.cast(), origin); } void Dipole::updateFinished() { m_updateNeeded = true; emit drawablesChanged(); } void Dipole::updateDipole() { QtGui::Molecule* molecule = qobject_cast(sender()); if (molecule == nullptr || molecule->isInteractive()) return; // if the molecule has a dipole moment set, use it if (molecule->hasData("dipoleMoment")) return; // otherwise, calculate it if (m_updateNeeded) { m_updateNeeded = false; m_dipoleVector = Calc::ChargeManager::instance().dipoleMoment(m_type, *molecule); // single-shot QTimer::singleShot(0, this, SLOT(updateFinished())); } } QWidget* Dipole::setupWidget() { return nullptr; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/dipole/dipole.h000066400000000000000000000030331506155467400230730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_DIPOLE_H #define AVOGADRO_QTPLUGINS_DIPOLE_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief Render a molecule dipole moment arrow */ class Dipole : public QtGui::ScenePlugin { Q_OBJECT public: explicit Dipole(QObject* parent = nullptr); ~Dipole() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Dipole Moment"); } QString description() const override { return tr("Render the dipole moment of the molecule."); } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } QWidget* setupWidget() override; bool hasSetupWidget() const override { return false; } public slots: void updateDipole(); void updateFinished(); private: std::string m_name = "Dipole Moment"; std::string m_type = "MMFF94"; std::vector m_types; Vector3 m_dipoleVector; Vector3 m_customDipoleVector; bool m_customDipole = false; // Custom dipole moment set bool m_updateNeeded = true; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_FORCE_H avogadrolibs-1.101.0/avogadro/qtplugins/editor/000077500000000000000000000000001506155467400214615ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/editor/CMakeLists.txt000066400000000000000000000004711506155467400242230ustar00rootroot00000000000000set(editor_srcs editor.cpp editortoolwidget.cpp ) set(editor_uis editortoolwidget.ui ) set(editor_rcs editor.qrc ) avogadro_plugin(Editor "Editor tool" ToolPlugin editor.h Editor "${editor_srcs}" "${editor_uis}" "${editor_rcs}" ) target_link_libraries(Editor PRIVATE Avogadro::QtOpenGL) avogadrolibs-1.101.0/avogadro/qtplugins/editor/editor.cpp000066400000000000000000000623541506155467400234650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "editor.h" #include "editortoolwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { const unsigned char INVALID_ATOMIC_NUMBER = std::numeric_limits::max(); } namespace Avogadro::QtPlugins { using QtGui::Molecule; using QtGui::RWAtom; using QtGui::RWBond; using Avogadro::Core::contrastColor; using Avogadro::Core::Elements; using Avogadro::Rendering::GeometryNode; using Avogadro::Rendering::GroupNode; using Avogadro::Rendering::Identifier; using Avogadro::Rendering::TextLabel2D; using Avogadro::Rendering::TextProperties; Editor::Editor(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_glWidget(nullptr), m_renderer(nullptr), m_toolWidget(new EditorToolWidget(qobject_cast(parent_))), m_pressedButtons(Qt::NoButton), m_clickedAtomicNumber(INVALID_ATOMIC_NUMBER), m_bondAdded(false), m_fixValenceLater(false), m_layerManager("Editor") { QString shortcut = tr("Ctrl+2", "control-key 2"); m_activateAction->setText(tr("Draw")); m_activateAction->setToolTip( tr("Draw Tool\t(%1)\n\n" "Left Mouse:\tClick and Drag to create Atoms and Bond\n" "Right Mouse:\tDelete Atom") .arg(shortcut)); setIcon(); reset(); } Editor::~Editor() {} void Editor::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/editor_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/editor_light.svg")); } QWidget* Editor::toolWidget() const { return m_toolWidget; } QUndoCommand* Editor::mousePressEvent(QMouseEvent* e) { clearKeyPressBuffer(); if (!m_renderer || !m_molecule) return nullptr; updatePressedButtons(e, false); m_clickPosition = e->pos(); if (m_pressedButtons & Qt::LeftButton) { m_clickedObject = m_renderer->hit(e->pos().x(), e->pos().y()); if (m_layerManager.activeLayerLocked()) { e->accept(); return nullptr; } switch (m_clickedObject.type) { case Rendering::InvalidType: m_molecule->beginMergeMode(tr("Draw Atom")); emptyLeftClick(e); return nullptr; case Rendering::AtomType: // We don't know yet if we are drawing a bond/atom or replacing an atom // unfortunately... m_molecule->beginMergeMode(tr("Draw")); atomLeftClick(e); return nullptr; case Rendering::BondType: m_molecule->beginMergeMode(tr("Change Bond Type")); bondLeftClick(e); return nullptr; } } else if (m_pressedButtons & Qt::RightButton) { m_clickedObject = m_renderer->hit(e->pos().x(), e->pos().y()); switch (m_clickedObject.type) { case Rendering::AtomType: m_molecule->beginMergeMode(tr("Remove Atom")); atomRightClick(e); return nullptr; case Rendering::BondType: m_molecule->beginMergeMode(tr("Remove Bond")); bondRightClick(e); return nullptr; default: break; } } return nullptr; } QUndoCommand* Editor::mouseReleaseEvent(QMouseEvent* e) { if (!m_renderer || !m_molecule) return nullptr; if (m_layerManager.activeLayerLocked()) { e->accept(); return nullptr; } updatePressedButtons(e, true); if (m_clickedObject.type == Rendering::InvalidType) return nullptr; switch (e->button()) { case Qt::LeftButton: case Qt::RightButton: reset(); e->accept(); m_molecule->endMergeMode(); // Let's cover all possible changes - the undo stack won't update // without this m_molecule->emitChanged(Molecule::Atoms | Molecule::Bonds | Molecule::Added | Molecule::Removed | Molecule::Modified); break; default: break; } return nullptr; } QUndoCommand* Editor::mouseMoveEvent(QMouseEvent* e) { if (!m_renderer) return nullptr; if (m_pressedButtons & Qt::LeftButton) if (m_clickedObject.type == Rendering::AtomType) { if (m_layerManager.activeLayerLocked()) { e->accept(); return nullptr; } atomLeftDrag(e); } return nullptr; } QUndoCommand* Editor::keyPressEvent(QKeyEvent* e) { if (e->text().isEmpty()) return nullptr; e->accept(); if (m_layerManager.activeLayerLocked()) { return nullptr; } // Set a timer to clear the buffer on first keypress: if (m_keyPressBuffer.isEmpty()) QTimer::singleShot(2000, this, SLOT(clearKeyPressBuffer())); m_keyPressBuffer.append(m_keyPressBuffer.isEmpty() ? e->text().toUpper() : e->text().toLower()); if (m_keyPressBuffer.size() >= 3) { clearKeyPressBuffer(); return nullptr; } bool ok = false; int atomicNum; int bondOrder = m_keyPressBuffer.toInt(&ok); if (ok && bondOrder > 0 && bondOrder <= 4) { m_toolWidget->setBondOrder(static_cast(bondOrder)); } else { atomicNum = Core::Elements::atomicNumberFromSymbol(m_keyPressBuffer.toStdString()); if (atomicNum != Avogadro::InvalidElement) m_toolWidget->setAtomicNumber(static_cast(atomicNum)); } return nullptr; } void Editor::draw(Rendering::GroupNode& node) { if (fabs(m_bondDistance) < 0.3) return; auto* geo = new GeometryNode; node.addChild(geo); // Determine the field width. Negate it to indicate left-alignment. QString distanceLabel = tr("Distance:"); int labelWidth = -1 * distanceLabel.size(); QString overlayText = tr("%1 %L2") .arg(distanceLabel, labelWidth) .arg(tr("%L1 Å").arg(m_bondDistance, 9, 'f', 3), 9); Vector3ub color(64, 255, 220); if (m_renderer) { auto backgroundColor = m_renderer->scene().backgroundColor(); color = contrastColor( Vector3ub(backgroundColor[0], backgroundColor[1], backgroundColor[2])); } TextProperties overlayTProp; overlayTProp.setFontFamily(TextProperties::Mono); overlayTProp.setColorRgb(color[0], color[1], color[2]); overlayTProp.setAlign(TextProperties::HLeft, TextProperties::VBottom); auto* label = new TextLabel2D; label->setText(overlayText.toStdString()); label->setTextProperties(overlayTProp); label->setRenderPass(Rendering::Overlay2DPass); label->setAnchor(Vector2i(10, 10)); geo->addDrawable(label); } void Editor::updatePressedButtons(QMouseEvent* e, bool release) { /// @todo Use modifier keys on mac if (release) m_pressedButtons &= e->buttons(); else m_pressedButtons |= e->buttons(); } void Editor::reset() { if (m_fixValenceLater) { Index a1 = m_newObject.index; Index a2 = m_bondedAtom.index; Index a3 = m_clickedObject.index; // don't order them // this caused bug: // https://github.com/OpenChemistry/avogadrolibs/issues/678 /* if (a1 > a2) std::swap(a1, a2); if (a1 > a3) std::swap(a1, a3); if (a2 > a3) std::swap(a2, a3); */ // This preserves the order so they are adjusted in order. // This is important for the undo stack to work correctly. Core::Array atomIds; atomIds.push_back(a3); atomIds.push_back(a2); atomIds.push_back(a1); // This function checks to make sure the ids are valid, so no need // to check out here. m_molecule->adjustHydrogens(atomIds); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Added; changes |= Molecule::Bonds | Molecule::Added | Molecule::Removed; m_molecule->emitChanged(changes); m_fixValenceLater = false; } m_clickedObject = Identifier(); m_newObject = Identifier(); m_bondedAtom = Identifier(); m_clickPosition = QPoint(); m_pressedButtons = Qt::NoButton; m_clickedAtomicNumber = INVALID_ATOMIC_NUMBER; m_bondAdded = false; m_bondDistance = 0.0f; emit drawablesChanged(); } void Editor::emptyLeftClick(QMouseEvent* e) { // Add an atom at the clicked position Vector2f windowPos(e->localPos().x(), e->localPos().y()); Vector3f atomPos = m_renderer->camera().unProject(windowPos); RWAtom newAtom = m_molecule->addAtom(m_toolWidget->atomicNumber(), atomPos.cast()); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Modified; if (m_toolWidget->adjustHydrogens()) { m_fixValenceLater = true; } // Update the clicked object m_clickedObject.type = Rendering::AtomType; m_clickedObject.molecule = m_molecule; m_clickedObject.index = newAtom.index(); // Emit changed signal m_molecule->emitChanged(changes); e->accept(); } void Editor::atomLeftClick(QMouseEvent* e) { RWAtom atom = m_molecule->atom(m_clickedObject.index); if (atom.isValid()) { // Store the original atomic number of the clicked atom before updating it. unsigned char atomicNumber = m_toolWidget->atomicNumber(); if (atom.atomicNumber() != atomicNumber) { // Okay, we're changing this element m_clickedAtomicNumber = atom.atomicNumber(); atom.setAtomicNumber(atomicNumber); const auto& atomBonds = m_molecule->bonds(atom); RWBond bond; if (atomBonds.size() == 1) { // If the clicked atom only has one bond, we can adjust the bond length bond = atomBonds[0]; } else if (atomBonds.size() > 1 && m_toolWidget->adjustHydrogens()) { // loop through to see if there's one bond and *only* one bond // that's not a hydrogen for (const RWBond& b : atomBonds) { RWAtom otherAtom = b.getOtherAtom(atom); if (otherAtom.atomicNumber() != Core::Hydrogen) { if (bond.isValid()) { // More than one non-H bond, so we can't adjust the bond length bond = RWBond(); break; } bond = b; } } } // If we found a valid bond, adjust the bond distance if (bond.isValid()) { RWAtom atom2 = bond.getOtherAtom(atom); m_bondDistance = Elements::radiusCovalent(atomicNumber) + Elements::radiusCovalent(atom2.atomicNumber()); // tweak the bond distance if we have a double or triple bond if (bond.order() == 2) { m_bondDistance *= 0.87; // e.g. C=C vs C-C } else if (bond.order() == 3) { m_bondDistance *= 0.78; // e.g. C#C vs C-C } Vector3 bondVector = atom.position3d() - atom2.position3d(); bondVector.normalize(); bondVector *= m_bondDistance; // okay set my new position Vector3 newPos = atom2.position3d() + bondVector; atom.setPosition3d(newPos); } Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Modified; if (m_toolWidget->adjustHydrogens()) m_fixValenceLater = true; m_molecule->emitChanged(changes); } e->accept(); } } void Editor::bondLeftClick(QMouseEvent* e) { RWBond bond = m_molecule->bond(m_clickedObject.index); bond.setOrder(static_cast((bond.order() % 3) + 1)); Molecule::MoleculeChanges changes = Molecule::Bonds | Molecule::Modified; // see if we should adjust the bond length bool adjustBondLength = false; RWAtom atom1 = bond.atom1(); RWAtom atom2 = bond.atom2(); // Estimate the adjusted bond length Real distance = Elements::radiusCovalent(atom1.atomicNumber()) + Elements::radiusCovalent(atom2.atomicNumber()); // tweak the bond distance if we have a double or triple bond if (bond.order() == 2) { distance *= 0.87; // e.g. C=C vs C-C } else if (bond.order() == 3) { distance *= 0.78; // e.g. C#C vs C-C } // check if at least one of the atoms either has only one bond // or all the other bonds are hydrogens const Core::Array& atom1Bonds = m_molecule->bonds(atom1); const Core::Array& atom2Bonds = m_molecule->bonds(atom2); // if both atoms have only one bond, we're set if (atom1Bonds.size() == 1 && atom2Bonds.size() == 1) { adjustBondLength = true; } else if (atom1Bonds.size() > 1) { // check to see if all the other bonds are hydrogens for (const RWBond& b : atom1Bonds) { RWAtom bondedAtom = b.getOtherAtom(atom1); if (bondedAtom == atom2) continue; // skip the bond we're changing if (bondedAtom.atomicNumber() != Core::Hydrogen) { adjustBondLength = false; break; } adjustBondLength = true; } } // can we move atom1? if (adjustBondLength) { Vector3 bondVector = atom1.position3d() - atom2.position3d(); bondVector.normalize(); bondVector *= distance; // okay move the atom Vector3 newPos = atom2.position3d() + bondVector; m_molecule->setAtomPosition3d(atom1.index(), newPos); changes |= Molecule::Atoms; } else { // instead, check if we can move atom2 for (const RWBond& b : atom2Bonds) { RWAtom bondedAtom = b.getOtherAtom(atom2); if (bondedAtom == atom1) continue; // skip the bond we're changing if (bondedAtom.atomicNumber() != Core::Hydrogen) { adjustBondLength = false; break; } adjustBondLength = true; } if (adjustBondLength) { Vector3 bondVector = atom2.position3d() - atom1.position3d(); bondVector.normalize(); bondVector *= distance; // okay move the atom Vector3 newPos = atom1.position3d() + bondVector; m_molecule->setAtomPosition3d(atom2.index(), newPos); changes |= Molecule::Atoms; } } if (m_toolWidget->adjustHydrogens()) { // change for the new bond order QtGui::HydrogenTools::adjustHydrogens(atom1); QtGui::HydrogenTools::adjustHydrogens(atom2); changes |= Molecule::Atoms | Molecule::Added | Molecule::Removed; } m_molecule->emitChanged(changes); e->accept(); } void Editor::atomRightClick(QMouseEvent* e) { e->accept(); // check to see if we need to adjust hydrogens Core::Array bondedAtoms; Core::Array hToRemove; // atoms to remove RWAtom atom = m_molecule->atom(m_clickedObject.index); if (m_toolWidget->adjustHydrogens()) { // before we remove the atom, we need to delete any H atoms // that are bonded to it -- unless it's a hydrogen atom itself if (atom.isValid() && atom.atomicNumber() != Core::Hydrogen) { // get the list of bonded atoms Core::Array atomBonds = m_molecule->bonds(atom); for (const RWBond& bond : atomBonds) { RWAtom bondedAtom = bond.getOtherAtom(atom); if (bondedAtom.atomicNumber() == Core::Hydrogen) { // remove the H atom hToRemove.push_back(m_molecule->atomUniqueId(bondedAtom)); } else { // save the atom to adjust after we remove the target bondedAtoms.push_back(m_molecule->atomUniqueId(bondedAtom)); } } } } if (atom.isValid()) m_molecule->removeAtom(atom); // remove the hydrogens for (Index hIndex : hToRemove) { RWAtom hAtom = m_molecule->atomByUniqueId(hIndex); if (hAtom.isValid()) { m_molecule->removeAtom(hAtom); } } // okay, now adjust any valence on the bonded atoms // (e.g., add back some hydrogens) for (Index atomIndex : bondedAtoms) { RWAtom atom = m_molecule->atomByUniqueId(atomIndex); if (atom.isValid()) QtGui::HydrogenTools::adjustHydrogens(atom, QtGui::HydrogenTools::Add); } m_molecule->emitChanged(Molecule::Atoms | Molecule::Removed); } void Editor::bondRightClick(QMouseEvent* e) { e->accept(); // see if we need to adjust hydrogens RWBond bond = m_molecule->bond(m_clickedObject.index); RWAtom atom1 = bond.atom1(); RWAtom atom2 = bond.atom2(); m_molecule->removeBond(m_clickedObject.index); if (m_toolWidget->adjustHydrogens()) { QtGui::HydrogenTools::adjustHydrogens(atom1); QtGui::HydrogenTools::adjustHydrogens(atom2); } m_molecule->emitChanged(Molecule::Atoms | Molecule::Bonds | Molecule::Removed); } int expectedBondOrder(RWAtom atom1, RWAtom atom2) { Vector3 bondVector = atom1.position3d() - atom2.position3d(); double bondDistance = bondVector.norm(); double radiiSum; radiiSum = Elements::radiusCovalent(atom1.atomicNumber()) + Elements::radiusCovalent(atom2.atomicNumber()); double ratio = bondDistance / radiiSum; int bondOrder; if (ratio > 1.0) bondOrder = 1; else if (ratio > 0.91 && ratio < 1.0) bondOrder = 2; else bondOrder = 3; return bondOrder; } void Editor::atomLeftDrag(QMouseEvent* e) { // Always accept move events when atoms are clicked: e->accept(); // Build up a MoleculeChanges bitfield Molecule::MoleculeChanges changes = Molecule::NoChange; // Get the list of hits at the current mouse position: const std::multimap hits = m_renderer->hits(e->pos().x(), e->pos().y()); // Check if the previously clicked atom is still under the mouse. float depth = -1.0f; for (const auto& hit : hits) { if (hit.second == m_clickedObject) { depth = hit.first; break; } } // If the clicked atom is under the mouse... if (depth >= 0.f) { // ...and we've created a new atom, remove the new atom and reset the // clicked atom's atomic number if (m_newObject.type == Rendering::AtomType && m_molecule == m_newObject.molecule) { m_molecule->removeAtom(m_newObject.index); changes |= Molecule::Atoms | Molecule::Bonds | Molecule::Removed; m_newObject = Identifier(); RWAtom atom = m_molecule->atom(m_clickedObject.index); if (atom.atomicNumber() != m_toolWidget->atomicNumber()) { m_clickedAtomicNumber = atom.atomicNumber(); atom.setAtomicNumber(m_toolWidget->atomicNumber()); changes |= Molecule::Atoms | Molecule::Modified; } m_molecule->emitChanged(changes); return; } // If there is no new atom, do nothing. return; } // If we get here, the clicked atom is no longer under the cursor. // If the clicked atom's identity has been changed from the initial click, // reset its atomic number if (m_clickedAtomicNumber != INVALID_ATOMIC_NUMBER) { RWAtom clickedAtom = m_molecule->atom(m_clickedObject.index); clickedAtom.setAtomicNumber(m_clickedAtomicNumber); m_clickedAtomicNumber = INVALID_ATOMIC_NUMBER; changes |= Molecule::Atoms | Molecule::Modified; } // Does a bonded atom already exist? if (m_bondedAtom.isValid()) { // Is it still under the mouse? depth = -1.0f; for (const auto& hit : hits) { if (hit.second == m_bondedAtom) { depth = hit.first; break; } } // If the bonded atom is no longer under the mouse, remove the bond. if (depth < 0.f) { RWAtom bondedAtom = m_molecule->atom(m_bondedAtom.index); RWAtom clickedAtom = m_molecule->atom(m_clickedObject.index); if (m_bondAdded) m_molecule->removeBond(clickedAtom, bondedAtom); changes |= Molecule::Bonds | Molecule::Removed; m_bondedAtom = Identifier(); m_bondAdded = false; } } // Is there another atom under the cursor, besides newAtom? If so, we'll draw // a bond to it. Identifier atomToBond; for (const auto& hit : hits) { const Identifier& ident = hit.second; // Are we on an atom if (ident.type == Rendering::AtomType) // besides the one that was clicked or a new atom if (ident != m_newObject && ident != m_clickedObject) { // then we have an atom that we should be drawing a bond to. atomToBond = ident; break; } } if (atomToBond.isValid() && (atomToBond.index != m_newObject.index)) { // If we have a newAtom, destroy it if (m_newObject.isValid() && m_newObject.type == Rendering::AtomType) { m_molecule->removeAtom(m_newObject.index); changes |= Molecule::Atoms | Molecule::Bonds | Molecule::Removed; m_newObject = Identifier(); } // Skip the rest of this block if atomToBond is already bonded if (m_bondedAtom != atomToBond) { // If the currently bonded atom exists, break the bond if (m_bondedAtom.isValid() && m_clickedObject.isValid() && m_bondedAtom.index < m_molecule->atomCount() && m_clickedObject.index < m_molecule->atomCount() && m_molecule->bond(m_bondedAtom.index, m_clickedObject.index) .isValid()) { if (m_molecule->removeBond(m_molecule->atom(m_bondedAtom.index), m_molecule->atom(m_clickedObject.index))) { changes |= Molecule::Bonds | Molecule::Removed; } m_bondedAtom = Identifier(); } // Create a new bond between clicked atom and atomToBond. RWAtom clickedAtom = m_molecule->atom(m_clickedObject.index); RWAtom bondedAtom = m_molecule->atom(atomToBond.index); if (!m_molecule->bond(clickedAtom, bondedAtom).isValid()) { int bondOrder = m_toolWidget->bondOrder(); if (bondOrder == 0) { // automatic - guess the size bondOrder = expectedBondOrder(clickedAtom, bondedAtom); } m_molecule->addBond(clickedAtom, bondedAtom, bondOrder); m_bondAdded = true; } // we have a bond, but it might be the wrong order else { RWBond bond = m_molecule->bond(clickedAtom, bondedAtom); int bondOrder = m_toolWidget->bondOrder(); if (bondOrder == 0) { // automatic - guess the size bondOrder = expectedBondOrder(clickedAtom, bondedAtom); } if (bond.order() != bondOrder) bond.setOrder(bondOrder); } m_bondedAtom = atomToBond; changes |= Molecule::Bonds | Molecule::Added; } m_molecule->emitChanged(changes); return; } // If we make it here, the cursor is not over any existing atom, with the // possible exception of a new atom we've added that's bonded to clicked atom. // We just need to create the new atom (if we haven't already), then update // its position. RWAtom newAtom; if (!m_newObject.isValid()) { // Add a new atom bonded to the clicked atom RWAtom clickedAtom = m_molecule->atom(m_clickedObject.index); newAtom = m_molecule->addAtom(m_toolWidget->atomicNumber(), clickedAtom.position3d()); // Handle the automatic bond order int bondOrder = m_toolWidget->bondOrder(); if (bondOrder == 0) { // automatic - guess the size bondOrder = expectedBondOrder(clickedAtom, newAtom); } m_molecule->addBond(clickedAtom, newAtom, bondOrder); // now if we need to adjust hydrogens, do it if (m_toolWidget->adjustHydrogens()) m_fixValenceLater = true; changes |= Molecule::Atoms | Molecule::Bonds | Molecule::Added; m_newObject.type = Rendering::AtomType; m_newObject.index = newAtom.index(); const Core::Molecule* mol = &m_molecule->molecule(); m_newObject.molecule = mol; } else if (m_newObject.type == Rendering::AtomType) { // Grab the previously created atom newAtom = m_molecule->atom(m_newObject.index); } else { // Shouldn't happen qWarning() << "Editor::atomLeftDrag: m_newObject already set and not an " "atom? This is a bug."; } if (newAtom.isValid()) { Vector2f windowPos(e->localPos().x(), e->localPos().y()); Vector3f oldPos(newAtom.position3d().cast()); Vector3f newPos = m_renderer->camera().unProject(windowPos, oldPos); newAtom.setPosition3d(newPos.cast()); changes |= Molecule::Atoms | Molecule::Modified; RWAtom clickedAtom = m_molecule->atom(m_clickedObject.index); if (clickedAtom.isValid()) { Vector3f bondVector = clickedAtom.position3d().cast() - newPos; m_bondDistance = bondVector.norm(); // need to check if bond order needs to change if (m_toolWidget->bondOrder() == 0) { // automatic RWBond bond = m_molecule->bond(newAtom, clickedAtom); if (bond.isValid()) { int bondOrder = expectedBondOrder(newAtom, clickedAtom); if (bondOrder != bond.order()) bond.setOrder(bondOrder); changes |= Molecule::Bonds | Molecule::Modified; } } // otherwise see if the bond order is different than what's there else { int bondOrder = m_toolWidget->bondOrder(); RWBond bond = m_molecule->bond(newAtom, clickedAtom); if (bond.isValid() && bondOrder != bond.order()) bond.setOrder(bondOrder); changes |= Molecule::Bonds | Molecule::Modified; } } } m_molecule->emitChanged(changes); return; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/editor/editor.h000066400000000000000000000061061506155467400231230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_EDITOR_H #define AVOGADRO_QTPLUGINS_EDITOR_H #include #include #include #include #include #include namespace Avogadro { namespace QtPlugins { class EditorToolWidget; /** * @class Editor editor.h * @brief The Editor tool extends and modifies molecules. * @author Allison Vacanti */ class Editor : public QtGui::ToolPlugin { Q_OBJECT public: explicit Editor(QObject* parent_ = nullptr); ~Editor() override; QString name() const override { return tr("Editor tool"); } QString description() const override { return tr("Editor tool"); } unsigned char priority() const override { return 20; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule* mol) override { if (mol) m_molecule = mol->undoMolecule(); } void setEditMolecule(QtGui::RWMolecule* mol) override { m_molecule = mol; } void setGLWidget(QtOpenGL::GLWidget* widget) override { m_glWidget = widget; } void setGLRenderer(Rendering::GLRenderer* renderer) override { m_renderer = renderer; } QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseMoveEvent(QMouseEvent* e) override; QUndoCommand* keyPressEvent(QKeyEvent* e) override; void draw(Rendering::GroupNode& node) override; private slots: void clearKeyPressBuffer() { m_keyPressBuffer.clear(); } private: /** * Update the currently pressed buttons, accounting for modifier keys. * @todo Account for modifier keys. */ void updatePressedButtons(QMouseEvent*, bool release); /** * Reset all state for this tool. */ void reset(); void emptyLeftClick(QMouseEvent* e); void atomLeftClick(QMouseEvent* e); void bondLeftClick(QMouseEvent* e); void atomRightClick(QMouseEvent* e); void bondRightClick(QMouseEvent* e); void atomLeftDrag(QMouseEvent* e); QAction* m_activateAction; QtGui::RWMolecule* m_molecule; QtOpenGL::GLWidget* m_glWidget; Rendering::GLRenderer* m_renderer; EditorToolWidget* m_toolWidget; Rendering::Identifier m_clickedObject; Rendering::Identifier m_newObject; Rendering::Identifier m_bondedAtom; Qt::MouseButtons m_pressedButtons; QPoint m_clickPosition; unsigned char m_clickedAtomicNumber; bool m_bondAdded; bool m_fixValenceLater; QString m_keyPressBuffer; QtGui::PluginLayerManager m_layerManager; Real m_bondDistance; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_NAVIGATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/editor/editor.qrc000066400000000000000000000002111506155467400234500ustar00rootroot00000000000000 editor_light.svg editor_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/editor/editor_dark.svg000066400000000000000000000005041506155467400244700ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/editor/editor_light.svg000066400000000000000000000006661506155467400246670ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/editor/editortoolwidget.cpp000066400000000000000000000144721506155467400255650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "editortoolwidget.h" #include "ui_editortoolwidget.h" #include #include #include #include #include #include namespace { // The ItemData of the "Other" entry in the combo box const int ELEMENT_SELECTOR_TAG = 255; } // namespace namespace Avogadro::QtPlugins { EditorToolWidget::EditorToolWidget(QWidget* parent_) : QWidget(parent_), m_ui(new Ui::EditorToolWidget), m_elementSelector(nullptr), m_currentElement(6) { m_ui->setupUi(this); buildElements(); buildBondOrders(); connect(m_ui->element, SIGNAL(currentIndexChanged(int)), this, SLOT(elementChanged(int))); // Show carbon at startup. selectElement(6); } EditorToolWidget::~EditorToolWidget() { delete m_ui; } void EditorToolWidget::setAtomicNumber(unsigned char atomicNum) { selectElement(atomicNum); if (m_elementSelector) m_elementSelector->setElement(static_cast(atomicNum)); } unsigned char EditorToolWidget::atomicNumber() const { int curIndex = m_ui->element->currentIndex(); QVariant itemData = m_ui->element->itemData(curIndex); if (!itemData.isValid()) return 0; auto atomicNum = static_cast(itemData.toUInt()); // "Other…" selected.... if (atomicNum == 0 && m_elementSelector) atomicNum = static_cast(m_elementSelector->element()); return atomicNum; } void EditorToolWidget::setBondOrder(unsigned char order) { if (order < m_ui->bondOrder->count()) m_ui->bondOrder->setCurrentIndex(static_cast(order)); } unsigned char EditorToolWidget::bondOrder() const { return static_cast(m_ui->bondOrder->currentIndex()); } bool EditorToolWidget::adjustHydrogens() const { return m_ui->adjustHydrogens->isChecked(); } void EditorToolWidget::elementChanged(int index) { QVariant itemData = m_ui->element->itemData(index); if (itemData.isValid()) { if (itemData.toInt() == ELEMENT_SELECTOR_TAG) { if (!m_elementSelector) { m_elementSelector = new QtGui::PeriodicTableView(this); connect(m_elementSelector, SIGNAL(elementChanged(int)), this, SLOT(elementSelectedFromTable(int))); } m_elementSelector->setElement(m_currentElement); m_elementSelector->show(); } else { if (m_elementSelector) m_elementSelector->setElement(itemData.toInt()); m_currentElement = static_cast(itemData.toInt()); } } } void EditorToolWidget::updateElementCombo() { // Build set of all elements: QList allElements; allElements << m_defaultElements; allElements << m_userElements; std::sort(allElements.begin(), allElements.end()); // Cache selected atomic number for later QVariant selectedData; int curIndex = m_ui->element->currentIndex(); if (curIndex >= 0) selectedData = m_ui->element->itemData(curIndex); // Clear and repopulate combo m_ui->element->clear(); foreach (unsigned char atomicNum, allElements) { m_ui->element->addItem(QString("%1 (%2)") .arg(QtGui::ElementTranslator::name(atomicNum)) .arg(atomicNum), atomicNum); } m_ui->element->insertSeparator(m_ui->element->count()); m_ui->element->addItem(tr("Other…"), ELEMENT_SELECTOR_TAG); // Reset the element if it still exists selectElement(static_cast( selectedData.isValid() ? selectedData.toInt() : -1)); } void EditorToolWidget::addUserElement(unsigned char element) { // Never add any of the common elements to the user list. if (m_defaultElements.contains(element)) return; // If the element is already in the user list, move it to the back of the // list. if (m_userElements.removeOne(element)) { m_userElements << element; return; } m_userElements << element; // Limit the number of user elements /// @todo Make this number of user elements configurable. while (m_userElements.size() > 15) m_userElements.pop_front(); updateElementCombo(); saveElements(); } void EditorToolWidget::elementSelectedFromTable(int element) { addUserElement(static_cast(element)); selectElement(static_cast(element)); } void EditorToolWidget::selectElement(unsigned char element) { int curIndex = element > 0 ? m_ui->element->findData(element) : -1; if (curIndex >= 0) m_ui->element->setCurrentIndex(curIndex); else { addUserElement(element); curIndex = m_ui->element->findData(element); if (curIndex >= 0) m_ui->element->setCurrentIndex(curIndex); // if we can't find it after adding it, something is very wrong! } } void EditorToolWidget::buildElements() { // Common elements that are always shown in the combo box. if (m_defaultElements.isEmpty()) { m_defaultElements.append(1); // Hydrogen m_defaultElements.append(5); // Boron m_defaultElements.append(6); // Carbon m_defaultElements.append(7); // Nitrogen m_defaultElements.append(8); // Oxygen m_defaultElements.append(9); // Fluorine m_defaultElements.append(15); // Phosphorus m_defaultElements.append(16); // Sulfur m_defaultElements.append(17); // Chlorine m_defaultElements.append(35); // Bromine } // User-added elements QVariantList userElementsVar = QSettings().value("editortool/userElements").toList(); foreach (const QVariant& var, userElementsVar) m_userElements << static_cast(var.toUInt()); updateElementCombo(); } void EditorToolWidget::buildBondOrders() { m_ui->bondOrder->clear(); m_ui->bondOrder->addItem(tr("Automatic"), 0); m_ui->bondOrder->addItem(tr("Single"), 1); m_ui->bondOrder->addItem(tr("Double"), 2); m_ui->bondOrder->addItem(tr("Triple"), 3); } void EditorToolWidget::saveElements() { QVariantList atomicNums; for (unsigned char m_userElement : m_userElements) atomicNums << QVariant(m_userElement); QSettings().setValue("editortool/userElements", atomicNums); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/editor/editortoolwidget.h000066400000000000000000000027231506155467400252260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_EDITORTOOLWIDGET_H #define AVOGADRO_QTPLUGINS_EDITORTOOLWIDGET_H #include namespace Avogadro { namespace QtGui { class PeriodicTableView; } namespace QtPlugins { namespace Ui { class EditorToolWidget; } class EditorToolWidget : public QWidget { Q_OBJECT public: explicit EditorToolWidget(QWidget* parent_ = nullptr); ~EditorToolWidget() override; void setAtomicNumber(unsigned char atomicNum); unsigned char atomicNumber() const; void setBondOrder(unsigned char order); unsigned char bondOrder() const; bool adjustHydrogens() const; private slots: void elementChanged(int index); void updateElementCombo(); void addUserElement(unsigned char element); void elementSelectedFromTable(int element); void selectElement(unsigned char element); private: void buildElements(); void buildBondOrders(); void saveElements(); Ui::EditorToolWidget* m_ui; QtGui::PeriodicTableView* m_elementSelector; QList m_defaultElements; QList m_userElements; unsigned char m_currentElement; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_EDITORTOOLWIDGET_H avogadrolibs-1.101.0/avogadro/qtplugins/editor/editortoolwidget.ui000066400000000000000000000025621506155467400254150ustar00rootroot00000000000000 Avogadro::QtPlugins::EditorToolWidget 0 0 400 300 Form QFormLayout::ExpandingFieldsGrow Element: Bond Order: Adjust Hydrogens true avogadrolibs-1.101.0/avogadro/qtplugins/fetchpdb/000077500000000000000000000000001506155467400217525ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/fetchpdb/CMakeLists.txt000066400000000000000000000005041506155467400245110ustar00rootroot00000000000000set(srcs fetchpdb.cpp ) avogadro_plugin(FetchPDB "Fetch PDB" ExtensionPlugin fetchpdb.h FetchPDB "${srcs}" "" ) target_link_libraries(FetchPDB PRIVATE Avogadro::IO Qt::Network) if(WIN32) # for https support target_link_libraries(FetchPDB PRIVATE OpenSSL::SSL OpenSSL::Crypto OpenSSL::applink) endif() avogadrolibs-1.101.0/avogadro/qtplugins/fetchpdb/fetchpdb.cpp000066400000000000000000000105441506155467400242410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "fetchpdb.h" #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { FetchPDB::FetchPDB(QObject* parent_) : ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr), m_network(nullptr), m_progressDialog(nullptr) { m_action->setEnabled(true); m_action->setText("Fetch from &PDB…"); m_action->setProperty("menu priority", 180); connect(m_action, SIGNAL(triggered()), SLOT(showDialog())); } FetchPDB::~FetchPDB() {} QList FetchPDB::actions() const { return QList() << m_action; } QStringList FetchPDB::menuPath(QAction*) const { return QStringList() << tr("&File") << tr("&Import"); } void FetchPDB::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } bool FetchPDB::readMolecule(QtGui::Molecule& mol) { if (m_moleculeData.isEmpty() || m_moleculeName.isEmpty()) return false; bool readOK = Io::FileFormatManager::instance().readFile( mol, m_tempFileName.toStdString(), "pdb"); if (readOK) // worked, so set the filename mol.setData("name", m_moleculeName.toStdString()); else // if it didn't read, show a dialog QMessageBox::warning( qobject_cast(parent()), tr("Fetch PDB"), tr("Could not read the PDB molecule: %1").arg(m_moleculeName)); return readOK; } void FetchPDB::showDialog() { // Prompt for a chemical structure name bool ok; QString pdbCode = QInputDialog::getText( qobject_cast(parent()), tr("PDB Code"), tr("Chemical structure to download."), QLineEdit::Normal, "", &ok); if (!ok || pdbCode.isEmpty()) return; // check if the PDB code matches the expected format if (pdbCode.length() != 4) { QMessageBox::warning(qobject_cast(parent()), tr("Invalid PDB Code"), tr("The PDB code must be exactly 4 characters long.")); return; } // first character should be 1-9 if (!pdbCode.at(0).isDigit() || pdbCode.at(0).toLatin1() == '0') { QMessageBox::warning( qobject_cast(parent()), tr("Invalid PDB Code"), tr("The first character of the PDB code must be 1-9.")); return; } if (!m_network) { m_network = new QNetworkAccessManager(this); connect(m_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); } // Hard coding the PDB download URL m_network->get(QNetworkRequest( QUrl("https://files.rcsb.org/download/" + pdbCode + ".pdb"))); if (!m_progressDialog) { m_progressDialog = new QProgressDialog(qobject_cast(parent())); } m_moleculeName = pdbCode; m_progressDialog->setLabelText(tr("Querying for %1").arg(pdbCode)); m_progressDialog->setRange(0, 0); m_progressDialog->show(); } void FetchPDB::replyFinished(QNetworkReply* reply) { m_progressDialog->hide(); // Read in all the data if (!reply->isReadable()) { QMessageBox::warning(qobject_cast(parent()), tr("Network Download Failed"), tr("Network timeout or other error.")); reply->deleteLater(); return; } m_moleculeData = reply->readAll(); // Check if the file was successfully downloaded if (m_moleculeData.contains("Not Found") || m_moleculeData.contains("Error report") || m_moleculeData.contains("Page not found (404)")) { QMessageBox::warning( qobject_cast(parent()), tr("Network Download Failed"), tr("Specified molecule could not be found: %1").arg(m_moleculeName)); reply->deleteLater(); return; } m_tempFileName = QDir::tempPath() + QDir::separator() + m_moleculeName + ".pdb"; QFile out(m_tempFileName); out.open(QIODevice::WriteOnly); out.write(m_moleculeData); out.close(); emit moleculeReady(1); reply->deleteLater(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/fetchpdb/fetchpdb.h000066400000000000000000000031661506155467400237100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_FETCHPDB_H #define AVOGADRO_QTPLUGINS_FETCHPDB_H #include #include #include class QNetworkAccessManager; class QNetworkReply; class QProgressDialog; namespace Avogadro { namespace QtPlugins { /** * @brief Queries online databases (currently the NIH structure resolver) and * loads the returned structure if one is found. */ class FetchPDB : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit FetchPDB(QObject* parent = nullptr); ~FetchPDB() override; QString name() const override { return tr("Fetch from PDB"); } QString description() const override { return tr("Download PDB models from the Protein Data Bank"); } QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; bool readMolecule(QtGui::Molecule& mol) override; private slots: void showDialog(); void replyFinished(QNetworkReply*); private: QAction* m_action; QtGui::Molecule* m_molecule; QNetworkAccessManager* m_network; QString m_moleculeName; QByteArray m_moleculeData; QProgressDialog* m_progressDialog; QString m_tempFileName; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_FETCHPDB_H avogadrolibs-1.101.0/avogadro/qtplugins/focus/000077500000000000000000000000001506155467400213125ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/focus/CMakeLists.txt000066400000000000000000000002611506155467400240510ustar00rootroot00000000000000avogadro_plugin(Focus "Focus the view on specific features." ExtensionPlugin focus.h Focus "focus.cpp" "" ) target_link_libraries(Focus PRIVATE Avogadro::QtOpenGL) avogadrolibs-1.101.0/avogadro/qtplugins/focus/focus.cpp000066400000000000000000000061561506155467400231450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "focus.h" #include #include #include #include #include namespace Avogadro::QtPlugins { using Avogadro::QtGui::ExtensionPlugin; Focus::Focus(QObject* parent_) : ExtensionPlugin(parent_), m_focusSelectionAction(new QAction(tr("Focus Selection"), this)), m_unfocusAction(new QAction(tr("Unfocus"), this)) { m_focusSelectionAction->setProperty("menu priority", 200); m_unfocusAction->setProperty("menu priority", 200); connect(m_focusSelectionAction, SIGNAL(triggered()), SLOT(focusSelection())); connect(m_unfocusAction, SIGNAL(triggered()), SLOT(unfocus())); } Focus::~Focus() {} QList Focus::actions() const { QList result; return result << m_focusSelectionAction << m_unfocusAction; } QStringList Focus::menuPath(QAction*) const { return QStringList() << tr("&View"); } void Focus::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void Focus::setCamera(Rendering::Camera* camera) { m_camera = camera; } void Focus::setScene(Rendering::Scene* scene) { m_scene = scene; } void Focus::setActiveWidget(QWidget* widget) { if (widget != nullptr) { m_glWidget = widget; connect(this, SIGNAL(updateRequested()), m_glWidget, SLOT(requestUpdate())); } } void Focus::newFocus(Eigen::Vector3f point, float distance) { Eigen::Vector3f cameraPoint = -m_camera->modelView().translation(); Eigen::Vector3f vectorOfSight = point - cameraPoint; float currentDistance = vectorOfSight.norm(); float scaleFactor = distance / currentDistance; vectorOfSight *= 1.0f - scaleFactor; cameraPoint += vectorOfSight; m_camera->setIdentity(); m_camera->lookAt(cameraPoint, point, Eigen::Vector3f(0, 1, 0)); m_camera->setFocus(point); } void Focus::focusSelection() { if (!m_molecule || !m_camera) return; if (m_molecule->atomPositions3d().size() != m_molecule->atomCount()) return; if (m_molecule->isSelectionEmpty()) return; Eigen::Vector3f selectionCenter(0, 0, 0); std::vector selection; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (m_molecule->atomSelected(i)) { selectionCenter += m_molecule->atomPosition3d(i).cast(); selection.push_back(i); } } selectionCenter /= selection.size(); float selectionRadius = 0.0f; for (Index i : selection) { Eigen::Vector3f pos = m_molecule->atomPosition3d(i).cast(); float distance = (pos - selectionCenter).norm(); if (distance > selectionRadius) selectionRadius = distance; } newFocus(selectionCenter, selectionRadius + 10.0f); emit updateRequested(); } void Focus::unfocus() { if (!m_camera || !m_scene) return; newFocus(m_scene->center(), 2.22f * m_scene->radius()); emit updateRequested(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/focus/focus.h000066400000000000000000000031731506155467400226060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_FOCUS_H #define AVOGADRO_QTPLUGINS_FOCUS_H #include #include #include namespace Avogadro { namespace QtPlugins { /** * @brief The Focus class is an extension to center the camera in the best * fit panel or the default camera position */ class Focus : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit Focus(QObject* parent_ = nullptr); ~Focus() override; QString name() const override { return tr("Focus"); } QString description() const override { return tr("Focus the view on specific features."); } QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void setCamera(Rendering::Camera* camera) override; void setScene(Rendering::Scene* scene) override; void setActiveWidget(QWidget* widget) override; signals: void updateRequested(); private slots: void focusSelection(); void unfocus(); private: QtGui::Molecule* m_molecule; Rendering::Camera* m_camera; Rendering::Scene* m_scene; QWidget* m_glWidget; QAction* m_focusSelectionAction; QAction* m_unfocusAction; void newFocus(Eigen::Vector3f point, float distance); }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/force/000077500000000000000000000000001506155467400212715ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/force/CMakeLists.txt000066400000000000000000000002351506155467400240310ustar00rootroot00000000000000avogadro_plugin(Force "Force rendering scheme" ScenePlugin force.h Force force.cpp "") target_link_libraries(Force PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/force/force.cpp000066400000000000000000000031341506155467400230740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "force.h" #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using QtGui::Molecule; using Rendering::ArrowGeometry; using Rendering::GeometryNode; using Rendering::GroupNode; Force::Force(QObject* p) : ScenePlugin(p) { m_layerManager = QtGui::PluginLayerManager(m_name); } Force::~Force() {} void Force::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { auto* geometry = new GeometryNode; node.addChild(geometry); auto* arrows = new ArrowGeometry; arrows->identifier().molecule = &molecule; geometry->addDrawable(arrows); for (Index i = 0; i < molecule.atomCount(); ++i) { if (!m_layerManager.atomEnabled(i)) continue; // ignore hidden atoms Core::Atom atom1 = molecule.atom(i); Vector3f pos1 = atom1.position3d().cast(); Vector3f forceVector = atom1.forceVector().cast(); arrows->addSingleArrow(pos1, pos1 + forceVector); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/force/force.h000066400000000000000000000022261506155467400225420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_FORCE_H #define AVOGADRO_QTPLUGINS_FORCE_H #include namespace Avogadro { namespace QtPlugins { /** * @brief Render a molecule in the wireframe style. */ class Force : public QtGui::ScenePlugin { Q_OBJECT public: explicit Force(QObject* parent = nullptr); ~Force() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Force"); } QString description() const override { return tr( "Render the force field visualizations for the atoms of the molecule."); } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } private: std::string m_name = "Force"; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_FORCE_H avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/000077500000000000000000000000001506155467400222755ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/CMakeLists.txt000066400000000000000000000023731506155467400250420ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 COMPONENTS Gui REQUIRED) else() find_package(Qt5 COMPONENTS Widgets REQUIRED) endif() set(forcefield_srcs forcefield.cpp forcefielddialog.cpp obmmenergy.cpp scriptenergy.cpp ) if (BUILD_GPL_PLUGINS) find_package(OpenBabel3) if (OpenBabel3_LIBRARIES) list(APPEND forcefield_srcs obenergy.cpp ) add_definitions(-DBUILD_GPL_PLUGINS) include_directories(${OpenBabel3_INCLUDE_DIRS} ${OpenBabel3_INCLUDE_DIR} ${AvogadroLibs_BINARY_DIR}/../prefix/include/openbabel3) endif() endif() avogadro_plugin(Forcefield "Force field optimization and dynamics" ExtensionPlugin forcefield.h Forcefield "${forcefield_srcs}" forcefielddialog.ui ) target_link_libraries(Forcefield PRIVATE Avogadro::Calc) if(QT_VERSION EQUAL 6) target_link_libraries(Forcefield PRIVATE Qt6::Gui) else() target_link_libraries(Forcefield PRIVATE Qt5::Widgets) endif() if (BUILD_GPL_PLUGINS AND OpenBabel3_LIBRARIES) target_link_libraries(Forcefield PRIVATE OpenBabel3) endif() if (NOT BUILD_GPL_PLUGINS) # install the OB / Pybel forcefield scripts list(APPEND forcefields scripts/gaff.py scripts/mmff94.py scripts/uff.py ) endif() # Don't install any scripts - we'll use these as plugins avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/forcefield.cpp000066400000000000000000000500611506155467400251050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "forcefield.h" #include "forcefielddialog.h" #include "obmmenergy.h" #include "scriptenergy.h" #ifdef BUILD_GPL_PLUGINS #include "obenergy.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro { namespace QtPlugins { using Avogadro::Calc::EnergyCalculator; using Avogadro::QtGui::Molecule; using Avogadro::QtGui::RWMolecule; const int energyAction = 0; const int optimizeAction = 1; const int configureAction = 2; const int freezeAction = 3; const int unfreezeAction = 4; const int constraintAction = 5; const int forcesAction = 6; const int fuseAction = 7; const int unfuseAction = 8; Forcefield::Forcefield(QObject* parent_) : ExtensionPlugin(parent_), m_method(nullptr) { QSettings settings; settings.beginGroup("forcefield"); m_autodetect = settings.value("autodetect", true).toBool(); m_methodName = settings.value("forcefield", "LJ").toString().toStdString(); m_nSteps = settings.value("steps", 10).toInt(); m_maxSteps = settings.value("maxSteps", 250).toInt(); m_tolerance = settings.value("tolerance", 1.0e-4).toDouble(); m_gradientTolerance = settings.value("gradientTolerance", 1.0e-4).toDouble(); settings.endGroup(); QAction* action = new QAction(this); action->setEnabled(true); action->setText(tr("Optimize Geometry")); action->setShortcut(QKeySequence("Ctrl+Alt+O")); action->setData(optimizeAction); action->setProperty("menu priority", 920); connect(action, SIGNAL(triggered()), SLOT(optimize())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Energy")); // calculate energy action->setData(energyAction); action->setProperty("menu priority", 910); connect(action, SIGNAL(triggered()), SLOT(energy())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Forces")); // calculate gradients action->setData(forcesAction); action->setProperty("menu priority", 910); connect(action, SIGNAL(triggered()), SLOT(forces())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Configure…")); action->setData(configureAction); action->setProperty("menu priority", 900); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.push_back(action); action = new QAction(this); action->setSeparator(true); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Freeze Selected Atoms")); action->setData(freezeAction); action->setProperty("menu priority", 790); connect(action, SIGNAL(triggered()), SLOT(freezeSelected())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Unfreeze Selected Atoms")); action->setData(unfreezeAction); action->setProperty("menu priority", 780); connect(action, SIGNAL(triggered()), SLOT(unfreezeSelected())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText( tr("Fuse Selected Atoms", "freeze atomic distances / glue atoms together")); action->setData(fuseAction); action->setProperty("menu priority", 770); connect(action, SIGNAL(triggered()), SLOT(fuseSelected())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Unfuse Selected Atoms", "freeze atomic distances / glue atoms together")); action->setData(unfuseAction); action->setProperty("menu priority", 760); connect(action, SIGNAL(triggered()), SLOT(unfuseSelected())); m_actions.push_back(action); // initialize the calculators // prefer to use Python interface scripts if available refreshScripts(); // add the openbabel calculators in case they don't exist #ifdef BUILD_GPL_PLUGINS // These directly use Open Babel and are fast qDebug() << " registering GPL plugins"; Calc::EnergyManager::registerModel(new OBEnergy("MMFF94")); Calc::EnergyManager::registerModel(new OBEnergy("GAFF")); #else // These call obmm and can be slower qDebug() << " registering obmm plugins"; Calc::EnergyManager::registerModel(new OBMMEnergy("MMFF94")); Calc::EnergyManager::registerModel(new OBMMEnergy("GAFF")); #endif } Forcefield::~Forcefield() {} QList Forcefield::actions() const { return m_actions; } QStringList Forcefield::menuPath(QAction* action) const { QStringList path; if (action->data().toInt() == optimizeAction) path << tr("&Extensions"); else path << tr("&Extensions") << tr("&Calculate"); return path; } void Forcefield::showDialog() { QStringList forceFields; auto list = Calc::EnergyManager::instance().identifiersForMolecule(*m_molecule); for (auto option : list) { forceFields << option.c_str(); } QSettings settings; QVariantMap options; options["forcefield"] = m_methodName.c_str(); options["nSteps"] = m_nSteps; options["maxSteps"] = m_maxSteps; options["tolerance"] = m_tolerance; options["gradientTolerance"] = m_gradientTolerance; options["autodetect"] = m_autodetect; QVariantMap results = ForceFieldDialog::prompt( nullptr, forceFields, options, recommendedForceField().c_str()); if (!results.isEmpty()) { // update settings settings.beginGroup("forcefield"); m_methodName = results["forcefield"].toString().toStdString(); settings.setValue("forcefield", m_methodName.c_str()); m_maxSteps = results["maxSteps"].toInt(); settings.setValue("maxSteps", m_maxSteps); m_tolerance = results["tolerance"].toDouble(); settings.setValue("tolerance", m_tolerance); m_gradientTolerance = results["gradientTolerance"].toDouble(); settings.setValue("gradientTolerance", m_gradientTolerance); m_autodetect = results["autodetect"].toBool(); settings.setValue("autodetect", m_autodetect); settings.endGroup(); } setupMethod(); } void Forcefield::setMolecule(QtGui::Molecule* mol) { if (mol == nullptr || m_molecule == mol) return; m_molecule = mol; setupMethod(); // TODO: connect to molecule changes, e.g. selection // connect(m_molecule, SIGNAL(changed(uint)), SLOT(updateActions())); } void Forcefield::updateActions() { if (m_molecule == nullptr) return; bool noSelection = m_molecule->isSelectionEmpty(); foreach (QAction* action, m_actions) { switch (action->data().toInt()) { case freezeAction: case unfreezeAction: case fuseAction: case unfuseAction: action->setEnabled(!noSelection); break; default: break; } } } void Forcefield::setupMethod() { if (m_molecule == nullptr) return; // nothing to do until its set if (m_autodetect) m_methodName = recommendedForceField(); // check if m_methodName even exists (e.g., saved preference) // or if that method doesn't work for this (e.g., unit cell, etc.) auto list = Calc::EnergyManager::instance().identifiersForMolecule(*m_molecule); bool found = false; for (auto option : list) { if (option == m_methodName) { found = true; break; } } // fall back to recommended if not found (LJ will always work) if (!found) { m_methodName = recommendedForceField(); } if (m_method != nullptr) { delete m_method; // delete the previous one } m_method = Calc::EnergyManager::instance().model(m_methodName); if (m_method != nullptr) m_method->setMolecule(m_molecule); } void Forcefield::setupConstraints() { if (m_molecule == nullptr || m_method == nullptr) return; // nothing to do auto n = m_molecule->atomCount(); // first set the frozen coordinate mask auto mask = m_molecule->frozenAtomMask(); if (mask.rows() != static_cast(3 * n)) { // set the mask to all ones mask = Eigen::VectorXd::Ones(static_cast(3 * n)); } m_method->setMolecule(m_molecule); m_method->setMask(mask); // now set the constraints m_method->setConstraints(m_molecule->constraints()); } void Forcefield::optimize() { if (m_molecule == nullptr) return; if (m_method == nullptr) setupMethod(); if (m_method == nullptr) return; // bad news if (!m_molecule->atomCount()) { QMessageBox::information(nullptr, tr("Avogadro"), tr("No atoms provided for optimization")); return; } // merge all coordinate updates into one step for undo bool isInteractive = m_molecule->undoMolecule()->isInteractive(); m_molecule->undoMolecule()->setInteractive(true); // TODO - use different solvers cppoptlib::LbfgsSolver solver; auto n = m_molecule->atomCount(); setupConstraints(); // we have to cast the current 3d positions into a VectorXd Core::Array pos = m_molecule->atomPositions3d(); double* p = pos[0].data(); Eigen::Map map(p, 3 * n); Eigen::VectorXd positions = map; Eigen::VectorXd lastPositions = positions; Eigen::VectorXd gradient = Eigen::VectorXd::Zero(3 * n); // just to get the right size / shape // we'll use this to draw the force arrows later Core::Array forces = m_molecule->atomPositions3d(); // Create a Criteria class so we can update coords every N steps cppoptlib::Criteria crit = cppoptlib::Criteria::defaults(); // e.g., every N steps, update coordinates crit.iterations = 5; // we don't set function or gradient criteria // .. these seem to be broken in the solver code // .. so we handle ourselves solver.setStopCriteria(crit); Real energy = m_method->value(positions); m_method->gradient(positions, gradient); // debug the gradients #ifndef NDEBUG for (Index i = 0; i < n; ++i) { qDebug() << " atom " << i << " grad: " << gradient[3 * i] << ", " << gradient[3 * i + 1] << ", " << gradient[3 * i + 2]; } #endif qDebug() << " initial " << energy << " gradNorm: " << gradient.norm(); qDebug() << " maxSteps" << m_maxSteps << " steps " << m_maxSteps / crit.iterations; QProgressDialog progress(tr("Optimize Geometry"), "Cancel", 0, m_maxSteps / crit.iterations); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(0); progress.setAutoClose(true); progress.show(); Real currentEnergy = 0.0; for (unsigned int i = 0; i < m_maxSteps / crit.iterations; ++i) { solver.minimize(*m_method, positions); // update the progress dialog progress.setValue(i); qApp->processEvents(QEventLoop::AllEvents, 500); currentEnergy = m_method->value(positions); progress.setLabelText( tr("Energy: %L1", "force field energy").arg(currentEnergy, 0, 'f', 3)); // get the current gradient for force visualization m_method->gradient(positions, gradient); #ifndef NDEBUG qDebug() << " optimize " << i << currentEnergy << " gradNorm: " << gradient.norm(); #endif // update coordinates bool isFinite = std::isfinite(currentEnergy); if (isFinite) { const double* d = positions.data(); [[maybe_unused]] bool allFinite = true; // casting back would be lovely... for (Index j = 0; j < n; ++j) { if (!std::isfinite(*d) || !std::isfinite(*(d + 1)) || !std::isfinite(*(d + 2))) { allFinite = false; break; } pos[j] = Vector3(*(d), *(d + 1), *(d + 2)); d += 3; forces[j] = -0.1 * Vector3(gradient[3 * j], gradient[3 * j + 1], gradient[3 * j + 2]); } } else { qDebug() << "Non-finite energy, stopping optimization"; // reset to last positions positions = lastPositions; gradient = Eigen::VectorXd::Zero(3 * n); break; } // todo - merge these into one undo step if (isFinite) { m_molecule->undoMolecule()->setAtomPositions3d(pos, tr("Optimize Geometry")); m_molecule->setForceVectors(forces); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Modified; m_molecule->emitChanged(changes); lastPositions = positions; // check for convergence if (fabs(gradient.maxCoeff()) < m_gradientTolerance) break; if (fabs(currentEnergy - energy) < m_tolerance) break; energy = currentEnergy; } if (progress.wasCanceled()) break; } m_molecule->undoMolecule()->setInteractive(isInteractive); } void Forcefield::energy() { if (m_molecule == nullptr) return; if (m_method == nullptr) setupMethod(); if (m_method == nullptr) return; // bad news int n = m_molecule->atomCount(); // we have to cast the current 3d positions into a VectorXd Core::Array pos = m_molecule->atomPositions3d(); double* p = pos[0].data(); Eigen::Map map(p, 3 * n); Eigen::VectorXd positions = map; // now get the energy m_method->setMolecule(m_molecule); Real energy = m_method->value(positions); QString msg(tr("%1 Energy = %L2").arg(m_methodName.c_str()).arg(energy)); QMessageBox::information(nullptr, tr("Avogadro"), msg); } void Forcefield::forces() { if (m_molecule == nullptr) return; if (m_method == nullptr) setupMethod(); if (m_method == nullptr) return; // bad news auto n = m_molecule->atomCount(); // double-check the mask auto mask = m_molecule->frozenAtomMask(); if (mask.rows() != 3 * n) { mask = Eigen::VectorXd::Zero(3 * n); // set to 1.0 for (Eigen::Index i = 0; i < 3 * n; ++i) { mask[i] = 1.0; } } m_method->setMolecule(m_molecule); m_method->setMask(mask); // we have to cast the current 3d positions into a VectorXd Core::Array pos = m_molecule->atomPositions3d(); double* p = pos[0].data(); Eigen::Map map(p, 3 * n); Eigen::VectorXd positions = map; Eigen::VectorXd gradient = Eigen::VectorXd::Zero(3 * n); // just to get the right size / shape // we'll use this to draw the force arrows Core::Array forces = m_molecule->atomPositions3d(); m_method->gradient(positions, gradient); #ifndef NDEBUG qDebug() << " current gradient "; for (Index i = 0; i < n; ++i) { qDebug() << " atom " << i << " element " << m_molecule->atom(i).atomicNumber() << " grad: " << gradient[3 * i] << ", " << gradient[3 * i + 1] << ", " << gradient[3 * i + 2]; } qDebug() << " numeric gradient "; m_method->finiteGradient(positions, gradient); for (Index i = 0; i < n; ++i) { qDebug() << " atom " << i << " element " << m_molecule->atom(i).atomicNumber() << " grad: " << gradient[3 * i] << ", " << gradient[3 * i + 1] << ", " << gradient[3 * i + 2]; } #endif for (Index i = 0; i < n; ++i) { forces[i] = -0.1 * Vector3(gradient[3 * i], gradient[3 * i + 1], gradient[3 * i + 2]); } m_molecule->setForceVectors(forces); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Modified; m_molecule->emitChanged(changes); QString msg( tr("%1 Force Norm = %L2").arg(m_methodName.c_str()).arg(gradient.norm())); QMessageBox::information(nullptr, tr("Avogadro"), msg); } std::string Forcefield::recommendedForceField() const { // if we have a unit cell, we need to use the LJ calculator // (implementing something better would be nice) if (m_molecule == nullptr || m_molecule->unitCell() != nullptr) return "LJ"; // otherwise, let's see what identifers are returned auto list = Calc::EnergyManager::instance().identifiersForMolecule(*m_molecule); if (list.empty()) return "LJ"; // this will always work // iterate to see what we have std::string bestOption; for (auto option : list) { // GAFF is better than MMFF94 which is better than UFF if (option == "UFF" && bestOption != "GAFF" && bestOption != "MMFF94") bestOption = option; if (option == "MMFF94" && bestOption != "GAFF") bestOption = option; } if (!bestOption.empty()) return bestOption; else return "LJ"; // this will always work } void Forcefield::freezeSelected() { if (m_molecule == nullptr || m_molecule->isSelectionEmpty()) return; // nothing to do until there's a valid selection auto numAtoms = m_molecule->atomCount(); // now freeze the specified atoms for (Index i = 0; i < numAtoms; ++i) { if (m_molecule->atomSelected(i)) { m_molecule->setFrozenAtom(i, true); } } m_molecule->emitChanged(QtGui::Molecule::Constraints); } void Forcefield::unfreezeSelected() { if (m_molecule == nullptr || m_molecule->isSelectionEmpty()) return; // nothing to do until there's a valid selection auto numAtoms = m_molecule->atomCount(); // now freeze the specified atoms for (Index i = 0; i < numAtoms; ++i) { if (m_molecule->atomSelected(i)) { m_molecule->setFrozenAtom(i, false); } } m_molecule->emitChanged(QtGui::Molecule::Constraints); } void Forcefield::unfuseSelected() { if (m_molecule == nullptr || m_molecule->isSelectionEmpty()) return; // nothing to do until there's a valid selection auto numAtoms = m_molecule->atomCount(); // now remove constraints between the specified atoms for (Index i = 0; i < numAtoms; ++i) { if (m_molecule->atomSelected(i)) { for (Index j = i + 1; j < numAtoms; ++j) { if (m_molecule->atomSelected(j)) { m_molecule->removeConstraint(i, j); } } } } m_molecule->emitChanged(QtGui::Molecule::Constraints); } void Forcefield::fuseSelected() { if (m_molecule == nullptr || m_molecule->isSelectionEmpty()) return; // nothing to do until there's a valid selection // loop through all selected atom pairs auto numAtoms = m_molecule->atomCount(); for (Index i = 0; i < numAtoms; ++i) { if (m_molecule->atomSelected(i)) { Vector3 iPos = m_molecule->atomPosition3d(i); for (Index j = i + 1; j < numAtoms; ++j) { if (m_molecule->atomSelected(j)) { // both selected, set the constraint Vector3 jPos = m_molecule->atomPosition3d(j); Real distance = (iPos - jPos).norm(); Core::Constraint constraint(i, j); constraint.setValue(distance); m_molecule->addConstraint(constraint); } } } } m_molecule->emitChanged(QtGui::Molecule::Constraints); } void Forcefield::refreshScripts() { unregisterScripts(); qDeleteAll(m_scripts); m_scripts.clear(); QMultiMap scriptPaths = QtGui::ScriptLoader::scriptList("energy"); foreach (const QString& filePath, scriptPaths) { auto* model = new ScriptEnergy(filePath); if (model->isValid()) m_scripts.push_back(model); else delete model; } registerScripts(); } void Forcefield::unregisterScripts() { for (QList::const_iterator it = m_scripts.constBegin(), itEnd = m_scripts.constEnd(); it != itEnd; ++it) { Calc::EnergyManager::unregisterModel((*it)->identifier()); } } void Forcefield::registerScripts() { for (QList::const_iterator it = m_scripts.constBegin(), itEnd = m_scripts.constEnd(); it != itEnd; ++it) { qDebug() << " register " << (*it)->identifier().c_str(); if (!Calc::EnergyManager::registerModel((*it)->newInstance())) { qDebug() << "Could not register model" << (*it)->identifier().c_str() << "due to name conflict."; } } } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/forcefield.h000066400000000000000000000045141506155467400245540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_FORCEFIELD_H #define AVOGADRO_QTPLUGINS_FORCEFIELD_H #include #include #include class QAction; class QDialog; namespace Avogadro { namespace Calc { class EnergyCalculator; } namespace QtPlugins { /** * @brief The Forcefield class implements the extension interface for * forcefield (and other) optimization * @author Geoffrey R. Hutchison */ class Forcefield : public QtGui::ExtensionPlugin { Q_OBJECT public: // Currently unused - defaults to LBFGS enum Minimizer { SteepestDescent = 0, ConjugateGradients, LBFGS, FIRE, }; explicit Forcefield(QObject* parent = 0); ~Forcefield() override; QString name() const override { return tr("Forcefield optimization"); } QString description() const override { return tr("Forcefield minimization, including scripts"); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; void setupMethod(); std::string recommendedForceField() const; public slots: /** * Scan for new scripts in the Forcefield directories. */ void refreshScripts(); void registerScripts(); void unregisterScripts(); void showDialog(); private slots: void energy(); void forces(); void optimize(); void freezeSelected(); void unfreezeSelected(); void setupConstraints(); // fuse adds all pairwise distance constraints void fuseSelected(); void unfuseSelected(); void updateActions(); private: QList m_actions; QtGui::Molecule* m_molecule = nullptr; Calc::EnergyCalculator* m_method = nullptr; std::string m_methodName; bool m_autodetect; // defaults Minimizer m_minimizer = LBFGS; unsigned int m_maxSteps = 100; unsigned int m_nSteps = 5; double m_tolerance = 1.0e-6; double m_gradientTolerance = 1.0e-4; QList m_scripts; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_FORCEFIELD_H avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/forcefielddialog.cpp000066400000000000000000000075541506155467400262760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "forcefielddialog.h" #include "ui_forcefielddialog.h" #include #include #include #include #include // for log10 namespace Avogadro { namespace QtPlugins { ForceFieldDialog::ForceFieldDialog(const QStringList& forceFields, QWidget* parent_) : QDialog(parent_), ui(new Ui::ForceFieldDialog) { ui->setupUi(this); ui->forceField->addItems(forceFields); updateRecommendedForceField(); connect(ui->useRecommended, SIGNAL(toggled(bool)), SLOT(useRecommendedForceFieldToggled(bool))); QSettings settings; bool autoDetect = settings.value("openbabel/optimizeGeometry/autoDetect", true).toBool(); ui->useRecommended->setChecked(autoDetect); } ForceFieldDialog::~ForceFieldDialog() { delete ui; } QVariantMap ForceFieldDialog::prompt(QWidget* parent_, const QStringList& forceFields, const QVariantMap& startingOptions, const QString& recommendedForceField_) { ForceFieldDialog dlg(forceFields, parent_); dlg.setOptions(startingOptions); dlg.setRecommendedForceField(recommendedForceField_); QVariantMap options; if (static_cast(dlg.exec()) == Accepted) options = dlg.options(); return options; } QVariantMap ForceFieldDialog::options() const { QVariantMap opts; opts["forcefield"] = ui->forceField->currentText(); opts["maxSteps"] = ui->stepLimit->value(); opts["tolerance"] = pow(10, ui->energyConv->value()); opts["gradientTolerance"] = pow(10, ui->gradConv->value()); opts["autodetect"] = ui->useRecommended->isChecked(); return opts; } void ForceFieldDialog::setOptions(const QVariantMap& opts) { if (opts.contains("forcefield") && opts["forcefield"].canConvert()) ui->forceField->setCurrentText(opts["forcefield"].toString()); if (opts.contains("maxSteps") && opts["maxSteps"].canConvert()) ui->stepLimit->setValue(opts["maxSteps"].toInt()); if (opts.contains("tolerance") && opts["tolerance"].canConvert()) ui->energyConv->setValue(log10(opts["tolerance"].toDouble())); if (opts.contains("gradientTolerance") && opts["gradientTolerance"].canConvert()) ui->gradConv->setValue(log10(opts["gradientTolerance"].toDouble())); if (opts.contains("autodetect") && opts["autodetect"].canConvert()) ui->useRecommended->setChecked(opts["autodetect"].toBool()); } void ForceFieldDialog::setRecommendedForceField(const QString& rff) { if (rff == m_recommendedForceField) return; if (ui->forceField->findText(rff) == -1) return; m_recommendedForceField = rff; updateRecommendedForceField(); } void ForceFieldDialog::useRecommendedForceFieldToggled(bool state) { if (!m_recommendedForceField.isEmpty()) { if (state) { int index = ui->forceField->findText(m_recommendedForceField); if (index >= 0) { ui->forceField->setCurrentIndex(index); } } } ui->forceField->setEnabled(!state); QSettings().setValue("forcefield/autoDetect", state); } void ForceFieldDialog::updateRecommendedForceField() { if (m_recommendedForceField.isEmpty()) { ui->useRecommended->hide(); ui->forceField->setEnabled(true); } else { ui->useRecommended->setText( tr("Autodetect (%1)").arg(m_recommendedForceField)); // Force the combo box to update if needed: useRecommendedForceFieldToggled(ui->useRecommended->isChecked()); ui->useRecommended->show(); } } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/forcefielddialog.h000066400000000000000000000047551506155467400257430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_FORCEFIELDDIALOG_H #define AVOGADRO_QTPLUGINS_FORCEFIELDDIALOG_H #include namespace Avogadro { namespace QtPlugins { namespace Ui { class ForceFieldDialog; } /** * @brief The ForceFieldDialog class is used to prompt the user for parameters * to be used in a force field optimization. */ class ForceFieldDialog : public QDialog { Q_OBJECT public: /** * Construct a new dialog using the forcefields in @a forceFields. */ explicit ForceFieldDialog(const QStringList& forceFields, QWidget* parent_ = 0); ~ForceFieldDialog() override; /** * Construct a new dialog using the forcefields in @a forceFields and * initialize the options to those in @a startingOptions (see setOptions). * If the user chooses the recommended force field, @a recommendedForceField_ * will be set. This is useful for preferring a specific force field for a * particular molecule. * When the user closes the dialog, the options they selected are returned. If * the user cancels the dialog, an empty list is returned. */ static QVariantMap prompt(QWidget* parent_, const QStringList& forceFields, const QVariantMap& startingOptions, const QString& recommendedForceField_ = QString()); /** * Get/set the options displayed in the dialog. */ QVariantMap options() const; void setOptions(const QVariantMap& opts); /** * Get/set the recommended forcefield for the current molecule. If an empty * string, the user will not be shown an option to use the recommended * forcefield. * If the string is non-empty (and in the forceFields list passed in the * constructor), the user will have the option of setting the forcefield to * this value. * * @{ */ QString recommendedForceField() const { return m_recommendedForceField; } void setRecommendedForceField(const QString& rff); /**@}*/ private slots: void useRecommendedForceFieldToggled(bool state); private: void updateRecommendedForceField(); Ui::ForceFieldDialog* ui; QString m_recommendedForceField; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_FORCEFIELDDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/forcefielddialog.ui000066400000000000000000000122031506155467400261140ustar00rootroot00000000000000 Avogadro::QtPlugins::ForceFieldDialog 0 0 365 275 0 0 Geometry Optimization Parameters QLayout::SetFixedSize Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Convergence Criteria Energy convergence: Step limit: units 10^ -10 9 -6 steps 0 100000 50 250 Gradient convergence: units 10^ -10 -1 -4 Optimization Method Force field: Autodetect forceField useRecommended energyConv stepLimit buttonBox buttonBox accepted() Avogadro::QtPlugins::ForceFieldDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::QtPlugins::ForceFieldDialog reject() 316 260 286 274 avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/obenergy.cpp000066400000000000000000000157241506155467400246240ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). It links to the Open Babel library, which is released under the GNU GPL v2. ******************************************************************************/ #include "obenergy.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace OpenBabel; namespace Avogadro::QtPlugins { class OBEnergy::Private { public: // OBMol and OBForceField are owned by this class OBMol* m_obmol = nullptr; OBForceField* m_forceField = nullptr; ~Private() { if (m_obmol != nullptr) delete m_obmol; } }; OBEnergy::OBEnergy(const std::string& method) : m_identifier(method), m_name(method), m_molecule(nullptr) { d = new Private; // make sure we set the Open Babel variables for data files #ifdef _WIN32 QByteArray dataDir = QString(QCoreApplication::applicationDirPath() + "/data").toLocal8Bit(); qputenv("BABEL_DATADIR", dataDir); #else // check if BABEL_DATADIR is set in the environment QStringList filters; filters << "3.*" << "2.*"; if (qgetenv("BABEL_DATADIR").isEmpty()) { QDir dir(QCoreApplication::applicationDirPath() + "/../share/openbabel"); QStringList dirs = dir.entryList(filters); if (dirs.size() == 1) { // versioned data directory QString dataDir = QCoreApplication::applicationDirPath() + "/../share/openbabel/" + dirs[0]; qputenv("BABEL_DATADIR", dataDir.toLocal8Bit()); } else { qDebug() << "Error, Open Babel data directory not found."; } } // Check if BABEL_LIBDIR is set if (qgetenv("BABEL_LIBDIR").isEmpty()) { QDir dir(QCoreApplication::applicationDirPath() + "/../lib/openbabel"); QStringList dirs = dir.entryList(filters); if (dirs.size() == 0) { QString libDir = QCoreApplication::applicationDirPath() + "/../lib/openbabel/"; qputenv("BABEL_LIBDIR", libDir.toLocal8Bit()); } else if (dirs.size() == 1) { QString libDir = QCoreApplication::applicationDirPath() + "/../lib/openbabel/" + dirs[0]; qputenv("BABEL_LIBDIR", libDir.toLocal8Bit()); } else { qDebug() << "Error, Open Babel plugins directory not found."; } } #endif // Ensure the plugins are loaded OBPlugin::LoadAllPlugins(); d->m_forceField = static_cast( OBPlugin::GetPlugin("forcefields", method.c_str())); qDebug() << "OBEnergy: method: " << method.c_str(); if (d->m_forceField == nullptr) { qDebug() << "OBEnergy: method not found: " << method.c_str(); qDebug() << OBPlugin::ListAsString("forcefields").c_str(); } if (method == "UFF") { m_description = tr("Universal Force Field"); m_elements.reset(); for (unsigned int i = 1; i < 102; ++i) m_elements.set(i); } else if (method == "GAFF") { m_description = tr("Generalized Amber Force Field"); // H, C, N, O, F, P, S, Cl, Br, and I m_elements.set(1); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(15); m_elements.set(16); m_elements.set(17); m_elements.set(35); m_elements.set(53); } else if (method == "MMFF94") { m_description = tr("Merck Molecular Force Field 94"); m_elements.reset(); // H, C, N, O, F, Si, P, S, Cl, Br, and I m_elements.set(1); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(14); m_elements.set(15); m_elements.set(16); m_elements.set(17); m_elements.set(35); m_elements.set(53); } } OBEnergy::~OBEnergy() {} bool OBEnergy::acceptsRadicals() const { if (m_identifier == "UFF") return true; return false; } Calc::EnergyCalculator* OBEnergy::newInstance() const { return new OBEnergy(m_name); } void OBEnergy::setMolecule(Core::Molecule* mol) { m_molecule = mol; if (mol == nullptr || mol->atomCount() == 0) { return; // nothing to do } // set up our internal OBMol d->m_obmol = new OBMol; // copy the atoms, bonds, and coordinates d->m_obmol->BeginModify(); for (size_t i = 0; i < mol->atomCount(); ++i) { const Core::Atom& atom = mol->atom(i); OBAtom* obAtom = d->m_obmol->NewAtom(); obAtom->SetAtomicNum(atom.atomicNumber()); auto pos = atom.position3d().cast(); obAtom->SetVector(pos.x(), pos.y(), pos.z()); } for (size_t i = 0; i < mol->bondCount(); ++i) { const Core::Bond& bond = mol->bond(i); d->m_obmol->AddBond(bond.atom1().index() + 1, bond.atom2().index() + 1, bond.order()); } d->m_obmol->EndModify(); // make sure we can set up the force field if (d->m_forceField != nullptr) { d->m_forceField->Setup(*d->m_obmol); } else { d->m_forceField = static_cast( OBPlugin::GetPlugin("forcefields", m_identifier.c_str())); if (d->m_forceField != nullptr) { d->m_forceField->Setup(*d->m_obmol); } } } Real OBEnergy::value(const Eigen::VectorXd& x) { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return 0.0; // nothing to do // update coordinates in our private OBMol for (size_t i = 0; i < m_molecule->atomCount(); ++i) { Eigen::Vector3d pos(x[i * 3], x[i * 3 + 1], x[i * 3 + 2]); d->m_obmol->GetAtom(i + 1)->SetVector(pos.x(), pos.y(), pos.z()); } double energy = 0.0; if (d->m_forceField != nullptr) { d->m_forceField->SetCoordinates(*d->m_obmol); energy = d->m_forceField->Energy(false); } // make sure to add in any constraint penalties energy += constraintEnergies(x); return energy; } void OBEnergy::gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; // update coordinates in our private OBMol for (size_t i = 0; i < m_molecule->atomCount(); ++i) { Eigen::Vector3d pos(x[i * 3], x[i * 3 + 1], x[i * 3 + 2]); d->m_obmol->GetAtom(i + 1)->SetVector(pos.x(), pos.y(), pos.z()); } if (d->m_forceField != nullptr) { d->m_forceField->SetCoordinates(*d->m_obmol); // make sure gradients are calculated double energy = d->m_forceField->Energy(true); for (size_t i = 0; i < m_molecule->atomCount(); ++i) { OBAtom* atom = d->m_obmol->GetAtom(i + 1); OpenBabel::vector3 obGrad = d->m_forceField->GetGradient(atom); grad[3 * i] = obGrad.x(); grad[3 * i + 1] = obGrad.y(); grad[3 * i + 2] = obGrad.z(); } grad *= -1; // OpenBabel outputs forces, not grads cleanGradients(grad); // add in any constraints constraintGradients(x, grad); } } } // namespace Avogadro::QtPlugins #include "obenergy.moc" avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/obenergy.h000066400000000000000000000037271506155467400242710ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). It links to the Open Babel library, which is released under the GNU GPL v2. ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OBENERGY_H #define AVOGADRO_QTPLUGINS_OBENERGY_H #include #include #include #include namespace Avogadro { namespace QtPlugins { class OBEnergy : public Avogadro::Calc::EnergyCalculator { Q_DECLARE_TR_FUNCTIONS(OBEnergy) public: OBEnergy(const std::string& method = ""); ~OBEnergy() override; std::string method() const { return m_identifier; } void setupProcess(); Calc::EnergyCalculator* newInstance() const override; std::string identifier() const override { return m_identifier; } std::string name() const override { return m_name; } std::string description() const override { return m_description.toStdString(); } Core::Molecule::ElementMask elements() const override { return (m_elements); } // This will check if the molecule is valid for this script // and then start the external process void setMolecule(Core::Molecule* mol) override; // energy Real value(const Eigen::VectorXd& x) override; // gradient (which may be unsupported and fall back to numeric) void gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) override; bool acceptsIons() const override { return true; } // UFF can handle radicals bool acceptsRadicals() const override; private: class Private; Core::Molecule* m_molecule; Private* d; Core::Molecule::ElementMask m_elements; std::string m_identifier; std::string m_name; QString m_description; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OBENERGY_H avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/obmmenergy.cpp000066400000000000000000000214501506155467400251470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "obmmenergy.h" #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { OBMMEnergy::OBMMEnergy(const std::string& method) : m_molecule(nullptr), m_process(nullptr), m_executable( #if defined(_WIN32) "obmm.exe" #else "obmm" #endif ), m_identifier(method), m_name(method) { // eventually CJSON might be nice m_inputFormat = new Io::CmlFormat; if (method == "UFF") { m_description = tr("Universal Force Field"); m_elements.reset(); for (unsigned int i = 1; i < 102; ++i) m_elements.set(i); } else if (method == "GAFF") { m_description = tr("Generalized Amber Force Field"); // H, C, N, O, F, P, S, Cl, Br, and I m_elements.set(1); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(15); m_elements.set(16); m_elements.set(17); m_elements.set(35); m_elements.set(53); } else if (method == "MMFF94") { m_description = tr("Merck Molecular Force Field 94"); m_elements.reset(); // H, C, N, O, F, Si, P, S, Cl, Br, and I m_elements.set(1); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(14); m_elements.set(15); m_elements.set(16); m_elements.set(17); m_elements.set(35); m_elements.set(53); } } OBMMEnergy::~OBMMEnergy() { delete m_inputFormat; if (m_process != nullptr) delete m_process; } bool OBMMEnergy::acceptsRadicals() const { // UFF will figure something out if (m_identifier == "UFF") return true; return false; } QByteArray OBMMEnergy::writeAndRead(const QByteArray& input) { if (m_process == nullptr) return QByteArray(); QByteArray result, line; m_process->write(input + "\n"); QThread::msleep(1); m_process->waitForReadyRead(5); while (m_process->canReadLine() && !line.startsWith("command >")) { line = m_process->readLine(); result += line; } // check if we've really flushed the output if (!result.contains("invalid command\n command >")) { m_process->write(" \n"); QThread::msleep(1); m_process->waitForReadyRead(5); while (m_process->canReadLine()) { line = m_process->readLine(); result += line; } } result += m_process->readAllStandardOutput(); return result; } void OBMMEnergy::setupProcess() { if (m_process != nullptr) { m_process->kill(); delete m_process; } m_process = new QProcess(); // Read the AVO_OBMM_EXECUTABLE env var to optionally override the // executable used. QByteArray obmmExec = qgetenv("AVO_OBMM_EXECUTABLE"); if (!obmmExec.isEmpty()) { m_executable = obmmExec; } else { // If not overridden, look for an obmm next to the executable. QDir baseDir(QCoreApplication::applicationDirPath()); if (!baseDir.absolutePath().startsWith("/usr/") && QFileInfo(baseDir.absolutePath() + '/' + m_executable).exists()) { m_executable = baseDir.absolutePath() + '/' + m_executable; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); #if defined(_WIN32) env.insert("BABEL_DATADIR", QCoreApplication::applicationDirPath() + "/data"); #else QDir dir(QCoreApplication::applicationDirPath() + "/../share/openbabel"); QStringList filters; filters << "3.*"; QStringList dirs = dir.entryList(filters); if (dirs.size() == 1) { env.insert("BABEL_DATADIR", QCoreApplication::applicationDirPath() + "/../share/openbabel/" + dirs[0]); } else { qDebug() << "Error, Open Babel data directory not found."; } dir.setPath(QCoreApplication::applicationDirPath() + "/../lib/openbabel"); dirs = dir.entryList(filters); if (dirs.size() == 1) { env.insert("BABEL_LIBDIR", QCoreApplication::applicationDirPath() + "/../lib/openbabel/" + dirs[0]); } else { env.insert("BABEL_LIBDIR", QCoreApplication::applicationDirPath() + "/../lib/openbabel/"); qDebug() << "Error, Open Babel plugins directory not found."; } #endif m_process->setProcessEnvironment(env); } } } Calc::EnergyCalculator* OBMMEnergy::newInstance() const { return new OBMMEnergy(m_name); } void OBMMEnergy::setMolecule(Core::Molecule* mol) { m_molecule = mol; // should check if the molecule is valid for this script // .. this should never happen, but let's be defensive if (mol == nullptr || mol->atomCount() == 0) { return; // nothing to do } setupProcess(); // start the process // we need a tempory file to write the molecule // get a temporary filename QString tempPath = QDir::tempPath(); if (!tempPath.endsWith(QDir::separator())) tempPath += QDir::separator(); QString tempPattern = tempPath + "avogadroOBMMXXXXXX.cml"; m_tempFile.setFileTemplate(tempPattern); if (!m_tempFile.open()) { // appendError("Error creating temporary file."); return; } // write the molecule m_inputFormat->writeFile(m_tempFile.fileName().toStdString(), *mol); m_tempFile.close(); // start the process m_process->start(m_executable, QStringList() << m_tempFile.fileName()); if (!m_process->waitForStarted()) { // appendError("Error starting process."); qDebug() << "OBMM: Error starting process."; return; } QByteArray input, line, result; m_process->waitForReadyRead(); result.clear(); while (!result.contains("command >")) { result += m_process->readLine(); if (!m_process->canReadLine()) break; } result += m_process->readAllStandardOutput(); // set the method m_identifier.c_str() + input = QByteArray("ff ") + m_identifier.c_str(); result = writeAndRead(input); // okay, we need to write "load " to the interpreter // and then read the response input = "load " + m_tempFile.fileName().toLocal8Bit(); result = writeAndRead(input); } Real OBMMEnergy::value(const Eigen::VectorXd& x) { if (m_molecule == nullptr || m_process == nullptr) return 0.0; // nothing to do QByteArray input, result; // write the new coordinates and read the energy input = "coord\n"; for (Eigen::Index i = 0; i < x.size(); i += 3) { // write as x y z (space separated) input += QString::number(x[i]).toUtf8() + " " + QString::number(x[i + 1]).toUtf8() + " " + QString::number(x[i + 2]).toUtf8() + "\n"; } result = writeAndRead(input); // now ask for the energy input = "energy\n"; result = writeAndRead(input); // go through lines in result until we see "total energy" QStringList lines = QString(result).remove('\r').split('\n'); double energy = 0.0; for (auto line : lines) { if (line.contains("total energy =")) { QStringList items = line.split(" ", Qt::SkipEmptyParts); if (items.size() > 4) energy = items[3].toDouble(); } } energy += constraintEnergies(x); return energy; // if conversion fails, returns 0.0 } void OBMMEnergy::gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (m_molecule == nullptr || m_process == nullptr) return; // write the new coordinates and read the energy QByteArray result, input = "coord\n"; for (Eigen::Index i = 0; i < x.size(); i += 3) { // write as x y z (space separated) input += QString::number(x[i]).toUtf8() + " " + QString::number(x[i + 1]).toUtf8() + " " + QString::number(x[i + 2]).toUtf8() + "\n"; } result = writeAndRead(input); // now ask for the energy input = "grad"; result = writeAndRead(input); // go through lines in result until we see "gradient " QStringList lines = QString(result).remove('\r').split('\n'); bool readingGradient = false; unsigned int i = 0; for (auto line : lines) { if (line.contains("gradient")) { readingGradient = true; continue; } if (readingGradient) { QStringList items = line.split(" ", Qt::SkipEmptyParts); if (items.size() == 3) { grad[3 * i] = items[0].toDouble(); grad[3 * i + 1] = items[1].toDouble(); grad[3 * i + 2] = items[2].toDouble(); ++i; } } } grad *= -1; // OpenBabel outputs forces, not grads cleanGradients(grad); constraintGradients(x, grad); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/obmmenergy.h000066400000000000000000000045351506155467400246210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OBMMENERGY_H #define AVOGADRO_QTPLUGINS_OBMMENERGY_H #include #include #include #include #include #include class QJsonObject; namespace Avogadro { namespace Io { class FileFormat; } namespace QtPlugins { class OBMMEnergy : public Avogadro::Calc::EnergyCalculator { Q_DECLARE_TR_FUNCTIONS(OBMMEnergy) public: /** Formats that may be written to the script's input. */ enum Format { NotUsed, Cjson, Cml, Mdl, // sdf Pdb, Xyz }; OBMMEnergy(const std::string& method = ""); ~OBMMEnergy() override; std::string method() const { return m_identifier; } void setupProcess(); Calc::EnergyCalculator* newInstance() const override; std::string identifier() const override { return m_identifier; } std::string name() const override { return m_name; } std::string description() const override { return m_description.toStdString(); } Core::Molecule::ElementMask elements() const override { return (m_elements); } // This will check if the molecule is valid for this script // and then start the external process void setMolecule(Core::Molecule* mol) override; // energy Real value(const Eigen::VectorXd& x) override; // gradient (which may be unsupported and fall back to numeric) void gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) override; bool acceptsIons() const override { return true; } // UFF can handle radicals bool acceptsRadicals() const override; /** * @brief Synchronous use of the QProcess. */ QByteArray writeAndRead(const QByteArray& input); private: Core::Molecule* m_molecule; Io::FileFormat* m_inputFormat; QProcess* m_process; QString m_executable; Core::Molecule::ElementMask m_elements; std::string m_identifier; std::string m_name; QString m_description; QTemporaryFile m_tempFile; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OBMMENERGY_H avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/scriptenergy.cpp000066400000000000000000000272631506155467400255310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "scriptenergy.h" #include #include // formats supported in scripts #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { ScriptEnergy::ScriptEnergy(const QString& scriptFileName_) : m_interpreter(new QtGui::PythonScript(scriptFileName_)), m_inputFormat(NotUsed), m_valid(true), m_gradients(false), m_ions(false), m_radicals(false), m_unitCells(false) { m_elements.reset(); readMetaData(); } ScriptEnergy::~ScriptEnergy() { delete m_interpreter; } QString ScriptEnergy::scriptFilePath() const { return m_interpreter->scriptFilePath(); } Calc::EnergyCalculator* ScriptEnergy::newInstance() const { return new ScriptEnergy(m_interpreter->scriptFilePath()); } void ScriptEnergy::setMolecule(Core::Molecule* mol) { m_molecule = mol; // should check if the molecule is valid for this script // .. this should never happen, but let's be defensive if (mol == nullptr || m_interpreter == nullptr) { return; // nothing to do } if (!m_unitCells && mol->unitCell()) { // appendError("Unit cell not supported for this script."); return; } if (!m_ions && mol->totalCharge() != 0) { // appendError("Ionized molecules not supported for this script."); return; } if (!m_radicals && mol->totalSpinMultiplicity() != 1) { // appendError("Radical molecules not supported for this script."); return; } // start the process // we need a tempory file to write the molecule QScopedPointer format(createFileFormat(m_inputFormat)); if (format.isNull()) { // appendError("Invalid input format."); return; } // get a temporary filename QString tempPath = QDir::tempPath(); if (!tempPath.endsWith(QDir::separator())) tempPath += QDir::separator(); QString tempPattern = tempPath + "avogadroenergyXXXXXX." + format->fileExtensions()[0].c_str(); m_tempFile.setFileTemplate(tempPattern); if (!m_tempFile.open()) { // appendError("Error creating temporary file."); return; } // write the molecule format->writeFile(m_tempFile.fileName().toStdString(), *mol); m_tempFile.close(); // construct the command line options QStringList options; options << "-f" << m_tempFile.fileName(); // if there was a previous process, kill it m_interpreter->asyncTerminate(); // start the interpreter m_interpreter->asyncExecute(options); } Real ScriptEnergy::value(const Eigen::VectorXd& x) { if (m_molecule == nullptr || m_interpreter == nullptr) return 0.0; // nothing to do // write the new coordinates and read the energy QByteArray input; for (Eigen::Index i = 0; i < x.size(); i += 3) { // write as x y z (space separated) input += QString::number(x[i]).toUtf8() + " " + QString::number(x[i + 1]).toUtf8() + " " + QString::number(x[i + 2]).toUtf8() + "\n"; } // qDebug() << " wrote coords "; QByteArray result = m_interpreter->asyncWriteAndResponse(input); // qDebug() << " got result " << result; // go through lines in result until we see "AvogadroEnergy: " QStringList lines = QString(result).remove('\r').split('\n'); double energy = 0.0; for (auto line : lines) { if (line.startsWith("AvogadroEnergy:")) { QStringList items = line.split(" ", Qt::SkipEmptyParts); if (items.size() > 1) { energy = items[1].toDouble(); break; } } } energy += constraintEnergies(x); return energy; // if conversion fails, returns 0.0 } void ScriptEnergy::gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) { if (!m_gradients) { EnergyCalculator::gradient(x, grad); return; } // Get the gradient from the script // write the new coordinates and read the energy QByteArray input; for (Eigen::Index i = 0; i < x.size(); i += 3) { // write as x y z (space separated) input += QString::number(x[i]).toUtf8() + " " + QString::number(x[i + 1]).toUtf8() + " " + QString::number(x[i + 2]).toUtf8() + "\n"; } QByteArray result = m_interpreter->asyncWriteAndResponse(input); // parse the result // first split on newlines QStringList lines = QString(result).remove('\r').split('\n'); unsigned int i = 0; bool readingGrad = false; for (auto line : lines) { if (line.startsWith("AvogadroGradient:")) { readingGrad = true; continue; // next line } if (readingGrad) { QStringList items = line.split(" ", Qt::SkipEmptyParts); if (items.size() == 3) { grad[i] = items[0].toDouble(); grad[i + 1] = items[1].toDouble(); grad[i + 2] = items[2].toDouble(); i += 3; } if (i > x.size()) break; } } cleanGradients(grad); constraintGradients(x, grad); } ScriptEnergy::Format ScriptEnergy::stringToFormat(const std::string& str) { if (str == "cjson") return Cjson; else if (str == "cml") return Cml; else if (str == "mdl" || str == "mol") return Mdl; else if (str == "pdb") return Pdb; else if (str == "sdf") return Sdf; else if (str == "xyz") return Xyz; return NotUsed; } Io::FileFormat* ScriptEnergy::createFileFormat(ScriptEnergy::Format fmt) { switch (fmt) { case Cjson: return new Io::CjsonFormat; case Cml: return new Io::CmlFormat; case Mdl: return new Io::MdlFormat; case Pdb: return new Io::PdbFormat; case Sdf: return new Io::SdfFormat; case Xyz: return new Io::XyzFormat; default: case NotUsed: return nullptr; } } void ScriptEnergy::resetMetaData() { m_valid = false; m_gradients = false; m_ions = false; m_radicals = false; m_unitCells = false; m_inputFormat = NotUsed; m_identifier.clear(); m_name.clear(); m_description.clear(); m_formatString.clear(); m_elements.reset(); } void ScriptEnergy::readMetaData() { resetMetaData(); QByteArray output(m_interpreter->execute(QStringList() << "--metadata")); if (m_interpreter->hasErrors()) { qWarning() << tr("Error retrieving metadata for energy script: %1") .arg(scriptFilePath()) << "\n" << m_interpreter->errorList(); return; } QJsonParseError parseError; QJsonDocument doc(QJsonDocument::fromJson(output, &parseError)); if (parseError.error != QJsonParseError::NoError) { qWarning() << tr("Error parsing metadata for energy script: %1") .arg(scriptFilePath()) << "\n" << parseError.errorString(); return; } if (!doc.isObject()) { qWarning() << tr("Error parsing metadata for energy script: %1\n" "Result is not a JSON object.\n") .arg(scriptFilePath()); return; } const QJsonObject metaData(doc.object()); // Read required inputs first. std::string identifierTmp; if (!parseString(metaData, "identifier", identifierTmp)) { qWarning() << "Error parsing metadata for energy script:" << scriptFilePath() << "\n" << "Error parsing required member 'identifier'" << "\n" << output; return; } m_identifier = identifierTmp; std::string nameTmp; if (!parseString(metaData, "name", nameTmp)) { qWarning() << "Error parsing metadata for energy script:" << scriptFilePath() << "\n" << "Error parsing required member 'name'" << "\n" << output; return; } m_name = nameTmp; std::string descriptionTmp; parseString(metaData, "description", descriptionTmp); m_description = descriptionTmp; // optional Format inputFormatTmp = NotUsed; std::string inputFormatStrTmp; if (!parseString(metaData, "inputFormat", inputFormatStrTmp)) { qWarning() << "Error parsing metadata for energy script:" << scriptFilePath() << "\n" << "Member 'inputFormat' required for writable formats." << "\n" << output; return; } m_formatString = inputFormatStrTmp.c_str(); // for the json key // Validate the input format inputFormatTmp = stringToFormat(inputFormatStrTmp); if (inputFormatTmp == NotUsed) { qWarning() << "Error parsing metadata for energy script:" << scriptFilePath() << "\n" << "Member 'inputFormat' not recognized:" << inputFormatStrTmp.c_str() << "\nValid values are cjson, cml, mdl/sdf, pdb, or xyz.\n" << output; return; } m_inputFormat = inputFormatTmp; // check ions, radicals, unit cells /* e.g., "unitCell": False, "gradients": True, "ion": False, "radical": False, */ if (!metaData["gradients"].isBool()) { return; // not valid } m_gradients = metaData["gradients"].toBool(); if (!metaData["unitCell"].isBool()) { return; // not valid } m_unitCells = metaData["unitCell"].toBool(); if (!metaData["ion"].isBool()) { return; // not valid } m_ions = metaData["ion"].toBool(); if (!metaData["radical"].isBool()) { return; // not valid } m_radicals = metaData["radical"].toBool(); // get the element mask // (if it doesn't exist, the default is no elements anyway) m_valid = parseElements(metaData); } bool ScriptEnergy::parseString(const QJsonObject& ob, const QString& key, std::string& str) { if (!ob[key].isString()) return false; str = ob[key].toString().toStdString(); return !str.empty(); } void ScriptEnergy::processElementString(const QString& str) { // parse the QString // first turn any commas into whitespace QString str2(str); str2.replace(',', ' '); // then split on whitespace QStringList strList = str2.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); foreach (QString sstr, strList) { // these should be numbers or ranges (e.g., 1-84) if (sstr.contains('-')) { // range, so split on the dash QStringList strList2 = sstr.split('-'); if (strList2.size() != 2) return; // get the two numbers bool ok; int start = strList2[0].toInt(&ok); if (!ok || start < 1 || start > 119) return; int end = strList2[1].toInt(&ok); if (!ok || end < 1 || end > 119) return; for (int i = start; i <= end; ++i) m_elements.set(i); } bool ok; int i = sstr.toInt(&ok); if (!ok || i < 1 || i > 119) return; m_elements.set(i); } } bool ScriptEnergy::parseElements(const QJsonObject& object) { m_elements.reset(); // we could either get a string or an array (of numbers) if (object["elements"].isString()) { auto str = object["elements"].toString(); processElementString(str); } else if (object["elements"].isArray()) { QJsonArray arr = object["elements"].toArray(); for (auto&& i : arr) { if (i.isString()) { processElementString(i.toString()); } else if (i.isDouble()) { int element = i.toInt(); if (element >= 1 && element <= 119) // check the range m_elements.set(element); } } } return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/forcefield/scriptenergy.h000066400000000000000000000056061506155467400251730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SCRIPTENERGY_H #define AVOGADRO_QTPLUGINS_SCRIPTENERGY_H #include #include #include #include #include class QJsonObject; namespace Avogadro { namespace Io { class FileFormat; } namespace QtGui { class PythonScript; } namespace QtPlugins { class ScriptEnergy : public Avogadro::Calc::EnergyCalculator { Q_DECLARE_TR_FUNCTIONS(ScriptEnergy) public: /** Formats that may be written to the script's input. */ enum Format { NotUsed, Cjson, Cml, Mdl, Pdb, Sdf, Xyz }; ScriptEnergy(const QString& scriptFileName = ""); ~ScriptEnergy() override; QString scriptFilePath() const; Format inputFormat() const { return m_inputFormat; } bool isValid() const { return m_valid; } Calc::EnergyCalculator* newInstance() const override; std::string identifier() const override { return m_identifier; } std::string name() const override { return m_name; } std::string description() const override { return m_description; } Core::Molecule::ElementMask elements() const override { return m_elements; } bool supportsGradients() const { return m_gradients; } bool acceptsIons() const override { return m_ions; } bool acceptsRadicals() const override { return m_radicals; } bool acceptsUnitCell() const override { return m_unitCells; } // This will check if the molecule is valid for this script // and then start the external process void setMolecule(Core::Molecule* mol) override; // energy Real value(const Eigen::VectorXd& x) override; // gradient (which may be unsupported and fall back to numeric) void gradient(const Eigen::VectorXd& x, Eigen::VectorXd& grad) override; private: static Format stringToFormat(const std::string& str); static Io::FileFormat* createFileFormat(Format fmt); void resetMetaData(); void readMetaData(); bool parseString(const QJsonObject& ob, const QString& key, std::string& str); void processElementString(const QString& str); bool parseElements(const QJsonObject& ob); private: QtGui::PythonScript* m_interpreter; Format m_inputFormat; Core::Molecule* m_molecule; // what's supported by this script Core::Molecule::ElementMask m_elements; bool m_valid; bool m_gradients; bool m_ions; bool m_radicals; bool m_unitCells; std::string m_identifier; std::string m_name; std::string m_description; QString m_formatString; QTemporaryFile m_tempFile; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SCRIPTENERGY_H avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/000077500000000000000000000000001506155467400225325ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/CMakeLists.txt000066400000000000000000000005041506155467400252710ustar00rootroot00000000000000# Extension set(gamessinput_srcs gamessinputdialog.cpp gamesshighlighter.cpp gamessinput.cpp ) avogadro_plugin(GamessInput "GAMESS input file generation" ExtensionPlugin gamessinput.h GamessInput "${gamessinput_srcs}" gamessinputdialog.ui ) target_link_libraries(GamessInput PRIVATE Avogadro::MoleQueue) avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/gamesshighlighter.cpp000066400000000000000000000224611506155467400267410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2009 Marcus D. Hanwell This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "gamesshighlighter.h" namespace Avogadro::QtPlugins { GamessHighlighter::GamessHighlighter(QTextDocument* parent_) : QSyntaxHighlighter(parent_) { HighlightingRule rule; m_keywordFormat.setForeground(Qt::darkBlue); m_keywordFormat.setFontWeight(QFont::Bold); m_keywords << R"(\s\$BASIS\b)" << R"(\s\$CONTRL\b)" << R"(\s\$SYSTEM\b)" << R"(\s\$ZMAT\b)" << R"(\s\$LIBE\b)" << R"(\s\$SCF\b)" << R"(\s\$SCFMI\b)" << R"(\s\$DFT\b)" << R"(\s\$TDDFT\b)" << R"(\s\$CIS\b)" << R"(\s\$CISVEC\b)" << R"(\s\$MP2\b)" << R"(\s\$CCINP\b)" << R"(\s\$EOMINP\b)" << R"(\s\$MOPAC\b)" << R"(\s\$GUESS\b)" << R"(\s\$VEC\b)" << R"(\s\$MOFRZ\b)" << R"(\s\$STATPT\b)" << R"(\s\$TRUDGE\b)" << R"(\s\$TRURST\b)" << R"(\s\$FORCE\b)" << R"(\s\$CPHF\b)" << R"(\s\$MASS\b)" << R"(\s\$HESS\b)" << R"(\s\$GRAD\b)" << R"(\s\$DIPDR\b)" << R"(\s\$VIB\b)" << R"(\s\$VIB2\b)" << R"(\s\$VSCF\b)" << R"(\s\$VIBSCF\b)" << R"(\s\$GAMMA\b)" << R"(\s\$EQGEOM\b)" << R"(\s\$HLOWT\b)" << R"(\s\$GLOWT\b)" << R"(\s\$IRC\b)" << R"(\s\$DRC\b)" << R"(\s\$MEX\b)" << R"(\s\$MD\b)" << R"(\s\$RDF\b)" << R"(\s\$GLOBOP\b)" << R"(\s\$GRADEX\b)" << R"(\s\$SURF\b)" << R"(\s\$LOCAL\b)" << R"(\s\$TWOEI\b)" << R"(\s\$TRUNCN\b)" << R"(\s\$ELMOM\b)" << R"(\s\$ELPOT\b)" << R"(\s\$ELDENS\b)" << R"(\s\$ELFLDG\b)" << R"(\s\$POINTS\b)" << R"(\s\$GRID\b)" << R"(\s\$PDC\b)" << R"(\s\$MOLGRF\b)" << R"(\s\$STONE\b)" << R"(\s\$RAMAN\b)" << R"(\s\$ALPDR\b)" << R"(\s\$NMR\b)" << R"(\s\$MOROKM\b)" << R"(\s\$FFCALC\b)" << R"(\s\$TDHF\b)" << R"(\s\$TDHFX\b)" << R"(\s\$EFRAG\b)" << R"(\s\$FRAGNAME\b)" << R"(\s\$FRGRPL\b)" << R"(\s\$EWALD\b)" << R"(\s\$MAKEFP\b)" << R"(\s\$PRTEFP\b)" << R"(\s\$DAMP\b)" << R"(\s\$DAMPGS\b)" << R"(\s\$PCM\b)" << R"(\s\$PCMGRD\b)" << R"(\s\$PCMCAV\b)" << R"(\s\$TESCAV\b)" << R"(\s\$NEWCAV\b)" << R"(\s\$IEFPCM\b)" << R"(\s\$PCMITR\b)" << R"(\s\$DISBS\b)" << R"(\s\$DISREP\b)" << R"(\s\$SVP\b)" << R"(\s\$SVPIRF\b)" << R"(\s\$COSGMS\b)" << R"(\s\$SCRF\b)" << R"(\s\$ECP\b)" << R"(\s\$MCP\b)" << R"(\s\$RELWFN\b)" << R"(\s\$EFIELD\b)" << R"(\s\$INTGRL\b)" << R"(\s\$FMM\b)" << R"(\s\$TRANS\b)" << R"(\s\$FMO\b)" << R"(\s\$FMOPRP\b)" << R"(\s\$FMOXYZ\b)" << R"(\s\$OPTFMO\b)" << R"(\s\$FMOHYB\b)" << R"(\s\$FMOBND\b)" << R"(\s\$FMOENM\b)" << R"(\s\$FMOEND\b)" << R"(\s\$OPTRST\b)" << R"(\s\$GDDI\b)" << R"(\s\$ELG\b)" << R"(\s\$DANDC\b)" << R"(\s\$DCCORR\b)" << R"(\s\$SUBSCF\b)" << R"(\s\$SUBCOR\b)" << R"(\s\$MP2RES\b)" << R"(\s\$CCRES\b)" << R"(\s\$CIINP\b)" << R"(\s\$DET\b)" << R"(\s\$CIDET\b)" << R"(\s\$GEN\b)" << R"(\s\$CIGEN\b)" << R"(\s\$ORMAS\b)" << R"(\s\$CEEIS\b)" << R"(\s\$CEDATA\b)" << R"(\s\$GCILST\b)" << R"(\s\$GMCPT\b)" << R"(\s\$PDET\b)" << R"(\s\$ADDDET\b)" << R"(\s\$REMDET\b)" << R"(\s\$SODET\b)" << R"(\s\$DRT\b)" << R"(\s\$CIDRT\b)" << R"(\s\$MCSCF\b)" << R"(\s\$MRMP\b)" << R"(\s\$DETPT\b)" << R"(\s\$MCQDPT\b)" << R"(\s\$CASCI\b)" << R"(\s\$IVOORB\b)" << R"(\s\$CISORT\b)" << R"(\s\$GUGEM\b)" << R"(\s\$GUGDIA\b)" << R"(\s\$GUGDM\b)" << R"(\s\$GUGDM2\b)" << R"(\s\$LAGRAN\b)" << R"(\s\$TRFDM2\b)" << R"(\s\$TRANST\b)" << R"(\s\$DATA\b)"; rule.format = m_keywordFormat; foreach (const QString& pattern, m_keywords) { rule.pattern = QRegularExpression(pattern); m_highlightingRules.append(rule); } rule.pattern = QRegularExpression(R"(\s\$END\b)"); m_highlightingRules.append(rule); m_singleLineCommentFormat.setForeground(Qt::green); rule.pattern = QRegularExpression("![^\n]*"); rule.format = m_singleLineCommentFormat; m_highlightingRules.append(rule); m_numberFormat.setForeground(Qt::blue); rule.pattern = QRegularExpression(R"((\b|[\s-])[0-9]+\.([0-9]+\b)?|\.[0-9]+\b)"); rule.format = m_numberFormat; m_highlightingRules.append(rule); m_numberFormat.setForeground(Qt::blue); rule.pattern = QRegularExpression(R"((\b|[\s-])[0-9]+\.([0-9]+\b)?|\.[0-9]+\b)"); rule.format = m_numberFormat; m_highlightingRules.append(rule); rule.pattern = QRegularExpression(R"((\b|[\s-])[0-9]+([0-9]+\b)?|\.[0-9]+\b)"); rule.format = m_numberFormat; m_highlightingRules.append(rule); m_inDataBlockFormat.setForeground(Qt::gray); m_errorFormat.setForeground(Qt::red); m_errorFormat.setBackground(Qt::yellow); } void GamessHighlighter::highlightBlock(const QString& text) { // Single line comments QRegularExpression pattern("![^\n]*"); QRegularExpressionMatch commentMatch = pattern.match(text); if (commentMatch.hasMatch()) setFormat(commentMatch.capturedStart(), commentMatch.capturedLength(), m_singleLineCommentFormat); setCurrentBlockState(0); int startIndex = 0; int keywordLength = 0; if (previousBlockState() != 1) { foreach (const QString& regexString, m_keywords) { QRegularExpression startExpression( regexString, QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatch startMatch = startExpression.match(text); startIndex = startMatch.capturedStart(); keywordLength = startMatch.capturedLength(); if (startMatch.hasMatch()) { setFormat(startIndex, keywordLength, m_keywordFormat); break; } } } while (startIndex >= 0) { QRegularExpression endExpression(R"(\s\$END\b)", QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatch match = endExpression.match(text, startIndex); int endIndex = match.capturedStart(); int blockLength; if (endIndex == -1) { setCurrentBlockState(1); blockLength = text.length() - startIndex - keywordLength; } else { setFormat(endIndex, match.capturedLength(), m_keywordFormat); blockLength = endIndex - startIndex - keywordLength; } setFormat(startIndex + keywordLength, blockLength, m_inDataBlockFormat); bool found = false; foreach (const QString& regexString, m_keywords) { QRegularExpression newExpression(regexString); QRegularExpressionMatch newMatch = newExpression.match(text, startIndex + blockLength); int index = newMatch.capturedStart(); if (index > startIndex) { found = true; startIndex = index; keywordLength = newMatch.capturedLength(); setFormat(startIndex, keywordLength, m_keywordFormat); break; } } if (!found) break; } if (previousBlockState() == 1) { // Anything outside of data blocks is a comment foreach (const HighlightingRule& rule, m_highlightingRules) { QRegularExpression otherExpression(rule.pattern); otherExpression.setPatternOptions( QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatchIterator iterator = otherExpression.globalMatch(text); while (iterator.hasNext()) { QRegularExpressionMatch otherMatch = iterator.next(); int index = otherMatch.capturedStart(); int length = otherMatch.capturedLength(); setFormat(index, length, rule.format); } } } // Anything over 80 columns will not be read if (text.length() > 80) setFormat(80, text.length(), m_errorFormat); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/gamesshighlighter.h000066400000000000000000000023731506155467400264060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef GAMESSHIGHLIGHTER_H #define GAMESSHIGHLIGHTER_H #include #include #include #include class QTextDocument; namespace Avogadro { namespace QtPlugins { class GamessHighlighter : public QSyntaxHighlighter { Q_OBJECT public: GamessHighlighter(QTextDocument* parent_ = nullptr); protected: void highlightBlock(const QString& text) override; private: struct HighlightingRule { QRegularExpression pattern; QTextCharFormat format; }; QVector m_highlightingRules; QStringList m_keywords; QRegularExpression m_commentStartExpression; QRegularExpression m_commentEndExpression; QTextCharFormat m_keywordFormat; QTextCharFormat m_numberFormat; QTextCharFormat m_singleLineCommentFormat; QTextCharFormat m_inDataBlockFormat; QTextCharFormat m_errorFormat; }; } // End namespace QtPlugins } // End namespace Avogadro #endif // GAMESSHIGHLIGHTER_H avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/gamessinput.cpp000066400000000000000000000055061506155467400256030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gamessinput.h" #include "gamessinputdialog.h" #include #include #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { using MoleQueue::JobObject; GamessInput::GamessInput(QObject* parent_) : ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr), m_dialog(nullptr), m_outputFormat(nullptr) { m_action->setEnabled(true); m_action->setText(tr("&GAMESS…")); connect(m_action, SIGNAL(triggered()), SLOT(menuActivated())); } GamessInput::~GamessInput() {} QList GamessInput::actions() const { QList actions_; actions_.append(m_action); return actions_; } QStringList GamessInput::menuPath(QAction*) const { QStringList path; path << tr("&Input"); return path; } void GamessInput::setMolecule(QtGui::Molecule* mol) { if (m_dialog) m_dialog->setMolecule(mol); m_molecule = mol; } void GamessInput::openJobOutput(const JobObject& job) { m_outputFormat = nullptr; m_outputFileName.clear(); QString outputPath(job.value("outputDirectory").toString()); using QtGui::FileFormatDialog; FileFormatDialog::FormatFilePair result = FileFormatDialog::fileToRead( qobject_cast(parent()), tr("Open Output File"), outputPath); if (result.first == nullptr) // User canceled return; m_outputFormat = result.first; m_outputFileName = result.second; emit moleculeReady(1); } bool GamessInput::readMolecule(QtGui::Molecule& mol) { Io::FileFormat* reader = m_outputFormat->newInstance(); bool success = reader->readFile(m_outputFileName.toStdString(), mol); if (!success) { QMessageBox::information(qobject_cast(parent()), tr("Error"), tr("Error reading output file '%1':\n%2") .arg(m_outputFileName) .arg(QString::fromStdString(reader->error()))); } m_outputFormat = nullptr; m_outputFileName.clear(); return success; } void GamessInput::menuActivated() { if (!m_dialog) { m_dialog = new GamessInputDialog(qobject_cast(parent())); connect(m_dialog, SIGNAL(openJobOutput(Avogadro::MoleQueue::JobObject)), this, SLOT(openJobOutput(Avogadro::MoleQueue::JobObject))); } m_dialog->setMolecule(m_molecule); m_dialog->show(); } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/gamessinput.h000066400000000000000000000030341506155467400252420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_GAMESSINPUT_H #define AVOGADRO_QTPLUGINS_GAMESSINPUT_H #include class QAction; class QDialog; namespace Avogadro { namespace Io { class FileFormat; } namespace MoleQueue { class JobObject; } namespace QtPlugins { class GamessInputDialog; class GamessInput : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit GamessInput(QObject* parent = nullptr); ~GamessInput() override; QString name() const override { return tr("GAMESS input"); } QString description() const override { return tr("Generate input for GAMESS."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; public slots: /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ void openJobOutput(const MoleQueue::JobObject& job); bool readMolecule(QtGui::Molecule& mol) override; private slots: void menuActivated(); private: QAction* m_action; QtGui::Molecule* m_molecule; GamessInputDialog* m_dialog; const Io::FileFormat* m_outputFormat; QString m_outputFileName; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_GAMESSINPUT_H avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/gamessinputdialog.cpp000066400000000000000000000607741506155467400267730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gamessinputdialog.h" #include "gamesshighlighter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::MoleQueue::JobObject; using Avogadro::MoleQueue::MoleQueueDialog; using Avogadro::MoleQueue::MoleQueueManager; namespace Avogadro::QtPlugins { enum CalculateOption { CalculateSinglePoint = 0, CalculateEquilibriumGeometry, CalculateTransitionState, CalculateForces, CalculateFrequencies, CalculateMakeEFP, CalculateCount }; enum TheoryOption { TheoryAM1 = 0, TheoryPM3, TheoryRHF, TheoryB3LYP, TheoryPBE0, TheorywB97X, TheorywB97XD, TheoryMP2, TheoryCCSDT, TheoryCRCCL, TheoryEOMCCSD, TheoryCount }; enum BasisOption { BasisSTO3G = 0, BasisSTO4G, BasisSTO5G, BasisSTO6G, BasisMINI, Basis321G, Basis631Gd, Basis631Gdp, Basis631PlusGdp, Basis631PlusG2dp, Basis6311PlusPlusG2dp, BasisMakeEFP, BasisCCD, BasisCCT, BasisCCQ, BasisCC5, BasisCC6, BasisAUGCCD, BasisAUGCCT, BasisAUGCCQ, BasisAUGCC5, BasisAUGCC6, BasisCorePotential, BasisCount }; enum StateOption { StateGas = 0, StateWater, StateCount }; enum MultiplicityOption { MultiplicitySinglet = 0, MultiplicityDoublet, MultiplicityTriplet, MultiplicityCount }; enum ChargeOption { ChargeDication = 0, ChargeCation, ChargeNeutral, ChargeAnion, ChargeDianion, ChargeCount }; enum DispersionCorrectionOption { DispersionNone = 0, DispersionD1, DispersionD2, DispersionD3, DispersionD4, DispersionCount }; GamessInputDialog::GamessInputDialog(QWidget* parent_, Qt::WindowFlags f) : QDialog(parent_, f), m_molecule(nullptr), m_highlighter(nullptr), m_updatePending(false) { ui.setupUi(this); m_highlighter = new GamessHighlighter(ui.previewText->document()); buildOptions(); connectBasic(); connectPreview(); connectButtons(); setBasicDefaults(); updatePreviewText(); } GamessInputDialog::~GamessInputDialog() {} void GamessInputDialog::setMolecule(QtGui::Molecule* mol) { if (mol == m_molecule) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; connect(mol, SIGNAL(changed(unsigned int)), SLOT(updatePreviewText())); connect(mol, SIGNAL(changed(unsigned int)), SLOT(updateTitlePlaceholder())); updateTitlePlaceholder(); updatePreviewText(); } void GamessInputDialog::showEvent(QShowEvent* e) { QWidget::showEvent(e); // Update the preview text if an update was requested while hidden. Use a // single shot to allow the dialog to show before popping up any warnings. if (m_updatePending) QTimer::singleShot(0, this, SLOT(updatePreviewText())); } void GamessInputDialog::connectBasic() { connect(ui.titleEdit, SIGNAL(textChanged(QString)), this, SLOT(updatePreviewText())); connect(ui.calculateCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.calculateCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTitlePlaceholder())); connect(ui.theoryCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.theoryCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTitlePlaceholder())); connect(ui.basisCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.basisCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTitlePlaceholder())); connect(ui.DCVerCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.DCVerCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTitlePlaceholder())); connect(ui.stateCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.multiplicityCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.chargeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewText())); connect(ui.maxscfspinBox, SIGNAL(valueChanged(int)), this, SLOT(updatePreviewText())); connect(ui.convergeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updatePreviewText())); } void GamessInputDialog::connectPreview() {} void GamessInputDialog::connectButtons() { connect(ui.resetAllButton, SIGNAL(clicked()), SLOT(resetClicked())); connect(ui.defaultsButton, SIGNAL(clicked()), SLOT(defaultsClicked())); connect(ui.generateButton, SIGNAL(clicked()), SLOT(generateClicked())); connect(ui.computeButton, SIGNAL(clicked()), SLOT(computeClicked())); connect(ui.closeButton, SIGNAL(clicked()), SLOT(close())); } void GamessInputDialog::buildOptions() { buildCalculateOptions(); buildTheoryOptions(); buildBasisOptions(); buildDispersionCorrectionOptions(); buildStateOptions(); buildMultiplicityOptions(); buildChargeOptions(); } void GamessInputDialog::updateOptionCache() { m_optionCache.clear(); m_optionCache.insert(ui.calculateCombo, ui.calculateCombo->currentIndex()); m_optionCache.insert(ui.theoryCombo, ui.theoryCombo->currentIndex()); m_optionCache.insert(ui.basisCombo, ui.basisCombo->currentIndex()); m_optionCache.insert(ui.DCVerCombo, ui.DCVerCombo->currentIndex()); m_optionCache.insert(ui.stateCombo, ui.stateCombo->currentIndex()); m_optionCache.insert(ui.multiplicityCombo, ui.multiplicityCombo->currentIndex()); m_optionCache.insert(ui.chargeCombo, ui.chargeCombo->currentIndex()); } void GamessInputDialog::restoreOptionCache() { foreach (QComboBox* combo, m_optionCache.keys()) { combo->blockSignals(true); combo->setCurrentIndex(m_optionCache.value(combo, 0)); combo->blockSignals(false); } } void GamessInputDialog::buildCalculateOptions() { for (int i = 0; i < static_cast(CalculateCount); ++i) { QString text = ""; switch (static_cast(i)) { case CalculateSinglePoint: text = tr("Single Point"); break; case CalculateEquilibriumGeometry: text = tr("Equilibrium Geometry"); break; case CalculateForces: text = tr("Forces"); break; case CalculateTransitionState: text = tr("Transition State"); break; case CalculateFrequencies: text = tr("Frequencies"); break; case CalculateMakeEFP: text = tr("Make EFP"); break; default: break; } ui.calculateCombo->addItem(text); } } void GamessInputDialog::buildTheoryOptions() { for (int i = 0; i < static_cast(TheoryCount); ++i) { QString text = ""; switch (static_cast(i)) { case TheoryAM1: text = "AM1"; break; case TheoryPM3: text = "PM3"; break; case TheoryRHF: text = "RHF"; break; case TheoryB3LYP: text = "B3LYP"; break; case TheoryPBE0: text = "PBE0"; break; case TheorywB97X: text = "wB97X"; break; case TheorywB97XD: text = "wB97X-D"; break; case TheoryMP2: text = "MP2"; break; case TheoryCCSDT: text = "CCSD(T)"; break; case TheoryCRCCL: text = "CR-CCL"; break; case TheoryEOMCCSD: text = "EOM-CCSD"; break; default: break; } ui.theoryCombo->addItem(text); } } void GamessInputDialog::buildBasisOptions() { for (int i = 0; i < static_cast(BasisCount); ++i) { QString text = ""; switch (static_cast(i)) { case BasisSTO3G: text = "STO-3G"; break; case BasisSTO4G: text = "STO-4G"; break; case BasisSTO5G: text = "STO-5G"; break; case BasisSTO6G: text = "STO-6G"; break; case BasisMINI: text = "MINI"; break; case Basis321G: text = "3-21 G"; break; case Basis631Gd: text = "6-31 G(d)"; break; case Basis631Gdp: text = "6-31 G(d,p)"; break; case Basis631PlusGdp: text = "6-31+G(d,p)"; break; case Basis631PlusG2dp: text = "6-31+G(2d,p)"; break; case Basis6311PlusPlusG2dp: text = "6-311++G(2d,p)"; break; case BasisMakeEFP: text = "6-311++G(3df,2p)"; break; case BasisCCD: text = "cc-pVDZ"; break; case BasisCCT: text = "cc-pVTZ"; break; case BasisCCQ: text = "cc-pVQZ"; break; case BasisCC5: text = "cc-pV5Z"; break; case BasisCC6: text = "cc-pV6Z"; break; case BasisAUGCCD: text = "aug-cc-pVDZ"; break; case BasisAUGCCT: text = "aug-cc-pVTZ"; break; case BasisAUGCCQ: text = "aug-cc-pVQZ"; break; case BasisAUGCC5: text = "aug-cc-pV5Z"; break; case BasisAUGCC6: text = "aug-cc-pV6Z"; break; case BasisCorePotential: text = tr("Core Potential"); break; default: break; } ui.basisCombo->addItem(text); } } void GamessInputDialog::buildDispersionCorrectionOptions() { for (int i = 0; i < static_cast(DispersionCount); ++i) { QString text = ""; switch (static_cast(i)) { case DispersionNone: text = tr("None"); break; case DispersionD1: text = tr("D1"); break; case DispersionD2: text = tr("D2"); break; case DispersionD3: text = tr("D3"); break; case DispersionD4: text = tr("D4"); break; default: break; } ui.DCVerCombo->addItem(text); } } void GamessInputDialog::buildStateOptions() { for (int i = 0; i < static_cast(StateCount); ++i) { QString text = ""; switch (static_cast(i)) { case StateGas: text = tr("Gas"); break; case StateWater: text = tr("Water"); break; default: break; } ui.stateCombo->addItem(text); } } void GamessInputDialog::buildMultiplicityOptions() { for (int i = 0; i < static_cast(MultiplicityCount); ++i) { QString text = ""; switch (static_cast(i)) { case MultiplicitySinglet: text = tr("Singlet"); break; case MultiplicityDoublet: text = tr("Doublet"); break; case MultiplicityTriplet: text = tr("Triplet"); break; default: break; } ui.multiplicityCombo->addItem(text); } } void GamessInputDialog::buildChargeOptions() { for (int i = 0; i < static_cast(ChargeCount); ++i) { QString text = ""; switch (static_cast(i)) { case ChargeDication: text = tr("Dication"); break; case ChargeCation: text = tr("Cation"); break; case ChargeNeutral: text = tr("Neutral"); break; case ChargeAnion: text = tr("Anion"); break; case ChargeDianion: text = tr("Dianion"); break; default: break; } ui.chargeCombo->addItem(text); } } void GamessInputDialog::setBasicDefaults() { ui.titleEdit->setText(QString()); ui.calculateCombo->setCurrentIndex(CalculateSinglePoint); ui.theoryCombo->setCurrentIndex(TheorywB97X); ui.basisCombo->setCurrentIndex(Basis631Gd); ui.DCVerCombo->setCurrentIndex(DispersionD4); ui.stateCombo->setCurrentIndex(StateGas); ui.multiplicityCombo->setCurrentIndex(MultiplicitySinglet); ui.chargeCombo->setCurrentIndex(ChargeNeutral); } QString GamessInputDialog::generateJobTitle() const { QString calculation(ui.calculateCombo->currentText()); QString theory(ui.theoryCombo->currentText()); QString basis(ui.basisCombo->currentText()); QString formula(m_molecule ? QString::fromStdString(m_molecule->formula()) : tr("[no molecule]")); // Merge theory/basis into theory theory += "/" + basis; theory.replace(QRegularExpression("\\s+"), ""); return QString("%1 | %2 | %3").arg(formula, calculation, theory); } void GamessInputDialog::updatePreviewText() { // If the dialog is not shown, delay the update in case we need to prompt the // user to overwrite changes. Set the m_updatePending flag to true so we'll // know to update in the show event. if (!isVisible()) { m_updatePending = true; return; } m_updatePending = false; // Has the preview text been modified? if (ui.previewText->document()->isModified()) { QString message = tr("The input file has been modified. " "Would you like to overwrite your changes to reflect " "the new geometry or job options?"); int response = QMessageBox::question( this, tr("Overwrite modified input file?"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (static_cast(response) != QMessageBox::Yes) { restoreOptionCache(); return; } } // Gather options: QString title(ui.titleEdit->text()); if (title.isEmpty()) title = generateJobTitle(); auto calculate( static_cast(ui.calculateCombo->currentIndex())); auto theory(static_cast(ui.theoryCombo->currentIndex())); auto basis(static_cast(ui.basisCombo->currentIndex())); auto dispersion( static_cast(ui.DCVerCombo->currentIndex())); auto state(static_cast(ui.stateCombo->currentIndex())); auto multiplicity( static_cast(ui.multiplicityCombo->currentIndex())); auto charge(static_cast(ui.chargeCombo->currentIndex())); // Disable basis selection for semiempirical methods. ui.basisCombo->setEnabled(theory != TheoryAM1 && theory != TheoryPM3); bool is_dft = (theory == TheoryB3LYP || theory == TheoryPBE0 || theory == TheorywB97X || theory == TheorywB97XD); ui.DCVerCombo->setEnabled(is_dft); // Generate text. // Variables: QString runTyp; QString scfTyp; QString gBasis; QString mult; QString iCharg; // Extra options for lines QString extraBasis; QString extraContrl; QString extraDFT; // Optional lines QString statPt; QString force; QString pcm; QString maxSCF = QString::number(ui.maxscfspinBox->value()); QString convthresh = QString::number(ui.convergeSpinBox->value()); switch (calculate) { case CalculateSinglePoint: runTyp = "ENERGY"; break; case CalculateEquilibriumGeometry: runTyp = "OPTIMIZE"; statPt = " $STATPT OPTTOL=0.0001 NSTEP=20 $END\n"; break; case CalculateTransitionState: runTyp = "SADPOINT"; statPt = " $STATPT OPTTOL=0.0001 NSTEP=20 $END\n"; break; case CalculateForces: runTyp = "FORCE"; force = " $FORCE METHOD=ANALYTIC $END\n"; break; case CalculateFrequencies: runTyp = "HESSIAN"; force = " $FORCE METHOD=ANALYTIC VIBANL=.TRUE. $END\n"; break; case CalculateMakeEFP: runTyp = "MAKEFP"; basis = BasisMakeEFP; break; default: break; } switch (theory) { case TheoryAM1: gBasis = "AM1"; break; case TheoryPM3: gBasis = "PM3"; break; case TheoryRHF: break; case TheoryB3LYP: extraDFT += " DFTTYP=B3LYP"; break; case TheoryPBE0: extraDFT += " DFTTYP=PBE0"; break; case TheorywB97X: extraDFT += " DFTTYP=wB97X"; break; case TheorywB97XD: extraDFT += " DFTTYP=wB97X-D"; break; case TheoryMP2: extraContrl += " MPLEVL=2"; break; case TheoryCCSDT: extraContrl += " CCTYP=CCSD(T)"; break; case TheoryCRCCL: extraContrl += " CCTYP=CR-CCL"; break; case TheoryEOMCCSD: extraContrl += " CCTYP=EOM-CCSD"; break; default: break; } if (theory != TheoryAM1 && theory != TheoryPM3) { switch (basis) { case BasisSTO3G: gBasis = "STO"; extraBasis += " NGAUSS=3"; break; case BasisSTO4G: gBasis = "STO"; extraBasis += " NGAUSS=4"; break; case BasisSTO5G: gBasis = "STO"; extraBasis += " NGAUSS=5"; break; case BasisSTO6G: gBasis = "STO"; extraBasis += " NGAUSS=6"; break; case BasisMINI: gBasis = "MINI"; break; case Basis321G: gBasis = "N21"; extraBasis += " NGAUSS=3"; break; case Basis631Gd: gBasis = "N31"; extraBasis += " NGAUSS=6 NDFUNC=1"; break; case Basis631Gdp: gBasis = "N31"; extraBasis += " NGAUSS=6 NDFUNC=1 NPFUNC=1"; break; case Basis631PlusGdp: gBasis = "N31"; extraBasis += " NGAUSS=6 NDFUNC=1 NPFUNC=1 DIFFSP=.TRUE."; break; case Basis631PlusG2dp: gBasis = "N31"; extraBasis += " NGAUSS=6 NDFUNC=2 NPFUNC=1 DIFFSP=.TRUE."; break; case Basis6311PlusPlusG2dp: gBasis = "N311"; extraBasis += " NGAUSS=6 NDFUNC=2 NPFUNC=1 DIFFSP=.TRUE. DIFFS=.TRUE."; break; case BasisMakeEFP: gBasis = "N311"; extraBasis += " NGAUSS=6 NDFUNC=3 NPFUNC=2 NFFUNC=3 \n DIFFSP=.TRUE. DIFFS=.TRUE."; break; case BasisCCD: gBasis = "ccd"; break; case BasisCCT: gBasis = "cct"; break; case BasisCCQ: gBasis = "ccq"; break; case BasisCC5: gBasis = "cc5"; break; case BasisCC6: gBasis = "cc6"; break; case BasisAUGCCD: gBasis = "accd"; break; case BasisAUGCCT: gBasis = "acct"; break; case BasisAUGCCQ: gBasis = "accq"; break; case BasisAUGCC5: gBasis = "acc5"; break; case BasisAUGCC6: gBasis = "acc6"; break; case BasisCorePotential: gBasis = "SBK"; extraBasis += " NGAUSS=3 NDFUNC=1"; extraContrl += " ECP=SBK"; break; default: break; } } switch (state) { case StateGas: break; case StateWater: pcm = " $PCM SOLVNT=WATER $END\n"; break; default: break; } switch (multiplicity) { case MultiplicitySinglet: scfTyp = "RHF"; mult = "1"; break; case MultiplicityDoublet: scfTyp = "ROHF"; mult = "2"; break; case MultiplicityTriplet: scfTyp = "ROHF"; mult = "3"; break; default: break; } switch (charge) { case ChargeDication: iCharg = "2"; break; case ChargeCation: iCharg = "1"; break; case ChargeNeutral: iCharg = "0"; break; case ChargeAnion: iCharg = "-1"; break; case ChargeDianion: iCharg = "-2"; break; default: break; } if (is_dft) { switch (dispersion) { case DispersionNone: extraDFT += " DC=.F. "; break; case DispersionD1: extraDFT += " DC=.T. IDCVER=1"; break; case DispersionD2: extraDFT += " DC=.T. IDCVER=2"; break; case DispersionD3: extraDFT += " DC=.T. IDCVER=3"; break; case DispersionD4: extraDFT += " DC=.T. IDCVER=4"; break; default: break; } } // build up the input file: QString file; file += "! Input file generated by Avogadro\n"; file += QString(" $BASIS GBASIS=%1%2 $END\n").arg(gBasis, extraBasis); file += pcm; file += QString(" $CONTRL SCFTYP=%1 RUNTYP=%2 ICHARG=%3 MULT=%4%5 $END\n") .arg(scfTyp, runTyp, iCharg, mult, extraContrl); file += QString(" $CONTRL ISPHER=1 MAXIT=%1 $END\n").arg(maxSCF); file += QString(" $SCF DIRSCF=.T. CONV=%1 $END\n").arg(convthresh); if (is_dft) { file += QString(" $DFT %1 $END\n").arg(extraDFT); } file += statPt; file += force; file += "\n"; file += " $DATA\n"; file += QString("%1\n").arg(title); file += "C1\n"; if (m_molecule) { for (size_t i = 0; i < m_molecule->atomCount(); ++i) { Core::Atom atom = m_molecule->atom(i); file += QString("%1 %2 %3 %4 %5\n") .arg(Core::Elements::symbol(atom.atomicNumber()), -3) .arg(static_cast(atom.atomicNumber()), 5, 'f', 1) .arg(atom.position3d().x(), 9, 'f', 5) .arg(atom.position3d().y(), 9, 'f', 5) .arg(atom.position3d().z(), 9, 'f', 5); } } file += " $END\n"; ui.previewText->setText(file); ui.previewText->document()->setModified(false); updateOptionCache(); } void GamessInputDialog::resetClicked() { setBasicDefaults(); updatePreviewText(); } void GamessInputDialog::defaultsClicked() { setBasicDefaults(); updatePreviewText(); } void GamessInputDialog::generateClicked() { QSettings settings; QString fileName = (ui.baseNameEdit->text().isEmpty() ? ui.baseNameEdit->placeholderText() : ui.baseNameEdit->text()) + ".inp"; QString targetFile = settings.value("gamessInput/outputDirectory", QDir::homePath()).toString(); targetFile = QDir(QFileInfo(targetFile).absoluteDir()).absoluteFilePath(fileName); fileName = QFileDialog::getSaveFileName(this, tr("Save GAMESS input file"), targetFile); // User cancel: if (fileName.isNull()) return; settings.setValue("gamessInput/outputDirectory", fileName); QFile file(fileName); bool success = false; if (file.open(QFile::WriteOnly | QFile::Text)) { if (file.write(ui.previewText->toPlainText().toLocal8Bit()) > 0) { success = true; } file.close(); } if (!success) { QMessageBox::critical(this, tr("Output Error"), tr("Failed to write to file %1.").arg(fileName)); } } void GamessInputDialog::computeClicked() { // Verify that molequeue is running: MoleQueueManager& mqManager = MoleQueueManager::instance(); if (!mqManager.connectIfNeeded()) { QMessageBox::information(this, tr("Cannot connect to MoleQueue"), tr("Cannot connect to MoleQueue server. Please " "ensure that it is running and try again.")); return; } QString description(ui.titleEdit->text()); if (description.isEmpty()) description = generateJobTitle(); QString fileNameBase = ui.baseNameEdit->text().isEmpty() ? ui.baseNameEdit->placeholderText() : ui.baseNameEdit->text(); JobObject job; job.setProgram("GAMESS"); job.setDescription(description); job.setInputFile(QString("%1.inp").arg(fileNameBase), ui.previewText->toPlainText()); MoleQueueDialog::SubmitStatus submitStatus = MoleQueueDialog::submitJob(this, tr("Submit GAMESS Calculation"), job, MoleQueueDialog::WaitForSubmissionResponse | MoleQueueDialog::SelectProgramFromTemplate); switch (submitStatus) { default: case MoleQueueDialog::SubmissionSuccessful: case MoleQueueDialog::SubmissionFailed: case MoleQueueDialog::SubmissionAttempted: case MoleQueueDialog::SubmissionAborted: // The dialog handles these cases adequately, we don't need to do // anything. break; case MoleQueueDialog::JobFailed: // Inform the user: QMessageBox::information(this, tr("Job Failed"), tr("The job did not complete successfully."), QMessageBox::Ok); break; case MoleQueueDialog::JobFinished: // Let the world know that the job is ready to open. job has been // overwritten with the final job details. emit openJobOutput(job); hide(); break; } } void GamessInputDialog::updateTitlePlaceholder() { ui.titleEdit->setPlaceholderText(generateJobTitle()); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/gamessinputdialog.h000066400000000000000000000037231506155467400264270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef GAMESSINPUTDIALOG_H #define GAMESSINPUTDIALOG_H #include #include #include #include "ui_gamessinputdialog.h" #include class QJsonObject; namespace Avogadro { namespace MoleQueue { class JobObject; } namespace QtGui { class Molecule; } namespace QtPlugins { class GamessHighlighter; class GamessInputDialog : public QDialog { Q_OBJECT public: explicit GamessInputDialog(QWidget* parent_ = nullptr, Qt::WindowFlags f = {}); ~GamessInputDialog() override; void setMolecule(QtGui::Molecule* mol); signals: /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ void openJobOutput(const Avogadro::MoleQueue::JobObject& job); protected: void showEvent(QShowEvent* e) override; private slots: void updatePreviewText(); void defaultsClicked(); void resetClicked(); void generateClicked(); void computeClicked(); void updateTitlePlaceholder(); private: void connectBasic(); void connectPreview(); void connectButtons(); void buildOptions(); void updateOptionCache(); void restoreOptionCache(); void buildCalculateOptions(); void buildTheoryOptions(); void buildBasisOptions(); void buildStateOptions(); void buildDispersionCorrectionOptions(); void buildMultiplicityOptions(); void buildChargeOptions(); void setBasicDefaults(); QString generateJobTitle() const; Ui::GamessInputDialog ui; QtGui::Molecule* m_molecule; GamessHighlighter* m_highlighter; bool m_updatePending; QMap m_optionCache; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // GAMESSINPUTDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/gamessinput/gamessinputdialog.ui000066400000000000000000000265101506155467400266140ustar00rootroot00000000000000 GamessInputDialog 0 0 785 660 GAMESS Input 0 1 0 &Basic Setup Qt::Horizontal 40 20 In: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter stateCombo MAX SCF 999 50 Qt::Horizontal 40 20 CONV THRESH 8 0.000010000000000 0.000010000000000 Qt::Horizontal 40 20 Multiplicity: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter multiplicityCombo With: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter theoryCombo Title: titleEdit Qt::Horizontal 40 20 Qt::Horizontal 40 20 Qt::Horizontal 16 20 Charge: chargeCombo Qt::Horizontal 40 20 Calculate: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter calculateCombo Qt::Vertical 20 0 Filename Base: job Reset All Defaults Qt::Horizontal QSizePolicy::MinimumExpanding 10 20 Submit Calculation… Save File… Close modeTab titleEdit calculateCombo theoryCombo basisCombo stateCombo multiplicityCombo chargeCombo previewText resetAllButton defaultsButton generateButton closeButton avogadrolibs-1.101.0/avogadro/qtplugins/hydrogens/000077500000000000000000000000001506155467400221755ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/hydrogens/CMakeLists.txt000066400000000000000000000002301506155467400247300ustar00rootroot00000000000000avogadro_plugin(Hydrogens "Extension that adds/removes hydrogens on a molecule." ExtensionPlugin hydrogens.h Hydrogens "hydrogens.cpp" "" ) avogadrolibs-1.101.0/avogadro/qtplugins/hydrogens/hydrogens.cpp000066400000000000000000000064241506155467400247110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "hydrogens.h" #include #include #include #include #include namespace Avogadro::QtPlugins { Hydrogens::Hydrogens(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr) { auto* action = new QAction(tr("&Adjust Hydrogens"), this); action->setShortcut(QKeySequence("Ctrl+Alt+H")); action->setProperty("menu priority", 760); connect(action, SIGNAL(triggered()), SLOT(adjustHydrogens())); m_actions.append(action); action = new QAction(tr("Add Hydrogens"), this); action->setProperty("menu priority", 750); connect(action, SIGNAL(triggered()), SLOT(addHydrogens())); m_actions.append(action); action = new QAction(tr("Remove E&xtra Hydrogens"), this); action->setProperty("menu priority", 740); connect(action, SIGNAL(triggered()), SLOT(removeHydrogens())); m_actions.append(action); action = new QAction(tr("&Remove All Hydrogens"), this); action->setProperty("menu priority", 730); connect(action, SIGNAL(triggered()), SLOT(removeAllHydrogens())); m_actions.append(action); } Hydrogens::~Hydrogens() {} QString Hydrogens::description() const { return tr("Add/remove hydrogens from the current molecule."); } QList Hydrogens::actions() const { return m_actions; } QStringList Hydrogens::menuPath(QAction*) const { return QStringList() << tr("&Build") << tr("&Hydrogens"); } void Hydrogens::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void Hydrogens::adjustHydrogens() { if (m_molecule) { QtGui::HydrogenTools::adjustHydrogens(*(m_molecule->undoMolecule()), QtGui::HydrogenTools::AddAndRemove); // Assume molecule changes... m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Bonds | QtGui::Molecule::Added | QtGui::Molecule::Removed); } } void Hydrogens::addHydrogens() { if (m_molecule) { QtGui::HydrogenTools::adjustHydrogens(*(m_molecule->undoMolecule()), QtGui::HydrogenTools::Add); // Assume molecule changes... m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Bonds | QtGui::Molecule::Added); } } void Hydrogens::removeHydrogens() { if (m_molecule) { QtGui::HydrogenTools::adjustHydrogens(*(m_molecule->undoMolecule()), QtGui::HydrogenTools::Remove); // Assume molecule changes... m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Bonds | QtGui::Molecule::Removed); } } void Hydrogens::removeAllHydrogens() { if (m_molecule) { QtGui::HydrogenTools::removeAllHydrogens(*(m_molecule->undoMolecule())); // Assume molecule changes... m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Bonds | QtGui::Molecule::Removed); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/hydrogens/hydrogens.h000066400000000000000000000023241506155467400243510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the MoleQueue project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_HYDROGENS_H #define AVOGADRO_QTPLUGINS_HYDROGENS_H #include namespace Avogadro { namespace QtPlugins { /** * @brief The Hydrogens class is an extension to modify hydrogens. */ class Hydrogens : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit Hydrogens(QObject* parent_ = nullptr); ~Hydrogens() override; QString name() const override { return tr("Hydrogens"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void adjustHydrogens(); void addHydrogens(); void removeHydrogens(); void removeAllHydrogens(); private: QList m_actions; QtGui::Molecule* m_molecule; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_HYDROGENS_H avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/000077500000000000000000000000001506155467400222305ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/CMakeLists.txt000066400000000000000000000007221506155467400247710ustar00rootroot00000000000000# Extension set(importpqr_srcs importpqr.cpp pqrwidget.cpp pqrrequest.cpp ) avogadro_plugin(ImportPQR "Download molecules from the Pitt Quantum Repository" ExtensionPlugin importpqr.h ImportPQR "${importpqr_srcs}" pqrwidget.ui "" ) target_link_libraries(ImportPQR PRIVATE Qt::Network nlohmann_json::nlohmann_json) if(WIN32) # for https support target_link_libraries(ImportPQR PRIVATE OpenSSL::SSL OpenSSL::Crypto OpenSSL::applink) endif() avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/importpqr.cpp000066400000000000000000000044521506155467400247760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "importpqr.h" #include "pqrwidget.h" #include #include #include #include #include namespace Avogadro::QtPlugins { ImportPQR::ImportPQR(QObject* parent_) : ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr), m_dialog(nullptr), m_outputFormat(nullptr), m_manager(new QNetworkAccessManager(this)) { m_action->setEnabled(false); m_action->setText(tr("&Search PQR…")); connect(m_action, SIGNAL(triggered()), SLOT(menuActivated())); // check if PQR is up connect(m_manager, SIGNAL(finished(QNetworkReply*)), SLOT(checkAccess(QNetworkReply*))); m_manager->get(QNetworkRequest(QUrl("https://pqr.pitt.edu"))); } ImportPQR::~ImportPQR() { delete m_outputFormat; m_manager->deleteLater(); } void ImportPQR::checkAccess(QNetworkReply* reply) { // only enable if we can access the site if (reply->error() == QNetworkReply::NoError) { m_action->setEnabled(true); } reply->deleteLater(); } QList ImportPQR::actions() const { QList actions_; actions_.append(m_action); return actions_; } QStringList ImportPQR::menuPath(QAction*) const { QStringList path; path << tr("&File") << tr("&Import"); return path; } void ImportPQR::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } bool ImportPQR::readMolecule(QtGui::Molecule& mol) { bool readOK = Io::FileFormatManager::instance().readString( mol, m_moleculeData.data(), "mol2"); if (readOK) // worked, so set the filename mol.setData("name", m_moleculeName.toStdString()); return readOK; } void ImportPQR::menuActivated() { if (!m_dialog) m_dialog = new PQRWidget(qobject_cast(this), this); m_dialog->show(); } // called by widget void ImportPQR::setMoleculeData(QByteArray& molData, QString name) { m_moleculeName = name; m_moleculeData = molData; m_dialog->hide(); emit moleculeReady(1); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/importpqr.h000066400000000000000000000032421506155467400244370ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_IMPORTPQR_H #define AVOGADRO_QTPLUGINS_IMPORTPQR_H #include #include #include #include #include #include class QAction; class QDialog; namespace Avogadro { namespace QtPlugins { class PQRWidget; class ImportPQR : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit ImportPQR(QObject* parent = nullptr); ~ImportPQR() override; QString name() const override { return tr("Import From PQR"); } QString description() const override { return tr("Download a molecule from PQR."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMoleculeData(QByteArray& molData, QString name); public slots: void setMolecule(QtGui::Molecule* mol) override; bool readMolecule(QtGui::Molecule& mol) override; private slots: void menuActivated(); void checkAccess(QNetworkReply* reply); private: QAction* m_action; QtGui::Molecule* m_molecule; PQRWidget* m_dialog; const Io::FileFormat* m_outputFormat; QString m_moleculeName; QString m_moleculePath; QByteArray m_moleculeData; QNetworkAccessManager* m_manager; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_IMPORTPQR_H avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/pqrrequest.cpp000066400000000000000000000165531506155467400251610ustar00rootroot00000000000000#include "pqrrequest.h" #include "pqrwidget.h" #include #include using json = nlohmann::json; namespace Avogadro::QtPlugins { /** * @brief Constructor to initialize the NetworkAccessManager and set pointers to * the widget's ui elements. */ PQRRequest::PQRRequest(QTableWidget* tw, QLabel* gv, QLineEdit* nd, QLabel* fd, PQRWidget* w) { // set pointers to ui elements now instead of in individual functions table = tw; // pointer to ui table pngPreview = gv; // png QLabel nameDisplay = nd; // name formulaDisplay = fd; // formula // used to load molecule in Avogadro when downloaded widget = w; oNetworkAccessManager = new QNetworkAccessManager(this); } /** * @brief Free the ui pointers */ PQRRequest::~PQRRequest() { delete oNetworkAccessManager; } /** * @brief Sends a network request to search for molecules from PQR; * @param url The url to query */ void PQRRequest::sendRequest(QString url) { reply = oNetworkAccessManager->get(QNetworkRequest(QUrl(url))); connect(reply, SIGNAL(finished()), this, SLOT(parseJson())); } /** * @brief Sends a network request to download a file from PQR * @param url The url to send the request to * @param mol2 The mol2 representation of the molecule to download */ void PQRRequest::sendRequest(QString url, QString) { reply = oNetworkAccessManager->get(QNetworkRequest(QUrl(url))); currentMolName = nameDisplay->text(); // needed to load mol into Avogadro connect(reply, SIGNAL(finished()), this, SLOT(getFile())); } /** * @brief Sends a network request to download a png form PQR * @param url The url to send the request to */ void PQRRequest::sendPNGRequest(QString url) { reply = oNetworkAccessManager->get(QNetworkRequest(QUrl(url))); connect(reply, SIGNAL(finished()), this, SLOT(SetPNG())); } /** * @brief Called when a molecule is selected to display information about the * molecule and start grabbing the SVG preview. * @param num The row number of the table result selected * @returns The mol2 of the result for the widget to reference */ QString PQRRequest::molSelected(int num) { if (results.empty() || num > static_cast(results.size())) return QString("N/A"); QString mol2 = results[num].mol2url; QString url = "https://pqr.pitt.edu/static/data/png/" + mol2 + ".png"; sendPNGRequest(url); formulaDisplay->setText(parseSubscripts(results[num].formula)); nameDisplay->setText(results[num].name); return mol2; } /** * @brief Parses the JSON response from querying PQR */ void PQRRequest::parseJson() { if (reply->error() == QNetworkReply::NoError) { // Reading the data from the response QByteArray bytes = reply->readAll(); // parse the json json root = json::parse(bytes.data()); int resultSize = root.size(); results.clear(); if (resultSize == 0) { table->setRowCount(1); table->setItem(0, 0, new QTableWidgetItem("No Results!")); table->setCellWidget(0, 1, new QLabel()); table->setItem(0, 2, new QTableWidgetItem("N/A")); } else { table->setRowCount(resultSize); for (int i = 0; i < resultSize; i++) { results.emplace_back(); // Loop through the keys for (auto it = root[i].cbegin(); it != root[i].cend(); ++it) { if (it.key() == "formula" && it.value().is_string()) results[i].formula = it.value().get().c_str(); else if (it.key() == "inchikey" && it.value().is_string()) results[i].inchikey = it.value().get().c_str(); else if (it.key() == "mol2url" && it.value().is_string()) results[i].mol2url = it.value().get().c_str(); else if (it.key() == "name" && it.value().is_string()) results[i].name = it.value().get().c_str(); } results[i].mass = getMolMass(results[i].formula); table->setItem(i, 0, new QTableWidgetItem(results[i].name)); // clear possible QTableWidget if there were no results previously table->setItem(i, 1, nullptr); // use this to display subscripts, should automatically delete previous // QLabel according to documentation table->setCellWidget(i, 1, new QLabel(parseSubscripts(results[i].formula))); // table->setItem(i, 2, new // QTableWidgetItem(QString::number(results[i].mass, 'f', 3) + QString(" // g/mol"))); auto* massItem = new QTableWidgetItem(); massItem->setData(Qt::DisplayRole, results[i].mass); table->setItem(i, 2, massItem); } } } else { table->setRowCount(3); table->setItem(0, 0, new QTableWidgetItem("Network Error!")); table->setItem(0, 1, new QTableWidgetItem("N/A")); table->setItem(0, 2, new QTableWidgetItem(reply->errorString())); } reply->deleteLater(); } /** * @brief Creates a file after requesting a file from PQR */ void PQRRequest::getFile() { QByteArray molData = reply->readAll(); widget->loadMolecule(molData, currentMolName); reply->deleteLater(); } /** * @brief Loads PNG data after sending a request */ void PQRRequest::SetPNG() { QByteArray pngData = reply->readAll(); widget->loadPNG(pngData); reply->deleteLater(); } /** * @brief Takes a formula string and returns a QString with subscript tags * @param formula The formula string */ QString PQRRequest::parseSubscripts(QString formula) { std::string str = formula.toStdString(); QString toReturn; for (char i : str) { if (isdigit(i)) { toReturn.append(""); toReturn.append(i); toReturn.append(""); } else { toReturn.append(i); } } return toReturn; } /** * @brief Takes a formula string and returns the molecular mass of the molecule * @param formula The formula string */ float PQRRequest::getMolMass(QString formula) { std::string str = formula.toStdString(); float totalMass = 0.0; int subscript = 1; std::string element; unsigned char atomicNum; for (size_t i = 0; i < str.length(); i++) { // each element will start with a capital letter if (isupper(str[i])) { // if next letter is a lower case then we know the whole element if (islower(str[i + 1])) { element = { str[i], str[i + 1] }; // this might be the last element of the formula if (isdigit(str[i + 2])) { subscript = (int)str[i + 2] - '0'; i += 2; // increment past lowercase and numeral } else { i += 1; subscript = 1; } } // get the subscript else if (isdigit(str[i + 1])) { if (isdigit(str[i + 2])) { // might be 2 digit subscript subscript = (int)str[i + 1] - '0'; subscript *= 10; // shift forward one decimal place subscript += (int)str[i + 2] - '0'; element = { str[i] }; i += 2; } else { subscript = (int)str[i + 1] - '0'; element = { str[i] }; i += 1; } } // if the next letter is another uppercase or null, the current subscript // is 1 else if (isupper(str[i + 1]) || str[i + 1] == 0) { subscript = 1; element = { str[i] }; } atomicNum = Core::Elements::atomicNumberFromSymbol(element); totalMass += (subscript * Core::Elements::mass(atomicNum)); } } return totalMass; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/pqrrequest.h000066400000000000000000000071221506155467400246160ustar00rootroot00000000000000#ifndef PQRREQUEST_H #define PQRREQUEST_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * @brief The PQRRequest class sends and receives network requests to PQR and * updates ui elements from the widget. */ namespace Avogadro { namespace QtPlugins { class PQRWidget; class PQRRequest : public QObject { Q_OBJECT public: /** * @brief Constructor to initialize the NetworkAccessManager and set pointers * to the widget's ui elements. * @param tw Pointer to ui's table widget * @param gv Pointer to ui's graphics view for SVG preview * @param nd Pointer to the name display * @param fd Pointer to the formula display */ PQRRequest(QTableWidget*, QLabel*, QLineEdit*, QLabel*, PQRWidget*); /** * @brief Free the ui pointers */ ~PQRRequest() override; /** * @brief Sends a network request to search for molecules from PQR; * @param url The url to query */ void sendRequest(QString); /** * @brief Sends a network request to download a file from PQR * @param url The url to send the request to * @param mol2 The mol2 representation of the molecule to download */ void sendRequest(QString, QString); /** * @brief Sends a network request to download a png form PQR * @param url The url to send the request to */ void sendPNGRequest(QString url); /** * @brief Called when a molecule is selected to display information about the * molecule and start grabbing the SVG preview. * @param num The row number of the table result selected * @returns The mol2 of the result for the widget to reference */ QString molSelected(int); private slots: /** * @brief Parses the JSON response from querying PQR */ void parseJson(); /** * @brief Creates a file after requesting a file from PQR */ void getFile(); /** * @brief Loads PNG data after sending a request */ void SetPNG(); private: /** * @brief The result struct holds all data received in each result from * querying PQR */ struct result { QString inchikey; QString name; QString mol2url; QString formula; float mass; // Default constructor result() : inchikey("Error"), name("Error"), mol2url("Error"), formula("Error"), mass(-1.0) { } }; /** An array to hold all results from a query */ std::vector results; /** Holds a reply from a network request */ QNetworkReply* reply; /** Used to send/receive network request */ QNetworkAccessManager* oNetworkAccessManager; /** Used to parse JSON results */ QVariantMap m_jsonResult; /** Pointer to dialog */ PQRWidget* widget; /** Pointers to a widget's ui elements */ QTableWidget* table; QLineEdit* nameDisplay; QLabel* formulaDisplay; QLabel* pngPreview; /** Variables to fold file download information for getFile() */ QString currentMolName; /** * @brief Takes a formula string and returns a QString with subscript tags * @param formula The formula string */ QString parseSubscripts(QString); /** * @brief Takes a formula string and returns the molecular mass of the * molecule * @param formula The formula string */ float getMolMass(QString); }; } // namespace QtPlugins } // namespace Avogadro #endif // PQRRequest_H avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/pqrwidget.cpp000066400000000000000000000053731506155467400247520ustar00rootroot00000000000000#include "pqrwidget.h" #include "importpqr.h" #include "pqrrequest.h" #include "ui_pqrwidget.h" namespace Avogadro::QtPlugins { PQRWidget::PQRWidget(QWidget* parent, ImportPQR* p) : QDialog(parent), ui(new Ui::PQRWidget) { plugin = p; ui->setupUi(this); ui->tableWidget->setColumnCount(3); ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "Name" << "Formula" << "Mass (g/mol)"); ui->tableWidget->horizontalHeader()->setStretchLastSection(true); ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); ui->tableWidget->setSortingEnabled(true); connect(ui->searchButton, SIGNAL(clicked(bool)), this, SLOT(searchAction())); connect(ui->downloadButton, SIGNAL(clicked(bool)), this, SLOT(downloadMol())); connect(ui->tableWidget, SIGNAL(cellClicked(int, int)), this, SLOT(molSelected(int, int))); request = new PQRRequest(ui->tableWidget, ui->pngPreview, ui->nameDisplay, ui->formulaDisplay, this); } PQRWidget::~PQRWidget() { delete request; delete ui; } /** * @brief Called when the search button is clicked to send a query to PQR */ void PQRWidget::searchAction() { ui->downloadButton->setEnabled(false); QString url = "https://pqr.pitt.edu/api/browse/" + ui->molName->text() + "/" + ui->searchTypeBox->currentText(); request->sendRequest(url); } /** * @brief Called when a table result is double clicked to display preview * information about the result before downloading. * @param row The row of the result selected. * @param col The column of the result selected. */ void PQRWidget::molSelected(int row, int) { currentlySelectedMol = request->molSelected(row); if (currentlySelectedMol == "N/A") return; ui->downloadButton->setEnabled(true); } /** * @brief Called when PNG data is ready to be loaded */ void PQRWidget::loadPNG(QByteArray& pngData) { QPixmap pixmap; pixmap.loadFromData(pngData, "PNG"); pixmap = pixmap.scaled(300, 300); ui->pngPreview->setPixmap(pixmap); ui->pngPreview->show(); } /** * @brief Called when the download button is clicked to send a request to * download molecule information from PQR. */ void PQRWidget::downloadMol() { QString mol2url = currentlySelectedMol; if (mol2url != "N/A" && mol2url != "") { mol2url.remove(0, 3); // remove first 3 characters to map to PQR's url QString url = "https://pqr.pitt.edu/api/mol/" + mol2url; request->sendRequest(url, mol2url); } } void PQRWidget::loadMolecule(QByteArray& molData, QString name) { plugin->setMoleculeData(molData, name); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/pqrwidget.h000066400000000000000000000035231506155467400244120ustar00rootroot00000000000000#ifndef AVOGADRO_PQRWIDGET_H #define AVOGADRO_PQRWIDGET_H #include #include #include #include #include #include #include #include #include #include #include /** * PQRWidget is a class extending QDialog to provide the ui for * importing/downloading * molecules from PQR. */ namespace Ui { class PQRWidget; } namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { class PQRRequest; class ImportPQR; class PQRWidget : public QDialog { Q_OBJECT public: PQRWidget(QWidget* parent = nullptr, ImportPQR* p = nullptr); ~PQRWidget() override; void loadMolecule(QByteArray&, QString); void loadPNG(QByteArray&); private slots: /** * @brief Called when the search button is clicked to send a query to PQR */ void searchAction(); /** * @brief Called when a table result is double clicked to display preview * information * about the result before downloading. * @param row The row of the result selected. * @param col The column of the result selected. */ void molSelected(int, int); /** * @brief Called when the download button is clicked to send a request to * download * molecule information from PQR. */ void downloadMol(); private: /** The mol2 of the molecule result currently selected */ QString currentlySelectedMol; /** Pointer to the ui objects */ Ui::PQRWidget* ui; /** Pointer to a PQRRequest object to handle network requests */ PQRRequest* request; /** Pointer to the plugin that opened the dialog */ ImportPQR* plugin; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_PQRWIDGET_H avogadrolibs-1.101.0/avogadro/qtplugins/importpqr/pqrwidget.ui000066400000000000000000000111411506155467400245730ustar00rootroot00000000000000 PQRWidget 0 0 1087 674 0 0 Import From PQR… 0 0 Search 0 0 name tag synonym formula inchi 0 0 Search By: true Formula: 5 0 750 500 false Download Name: 0 0 300 300 300 300 avogadrolibs-1.101.0/avogadro/qtplugins/insertdna/000077500000000000000000000000001506155467400221625ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/insertdna/CMakeLists.txt000066400000000000000000000003341506155467400247220ustar00rootroot00000000000000avogadro_plugin(NucleicInput "Insert DNA/RNA sequences." ExtensionPlugin insertdna.h InsertDna "insertdna.cpp" "insertdnadialog.ui" ) target_link_libraries(NucleicInput PRIVATE nlohmann_json::nlohmann_json) avogadrolibs-1.101.0/avogadro/qtplugins/insertdna/insertdna.cpp000066400000000000000000000136251506155467400246640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "insertdna.h" #include "ui_insertdnadialog.h" #include #include #include #include #include #include #include #include #include #include using json = nlohmann::json; using Avogadro::Io::FileFormat; using Avogadro::QtGui::FileFormatDialog; namespace Avogadro::QtPlugins { class InsertDNADialog : public QDialog, public Ui::InsertDNADialog { public: InsertDNADialog(QWidget* parent = nullptr) : QDialog(parent) { setWindowFlags(Qt::Dialog | Qt::Tool); setupUi(this); } }; InsertDna::InsertDna(QObject* p) : Avogadro::QtGui::ExtensionPlugin(p), m_molecule(nullptr), m_reader(nullptr), m_dialog(nullptr) { auto* action = new QAction(tr("DNA/RNA…"), this); action->setProperty("menu priority", 870); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); } InsertDna::~InsertDna() { delete m_reader; } QList InsertDna::actions() const { return m_actions; } QStringList InsertDna::menuPath(QAction*) const { return QStringList() << tr("&Build") << tr("&Insert"); } void InsertDna::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void InsertDna::showDialog() { if (m_molecule == nullptr) return; // check to see if FASTA format is available from Open Babel QWidget* parentAsWidget = qobject_cast(parent()); const FileFormat::Operations ops = FileFormat::Read | FileFormat::String; const FileFormat* fmt = FileFormatDialog::findFileFormat( parentAsWidget, tr("Insert DNA/RNA…"), QString("file.fasta"), ops); if (fmt == nullptr) { return; } else { m_reader = fmt->newInstance(); } // Prompt user for input: if (m_dialog == nullptr) { constructDialog(); } m_dialog->show(); } void InsertDna::constructDialog() { if (m_dialog == nullptr) { m_dialog = new InsertDNADialog(qobject_cast(parent())); auto* numStrands = new QButtonGroup(m_dialog); numStrands->addButton(m_dialog->singleStrandRadio, 0); numStrands->addButton(m_dialog->doubleStrandRadio, 1); numStrands->setExclusive(true); connect(m_dialog->insertButton, SIGNAL(clicked()), this, SLOT(performInsert())); connect(m_dialog->bpCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateBPTurns(int))); connect(m_dialog->typeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeNucleicType(int))); // Set the nucleic buttons to update the sequence foreach (const QToolButton* child, m_dialog->findChildren()) { connect(child, SIGNAL(clicked()), this, SLOT(updateText())); } connect(m_dialog, SIGNAL(destroyed()), this, SLOT(dialogDestroyed())); } m_dialog->sequenceText->setPlainText(QString()); } void InsertDna::updateText() { auto* button = qobject_cast(sender()); if (button) { QString sequenceText = m_dialog->sequenceText->toPlainText(); sequenceText += button->text(); m_dialog->sequenceText->setPlainText(sequenceText); } } void InsertDna::updateBPTurns(int type) { switch (type) { case 0: // A-DNA m_dialog->bpTurnsSpin->setValue(11.0); break; case 1: // B-DNA m_dialog->bpTurnsSpin->setValue(10.5); break; case 2: // Z-DNA m_dialog->bpTurnsSpin->setValue(12.0); break; default: // anything the user wants break; } } void InsertDna::changeNucleicType(int type) { if (type == 1) { // RNA m_dialog->bpCombo->setCurrentIndex(3); // other m_dialog->bpTurnsSpin->setValue(11.0); // standard RNA m_dialog->singleStrandRadio->setChecked(true); m_dialog->singleStrandRadio->setEnabled(false); m_dialog->doubleStrandRadio->setEnabled(false); m_dialog->toolButton_TU->setText(tr("U", "uracil")); m_dialog->toolButton_TU->setToolTip(tr("Uracil")); return; } // DNA m_dialog->singleStrandRadio->setEnabled(true); m_dialog->doubleStrandRadio->setEnabled(true); m_dialog->toolButton_TU->setText(tr("T", "thymine")); m_dialog->toolButton_TU->setToolTip(tr("Thymine")); } void InsertDna::performInsert() { if (m_dialog == nullptr || m_molecule == nullptr || m_reader == nullptr) return; QString sequence = m_dialog->sequenceText->toPlainText().toLower(); bool dna = (m_dialog->typeComboBox->currentIndex() == 0); if (sequence.isEmpty()) return; // also nothing to do // Add DNA/RNA tag for FASTA sequence = '>' + m_dialog->typeComboBox->currentText() + '\n' + sequence; // options // if DNA, check if the user wants single-strands json options; json arguments; // if it's DNA, allow single-stranded if (dna && m_dialog->singleStrandRadio->isChecked()) arguments.push_back("-a1"); // Add the number of turns QString turns = QString("-at %1").arg(m_dialog->bpTurnsSpin->value()); arguments.push_back(turns.toStdString()); options["arguments"] = arguments; options["format"] = "pdb"; QProgressDialog progDlg; progDlg.setModal(true); progDlg.setWindowTitle(tr("Insert Molecule…")); progDlg.setLabelText(tr("Generating 3D molecule…")); progDlg.setRange(0, 0); progDlg.setValue(0); progDlg.show(); QtGui::Molecule newMol; m_reader->setOptions(options.dump()); m_reader->readString(sequence.toStdString(), newMol); m_molecule->undoMolecule()->appendMolecule(newMol, "Insert Molecule"); emit requestActiveTool("Manipulator"); } void InsertDna::dialogDestroyed() { m_dialog = nullptr; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/insertdna/insertdna.h000066400000000000000000000030601506155467400243210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_INSERTDNA_H #define AVOGADRO_QTPLUGINS_INSERTDNA_H #include #include #include namespace Avogadro { namespace Io { class FileFormat; } namespace QtPlugins { class InsertDNADialog; /** * @brief Load single-line molecule descriptors through an input dialog. */ class InsertDna : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit InsertDna(QObject* parent_ = nullptr); ~InsertDna() override; QString name() const override { return tr("InsertDNA"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule*) override; private slots: void showDialog(); void performInsert(); void updateText(); void updateBPTurns(int type); void changeNucleicType(int type); void dialogDestroyed(); private: QList m_actions; QtGui::Molecule* m_molecule; Io::FileFormat* m_reader; InsertDNADialog* m_dialog; void constructDialog(); }; inline QString InsertDna::description() const { return tr("Insert DNA / RNA fragments through a dialog."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_INSERTDNA_H avogadrolibs-1.101.0/avogadro/qtplugins/insertdna/insertdnadialog.ui000066400000000000000000000162761506155467400257040ustar00rootroot00000000000000 InsertDNADialog 0 0 365 384 Insert Nucleic Acids DNA/RNA Builder DNA RNA Nucleic Acids: Adenine A Cytosine C Guanine G Thymine T Qt::Horizontal 40 20 Sequence: Qt::StrongFocus the number of base pairs per helix turn 1 A B Z Other the number of base pairs per helix turn 1 0.000000000000000 15.000000000000000 10.500000000000000 Bases Per Turn: Strands: Single Double true Qt::Horizontal 40 20 Insert true Qt::Horizontal 40 20 Qt::Vertical 20 0 avogadrolibs-1.101.0/avogadro/qtplugins/insertfragment/000077500000000000000000000000001506155467400232235ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/insertfragment/CMakeLists.txt000066400000000000000000000026301506155467400257640ustar00rootroot00000000000000include(ExternalProject) avogadro_plugin(InsertFragment "Insert molecular fragments and crystals." ExtensionPlugin insertfragment.h InsertFragment insertfragment.cpp ) # Install the fragments & crystals # TODO - make this a for loop set(_molecules "${AvogadroLibs_SOURCEDATA_DIR}/molecules") set(_crystals "${AvogadroLibs_SOURCEDATA_DIR}/crystals") # Look in parallel directory for the molecule fragment repository if(NOT EXISTS "${_molecules}") # download molecules... ExternalProject_Add(molecules GIT_REPOSITORY https://github.com/openchemistry/molecules # or https://github.com/OpenChemistry/molecules/archive/refs/heads/master.zip SOURCE_DIR "${AvogadroLibs_SOURCEDATA_DIR}/molecules" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ) endif() install(DIRECTORY "${AvogadroLibs_SOURCEDATA_DIR}/molecules" DESTINATION "${INSTALL_DATA_DIR}/avogadro2" PATTERN ".git" EXCLUDE ) # crystals if(NOT EXISTS "${_crystals}") # download molecules... ExternalProject_Add(crystals GIT_REPOSITORY https://github.com/openchemistry/crystals # or https://github.com/OpenChemistry/crystals/archive/refs/heads/master.zip SOURCE_DIR "${AvogadroLibs_SOURCEDATA_DIR}/crystals" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ) endif() install(DIRECTORY "${AvogadroLibs_SOURCEDATA_DIR}/crystals" DESTINATION "${INSTALL_DATA_DIR}/avogadro2" PATTERN ".git" EXCLUDE ) avogadrolibs-1.101.0/avogadro/qtplugins/insertfragment/insertfragment.cpp000066400000000000000000000075211506155467400267640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "insertfragment.h" #include #include #include #include #include #include #include #include #include #include using Avogadro::Io::FileFormatManager; using Avogadro::QtGui::InsertFragmentDialog; using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { InsertFragment::InsertFragment(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_moleculeDialog(nullptr), m_crystalDialog(nullptr), m_molecule(nullptr), m_reader(nullptr) { auto* action = new QAction(tr("Molecule…"), this); action->setProperty("menu priority", 890); action->setData("molecules"); // will also work for crystals connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); action = new QAction(tr("Crystal…"), this); action->setData("crystals"); // will also work for crystals action->setProperty("menu priority", 170); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); } InsertFragment::~InsertFragment() { delete m_reader; } QList InsertFragment::actions() const { return m_actions; } QStringList InsertFragment::menuPath(QAction* action) const { if (action->data() == "crystals") return QStringList() << tr("&File") << tr("&Import"); else return QStringList() << tr("&Build") << tr("&Insert"); } void InsertFragment::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void InsertFragment::showDialog() { if (!m_molecule) return; QWidget* parentAsWidget = qobject_cast(parent()); auto* theSender = qobject_cast(sender()); // Prompt user for input: bool crystal = theSender->data().toString() == "crystals"; if (crystal) { // create the dialog if it doesn't exist if (!m_crystalDialog) { m_crystalDialog = new InsertFragmentDialog(parentAsWidget, "crystals"); connect(m_crystalDialog, &InsertFragmentDialog::performInsert, this, &InsertFragment::performInsert); } m_crystalDialog->show(); } else { // fragments - create the dialog if it doesn't exist if (!m_moleculeDialog) { m_moleculeDialog = new InsertFragmentDialog(parentAsWidget, "molecules"); connect(m_moleculeDialog, &InsertFragmentDialog::performInsert, this, &InsertFragment::performInsert); } m_moleculeDialog->show(); } } void InsertFragment::performInsert(const QString& fileName, bool crystal) { if (m_molecule == nullptr) return; // read the file into the new fragment Avogadro::QtGui::Molecule newMol(m_molecule->parent()); bool ok = Io::FileFormatManager::instance().readFile(newMol, fileName.toStdString()); if (!ok) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error reading file (%1).").arg(fileName)); return; } if (crystal) { Molecule::MoleculeChanges changes = (Molecule::Atoms | Molecule::Bonds | Molecule::Added | Molecule::Removed); m_molecule->undoMolecule()->modifyMolecule(newMol, changes, tr("Import Crystal")); emit requestActiveTool("Navigator"); } else { // insert mol into m_molecule m_molecule->undoMolecule()->appendMolecule(newMol, tr("Insert Fragment")); emit requestActiveTool("Manipulator"); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/insertfragment/insertfragment.h000066400000000000000000000032131506155467400264230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_INSERTFRAGMENT_H #define AVOGADRO_QTPLUGINS_INSERTFRAGMENT_H #include #include #include namespace Avogadro { namespace Io { class FileFormat; } namespace QtGui { class InsertFragmentDialog; } namespace QtPlugins { /** * @brief Load molecules through a tree browser. */ class InsertFragment : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit InsertFragment(QObject* parent_ = nullptr); ~InsertFragment() override; QString name() const override { return tr("InsertFragment"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule*) override; private slots: void showDialog(); void performInsert(const QString& fileName, bool crystal); private: QList m_actions; QtGui::InsertFragmentDialog* m_moleculeDialog; QtGui::InsertFragmentDialog* m_crystalDialog; /// Maps identifier to extension: QMap m_formats; QtGui::Molecule* m_molecule; Io::FileFormat* m_reader; }; inline QString InsertFragment::description() const { return tr("Insert molecular fragments for building larger molecules."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_INSERTFRAGMENT_H avogadrolibs-1.101.0/avogadro/qtplugins/label/000077500000000000000000000000001506155467400212525ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/label/CMakeLists.txt000066400000000000000000000012321506155467400240100ustar00rootroot00000000000000avogadro_plugin(Label "Labels rendering scheme" ScenePlugin label.h Label label.cpp "") set(label_rcs label.qrc ) set(label_srcs labeleditor.cpp ) avogadro_plugin(LabelEditor "Labels rendering scheme" ToolPlugin labeleditor.h LabelEditor "${label_srcs}" "" "${label_rcs}" ) target_link_libraries(Label LINK_PRIVATE Avogadro::Rendering) target_link_libraries(Label PRIVATE Avogadro::Calc) target_link_libraries(LabelEditor LINK_PRIVATE Avogadro::Rendering Avogadro::QtOpenGL) target_include_directories(LabelEditor PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../../rendering ${CMAKE_CURRENT_BINARY_DIR}/../../qtopengl) avogadrolibs-1.101.0/avogadro/qtplugins/label/label.cpp000066400000000000000000000423571506155467400230500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "label.h" #include #include #include // for partial charges #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Avogadro::Rendering::TextLabel3D; using Core::Array; using Core::Atom; using Core::contrastColor; using Core::Elements; using Core::Molecule; using QtGui::PluginLayerManager; using Rendering::GeometryNode; using Rendering::GroupNode; using std::map; typedef Array NeighborListType; namespace { TextLabel3D* createLabel(const std::string& text, const Vector3f& pos, float radius, const Vector3ub& color) { Rendering::TextProperties tprop; tprop.setAlign(Rendering::TextProperties::HCenter, Rendering::TextProperties::VCenter); tprop.setFontFamily(Rendering::TextProperties::SansSerif); tprop.setColorRgb(color.data()); auto* label = new TextLabel3D; label->setText(text); label->setRenderPass(Rendering::TranslucentPass); label->setTextProperties(tprop); label->setRadius(radius); label->setAnchor(pos); return label; } } // namespace struct LayerLabel : Core::LayerData { enum LabelOptions { None = 0, Index = 1, Name = 2, Custom = 4, Ordinal = 8, UniqueID = 16, PartialCharge = 32, Length = 64 // for bonds obviously }; unsigned short atomOptions; unsigned short residueOptions; unsigned short bondOptions; QWidget* widget; float radiusScalar; Vector3ub color; LayerLabel() { widget = nullptr; QSettings settings; atomOptions = settings.value("label/atomoptions", LabelOptions::Name).toInt(); residueOptions = settings.value("label/residueoptions", LabelOptions::None).toInt(); bondOptions = settings.value("label/bondoptions", LabelOptions::None).toInt(); radiusScalar = settings.value("label/radiusscalar", 0.5).toDouble(); auto q_color = settings.value("label/color", QColor(Qt::white)).value(); color[0] = static_cast(q_color.red()); color[1] = static_cast(q_color.green()); color[2] = static_cast(q_color.blue()); } LayerLabel(std::string settings) { widget = nullptr; deserialize(settings); } LayerData* clone() final { return new LayerLabel(serialize()); } ~LayerLabel() override { if (widget) widget->deleteLater(); } std::string serialize() final { std::stringstream output; output << atomOptions << " " << residueOptions << " " << radiusScalar << " " << (int)color[0] << " " << (int)color[1] << " " << (int)color[2] << " " << bondOptions; return output.str(); } void deserialize(std::string text) final { std::stringstream ss(text); std::string aux; ss >> aux; atomOptions = std::stoi(aux); ss >> aux; residueOptions = std::stoi(aux); ss >> aux; radiusScalar = std::stof(aux); ss >> aux; color[0] = std::stoi(aux); ss >> aux; color[1] = std::stoi(aux); ss >> aux; color[2] = std::stoi(aux); ss >> aux; if (!aux.empty()) bondOptions = std::stoi(aux); // backwards compatibility } void setupWidget(Label* slot) { if (!widget) { widget = new QWidget(qobject_cast(slot->parent())); auto* v = new QVBoxLayout; auto* form = new QFormLayout; // color button auto* colorButton = new QtGui::ColorButton; QObject::connect(colorButton, SIGNAL(colorChanged(const QColor&)), slot, SLOT(setColor(const QColor&))); form->addRow(QObject::tr("Color:"), colorButton); // radius scalar auto* spin = new QDoubleSpinBox; spin->setRange(0.0, 1.5); spin->setSingleStep(0.1); spin->setDecimals(1); spin->setValue(radiusScalar); QObject::connect(spin, SIGNAL(valueChanged(double)), slot, SLOT(setRadiusScalar(double))); form->addRow(QObject::tr("Distance from center:"), spin); auto* atom = new QComboBox; atom->setObjectName("atom"); // set up the various atom options [[maybe_unused]] char val = LabelOptions::None; QStringList text; // first add the individual options atom->addItem(QObject::tr("None"), int(LabelOptions::None)); atom->addItem(QObject::tr("Index"), int(LabelOptions::Index)); atom->addItem(QObject::tr("Unique ID"), int(LabelOptions::UniqueID)); atom->addItem(QObject::tr("Element"), int(LabelOptions::Name)); atom->addItem(QObject::tr("Element & Number"), int(LabelOptions::Ordinal)); atom->addItem(QObject::tr("Element & ID"), int(LabelOptions::Name | LabelOptions::UniqueID)); atom->addItem(QObject::tr("Partial Charge", "atomic partial charge"), int(LabelOptions::PartialCharge)); atom->addItem(QObject::tr("Custom"), int(LabelOptions::Custom)); // check for current option based on item data for (int i = 0; i < atom->count(); ++i) { if (atom->itemData(i).toInt() == atomOptions) { atom->setCurrentIndex(i); break; } } QObject::connect(atom, SIGNAL(currentIndexChanged(int)), slot, SLOT(atomLabelType(int))); form->addRow(QObject::tr("Atom Label:"), atom); // bond label auto* bond = new QComboBox; bond->setObjectName("bond"); // set up the various bond options bond->addItem(QObject::tr("None"), int(LabelOptions::None)); bond->addItem(QObject::tr("Length"), int(LabelOptions::Length)); bond->addItem(QObject::tr("Index"), int(LabelOptions::Index)); bond->addItem(QObject::tr("Custom"), int(LabelOptions::Custom)); QObject::connect(bond, SIGNAL(currentIndexChanged(int)), slot, SLOT(bondLabelType(int))); form->addRow(QObject::tr("Bond Label:"), bond); auto* residue = new QComboBox; residue->setObjectName("residue"); for (char i = 0x00; i < std::pow(2, 2); ++i) { if (i == 0) { residue->addItem(QObject::tr("None"), QVariant(LabelOptions::None)); } else { char optval = 0x00; QStringList opttext; if (i & LabelOptions::Index) { opttext << QObject::tr("ID"); optval |= LabelOptions::Index; } if (i & LabelOptions::Name) { opttext << QObject::tr("Name"); optval |= LabelOptions::Name; } if (optval != 0x00) { QString join = QObject::tr(" & "); residue->addItem(opttext.join(join), QVariant(optval)); if (optval == residueOptions) { residue->setCurrentText(opttext.join(join)); } } } } QObject::connect(residue, SIGNAL(currentIndexChanged(int)), slot, SLOT(residueLabelType(int))); // residue->model()->sort(0, Qt::AscendingOrder); form->addRow(QObject::tr("Residue Label:"), residue); v->addLayout(form); v->addStretch(1); widget->setLayout(v); } } }; Label::Label(QObject* parent_) : QtGui::ScenePlugin(parent_) { m_layerManager = PluginLayerManager(m_name); } Label::~Label() {} void Label::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { m_layerManager.load(); for (size_t layer = 0; layer < m_layerManager.layerCount(); ++layer) { auto* interface = m_layerManager.getSetting(layer); if (interface->residueOptions) { processResidue(molecule, node, layer); } if (interface->atomOptions) { processAtom(molecule, node, layer); } if (interface->bondOptions) { processBond(molecule, node, layer); } } } void Label::processResidue(const Core::Molecule& molecule, Rendering::GroupNode& node, size_t layer) { auto* geometry = new GeometryNode; node.addChild(geometry); for (const auto& residue : molecule.residues()) { Atom caAtom = residue.getAtomByName("CA"); if (!caAtom.isValid() || !m_layerManager.atomEnabled(layer, caAtom.index())) { continue; } auto name = residue.residueName(); const auto atoms = residue.residueAtoms(); Vector3f pos = Vector3f::Zero(); for (const auto& atom : atoms) { pos += atom.position3d().cast(); } pos /= static_cast(atoms.size()); float radius = 0.0f; for (const auto& atom : atoms) { unsigned char atomicNumber = atom.atomicNumber(); auto auxR = static_cast(Elements::radiusVDW(atomicNumber)); auxR += (atom.position3d().cast() - pos).norm(); if (auxR > radius) { auxR = radius; } } auto* interface = m_layerManager.getSetting(layer); Vector3ub color = interface->color; std::string text = ""; if (interface->residueOptions & LayerLabel::LabelOptions::Index) { text = std::to_string(residue.residueId()); } if (interface->residueOptions & LayerLabel::LabelOptions::Name) { text += (text == "" ? "" : " / ") + name; } TextLabel3D* residueLabel = createLabel(text, pos, radius, color); geometry->addDrawable(residueLabel); } } QString partialCharge(Molecule* molecule, int atom) { // TODO: we need to track type and/or calling the charge calculator float charge = 0.0; std::set types = molecule->partialChargeTypes(); if (types.size() > 0) { auto first = types.cbegin(); MatrixX charges = molecule->partialCharges((*first)); charge = charges(atom, 0); } else { // find something const auto options = Calc::ChargeManager::instance().identifiersForMolecule(*molecule); if (options.size() > 0) { // look for GFN2 or AM1BCC, then MMFF94 then Gasteiger std::string type; if (options.find("GFN2") != options.end()) type = "GFN2"; else if (options.find("am1bcc") != options.end()) type = "am1bcc"; else if (options.find("mmff94") != options.end()) type = "mmff94"; else if (options.find("gasteiger") != options.end()) type = "gasteiger"; else type = *options.begin(); MatrixX charges = Calc::ChargeManager::instance().partialCharges(type, *molecule); charge = charges(atom, 0); } } // e.g. '-0.12' => 5 characters return QString("%L1").arg(charge, 5, 'f', 2); } void Label::processAtom(const Core::Molecule& molecule, Rendering::GroupNode& node, size_t layer) { auto* geometry = new GeometryNode; node.addChild(geometry); std::map atomCount; for (Index i = 0; i < molecule.atomCount(); ++i) { Core::Atom atom = molecule.atom(i); unsigned char atomicNumber = atom.atomicNumber(); if (atomCount.find(atomicNumber) == atomCount.end()) { atomCount[atomicNumber] = 1; } else { ++atomCount[atomicNumber]; } if (!m_layerManager.atomEnabled(layer, i)) { continue; } auto* interface = m_layerManager.getSetting(layer); std::string text = atom.label(); if (interface->atomOptions & LayerLabel::LabelOptions::PartialCharge) { QString charge = partialCharge(const_cast(&molecule), i); text += charge.toStdString(); } if (interface->atomOptions & LayerLabel::LabelOptions::Custom) { // already set } if (interface->atomOptions & LayerLabel::LabelOptions::Index) { text += (text == "" ? "" : " / ") + std::to_string(atom.index() + 1); } if (interface->atomOptions & LayerLabel::LabelOptions::Name) { text += (text == "" ? "" : " / ") + std::string(Elements::symbol(atomicNumber)); } if (interface->atomOptions & LayerLabel::LabelOptions::Ordinal) { text += (text == "" ? "" : " / ") + std::string(Elements::symbol(atomicNumber) + std::to_string(atomCount[atomicNumber])); } if (interface->atomOptions & LayerLabel::LabelOptions::UniqueID) { text += (text == "" ? "" : " / ") + std::to_string(atom.index()); } if (text != "") { const Vector3f pos(atom.position3d().cast()); Vector3ub color = atom.color(); float radius = static_cast(Elements::radiusVDW(atomicNumber)) * interface->radiusScalar; TextLabel3D* atomLabel = createLabel(text, pos, radius, contrastColor(color)); geometry->addDrawable(atomLabel); } } } void Label::processBond(const Core::Molecule& molecule, Rendering::GroupNode& node, [[maybe_unused]] size_t layer) { auto* geometry = new GeometryNode; node.addChild(geometry); std::map bondCount; for (Index i = 0; i < molecule.bondCount(); ++i) { Core::Bond bond = molecule.bond(i); // check if the bond is enabled in this layer if (!m_layerManager.bondEnabled(bond.atom1().index(), bond.atom2().index())) { continue; } // get the options for this bond Core::Atom atom1 = bond.atom1(); Core::Atom atom2 = bond.atom2(); auto* interface1 = m_layerManager.getSetting( m_layerManager.getLayerID(atom1.index())); auto* interface2 = m_layerManager.getSetting( m_layerManager.getLayerID(atom2.index())); // get the union of the options char options = interface1->bondOptions | interface2->bondOptions; Vector3ub color = interface1->color; unsigned char atomicNumber1 = atom1.atomicNumber(); unsigned char atomicNumber2 = atom2.atomicNumber(); float radiusVDW1 = static_cast(Elements::radiusVDW(atomicNumber1)); float radiusVDW2 = static_cast(Elements::radiusVDW(atomicNumber2)); float radius = (radiusVDW1 + radiusVDW2) * interface1->radiusScalar / 2.0f; std::stringstream text; // hopefully not all at once if (options & LayerLabel::LabelOptions::Index) { text << bond.index(); } if (options & LayerLabel::LabelOptions::Custom) { text << bond.label(); } if (options & LayerLabel::LabelOptions::Length) { text << std::fixed << std::setprecision(2) << bond.length(); } // position will be between the two atoms Vector3f pos = (atom1.position3d().cast() + atom2.position3d().cast()) / 2.0f; TextLabel3D* bondLabel = createLabel(text.str(), pos, radius, color); geometry->addDrawable(bondLabel); } } void Label::setColor(const QColor& color) { auto* interface = m_layerManager.getSetting(); interface->color[0] = static_cast(color.red()); interface->color[1] = static_cast(color.green()); interface->color[2] = static_cast(color.blue()); emit drawablesChanged(); QSettings settings; settings.setValue("label/color", color); } void Label::atomLabelType(int index) { auto* interface = m_layerManager.getSetting(); interface->atomOptions = char(setupWidget() ->findChildren("atom")[0] ->itemData(index) .toInt()); emit drawablesChanged(); QSettings settings; settings.setValue("label/atomoptions", interface->atomOptions); } void Label::bondLabelType(int index) { auto* interface = m_layerManager.getSetting(); interface->bondOptions = char(setupWidget() ->findChildren("bond")[0] ->itemData(index) .toInt()); emit drawablesChanged(); QSettings settings; settings.setValue("label/bondoptions", interface->bondOptions); } void Label::residueLabelType(int index) { auto* interface = m_layerManager.getSetting(); interface->residueOptions = char(setupWidget() ->findChildren("residue")[0] ->itemData(index) .toInt()); emit drawablesChanged(); QSettings settings; settings.setValue("label/residueoptions", interface->residueOptions); } void Label::setRadiusScalar(double radius) { auto* interface = m_layerManager.getSetting(); interface->radiusScalar = float(radius); emit drawablesChanged(); QSettings settings; settings.setValue("label/radiusscalar", interface->radiusScalar); } QWidget* Label::setupWidget() { auto* interface = m_layerManager.getSetting(); interface->setupWidget(this); return interface->widget; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/label/label.h000066400000000000000000000034641506155467400225110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_LABEL_H #define AVOGADRO_QTPLUGINS_LABEL_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief Render labels to each atom. */ class Label : public QtGui::ScenePlugin { Q_OBJECT public: explicit Label(QObject* parent = nullptr); ~Label() override; QString name() const override { return tr("Labels"); } QString description() const override { return tr("Display labels on ball and stick style."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } public slots: void atomLabelType(int index); void bondLabelType(int index); void residueLabelType(int index); void setRadiusScalar(double radius); void setColor(const QColor& color); private: void processAtom(const Core::Molecule& molecule, Rendering::GroupNode& node, size_t layer); void processBond(const Core::Molecule& molecule, Rendering::GroupNode& node, size_t layer); void processResidue(const Core::Molecule& molecule, Rendering::GroupNode& node, size_t layer); Rendering::GroupNode* m_group; std::string m_name = "Labels"; Vector3ub m_color; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_LABEL_H avogadrolibs-1.101.0/avogadro/qtplugins/label/label.qrc000066400000000000000000000002071506155467400230370ustar00rootroot00000000000000 label_light.svg label_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/label/label_dark.svg000066400000000000000000000017541506155467400240620ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/label/label_light.svg000066400000000000000000000017541506155467400242500ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/label/labeleditor.cpp000066400000000000000000000102641506155467400242470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "labeleditor.h" #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Elements; using QtGui::RWAtom; using Rendering::GeometryNode; using Rendering::Identifier; using Rendering::TextLabel3D; LabelEditor::LabelEditor(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_glWidget(nullptr), m_renderer(nullptr), m_selected(false), m_text("") { QString shortcut = tr("Ctrl+4", "control-key 4"); m_activateAction->setText(tr("Edit Labels")); m_activateAction->setToolTip( tr("Atom Label Tool\t(%1)\n\n" "Left Mouse:\tClick on Atoms to add Custom Labels") .arg(shortcut)); setIcon(); } LabelEditor::~LabelEditor() {} void LabelEditor::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/label_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/label_light.svg")); } QUndoCommand* LabelEditor::mouseReleaseEvent(QMouseEvent* e) { e->ignore(); return nullptr; } QUndoCommand* LabelEditor::mouseMoveEvent(QMouseEvent* e) { e->ignore(); return nullptr; } QUndoCommand* LabelEditor::keyPressEvent(QKeyEvent* e) { if (m_selected && !e->text().isEmpty()) { e->accept(); const QChar text = e->text()[0]; if (text.isPrint()) { m_text.append(text); } else if (e->key() == Qt::Key_Backspace) { m_text.chop(1); } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { save(); } emit drawablesChanged(); } return nullptr; } void LabelEditor::save() { m_molecule->beginMergeMode(tr("Create Label")); m_selectedAtom.setLabel(m_text.toStdString()); m_molecule->endMergeMode(); m_text.clear(); m_selectedAtom = RWAtom(); // make sure the label display is made active emit requestActiveDisplayTypes(QStringList() << "Labels"); } QUndoCommand* LabelEditor::mousePressEvent(QMouseEvent* e) { if (!m_renderer || !m_molecule) return nullptr; if (e->buttons() & Qt::LeftButton) { if (m_selectedAtom.isValid()) { e->accept(); save(); emit drawablesChanged(); } Identifier clickedObject = m_renderer->hit(e->pos().x(), e->pos().y()); m_selected = (clickedObject.type == Rendering::AtomType); if (m_selected) { e->accept(); m_selectedAtom = m_molecule->atom(clickedObject.index); m_text = QString::fromStdString(m_selectedAtom.label()); emit drawablesChanged(); } else { // clicked on empty space e->ignore(); } } else { e->ignore(); } return nullptr; } namespace { TextLabel3D* createLabel(const std::string& text, const Vector3f& pos, float radius) { Rendering::TextProperties tprop; tprop.setAlign(Rendering::TextProperties::HCenter, Rendering::TextProperties::VCenter); tprop.setFontFamily(Rendering::TextProperties::SansSerif); tprop.setColorRgb(255, 255, 255); auto* label = new TextLabel3D; label->setText(text); label->setRenderPass(Rendering::Overlay3DPass); label->setTextProperties(tprop); label->setRadius(radius); label->setAnchor(pos); return label; } } // namespace void LabelEditor::draw(Rendering::GroupNode& node) { if (m_renderer == nullptr || m_molecule == nullptr || !m_selected || !m_selectedAtom.isValid()) { return; } auto* geometry = new GeometryNode; node.addChild(geometry); unsigned char atomicNumber = m_selectedAtom.atomicNumber(); const Vector3f pos(m_selectedAtom.position3d().cast()); float radius = static_cast(Elements::radiusVDW(atomicNumber)) * 0.6f; TextLabel3D* atomLabel = createLabel(m_text.toStdString(), pos, radius); geometry->addDrawable(atomLabel); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/label/labeleditor.h000066400000000000000000000037241506155467400237170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_LABELEDITOR_H #define AVOGADRO_QTPLUGINS_LABELEDITOR_H #include #include #include namespace Avogadro { namespace QtPlugins { class LabelEditor : public QtGui::ToolPlugin { Q_OBJECT public: explicit LabelEditor(QObject* parent_ = nullptr); ~LabelEditor() override; QString name() const override { return tr("Label editor tool"); } QString description() const override { return tr("Label editor tool"); } unsigned char priority() const override { return 25; } QAction* activateAction() const override { return m_activateAction; } void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule* mol) override { if (mol) m_molecule = mol->undoMolecule(); } void setEditMolecule(QtGui::RWMolecule* mol) override { m_molecule = mol; } void setGLWidget(QtOpenGL::GLWidget* widget) override { m_glWidget = widget; } void setGLRenderer(Rendering::GLRenderer* renderer) override { m_renderer = renderer; } QWidget* toolWidget() const override { return nullptr; } QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseMoveEvent(QMouseEvent* e) override; QUndoCommand* keyPressEvent(QKeyEvent* e) override; void draw(Rendering::GroupNode& node) override; private: void save(); QAction* m_activateAction; QtGui::RWMolecule* m_molecule; QtOpenGL::GLWidget* m_glWidget; Rendering::GLRenderer* m_renderer; bool m_selected; QtGui::RWAtom m_selectedAtom; QString m_text; }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/lammpsinput/000077500000000000000000000000001506155467400225445ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/lammpsinput/CMakeLists.txt000066400000000000000000000004201506155467400253000ustar00rootroot00000000000000# Extension set(gamessinput_srcs lammpsinputdialog.cpp lammpsinput.cpp ) avogadro_plugin(LammpsInput "LAMMPS input file generation" ExtensionPlugin lammpsinput.h LammpsInput "${gamessinput_srcs}" lammpsinputdialog.ui ) target_link_libraries(LammpsInput) avogadrolibs-1.101.0/avogadro/qtplugins/lammpsinput/lammpsinput.cpp000066400000000000000000000041331506155467400256220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "lammpsinput.h" #include "lammpsinputdialog.h" #include #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { LammpsInput::LammpsInput(QObject* parent_) : ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr), m_dialog(nullptr), m_outputFormat(nullptr) { m_action->setEnabled(true); m_action->setText(tr("&LAMMPS…")); connect(m_action, SIGNAL(triggered()), SLOT(menuActivated())); } LammpsInput::~LammpsInput() {} QList LammpsInput::actions() const { QList actions_; actions_.append(m_action); return actions_; } QStringList LammpsInput::menuPath(QAction*) const { QStringList path; path << tr("&Input"); return path; } void LammpsInput::setMolecule(QtGui::Molecule* mol) { if (m_dialog) m_dialog->setMolecule(mol); m_molecule = mol; } bool LammpsInput::readMolecule(QtGui::Molecule& mol) { Io::FileFormat* reader = m_outputFormat->newInstance(); bool success = reader->readFile(m_outputFileName.toStdString(), mol); if (!success) { QMessageBox::information(qobject_cast(parent()), tr("Error"), tr("Error reading output file '%1':\n%2") .arg(m_outputFileName) .arg(QString::fromStdString(reader->error()))); } m_outputFormat = nullptr; m_outputFileName.clear(); return success; } void LammpsInput::menuActivated() { if (!m_dialog) { m_dialog = new LammpsInputDialog(qobject_cast(parent())); } m_dialog->setMolecule(m_molecule); m_dialog->show(); } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/lammpsinput/lammpsinput.h000066400000000000000000000027661506155467400253010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_LAMMPSINPUT_H #define AVOGADRO_QTPLUGINS_LAMMPSINPUT_H #include class QAction; class QDialog; namespace Avogadro { namespace Io { class FileFormat; } namespace QtPlugins { class LammpsInputDialog; class LammpsInput : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit LammpsInput(QObject* parent = nullptr); ~LammpsInput() override; QString name() const override { return tr("LAMMPS input"); } QString description() const override { return tr("Generate input for LAMMPS."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; public slots: /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ // void openJobOutput(const MoleQueue::JobObject& job); bool readMolecule(QtGui::Molecule& mol) override; private slots: void menuActivated(); private: QAction* m_action; QtGui::Molecule* m_molecule; LammpsInputDialog* m_dialog; const Io::FileFormat* m_outputFormat; QString m_outputFileName; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_LAMMPSINPUT_H avogadrolibs-1.101.0/avogadro/qtplugins/lammpsinput/lammpsinputdialog.cpp000066400000000000000000000633431506155467400270120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "lammpsinputdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { LammpsInputDialog::LammpsInputDialog(QWidget* parent, Qt::WindowFlags flag) : QDialog(parent, flag), m_molecule(nullptr), m_unitType(real), m_title("Title"), m_savePath(""), m_dimensionType(d3), m_xBoundaryType(p), m_yBoundaryType(p), m_zBoundaryType(p), m_atomStyle(full), m_waterPotential(NONE), m_ensemble(NVT), m_temperature(298.15), m_nhChain(1), m_timeStep(2.0), m_runSteps(50), m_xReplicate(1), m_yReplicate(1), m_zReplicate(1), m_dumpStep(1), m_velocityDist(gaussian), m_velocityTemp(298.15), m_zeroMOM(true), m_zeroL(true), m_thermoStyle(one), m_thermoInterval(50), m_output(), m_dirty(false), m_warned(false), readData(false), m_jobEdit(nullptr), m_moleculeEdit(nullptr) { ui.setupUi(this); // Connect the GUI elements to the correct slots connect(ui.titleLine, SIGNAL(editingFinished()), this, SLOT(setTitle())); // now for something useful connect(ui.unitsCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setUnits(int))); connect(ui.atomStyleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setAtomStyle(int))); connect(ui.dimensionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setDimensionType(int))); connect(ui.xBoundaryCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setXBoundaryType(int))); connect(ui.yBoundaryCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setYBoundaryType(int))); connect(ui.zBoundaryCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setZBoundaryType(int))); connect(ui.waterPotentialCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setWaterPotential(int))); connect(ui.readDataLine, SIGNAL(editingFinished()), this, SLOT(setReadData())); connect(ui.ensembleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setEnsemble(int))); connect(ui.tempSpin, SIGNAL(valueChanged(double)), this, SLOT(setTemperature(double))); connect(ui.nhChainSpin, SIGNAL(valueChanged(int)), this, SLOT(setNHChain(int))); connect(ui.stepSpin, SIGNAL(valueChanged(double)), this, SLOT(setTimeStep(double))); connect(ui.runSpin, SIGNAL(valueChanged(int)), this, SLOT(setRunSteps(int))); connect(ui.xReplicateSpin, SIGNAL(valueChanged(int)), this, SLOT(setXReplicate(int))); connect(ui.yReplicateSpin, SIGNAL(valueChanged(int)), this, SLOT(setYReplicate(int))); connect(ui.zReplicateSpin, SIGNAL(valueChanged(int)), this, SLOT(setZReplicate(int))); connect(ui.dumpXYZEdit, SIGNAL(editingFinished()), this, SLOT(setDumpXYZ())); connect(ui.dumpStepSpin, SIGNAL(valueChanged(int)), this, SLOT(setDumpStep(int))); connect(ui.velocityDistCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setVelocityDist(int))); connect(ui.velocityTempSpin, SIGNAL(valueChanged(double)), this, SLOT(setVelocityTemp(double))); connect(ui.zeroMOMCheck, SIGNAL(toggled(bool)), this, SLOT(setZeroMOM(bool))); connect(ui.zeroLCheck, SIGNAL(toggled(bool)), this, SLOT(setZeroL(bool))); connect(ui.thermoStyleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setThermoStyle(int))); connect(ui.thermoSpin, SIGNAL(valueChanged(int)), this, SLOT(setThermoInterval(int))); connect(ui.generateButton, SIGNAL(clicked()), this, SLOT(generateClicked())); connect(ui.resetButton, SIGNAL(clicked()), this, SLOT(resetClicked())); connect(ui.enableFormButton, SIGNAL(clicked()), this, SLOT(enableFormClicked())); QSettings settings; readSettings(settings); // Generate an initial preview of the input deck updatePreviewText(); addMoleculeDataTab(); } LammpsInputDialog::~LammpsInputDialog() { QSettings settings; writeSettings(settings); } void LammpsInputDialog::showEvent(QShowEvent*) { updatePreviewText(); addMoleculeDataTab(); } void LammpsInputDialog::updatePreviewText() { if (!isVisible()) return; int jobTabPosition = 0; // Store the currently displayed tab int currIndex = ui.tabWidget->currentIndex(); // Generate the input deck and display it if (m_dirty) { QString message = tr("Would you like to update the preview text, losing all " "changes made in the Lammps input deck preview pane?"); int response = QMessageBox::question( this, tr("Overwrite modified input files?"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (static_cast(response) == QMessageBox::No) { return; } } ui.tabWidget->removeTab(jobTabPosition); m_jobFileName = (ui.baseNameEdit->text().isEmpty() ? ui.baseNameEdit->placeholderText() : ui.baseNameEdit->text()) + ".lmp"; m_jobEdit = new QTextEdit(this); m_jobEdit->setObjectName(m_jobFileName); m_jobEdit->setFontFamily("monospace"); connect(m_jobEdit, SIGNAL(textChanged()), this, SLOT(textEditModified())); m_jobEdit->setText(generateInputDeck()); ui.tabWidget->insertTab(jobTabPosition, m_jobEdit, m_jobFileName); deckDirty(false); // Restore current tab ui.tabWidget->setCurrentIndex(currIndex); } void LammpsInputDialog::addMoleculeDataTab() { int molTabPosition = 1; if (m_molecule) { ui.tabWidget->removeTab(molTabPosition); std::string molOutput, extension = "lmpdat"; m_moleculeFileName = (ui.baseNameEdit->text().isEmpty() ? ui.baseNameEdit->placeholderText() : ui.baseNameEdit->text()) + "." + QString::fromStdString(extension); bool writeSDF = Io::FileFormatManager::instance().writeString( *m_molecule, molOutput, extension); if (writeSDF) { m_moleculeEdit = new QTextEdit(this); m_moleculeEdit->setObjectName(m_moleculeFileName); m_moleculeEdit->setFontFamily("monospace"); m_moleculeEdit->setText(QString::fromStdString(molOutput)); ui.tabWidget->insertTab(molTabPosition, m_moleculeEdit, m_moleculeFileName); } } } void LammpsInputDialog::textEditModified() { if (auto* edit = qobject_cast(sender())) { if (edit->document()->isModified()) { deckDirty(true); } else { deckDirty(false); } } } void LammpsInputDialog::resetClicked() { // Reset the form to defaults deckDirty(false); ui.unitsCombo->setCurrentIndex(1); ui.atomStyleCombo->setCurrentIndex(7); ui.dimensionCombo->setCurrentIndex(1); ui.xBoundaryCombo->setCurrentIndex(0); ui.yBoundaryCombo->setCurrentIndex(0); ui.zBoundaryCombo->setCurrentIndex(0); ui.waterPotentialCombo->setCurrentIndex(0); ui.ensembleCombo->setCurrentIndex(0); ui.tempSpin->setValue(298.15); ui.nhChainSpin->setValue(1); ui.stepSpin->setValue(2.0); ui.runSpin->setValue(50); ui.xReplicateSpin->setValue(1); ui.yReplicateSpin->setValue(1); ui.zReplicateSpin->setValue(1); ui.dumpStepSpin->setValue(1); ui.thermoStyleCombo->setCurrentIndex(0); ui.thermoSpin->setValue(50); updatePreviewText(); addMoleculeDataTab(); } void LammpsInputDialog::generateClicked() { QSettings settings; QString directory = settings.value("lammpsInput/outputDirectory", QDir::homePath()).toString(); if (directory.isEmpty()) directory = QDir::homePath(); directory = QFileDialog::getExistingDirectory( this, tr("Select output directory"), directory); // User cancel: if (directory.isNull()) return; settings.setValue("lammpsInput/outputDirectory", directory); QDir dir(directory); // Check for problems: QStringList errors; bool fatalError = false; do { // Do/while to break on fatal errors if (!dir.exists()) { errors << tr("%1: Directory does not exist!").arg(dir.absolutePath()); fatalError = true; break; } if (!dir.isReadable()) { errors << tr("%1: Directory cannot be read!").arg(dir.absolutePath()); fatalError = true; break; } QFileInfo jobFileInfo(dir.absoluteFilePath(m_jobFileName)); if (jobFileInfo.exists()) { errors << tr("%1: File will be overwritten.") .arg(jobFileInfo.absoluteFilePath()); } // Attempt to open the file for writing if (!QFile(jobFileInfo.absoluteFilePath()).open(QFile::WriteOnly)) { errors << tr("%1: File is not writable.").arg(jobFileInfo.absoluteFilePath()); fatalError = true; break; } QFileInfo molFileInfo(dir.absoluteFilePath(m_moleculeFileName)); if (molFileInfo.exists()) { errors << tr("%1: File will be overwritten.") .arg(molFileInfo.absoluteFilePath()); } // Attempt to open the file for writing if (!QFile(molFileInfo.absoluteFilePath()).open(QFile::WriteOnly)) { errors << tr("%1: File is not writable.").arg(molFileInfo.absoluteFilePath()); fatalError = true; break; } } while (false); // only run once // Handle fatal errors: if (fatalError) { QString formattedError; switch (errors.size()) { case 0: formattedError = tr("The input files cannot be written due to an unknown error."); break; case 1: formattedError = tr("The input files cannot be written:\n\n%1").arg(errors.first()); break; default: { // If a fatal error occurred, it will be last one in the list. Pop it // off and tell the user that it was the reason we had to stop. QString fatal = errors.last(); QStringList tmp(errors); tmp.pop_back(); formattedError = tr("The input files cannot be written:\n\n%1\n\nWarnings:\n\n%2") .arg(fatal, tmp.join("\n")); break; } } QMessageBox::critical(this, tr("Output Error"), formattedError); return; } // Non-fatal errors: if (!errors.isEmpty()) { QString formattedError = tr("Warning:\n\n%1\n\nWould you like to continue?") .arg(errors.join("\n")); QMessageBox::StandardButton reply = QMessageBox::warning(this, tr("Write input files"), formattedError, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (reply != QMessageBox::Yes) return; } bool success = false; if (m_jobEdit && m_moleculeEdit) { QFile jobFile(dir.absoluteFilePath(m_jobFileName)); if (jobFile.open(QFile::WriteOnly | QFile::Text)) { if (jobFile.write(m_jobEdit->toPlainText().toLocal8Bit()) > 0) { success = true; } jobFile.close(); } if (!success) { QMessageBox::critical( this, tr("Output Error"), tr("Failed to write to file %1.").arg(jobFile.fileName())); } QFile molFile(dir.absoluteFilePath(m_moleculeFileName)); if (molFile.open(QFile::WriteOnly | QFile::Text)) { if (molFile.write(m_moleculeEdit->toPlainText().toLocal8Bit()) > 0) { success = true; } molFile.close(); } if (!success) { QMessageBox::critical( this, tr("Output Error"), tr("Failed to write to file %1.").arg(molFile.fileName())); } } } void LammpsInputDialog::enableFormClicked() { updatePreviewText(); } void LammpsInputDialog::setTitle() { m_title = ui.titleLine->text(); updatePreviewText(); } void LammpsInputDialog::setUnits(int n) { m_unitType = (LammpsInputDialog::unitType)n; ui.unitsCombo->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setAtomStyle(int n) { m_atomStyle = (LammpsInputDialog::atomStyle)n; ui.atomStyleCombo->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setDimensionType(int n) { m_dimensionType = static_cast(n); ui.dimensionCombo->setEnabled(true); if (n == 0) { setZBoundaryType(0); ui.zBoundaryCombo->setCurrentIndex(0); ui.zBoundaryCombo->setEnabled(false); ui.zReplicateSpin->setValue(1); ui.zReplicateSpin->setEnabled(false); } if (n == 1) { ui.zBoundaryCombo->setEnabled(true); ui.zReplicateSpin->setEnabled(true); } updatePreviewText(); } void LammpsInputDialog::setXBoundaryType(int n) { m_xBoundaryType = static_cast(n); ui.xBoundaryCombo->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setYBoundaryType(int n) { m_yBoundaryType = static_cast(n); ui.yBoundaryCombo->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setZBoundaryType(int n) { m_zBoundaryType = static_cast(n); // should be careful here // z boundary must be p for 2d!!! ui.zBoundaryCombo->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setWaterPotential(int n) { m_waterPotential = static_cast(n); ui.waterPotentialCombo->setEnabled(true); if (n == 1) { setAtomStyle(7); ui.atomStyleCombo->setCurrentIndex(7); ui.atomStyleCombo->setEnabled(false); } if (n == 0) { ui.atomStyleCombo->setEnabled(true); } updatePreviewText(); } void LammpsInputDialog::setReadData() { m_readData = ui.readDataLine->text(); if (m_readData != "") readData = true; else readData = false; updatePreviewText(); } void LammpsInputDialog::setMolecule(QtGui::Molecule* molecule) { // Disconnect the old molecule first... if (molecule == m_molecule) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = molecule; // Update the preview text whenever primitives are changed connect(molecule, SIGNAL(changed(unsigned int)), SLOT(updatePreviewText())); updatePreviewText(); } void LammpsInputDialog::setEnsemble(int n) { m_ensemble = static_cast(n); ui.ensembleCombo->setEnabled(true); if (n == 1) { ui.tempSpin->setValue(0.0); ui.tempSpin->setEnabled(false); ui.nhChainSpin->setValue(0); ui.nhChainSpin->setEnabled(false); } else if (n == 0) { ui.tempSpin->setEnabled(true); ui.nhChainSpin->setEnabled(true); ui.nhChainSpin->setValue(1); } updatePreviewText(); } void LammpsInputDialog::setTemperature(double n) { m_temperature = n; ui.tempSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setNHChain(int n) { m_nhChain = n; ui.nhChainSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setTimeStep(double n) { m_timeStep = n; ui.stepSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setRunSteps(int n) { m_runSteps = n; ui.runSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setXReplicate(int n) { m_xReplicate = n; ui.xReplicateSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setYReplicate(int n) { m_yReplicate = n; ui.yReplicateSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setZReplicate(int n) { m_zReplicate = n; ui.zReplicateSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setDumpStep(int n) { m_dumpStep = n; ui.dumpStepSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setDumpXYZ() { m_dumpXYZ = ui.dumpXYZEdit->text(); updatePreviewText(); } void LammpsInputDialog::setVelocityDist(int n) { m_velocityDist = static_cast(n); ui.velocityDistCombo->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setVelocityTemp(double n) { m_velocityTemp = n; ui.velocityTempSpin->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setZeroMOM(bool state) { m_zeroMOM = state; ui.zeroMOMCheck->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setZeroL(bool state) { m_zeroL = state; ui.zeroLCheck->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setThermoStyle(int n) { m_thermoStyle = static_cast(n); ui.thermoStyleCombo->setEnabled(true); updatePreviewText(); } void LammpsInputDialog::setThermoInterval(int n) { m_thermoInterval = n; ui.thermoSpin->setEnabled(true); updatePreviewText(); } QString LammpsInputDialog::generateInputDeck() { // Generate an input deck based on the settings of the dialog QString buffer; QTextStream mol(&buffer); mol << "# LAMMPS Input file generated by Avogadro\n"; mol << "# " << m_title << "\n\n"; mol << "# Initialization\n"; mol << "units " << getUnitType(m_unitType) << "\n"; mol << "dimension " << getDimensionType(m_dimensionType) << "\n"; mol << "boundary " << getXBoundaryType(m_xBoundaryType) << " " << getYBoundaryType(m_yBoundaryType) << " " << getZBoundaryType(m_zBoundaryType) << "\n"; mol << "atom_style " << getAtomStyle(m_atomStyle) << "\n"; mol << "\n"; mol << "# Atom Definition\n"; if (readData) mol << "read_data " << m_readData << "\n"; mol << "replicate " << m_xReplicate << " " << m_yReplicate << " " << m_zReplicate << "\n"; mol << "\n" << getWaterPotential(m_waterPotential) << "\n"; mol << "# Settings\n"; mol << "velocity all create " << Qt::fixed << qSetRealNumberPrecision(2) << m_velocityTemp << " " << "4928459 " << "rot " << getZeroL() << " " << "mom " << getZeroMOM() << " " << "dist " << getVelocityDist(m_velocityDist) << "\n"; mol << getEnsemble(m_ensemble) << "\n"; mol << "timestep " << Qt::fixed << qSetRealNumberPrecision(1) << m_timeStep << "\n"; mol << "\n"; mol << "# Output\n"; if (m_dumpXYZ != "") { mol << "dump dumpXYZ all xyz " << m_dumpStep << " " << m_dumpXYZ << "\n"; } mol << "thermo_style " << getThermoStyle(m_thermoStyle) << "\n"; mol << "thermo " << m_thermoInterval << "\n"; mol << "\n"; mol << "# Run the simulation\n"; mol << "run " << m_runSteps << "\n"; mol << "\n"; return buffer; } QString LammpsInputDialog::getUnitType(unitType t) { // Translate the enum to text for the output generation switch (t) { case lj: return "lj"; case real: return "real"; case metal: return "metal"; case si: return "si"; case cgs: return "cgs"; case u_electron: return "electron"; default: return "lj"; } } QString LammpsInputDialog::getAtomStyle(atomStyle t) { switch (t) { case angle: return "angle"; case atomic: return "atomic"; case bond: return "bond"; case charge: return "charge"; case dipole: return "dipole"; case a_electron: return "electron"; case ellipsoid: return "ellipsoid"; case full: return "full"; case line: return "line"; case meso: return "meso"; case molecular: return "molecular"; case peri: return "peri"; case sphere: return "sphere"; case tri: return "tri"; case wavepacket: return "wavepacket"; default: return "full"; } } QString LammpsInputDialog::getDimensionType(dimensionType t) { switch (t) { case d2: return "2d"; case d3: return "3d"; default: return "3d"; } } QString LammpsInputDialog::getXBoundaryType(boundaryType t) { switch (t) { case p: return "p"; case f: return "f"; case s: return "s"; case m: return "m"; case fs: return "fs"; case fm: return "fm"; default: return "p"; } } QString LammpsInputDialog::getYBoundaryType(boundaryType t) { switch (t) { case p: return "p"; case f: return "f"; case s: return "s"; case m: return "m"; case fs: return "fs"; case fm: return "fm"; default: return "p"; } } QString LammpsInputDialog::getZBoundaryType(boundaryType t) { switch (t) { case p: return "p"; case f: return "f"; case s: return "s"; case m: return "m"; case fs: return "fs"; case fm: return "fm"; default: return "p"; } } QString LammpsInputDialog::getWaterPotential(waterPotential t) { switch (t) { case NONE: { QString waterPotentialInput; QTextStream water(&waterPotentialInput); water << ""; return waterPotentialInput; } case SPC: { QString waterPotentialInput; QTextStream water(&waterPotentialInput); int Hydrogen; int Oxygen; determineAtomTypesSPC(Hydrogen, Oxygen); water << "# The SPC water potential\n" << "pair_style lj/cut/coul/cut 9.8 9.8\n" << "pair_coeff " << Oxygen << " " << Oxygen << " 0.15535 3.5533\n" << "pair_coeff " << "* " << Hydrogen << " 0.00000 0.0000\n" << "bond_style harmonic\n" << "angle_style harmonic\n" << "dihedral_style none\n" << "improper_style none\n" << "bond_coeff 1 100.00 1.000\n" << "angle_coeff 1 100.00 109.47\n" << "special_bonds lj/coul 0.0 0.0 0.5\n" << "fix RigidOHBonds all shake 0.0001 20 0 b 1 a 1\n"; return waterPotentialInput; } case SPCE: { QString waterPotentialInput; QTextStream water(&waterPotentialInput); int Hydrogen; int Oxygen; determineAtomTypesSPC(Hydrogen, Oxygen); water << "# The SPC/E water potential\n" << "pair_style lj/cut/coul/long 9.8 9.8\n" << "kspace_style pppm 1.0e-4\n" << "pair_coeff " << Oxygen << " " << Oxygen << " 0.15535 3.5533\n" << "pair_coeff " << "* " << Hydrogen << " 0.00000 0.0000\n" << "bond_style harmonic\n" << "angle_style harmonic\n" << "dihedral_style none\n" << "improper_style none\n" << "bond_coeff 1 100.00 1.000\n" << "angle_coeff 1 100.00 109.47\n" << "special_bonds lj/coul 0.0 0.0 0.5\n" << "fix RigidOHBonds all shake 0.0001 20 0 b 1 a 1\n"; return waterPotentialInput; } default: { QString waterPotentialInput; QTextStream water(&waterPotentialInput); water << "\n"; return waterPotentialInput; } } } QString LammpsInputDialog::getEnsemble(ensemble t) { switch (t) { case NVT: { QString ensembleInput; QTextStream fix(&ensembleInput); fix << "fix ensemble all nvt" << " temp " << Qt::fixed << qSetRealNumberPrecision(2) << m_temperature << " " << Qt::fixed << qSetRealNumberPrecision(2) << m_temperature << " 100 " << "tchain " << m_nhChain << "\n"; return ensembleInput; } case NVE: { QString ensembleInput; QTextStream fix(&ensembleInput); fix << "fix ensemble all nve\n"; return ensembleInput; } default: { QString ensembleInput; QTextStream fix(&ensembleInput); fix << "fix ensemble all nvt" << " temp " << Qt::fixed << qSetRealNumberPrecision(2) << m_temperature << " " << Qt::fixed << qSetRealNumberPrecision(2) << m_temperature << " 100 " << "tchain " << m_nhChain << "\n"; return ensembleInput; } } } QString LammpsInputDialog::getVelocityDist(velocityDist t) { switch (t) { case gaussian: return "gaussian"; case uniform: return "uniform"; default: return "gaussian"; } } QString LammpsInputDialog::getZeroMOM() { if (m_zeroMOM) return "yes"; else return "no"; } QString LammpsInputDialog::getZeroL() { if (m_zeroL) return "yes"; else return "no"; } QString LammpsInputDialog::getThermoStyle(thermoStyle t) { switch (t) { case one: return "one"; case multi: return "multi"; default: return "one"; } } void LammpsInputDialog::deckDirty(bool dirty) { m_dirty = dirty; ui.titleLine->setEnabled(!dirty); // ui.calculationCombo->setEnabled(!dirty); // ui.theoryCombo->setEnabled(!dirty); // ui.basisCombo->setEnabled(!dirty); // ui.multiplicitySpin->setEnabled(!dirty); // ui.chargeSpin->setEnabled(!dirty); ui.enableFormButton->setEnabled(dirty); } void LammpsInputDialog::readSettings(QSettings& settings) { m_savePath = settings.value("lammps/savepath").toString(); } void LammpsInputDialog::writeSettings(QSettings& settings) const { settings.setValue("lammps/savepath", m_savePath); } void LammpsInputDialog::determineAtomTypesSPC(int& hyd, int& oxy) { double ThisMass; QString ThisAtom; // QList atoms = m_molecule->atoms(); for (size_t i = 0; i < m_molecule->atomCount(); ++i) { Core::Atom atom = m_molecule->atom(i); ThisMass = Core::Elements::mass(atom.atomicNumber()); ThisAtom = Core::Elements::symbol(atom.atomicNumber()); AtomMass[ThisAtom] = ThisMass; } int AtomIndex = 0; // Set AtomType integer for (itr = AtomMass.begin(); itr != AtomMass.end(); ++itr) { AtomIndex++; AtomType[itr.key()] = AtomIndex; } // this is on purpose due to the use of // unordered_map in OpenBabel, which // returns a different order for O and H. hyd = AtomType.value("O"); oxy = AtomType.value("H"); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/lammpsinput/lammpsinputdialog.h000066400000000000000000000105711506155467400264520ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef LAMMPSINPUTDIALOG_H #define LAMMPSINPUTDIALOG_H #include "ui_lammpsinputdialog.h" #include #include class QJsonObject; class QTextEdit; namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { class LammpsInputDialog : public QDialog { Q_OBJECT public: explicit LammpsInputDialog(QWidget* parent = nullptr, Qt::WindowFlags flag = Qt::WindowFlags()); ~LammpsInputDialog() override; void readSettings(QSettings&); void writeSettings(QSettings&) const; enum unitType { lj, real, metal, si, cgs, u_electron }; enum dimensionType { d2, d3 }; enum boundaryType { p, s, f, m, fs, fm }; enum atomStyle { angle, atomic, bond, charge, dipole, a_electron, ellipsoid, full, line, meso, molecular, peri, sphere, tri, wavepacket }; enum waterPotential { NONE, SPC, SPCE }; void setMolecule(QtGui::Molecule* molecule); enum ensemble { NVT, NVE }; enum velocityDist { gaussian, uniform }; enum thermoStyle { one, multi }; protected: /** * Reimplemented to update the dialog when it is shown */ void showEvent(QShowEvent* event) override; private: Ui::LammpsInputDialog ui; QtGui::Molecule* m_molecule; // QString m_title; QString m_readData; unitType m_unitType; QString m_title; QString m_savePath; dimensionType m_dimensionType; boundaryType m_xBoundaryType; boundaryType m_yBoundaryType; boundaryType m_zBoundaryType; atomStyle m_atomStyle; waterPotential m_waterPotential; // coordType m_coordType; ensemble m_ensemble; double m_temperature; int m_nhChain; double m_timeStep; int m_runSteps; int m_xReplicate; int m_yReplicate; int m_zReplicate; QString m_dumpXYZ; int m_dumpStep; velocityDist m_velocityDist; double m_velocityTemp; bool m_zeroMOM; bool m_zeroL; thermoStyle m_thermoStyle; int m_thermoInterval; QString m_output; bool m_dirty; bool m_warned; bool readData; QTextEdit* m_jobEdit; QTextEdit* m_moleculeEdit; QString m_moleculeFileName; QString m_jobFileName; // Generate an input deck as a string QString generateInputDeck(); QString getUnitType(unitType t); QString getAtomStyle(atomStyle t); QString getDimensionType(dimensionType t); QString getXBoundaryType(boundaryType t); QString getYBoundaryType(boundaryType t); QString getZBoundaryType(boundaryType t); QString getWaterPotential(waterPotential t); QString getEnsemble(ensemble t); QString getVelocityDist(velocityDist t); QString getZeroMOM(); QString getZeroL(); QString getThermoStyle(thermoStyle t); // Translate enums to strings // QString getCalculationType(calculationType t); // QString getWavefunction(void); // QString getTheoryType(theoryType t); // QString getBasisType(basisType t); // Enable/disable form elements void deckDirty(bool); void determineAtomTypesSPC(int& hyd, int& oxy); void addMoleculeDataTab(); // system typing QHash AtomType; QHash AtomMass; QHash::iterator itr; public Q_SLOTS: void updatePreviewText(); private Q_SLOTS: //! Button Slots void textEditModified(); void resetClicked(); void generateClicked(); void enableFormClicked(); void setTitle(); void setReadData(); void setUnits(int); void setAtomStyle(int); void setDimensionType(int); void setXBoundaryType(int); void setYBoundaryType(int); void setZBoundaryType(int); void setWaterPotential(int); void setEnsemble(int); void setTemperature(double); void setNHChain(int); void setTimeStep(double); void setRunSteps(int); void setXReplicate(int); void setYReplicate(int); void setZReplicate(int); void setDumpXYZ(); void setDumpStep(int); void setVelocityDist(int); void setVelocityTemp(double); void setZeroMOM(bool); void setZeroL(bool); void setThermoStyle(int); void setThermoInterval(int); }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/lammpsinput/lammpsinputdialog.ui000066400000000000000000000725741506155467400266530ustar00rootroot00000000000000 LammpsInputDialog 0 0 774 697 0 0 LAMMPS Input true QLayout::SetNoConstraint QLayout::SetDefaultConstraint Input file comments Title: titleLine Input file comments Title Filename Base: job Select the unit style to be used during the simulation. <!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" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; 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;"><a href="http://lammps.sandia.gov/doc/units.html"><span style=" text-decoration: underline; color:#0057ae;">http://lammps.sandia.gov/doc/units.html</span></a></p></body></html> Units unitsCombo Select the unit style to be used during the simulation. <!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" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; 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;"><a href="http://lammps.sandia.gov/doc/units.html"><span style=" text-decoration: underline; color:#0057ae;">http://lammps.sandia.gov/doc/units.html</span></a></p></body></html> 1 lj real metal si cgs electron Water Potential NONE SPC SPC/E Qt::Horizontal Select atom_style used by the data file. <!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" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; 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;"><a href="http://lammps.sandia.gov/doc/atom_style.html"><span style=" text-decoration: underline; color:#0057ae;">http://lammps.sandia.gov/doc/atom_style.html</span></a></p></body></html> Atom Style atomStyleCombo Select atom_style used by the data file. <!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" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; 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;"><a href="http://lammps.sandia.gov/doc/atom_style.html"><span style=" text-decoration: underline; color:#0057ae;">http://lammps.sandia.gov/doc/atom_style.html</span></a></p></body></html> 7 angle atomic bond charge dipole electron ellipsoid full line meso molecular peri sphere tri wavepacket Specify the name to be used for the coordinate file. Coordinate Data File Specify the name to be used for the coordinate file. Ensemble NVT NVE Temperature 2 20000.000000000000000 298.149999999999977 Select the number of Nosé-Hoover chains in the NVT ensemble. NH Chains Select the number of Nosé-Hoover chains in the NVT ensemble. 0 1 Qt::Horizontal 40 20 1 Qt::Horizontal Time step for the simulation in units according to "Units" specification. Time Step Time step for the simulation in units according to "Units" specification. 0.500000000000000 2.000000000000000 Filename of the XYZ file to write during the simulation. Dump XYZ Qt::Horizontal Number of dimensions in the system. Dimensions dimensionCombo Change Z boundary style. 0 p s f m fs fm Change Y boundary style. 0 p s f m fs fm Change X boundary style. 0 p s f m fs fm Select boundary Styles in X, Y and Z directions. Boundary Qt::Horizontal 40 20 Number of replicants in X, Y and Z directions. Replicate Qt::Horizontal 40 20 Replicate the X direction. 1 Replicate the Y direction. 1 Replicate the Z direction. 1 Filename of the XYZ file to write during the simulation. Total number of timesteps to run the simulation. Total Steps Total number of timesteps to run the simulation. 1000000000 50 Qt::Horizontal 40 20 Dump Interval 10000 1 Qt::Horizontal 40 20 Set the initial atom velocities for the simulation. Initial Velocities Select the distribution of initial atom velocities. gaussian uniform Set the initial atom velocities to match this temperature. Temperature Set the initial atom velocities to match this temperature. 20000.000000000000000 0.500000000000000 298.149999999999977 Remove system linear momentum from initial velocities. Zero Linear Momentum true Remove system angular momentum from initial velocities. Zero Angular Momentum true Qt::Horizontal 40 20 Control the thermodynamic output during the simulation. Output Output Interval 10000 50 Qt::Horizontal 40 20 Number of dimensions in the system. 1 2d 3d Thermodynamic output style. One Line Multi Line Qt::Horizontal 40 20 0 0 QLayout::SetFixedSize Reset false Use Form Qt::Horizontal 48 26 Generate… Close titleLine moreButton previewText generateButton closeButton resetButton enableFormButton closeButton clicked() LammpsInputDialog close() 451 411 258 243 avogadrolibs-1.101.0/avogadro/qtplugins/licorice/000077500000000000000000000000001506155467400217645ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/licorice/CMakeLists.txt000066400000000000000000000002571506155467400245300ustar00rootroot00000000000000avogadro_plugin(Licorice "Licorice rendering scheme" ScenePlugin licorice.h Licorice licorice.cpp "") target_link_libraries(Licorice PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/licorice/licorice.cpp000066400000000000000000000141471506155467400242700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "licorice.h" #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using QtGui::Molecule; using QtGui::PluginLayerManager; using Rendering::CylinderGeometry; using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::SphereGeometry; struct LayerLicorice : Core::LayerData { QWidget* widget; float opacity; LayerLicorice() { widget = nullptr; QSettings settings; opacity = settings.value("licorice/opacity", 1.0).toFloat(); } LayerLicorice(std::string settings) { widget = nullptr; deserialize(settings); } LayerData* clone() final { return new LayerLicorice(*this); } ~LayerLicorice() override { if (widget) widget->deleteLater(); } std::string serialize() final { return std::to_string(opacity); } void deserialize(std::string text) final { std::stringstream ss(text); std::string aux; ss >> aux; opacity = std::stof(aux); } void setupWidget(Licorice* slot) { if (!widget) { widget = new QWidget(qobject_cast(slot->parent())); auto* form = new QFormLayout; // Opacity auto* slider = new QSlider(Qt::Horizontal); slider->setRange(0, 100); slider->setTickInterval(1); slider->setValue(round(opacity * 100)); QObject::connect(slider, &QSlider::valueChanged, slot, &Licorice::setOpacity); form->addRow(QObject::tr("Opacity:"), slider); widget->setLayout(form); } } }; Licorice::Licorice(QObject* p) : ScenePlugin(p) { m_layerManager = PluginLayerManager(m_name); } Licorice::~Licorice() {} void Licorice::process(const Molecule& molecule, Rendering::GroupNode& node) { m_layerManager.load(); // Use a common radius for all spheres and cylinders. float radius(0.2f); float selectedRadius(radius * 2.0f); // Add a sphere node to contain all of the spheres. auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; spheres->identifier().molecule = &molecule; spheres->identifier().type = Rendering::AtomType; auto selectedSpheres = new SphereGeometry; selectedSpheres->setOpacity(0.42); auto translucentSpheres = new SphereGeometry; translucentSpheres->setRenderPass(Rendering::TranslucentPass); translucentSpheres->identifier().molecule = &molecule; translucentSpheres->identifier().type = Rendering::AtomType; geometry->addDrawable(spheres); geometry->addDrawable(selectedSpheres); geometry->addDrawable(translucentSpheres); float opacity = 1.0; for (Index i = 0; i < molecule.atomCount(); ++i) { Core::Atom atom = molecule.atom(i); if (!m_layerManager.atomEnabled(i)) { continue; } Vector3ub color = atom.color(); auto* interface = m_layerManager.getSetting(m_layerManager.getLayerID(i)); opacity = interface->opacity; if (opacity < 1.0f) { translucentSpheres->addSphere(atom.position3d().cast(), color, radius, i); translucentSpheres->setOpacity(opacity); } else spheres->addSphere(atom.position3d().cast(), color, radius, i); if (atom.selected()) { color = Vector3ub(0, 0, 255); selectedSpheres->addSphere(atom.position3d().cast(), color, selectedRadius, i); } } auto* cylinders = new CylinderGeometry; cylinders->identifier().molecule = &molecule; cylinders->identifier().type = Rendering::BondType; auto* translucentCylinders = new CylinderGeometry; translucentCylinders->setRenderPass(Rendering::TranslucentPass); translucentCylinders->identifier().molecule = &molecule; translucentCylinders->identifier().type = Rendering::BondType; geometry->addDrawable(cylinders); geometry->addDrawable(translucentCylinders); for (Index i = 0; i < molecule.bondCount(); ++i) { Core::Bond bond = molecule.bond(i); if (!m_layerManager.bondEnabled(bond.atom1().index(), bond.atom2().index())) { continue; } auto* interface1 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom1().index())); auto* interface2 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom2().index())); bool doOpaque = true; if (interface1->opacity < 1.0f || interface2->opacity < 1.0f) { opacity = std::min(interface1->opacity, interface2->opacity); translucentCylinders->setOpacity(opacity); doOpaque = false; } Vector3f pos1 = bond.atom1().position3d().cast(); Vector3f pos2 = bond.atom2().position3d().cast(); Vector3ub color1 = bond.atom1().color(); Vector3ub color2 = bond.atom2().color(); Vector3f bondVector = pos2 - pos1; float bondLength = bondVector.norm(); bondVector /= bondLength; if (doOpaque) cylinders->addCylinder(pos1, pos2, radius, color1, color2, i); else translucentCylinders->addCylinder(pos1, pos2, radius, color1, color2, i); } } void Licorice::setOpacity(int opacity) { m_opacity = static_cast(opacity) / 100.0f; auto* interface = m_layerManager.getSetting(); if (m_opacity != interface->opacity) { interface->opacity = m_opacity; emit drawablesChanged(); } QSettings settings; settings.setValue("licorice/opacity", m_opacity); } QWidget* Licorice::setupWidget() { auto* interface = m_layerManager.getSetting(); interface->setupWidget(this); return interface->widget; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/licorice/licorice.h000066400000000000000000000026171506155467400237340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_LICORICE_H #define AVOGADRO_QTPLUGINS_LICORICE_H #include namespace Avogadro { namespace QtPlugins { /** * @brief Render a molecule in the licorice style. * @author Marcus D. Hanwell */ class Licorice : public QtGui::ScenePlugin { Q_OBJECT public: explicit Licorice(QObject* parent = nullptr); ~Licorice() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Licorice", "stick / licorice rendering"); } QString description() const override { return tr("Render atoms as licorice / sticks."); } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } public slots: void setOpacity(int opacity); private: std::string m_name = "Licorice"; QWidget* m_setupWidget = nullptr; float m_opacity = 1.0; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_LICORICE_H avogadrolibs-1.101.0/avogadro/qtplugins/lineformatinput/000077500000000000000000000000001506155467400234135ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/lineformatinput/CMakeLists.txt000066400000000000000000000003241506155467400261520ustar00rootroot00000000000000avogadro_plugin(LineFormatInput "Enter line formats in a dialog window." ExtensionPlugin lineformatinput.h LineFormatInput "lineformatinput.cpp;lineformatinputdialog.cpp" "lineformatinputdialog.ui" ) avogadrolibs-1.101.0/avogadro/qtplugins/lineformatinput/lineformatinput.cpp000066400000000000000000000102011506155467400273310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "lineformatinput.h" #include "lineformatinputdialog.h" #include #include #include #include #include #include #include #include using Avogadro::Io::FileFormat; using Avogadro::Io::FileFormatManager; using Avogadro::QtGui::FileFormatDialog; using namespace std::string_literals; namespace Avogadro::QtPlugins { LineFormatInput::LineFormatInput(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_reader(nullptr) { auto* action = new QAction(tr("SMILES…"), this); action->setProperty("menu priority", 800); action->setData("SMILES"); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); action = new QAction(tr("InChI…"), this); action->setProperty("menu priority", 810); action->setData("InChI"); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); // These are the line formats that we can load -- key is a user-friendly name, // value is the file extension used to identify the file format. m_formats.insert(tr("InChI"), "inchi"s); m_formats.insert(tr("SMILES"), "smi"s); } LineFormatInput::~LineFormatInput() { delete m_reader; } QList LineFormatInput::actions() const { return m_actions; } QStringList LineFormatInput::menuPath(QAction*) const { return QStringList() << tr("&Build") << tr("&Insert"); } void LineFormatInput::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void LineFormatInput::showDialog() { if (!m_molecule) return; QWidget* parentAsWidget = qobject_cast(parent()); auto* theSender = qobject_cast(sender()); // Create a list of file formats that we can read: QStringList availableFormats; FileFormatManager& ffm = FileFormatManager::instance(); const FileFormat::Operations ops = FileFormat::Read | FileFormat::String; foreach (const QString& ident, m_formats.keys()) { const std::string& ext = m_formats[ident]; if (!ffm.fileFormatsFromFileExtension(ext, ops).empty()) availableFormats.push_back(ident); } if (availableFormats.empty()) { QMessageBox::warning(parentAsWidget, tr("No descriptors found!"), tr("No line format readers found!"), QMessageBox::Ok); return; } // Prompt user for input: LineFormatInputDialog dlg; dlg.setFormats(availableFormats); if (theSender != nullptr) dlg.setCurrentFormat(theSender->data().toString()); dlg.exec(); // check if the reply is empty if (dlg.result() != LineFormatInputDialog::Accepted || dlg.descriptor().isEmpty()) return; // nothing to do // Resolve any format conflicts: const std::string& ext = m_formats[dlg.format()]; const FileFormat* fmt = FileFormatDialog::findFileFormat( parentAsWidget, tr("Insert Molecule…"), QString("file.%1").arg(QString::fromStdString(ext)), ops); if (fmt == nullptr) { QMessageBox::warning(parentAsWidget, tr("No descriptors found!"), tr("Unable to load requested format reader."), QMessageBox::Ok); return; } m_reader = fmt->newInstance(); m_descriptor = dlg.descriptor().toStdString(); QProgressDialog progDlg(parentAsWidget); progDlg.setModal(true); progDlg.setWindowTitle(tr("Insert Molecule…")); progDlg.setLabelText(tr("Generating 3D molecule…")); progDlg.setRange(0, 0); progDlg.setValue(0); progDlg.show(); QtGui::Molecule newMol; m_reader->readString(m_descriptor, newMol); m_molecule->undoMolecule()->appendMolecule(newMol, "Insert Molecule"); emit requestActiveTool("Manipulator"); dlg.hide(); m_descriptor.clear(); delete m_reader; m_reader = nullptr; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/lineformatinput/lineformatinput.h000066400000000000000000000027751506155467400270170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_LINEFORMATINPUT_H #define AVOGADRO_QTPLUGINS_LINEFORMATINPUT_H #include #include #include namespace Avogadro { namespace Io { class FileFormat; } namespace QtPlugins { /** * @brief Load single-line molecule descriptors through an input dialog. */ class LineFormatInput : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit LineFormatInput(QObject* parent_ = nullptr); ~LineFormatInput() override; QString name() const override { return tr("LineFormatInput"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule*) override; private slots: void showDialog(); private: QList m_actions; /// Maps identifier to extension: QMap m_formats; QtGui::Molecule* m_molecule; Io::FileFormat* m_reader; std::string m_descriptor; }; inline QString LineFormatInput::description() const { return tr("Load single-line molecule descriptors through an input dialog."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_LINEFORMATINPUT_H avogadrolibs-1.101.0/avogadro/qtplugins/lineformatinput/lineformatinputdialog.cpp000066400000000000000000000035471506155467400305300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "lineformatinputdialog.h" #include "ui_lineformatinputdialog.h" #include namespace Avogadro::QtPlugins { LineFormatInputDialog::LineFormatInputDialog(QWidget* aParent) : QDialog(aParent), m_ui(new Ui::LineFormatInputDialog) { m_ui->setupUi(this); QSettings settings; // set the last used descriptor QString lastDescriptor = settings.value("lineformatinput/lastDescriptor").toString(); setDescriptor(lastDescriptor); } LineFormatInputDialog::~LineFormatInputDialog() { delete m_ui; } void LineFormatInputDialog::setFormats(const QStringList& indents) { m_ui->formats->clear(); m_ui->formats->addItems(indents); QSettings settings; QString lastUsed = settings.value("lineformatinput/lastUsed").toString(); int index = m_ui->formats->findText(lastUsed); if (index >= 0) m_ui->formats->setCurrentIndex(index); } QString LineFormatInputDialog::format() const { return m_ui->formats->currentText(); } void LineFormatInputDialog::setCurrentFormat(const QString& format) { int index = m_ui->formats->findText(format); if (index >= 0) m_ui->formats->setCurrentIndex(index); } void LineFormatInputDialog::setDescriptor(const QString& descriptor) { m_ui->descriptor->setText(descriptor); } QString LineFormatInputDialog::descriptor() const { return m_ui->descriptor->text(); } void LineFormatInputDialog::accept() { QSettings settings; settings.setValue("lineformatinput/lastUsed", format()); settings.setValue("lineformatinput/lastDescriptor", descriptor()); QDialog::accept(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/lineformatinput/lineformatinputdialog.h000066400000000000000000000021741506155467400301700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_LINEFORMATINPUTDIALOG_H #define AVOGADRO_QTPLUGINS_LINEFORMATINPUTDIALOG_H #include namespace Avogadro { namespace QtPlugins { namespace Ui { class LineFormatInputDialog; } /** * @brief Dialog to prompt a format and descriptor string. */ class LineFormatInputDialog : public QDialog { Q_OBJECT public: explicit LineFormatInputDialog(QWidget* parent = nullptr); ~LineFormatInputDialog() override; void setFormats(const QStringList& indents); QString format() const; void setCurrentFormat(const QString& format); void setDescriptor(const QString& descriptor); QString descriptor() const; protected slots: void accept() override; private: Ui::LineFormatInputDialog* m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_LINEFORMATINPUTDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/lineformatinput/lineformatinputdialog.ui000066400000000000000000000061721506155467400303600ustar00rootroot00000000000000 Avogadro::QtPlugins::LineFormatInputDialog 0 0 269 123 0 0 Insert Molecule… QFormLayout::ExpandingFieldsGrow Format: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Descriptor: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() Avogadro::QtPlugins::LineFormatInputDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::QtPlugins::LineFormatInputDialog reject() 316 260 286 274 avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/000077500000000000000000000000001506155467400225265ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/CMakeLists.txt000066400000000000000000000005361506155467400252720ustar00rootroot00000000000000set(manipulator_srcs manipulator.cpp ) set(manipulator_uis manipulatewidget.ui ) set(manipulator_rcs manipulator.qrc ) avogadro_plugin(Manipulator "Manipulator" ToolPlugin manipulator.h Manipulator "${manipulator_srcs}" "${manipulator_uis}" "${manipulator_rcs}" ) target_link_libraries(Manipulator PRIVATE Avogadro::QtOpenGL) avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/manipulatewidget.ui000066400000000000000000000162551506155467400264410ustar00rootroot00000000000000 ManipulateWidget 0 0 371 292 Form Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 4 -999.990000000000009 999.990000000000009 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 4 -999.990000000000009 999.990000000000009 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 4 -999.990000000000009 999.990000000000009 X Y Z Translate by: 0 Rotate around: Origin Center of Molecule Center of Selection Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter ° -360.000000000000000 360.000000000000000 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter ° -360.000000000000000 360.000000000000000 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter ° -360.000000000000000 360.000000000000000 X-axis Y-axis Z-axis 0 Move: Selected Atoms Everything Else QDialogButtonBox::Apply|QDialogButtonBox::Reset true Qt::Vertical 20 262 avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/manipulator.cpp000066400000000000000000000301301506155467400255620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "manipulator.h" #include "ui_manipulatewidget.h" #include #include #include #include #include #include #include #include #include #include #include using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { using QtGui::Molecule; using QtGui::RWAtom; #define ROTATION_SPEED 0.5 class ManipulateWidget : public QWidget, public Ui::ManipulateWidget { public: ManipulateWidget(QWidget* parent = nullptr) : QWidget(parent) { setupUi(this); } }; Manipulator::Manipulator(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_renderer(nullptr), m_pressedButtons(Qt::NoButton), m_toolWidget(new ManipulateWidget(dynamic_cast(parent_))), m_currentAction(Nothing) { QString shortcut = tr("Ctrl+6", "control-key 6"); m_activateAction->setText(tr("Manipulate")); m_activateAction->setToolTip( tr("Manipulation Tool\t(%1)\n\n" "Left Mouse:\tClick and drag to move atoms\n" "Right Mouse:\tClick and drag to rotate atoms.") .arg(shortcut)); setIcon(); connect(m_toolWidget->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); } Manipulator::~Manipulator() {} void Manipulator::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/manipulator_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/manipulator_light.svg")); } QWidget* Manipulator::toolWidget() const { return m_toolWidget; } void Manipulator::buttonClicked(QAbstractButton* button) { if (m_toolWidget == nullptr) return; // clear focus from the boxes (they eat up keystrokes) m_toolWidget->xTranslateSpinBox->clearFocus(); m_toolWidget->yTranslateSpinBox->clearFocus(); m_toolWidget->zTranslateSpinBox->clearFocus(); m_toolWidget->xRotateSpinBox->clearFocus(); m_toolWidget->yRotateSpinBox->clearFocus(); m_toolWidget->zRotateSpinBox->clearFocus(); if (m_toolWidget->buttonBox->buttonRole(button) != QDialogButtonBox::ApplyRole) { // reset values m_toolWidget->xTranslateSpinBox->setValue(0.0); m_toolWidget->yTranslateSpinBox->setValue(0.0); m_toolWidget->zTranslateSpinBox->setValue(0.0); m_toolWidget->xRotateSpinBox->setValue(0.0); m_toolWidget->yRotateSpinBox->setValue(0.0); m_toolWidget->zRotateSpinBox->setValue(0.0); return; } bool moveSelected = (m_toolWidget->moveComboBox->currentIndex() == 0); // apply values Vector3 delta(m_toolWidget->xTranslateSpinBox->value(), m_toolWidget->yTranslateSpinBox->value(), m_toolWidget->zTranslateSpinBox->value()); translate(delta, moveSelected); Vector3 rotation(m_toolWidget->xRotateSpinBox->value(), m_toolWidget->yRotateSpinBox->value(), m_toolWidget->zRotateSpinBox->value()); Vector3 center(0.0, 0.0, 0.0); // Check if we're rotating around the origin, the molecule centroid // or the center of selected atoms // == 0 is the default = origin if (m_toolWidget->rotateComboBox->currentIndex() == 1) { // molecule centroid center = m_molecule->molecule().centerOfGeometry(); } else if (m_toolWidget->rotateComboBox->currentIndex() == 2) { // center of selected atoms unsigned long selectedAtomCount = 0; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!m_molecule->atomSelected(i)) continue; center += m_molecule->atomPosition3d(i); selectedAtomCount++; } if (selectedAtomCount > 0) center /= selectedAtomCount; } // Settings are in degrees #ifndef DEG_TO_RAD #define DEG_TO_RAD 0.0174532925 #endif axisRotate(rotation * DEG_TO_RAD, center, moveSelected); m_molecule->emitChanged(Molecule::Atoms | Molecule::Modified); } QUndoCommand* Manipulator::keyPressEvent(QKeyEvent* e) { switch (e->key()) { case Qt::Key_Left: case Qt::Key_H: case Qt::Key_A: translate(Vector3(-0.1, 0.0, 0.0)); e->accept(); break; case Qt::Key_Right: case Qt::Key_L: case Qt::Key_D: translate(Vector3(+0.1, 0.0, 0.0)); e->accept(); break; case Qt::Key_Up: case Qt::Key_K: case Qt::Key_W: translate(Vector3(0.0, +0.1, 0.0)); e->accept(); break; case Qt::Key_Down: case Qt::Key_J: case Qt::Key_S: translate(Vector3(0.0, -0.1, 0.0)); e->accept(); break; default: e->ignore(); } m_molecule->emitChanged(Molecule::Atoms | Molecule::Modified); return nullptr; } QUndoCommand* Manipulator::mousePressEvent(QMouseEvent* e) { if (!m_renderer) return nullptr; updatePressedButtons(e, false); m_lastMousePosition = e->pos(); Vector2f windowPos(e->localPos().x(), e->localPos().y()); m_lastMouse3D = m_renderer->camera().unProject(windowPos); if (m_molecule) { m_molecule->setInteractive(true); } if (m_pressedButtons & Qt::LeftButton) { m_object = m_renderer->hit(e->pos().x(), e->pos().y()); switch (m_object.type) { case Rendering::AtomType: e->accept(); return nullptr; default: break; } } return nullptr; } QUndoCommand* Manipulator::mouseReleaseEvent(QMouseEvent* e) { if (!m_renderer) return nullptr; updatePressedButtons(e, true); if (m_object.type == Rendering::InvalidType) return nullptr; if (m_molecule) { m_molecule->setInteractive(false); } switch (e->button()) { case Qt::LeftButton: case Qt::RightButton: resetObject(); e->accept(); break; default: break; } return nullptr; } QUndoCommand* Manipulator::mouseMoveEvent(QMouseEvent* e) { // if we're dragging through empty space, just return and ignore // (e.g., fall back to the navigate tool) const Core::Molecule* mol = &m_molecule->molecule(); if (mol->isSelectionEmpty() && m_object.type == Rendering::InvalidType) { e->ignore(); return nullptr; } updatePressedButtons(e, false); e->ignore(); Vector2f windowPos(e->localPos().x(), e->localPos().y()); if (mol->isSelectionEmpty() && m_object.type == Rendering::AtomType && m_object.molecule == &m_molecule->molecule()) { // translate single atom position RWAtom atom = m_molecule->atom(m_object.index); Vector3f oldPos(atom.position3d().cast()); Vector3f newPos = m_renderer->camera().unProject(windowPos, oldPos); atom.setPosition3d(newPos.cast()); } else if (!mol->isSelectionEmpty()) { // update all selected atoms Vector3f newPos = m_renderer->camera().unProject(windowPos); Vector3 delta = (newPos - m_lastMouse3D).cast(); if (m_currentAction == Translation) { translate(delta); } else { // get the center of the selected atoms Vector3 centroid(0.0, 0.0, 0.0); unsigned long selectedAtomCount = 0; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!m_molecule->atomSelected(i)) continue; centroid += m_molecule->atomPosition3d(i); selectedAtomCount++; } if (selectedAtomCount > 0) centroid /= selectedAtomCount; if (m_currentAction == Rotation) { rotate(delta, centroid); } else if (m_currentAction == ZoomTilt) { tilt(delta, centroid); } } // now that we've moved things, save the position m_lastMouse3D = newPos; } m_molecule->emitChanged(Molecule::Atoms | Molecule::Modified); e->accept(); return nullptr; } void Manipulator::translate(Vector3 delta, bool moveSelected) { for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (moveSelected && !m_molecule->atomSelected(i)) continue; else if (!moveSelected && m_molecule->atomSelected(i)) continue; Vector3 currentPos = m_molecule->atomPosition3d(i); m_molecule->setAtomPosition3d(i, currentPos + delta.cast()); } } void Manipulator::rotate(Vector3 delta, Vector3 centroid, bool moveSelected) { // Rotate the selected atoms about the center // rotate only selected primitives Rendering::Camera* camera = &m_renderer->camera(); Eigen::Vector3d backTransformX = camera->modelView().cast().linear().row(0).transpose().normalized(); Eigen::Vector3d backTransformY = camera->modelView().cast().linear().row(1).transpose().normalized(); Eigen::Projective3d fragmentRotation; fragmentRotation.matrix().setIdentity(); fragmentRotation.translation() = centroid; fragmentRotation.rotate( Eigen::AngleAxisd(delta[1] * ROTATION_SPEED, backTransformX)); fragmentRotation.rotate( Eigen::AngleAxisd(delta[0] * ROTATION_SPEED, backTransformY)); fragmentRotation.translate(-centroid); for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (moveSelected && !m_molecule->atomSelected(i)) continue; else if (!moveSelected && m_molecule->atomSelected(i)) continue; Vector3 currentPos = m_molecule->atomPosition3d(i); m_molecule->setAtomPosition3d( i, (fragmentRotation * currentPos.homogeneous()).head<3>()); } } void Manipulator::axisRotate(Vector3 delta, Vector3 centroid, bool moveSelected) { // rotate by the x, y, z axes by delta[0], delta[1], delta[2] // (in radians) for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (moveSelected && !m_molecule->atomSelected(i)) continue; else if (!moveSelected && m_molecule->atomSelected(i)) continue; Vector3 currentPos = m_molecule->atomPosition3d(i); Eigen::Projective3d fragmentRotation; fragmentRotation.matrix().setIdentity(); fragmentRotation.translation() = centroid; // Rotate around the x-axis fragmentRotation.rotate( Eigen::AngleAxisd(delta[0], Vector3(1.0, 0.0, 0.0))); // Rotate around the y-axis fragmentRotation.rotate( Eigen::AngleAxisd(delta[1], Vector3(0.0, 1.0, 0.0))); // Rotate around the z-axis fragmentRotation.rotate( Eigen::AngleAxisd(delta[2], Vector3(0.0, 0.0, 1.0))); fragmentRotation.translate(-centroid); m_molecule->setAtomPosition3d( i, (fragmentRotation * currentPos.homogeneous()).head<3>()); } } void Manipulator::tilt(Vector3 delta, Vector3 centroid) { // Rotate the selected atoms about the center // rotate only selected primitives Rendering::Camera* camera = &m_renderer->camera(); Eigen::Vector3d backTransformZ = camera->modelView().cast().linear().row(2).transpose().normalized(); Eigen::Projective3d fragmentRotation; fragmentRotation.matrix().setIdentity(); fragmentRotation.translation() = centroid; fragmentRotation.rotate( Eigen::AngleAxisd(delta[0] * ROTATION_SPEED, backTransformZ)); fragmentRotation.translate(-centroid); for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!m_molecule->atomSelected(i)) continue; Vector3 currentPos = m_molecule->atomPosition3d(i); m_molecule->setAtomPosition3d( i, (fragmentRotation * currentPos.homogeneous()).head<3>()); } } void Manipulator::updatePressedButtons(QMouseEvent* e, bool release) { if (release) m_pressedButtons &= e->buttons(); else m_pressedButtons |= e->buttons(); // check for modifier keys (e.g., Mac) if (e->buttons() & Qt::LeftButton && e->modifiers() == Qt::NoModifier) { m_currentAction = Translation; } else if (e->buttons() & Qt::MiddleButton || (e->buttons() & Qt::LeftButton && e->modifiers() == Qt::ShiftModifier)) { m_currentAction = ZoomTilt; } else if (e->buttons() & Qt::RightButton || (e->buttons() & Qt::LeftButton && (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::MetaModifier))) { m_currentAction = Rotation; } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/manipulator.h000066400000000000000000000055741506155467400252450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_MANIPULATOR_H #define AVOGADRO_QTPLUGINS_MANIPULATOR_H #include #include #include #include #include // for Qt:: namespace #include namespace Avogadro { namespace QtPlugins { class ManipulateWidget; /** * @class Manipulator manipulator.h * * @brief The Manipulator class manipulates a molecule's geometry. * @author Allison Vacanti */ class Manipulator : public QtGui::ToolPlugin { Q_OBJECT public: explicit Manipulator(QObject* parent_ = nullptr); ~Manipulator() override; QString name() const override { return tr("Manipulate tool"); } QString description() const override { return tr("Manipulate tool"); } unsigned char priority() const override { return 30; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule* mol) override { if (mol) m_molecule = mol->undoMolecule(); } void setEditMolecule(QtGui::RWMolecule* mol) override { m_molecule = mol; } void setGLRenderer(Rendering::GLRenderer* renderer) override { m_renderer = renderer; } QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseMoveEvent(QMouseEvent* e) override; QUndoCommand* keyPressEvent(QKeyEvent* e) override; public slots: void buttonClicked(QAbstractButton* button); private: /** * Update the currently pressed buttons, accounting for modifier keys. * \todo Account for modifier keys. */ void updatePressedButtons(QMouseEvent*, bool release); void resetObject() { m_object = Rendering::Identifier(); } void translate(Vector3 delta, bool moveSelected = true); void rotate(Vector3 delta, Vector3 centroid, bool moveSelected = true); void axisRotate(Vector3 delta, Vector3 centroid, bool moveSelected = true); void tilt(Vector3 delta, Vector3 centroid); QAction* m_activateAction; QtGui::RWMolecule* m_molecule; Rendering::GLRenderer* m_renderer; Rendering::Identifier m_object; QPoint m_lastMousePosition; Vector3f m_lastMouse3D; Qt::MouseButtons m_pressedButtons; ManipulateWidget* m_toolWidget; enum ToolAction { Nothing = 0, Rotation, Translation, ZoomTilt, Zoom }; ToolAction m_currentAction; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTOPENGL_MANIPULATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/manipulator.qrc000066400000000000000000000002231506155467400255650ustar00rootroot00000000000000 manipulator_light.svg manipulator_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/manipulator_dark.svg000066400000000000000000000030331506155467400266020ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/manipulator/manipulator_light.svg000066400000000000000000000071431506155467400267760ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/measuretool/000077500000000000000000000000001506155467400225325ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/measuretool/CMakeLists.txt000066400000000000000000000005111506155467400252670ustar00rootroot00000000000000set(measuretool_srcs measuretool.cpp ) set(measuretool_uis ) set(measuretool_rcs measuretool.qrc ) avogadro_plugin(MeasureTool "Measure tool" ToolPlugin measuretool.h MeasureTool "${measuretool_srcs}" "${measuretool_uis}" "${measuretool_rcs}" ) target_link_libraries(MeasureTool PRIVATE Avogadro::QtOpenGL) avogadrolibs-1.101.0/avogadro/qtplugins/measuretool/measure_dark.svg000066400000000000000000000071251506155467400257220ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/measuretool/measure_light.svg000066400000000000000000000070611506155467400261070ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/measuretool/measuretool.cpp000066400000000000000000000174611506155467400256060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "measuretool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Core::contrastColor; using Avogadro::Core::Elements; using Avogadro::Rendering::GeometryNode; using Avogadro::Rendering::GroupNode; using Avogadro::Rendering::Identifier; using Avogadro::Rendering::TextLabel2D; using Avogadro::Rendering::TextLabel3D; using Avogadro::Rendering::TextProperties; namespace Avogadro::QtPlugins { MeasureTool::MeasureTool(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_rwMolecule(nullptr), m_renderer(nullptr) { QString shortcut = tr("Ctrl+8", "control-key 8"); m_activateAction->setText(tr("Measure")); m_activateAction->setToolTip( tr("Measure Tool\t(%1)\n\n" "Left Mouse:\tSelect up to four Atoms.\n" "\tDistances are measured between 1-2 and 2-3\n" "\tAngle is measured between 1-3 using 2 as the common point\n" "\tDihedral is measured between 1-2-3-4\n" "Right Mouse:\tReset the measurements.") .arg(shortcut)); setIcon(); } MeasureTool::~MeasureTool() {} void MeasureTool::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/measure_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/measure_light.svg")); } QWidget* MeasureTool::toolWidget() const { return nullptr; } QUndoCommand* MeasureTool::mousePressEvent(QMouseEvent* e) { if (e->button() != Qt::LeftButton || !m_renderer) return nullptr; Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y()); // If an atom is clicked, accept the event, but don't add it to the atom list // until the button is released (this way the user can cancel the click by // moving off the atom, and the click won't get passed to the default tool). if (hit.type == Rendering::AtomType) e->accept(); return nullptr; } QUndoCommand* MeasureTool::mouseReleaseEvent(QMouseEvent* e) { // If the click is released on an atom, add it to the list if (e->button() != Qt::LeftButton || !m_renderer) return nullptr; Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y()); // Now add the atom on release. if (hit.type == Rendering::AtomType) { if (toggleAtom(hit)) emit drawablesChanged(); e->accept(); } return nullptr; } QUndoCommand* MeasureTool::mouseDoubleClickEvent(QMouseEvent* e) { // Reset the atom list if (e->button() == Qt::LeftButton && !m_atoms.isEmpty()) { m_atoms.clear(); emit drawablesChanged(); e->accept(); } return nullptr; } template void MeasureTool::createLabels(T* mol, GeometryNode* geo, QVector& positions) { TextProperties atomLabelProp; atomLabelProp.setFontFamily(TextProperties::SansSerif); atomLabelProp.setAlign(TextProperties::HCenter, TextProperties::VCenter); for (int i = 0; i < m_atoms.size(); ++i) { Identifier& ident = m_atoms[i]; Q_ASSERT(ident.type == Rendering::AtomType); Q_ASSERT(ident.molecule != nullptr); typename T::AtomType atom = mol->atom(ident.index); Q_ASSERT(atom.isValid()); unsigned char atomicNumber(atom.atomicNumber()); positions[i] = atom.position3d(); const unsigned char* color = Elements::color(atomicNumber); atomLabelProp.setColorRgb(contrastColor(Vector3ub(color)).data()); auto* label = new TextLabel3D; label->setText(QString("#%1").arg(i + 1).toStdString()); label->setTextProperties(atomLabelProp); label->setAnchor(positions[i].cast()); label->setRadius( static_cast(Elements::radiusCovalent(atomicNumber)) + 0.1f); geo->addDrawable(label); } } void MeasureTool::draw(Rendering::GroupNode& node) { if (m_atoms.size() == 0) return; auto* geo = new GeometryNode; node.addChild(geo); // Add labels, extract positions QVector positions(m_atoms.size(), Vector3()); if (m_molecule) createLabels(m_molecule, geo, positions); else if (m_rwMolecule) createLabels(m_rwMolecule, geo, positions); // Calculate angles and distances Vector3 v1; Vector3 v2; Vector3 v3; Real v1Norm = -1.f; Real v2Norm = -1.f; Real v3Norm = -1.f; switch (m_atoms.size()) { case 4: v3 = positions[3] - positions[2]; v3Norm = v3.norm(); [[fallthrough]]; case 3: v2 = positions[2] - positions[1]; v2Norm = v2.norm(); [[fallthrough]]; case 2: v1 = positions[1] - positions[0]; v1Norm = v1.norm(); [[fallthrough]]; default: break; } QString overlayText; float angle23 = 361.f; float angle12 = 361.f; QString dihedralLabel = tr("Dihedral:"); QString angleLabel = tr("Angle:"); QString distanceLabel = tr("Distance:"); // Use the longest label size to determine the field width. Negate it to // indicate left-alignment. int labelWidth = -std::max(std::max(dihedralLabel.size(), angleLabel.size()), distanceLabel.size()); switch (m_atoms.size()) { case 4: overlayText += QString("%1 %L2\n") .arg(tr("Dihedral:"), labelWidth) .arg(tr("%L1°").arg(dihedralAngle(v1, v2, v3), 9, 'f', 3), 9); angle23 = bondAngle(v2, v3); // fall through case 3: angle12 = bondAngle(v1, v2); overlayText += QString("%1 %L2 %L3\n") .arg(tr("Angles:"), labelWidth) .arg(tr("%L1°").arg(angle12, 9, 'f', 3), 9) .arg(angle23 < 360.f ? tr("%L1°").arg(angle23, 9, 'f', 3) : QString(), 10); // fall through case 2: overlayText += QString("%1 %L2%L3%L4") .arg(tr("Distance:"), labelWidth) .arg(tr("%L1 Å").arg(v1Norm, 9, 'f', 3), 9) .arg(v2Norm >= 0.f ? tr("%L1 Å").arg(v2Norm, 9, 'f', 3) : QString(), 9) .arg(v3Norm >= 0.f ? tr("%L1 Å").arg(v3Norm, 9, 'f', 3) : QString(), 9); default: break; } if (overlayText.isEmpty()) return; TextProperties overlayTProp; overlayTProp.setFontFamily(TextProperties::Mono); Vector3ub color(64, 255, 220); if (m_renderer) { auto backgroundColor = m_renderer->scene().backgroundColor(); color = contrastColor( Vector3ub(backgroundColor[0], backgroundColor[1], backgroundColor[2])); } overlayTProp.setColorRgb(color[0], color[1], color[2]); overlayTProp.setAlign(TextProperties::HLeft, TextProperties::VBottom); auto* label = new TextLabel2D; label->setText(overlayText.toStdString()); label->setTextProperties(overlayTProp); label->setRenderPass(Rendering::Overlay2DPass); label->setAnchor(Vector2i(10, 10)); geo->addDrawable(label); } bool MeasureTool::toggleAtom(const Rendering::Identifier& atom) { int ind = m_atoms.indexOf(atom); if (ind >= 0) { m_atoms.remove(ind); return true; } if (m_atoms.size() >= 4) return false; m_atoms.push_back(atom); return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/measuretool/measuretool.h000066400000000000000000000050531506155467400252450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_MEASURETOOL_H #define AVOGADRO_QTPLUGINS_MEASURETOOL_H #include #include #include #include #include namespace Avogadro { namespace QtPlugins { /** * @brief MeasureTool displays distances and angles between selected atoms. * * Based on the Avogadro 1.x implementation by Donald Ephraim Curtis and Marcus * D. Hanwell. */ class MeasureTool : public QtGui::ToolPlugin { Q_OBJECT public: explicit MeasureTool(QObject* parent_ = nullptr); ~MeasureTool() override; QString name() const override { return tr("Measure tool"); } QString description() const override { return tr("Measure tool"); } unsigned char priority() const override { return 60; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule*) override; void setEditMolecule(QtGui::RWMolecule*) override; void setGLRenderer(Rendering::GLRenderer* renderer) override; QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseDoubleClickEvent(QMouseEvent* e) override; void draw(Rendering::GroupNode& node) override; private: bool toggleAtom(const Rendering::Identifier& atom); template void createLabels(T* mol, Rendering::GeometryNode* geo, QVector& positions); QAction* m_activateAction; QtGui::Molecule* m_molecule; QtGui::RWMolecule* m_rwMolecule; Rendering::GLRenderer* m_renderer; QVector m_atoms; }; inline void MeasureTool::setMolecule(QtGui::Molecule* mol) { if (m_molecule != mol) { m_atoms.clear(); m_molecule = mol; m_rwMolecule = nullptr; } } inline void MeasureTool::setEditMolecule(QtGui::RWMolecule* mol) { if (m_rwMolecule != mol) { m_atoms.clear(); m_rwMolecule = mol; m_molecule = nullptr; } } inline void MeasureTool::setGLRenderer(Rendering::GLRenderer* renderer) { m_renderer = renderer; } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_MEASURETOOL_H avogadrolibs-1.101.0/avogadro/qtplugins/measuretool/measuretool.qrc000066400000000000000000000002131506155467400255740ustar00rootroot00000000000000 measure_light.svg measure_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/meshes/000077500000000000000000000000001506155467400214575ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/meshes/CMakeLists.txt000066400000000000000000000002461506155467400242210ustar00rootroot00000000000000avogadro_plugin(Meshes "Surface and mesh rendering" ScenePlugin meshes.h Meshes meshes.cpp "") target_link_libraries(Meshes PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/meshes/meshes.cpp000066400000000000000000000134451506155467400234560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "meshes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Mesh; using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::MeshGeometry; Meshes::Meshes(QObject* p) : ScenePlugin(p), m_setupWidget(nullptr) { m_layerManager = QtGui::PluginLayerManager(m_name); QSettings settings; // out of 255 m_opacity = settings.value("meshes/opacity", 150).toUInt(); auto color = settings.value("meshes/color1", QColor(Qt::red)).value(); m_color1[0] = static_cast(color.red()); m_color1[1] = static_cast(color.green()); m_color1[2] = static_cast(color.blue()); color = settings.value("meshes/color2", QColor(Qt::blue)).value(); m_color2[0] = static_cast(color.red()); m_color2[1] = static_cast(color.green()); m_color2[2] = static_cast(color.blue()); } Meshes::~Meshes() {} // Generator for std::generate call below: namespace { struct Sequence { Sequence() : i(0) {} unsigned int operator()() { return i++; } void reset() { i = 0; } unsigned int i; }; } // namespace void Meshes::process(const QtGui::Molecule& mol, GroupNode& node) { if (mol.meshCount()) { auto* geometry = new GeometryNode; node.addChild(geometry); // Handle the first mesh const Mesh* mesh = mol.mesh(0); Core::Array triangles = mesh->triangles(); bool hasColors = (mesh->colors().size() != 0); auto* mesh1 = new MeshGeometry; geometry->addDrawable(mesh1); mesh1->setOpacity(m_opacity); if (hasColors) { auto colors = mesh->colors(); Core::Array colorsRGB(colors.size()); for (size_t i = 0; i < colors.size(); i++) colorsRGB[i] = Vector3ub(static_cast(colors[i].red() * 255), static_cast(colors[i].green() * 255), static_cast(colors[i].blue() * 255)); mesh1->addVertices(mesh->vertices(), mesh->normals(), colorsRGB); } else { mesh1->setColor(m_color1); mesh1->addVertices(mesh->vertices(), mesh->normals()); } // Add the triangles for the first mesh for (size_t i = 0; i < triangles.size(); ++i) { mesh1->addTriangle(triangles[i][0], triangles[i][1], triangles[i][2]); } mesh1->setRenderPass(m_opacity == 255 ? Rendering::SolidPass : Rendering::TranslucentPass); // Handle the second mesh if present if (mol.meshCount() >= 2) { auto* mesh2 = new MeshGeometry; geometry->addDrawable(mesh2); mesh = mol.mesh(1); // Retrieve the second mesh’s triangles Core::Array triangles2 = mesh->triangles(); mesh2->setColor(m_color2); mesh2->setOpacity(m_opacity); mesh2->addVertices(mesh->vertices(), mesh->normals()); // Add the correct triangles for the second mesh for (size_t i = 0; i < triangles2.size(); ++i) { mesh2->addTriangle(triangles2[i][0], triangles2[i][1], triangles2[i][2]); } mesh2->setRenderPass(m_opacity == 255 ? Rendering::SolidPass : Rendering::TranslucentPass); } } } void Meshes::setOpacity(int opacity) { m_opacity = opacity; emit drawablesChanged(); QSettings settings; settings.setValue("meshes/opacity", m_opacity); } void Meshes::setColor1(const QColor& color) { m_color1[0] = static_cast(color.red()); m_color1[1] = static_cast(color.green()); m_color1[2] = static_cast(color.blue()); emit drawablesChanged(); QSettings settings; settings.setValue("meshes/color1", color); } void Meshes::setColor2(const QColor& color) { m_color2[0] = static_cast(color.red()); m_color2[1] = static_cast(color.green()); m_color2[2] = static_cast(color.blue()); emit drawablesChanged(); QSettings settings; settings.setValue("meshes/color2", color); } QWidget* Meshes::setupWidget() { if (!m_setupWidget) { m_setupWidget = new QWidget(qobject_cast(parent())); auto* v = new QVBoxLayout; // Opacity auto* slide = new QSlider(Qt::Horizontal); slide->setRange(0, 255); slide->setTickInterval(5); slide->setValue(m_opacity); connect(slide, SIGNAL(valueChanged(int)), SLOT(setOpacity(int))); auto* form = new QFormLayout; form->addRow(tr("Opacity:"), slide); auto* color1 = new QtGui::ColorButton; color1->setColor(QColor(m_color1[0], m_color1[1], m_color1[2])); connect(color1, SIGNAL(colorChanged(const QColor&)), SLOT(setColor1(const QColor&))); form->addRow(tr("Color:"), color1); auto* color2 = new QtGui::ColorButton; color2->setColor(QColor(m_color2[0], m_color2[1], m_color2[2])); connect(color2, SIGNAL(colorChanged(const QColor&)), SLOT(setColor2(const QColor&))); form->addRow(tr("Color:"), color2); v->addLayout(form); v->addStretch(1); m_setupWidget->setLayout(v); } return m_setupWidget; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/meshes/meshes.h000066400000000000000000000027241506155467400231210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_MESHES_H #define AVOGADRO_QTPLUGINS_MESHES_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief Render one or more triangular meshes. * @author Marcus D. Hanwell */ class Meshes : public QtGui::ScenePlugin { Q_OBJECT public: explicit Meshes(QObject* parent = nullptr); ~Meshes() override; void process(const QtGui::Molecule& mol, Rendering::GroupNode& node) override; QString name() const override { return tr("Surfaces"); } QString description() const override { return tr("Render molecular surfaces."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } private slots: void setColor1(const QColor& color); void setColor2(const QColor& color); void setOpacity(int opacity); private: std::string m_name = "Surfaces"; QWidget* m_setupWidget; unsigned char m_opacity; Vector3ub m_color1; Vector3ub m_color2; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_MESHES_H avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/000077500000000000000000000000001506155467400242735ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/CMakeLists.txt000066400000000000000000000010221506155467400270260ustar00rootroot00000000000000set(molecularproperties_srcs molecularproperties.cpp molecularmodel.cpp molecularview.cpp ) avogadro_plugin(MolecularProperties "Show a window with basic molecular properties." ExtensionPlugin molecularproperties.h MolecularProperties "${molecularproperties_srcs}" ) # to fetch the molecule name from PubChem target_link_libraries(MolecularProperties PRIVATE Qt::Network) if(WIN32) # for https support target_link_libraries(MolecularProperties PRIVATE OpenSSL::SSL OpenSSL::Crypto OpenSSL::applink) endif() avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/molecularmodel.cpp000066400000000000000000000354421506155467400300130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molecularmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro { using Avogadro::Core::BasisSet; using Avogadro::Core::GaussianSet; using Avogadro::QtGui::Molecule; using QtGui::Molecule; // CODATA 2022 // https://physics.nist.gov/cgi-bin/cuu/Value?hrev #define AU_TO_EV 27.211386245981 MolecularModel::MolecularModel(QObject* parent) : QAbstractTableModel(parent), m_molecule(nullptr) { m_network = new QNetworkAccessManager(this); connect(m_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(updateNameReady(QNetworkReply*))); } void MolecularModel::setMolecule(QtGui::Molecule* molecule) { m_molecule = molecule; // check if it has a pre-defined name if (molecule) { if (m_molecule->data("name").toString().empty()) m_autoName = true; else m_autoName = false; m_name = QString::fromStdString(molecule->data("name").toString()); } // make sure we know if the molecule changed connect(m_molecule, &QtGui::Molecule::changed, this, &MolecularModel::updateTable); updateTable(QtGui::Molecule::Added); } QString MolecularModel::name() const { if (!m_molecule || m_molecule->atomCount() == 0) return m_name; // empty // if we have a defined name // or we're not ready to update // then return the current name if (!m_autoName || !m_nameUpdateNeeded || m_nameRequestPending) return m_name; // okay, kick off the update m_name = tr("(pending)", "asking server for molecule name"); m_nameRequestPending = true; std::string smiles; Io::FileFormatManager::instance().writeString(*m_molecule, smiles, "smi"); QString smilesString = QString::fromStdString(smiles); smilesString.remove(QRegularExpression("\\s+.*")); QString requestURL = QString("https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/smiles/" + QUrl::toPercentEncoding(smilesString) + "/json"); m_network->get(QNetworkRequest(QUrl(requestURL))); // don't update again until we're ready - 5 seconds QTimer::singleShot(5000, this, SLOT(canUpdateName())); return m_name; } void MolecularModel::canUpdateName() { m_nameRequestPending = false; } void MolecularModel::updateNameReady(QNetworkReply* reply) { // finished a request, don't need this until next modification m_nameUpdateNeeded = false; // Read in all the data if (!reply->isReadable()) { reply->deleteLater(); m_name = tr("unknown molecule"); return; } // check if the data came through QByteArray data = reply->readAll(); if (data.contains("Error report") || data.contains("

")) { reply->deleteLater(); m_name = tr("unknown molecule"); return; } // parse the JSON // https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/smiles/…/json // PC_Compounds[0].props // iterate // get "urn" / "name" == "Markup" and "Preferred" // .. get "value" / "sval" QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject obj = doc.object(); QJsonArray array = obj["PC_Compounds"].toArray(); if (array.isEmpty()) { reply->deleteLater(); m_name = tr("unknown molecule"); return; } obj = array.first().toObject(); array = obj["props"].toArray(); // props is an array of objects for (const QJsonValue& value : array) { obj = value.toObject(); QJsonObject urn = obj["urn"].toObject(); if (urn["name"].toString() == "Markup") { // HTML version for dialog QJsonObject nameValue = obj["value"].toObject(); m_name = nameValue["sval"].toString(); } else if (urn["name"].toString() == "Preferred") { // save this text version for files and copy/paste QJsonObject nameValue = obj["value"].toObject(); m_molecule->setData("name", nameValue["sval"].toString().toStdString()); m_name = nameValue["sval"].toString(); } } emit dataChanged(index(Name, 0), index(Name, 0)); reply->deleteLater(); } int MolecularModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); if (!m_molecule) return 0; return m_propertiesCache.size(); } int MolecularModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 1; // values } QString formatPointGroup(std::string pointgroup) { // first character is in capital // then everything else is in subscript using ... QString formatted = QString::fromStdString(pointgroup); QString output = formatted.at(0).toUpper(); output += QString("%1").arg(formatted.mid(1)); return output; } // Qt calls this for multiple "roles" across row / columns in the index // we also combine multiple types into this class, so lots of special cases QVariant MolecularModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || m_molecule == nullptr) return QVariant(); int row = index.row(); [[maybe_unused]] int col = index.column(); // Simple lambda to convert QFlags to variant as in Qt 6 this needs help. auto toVariant = [&](auto flags) { return static_cast(flags); }; // handle text alignments if (role == Qt::TextAlignmentRole) { return toVariant(Qt::AlignRight); } if (role != Qt::UserRole && role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); const auto map = m_propertiesCache; auto it = map.begin(); switch (row) { case 0: return name(); case 1: return m_molecule->mass(); case 2: return m_molecule->formattedFormula(); case 3: return QVariant::fromValue(m_molecule->atomCount()); case 4: return QVariant::fromValue(m_molecule->bondCount()); } std::advance(it, row); auto key = it->first; if (key == " 6coordinateSets") return QVariant::fromValue(m_molecule->coordinate3dCount()); else if (key == " 7residues") return QVariant::fromValue(m_molecule->residueCount()); else if (key == " 9totalCharge") return QVariant::fromValue(static_cast(m_molecule->totalCharge())); else if (key == " 9totalSpinMultiplicity") return QVariant::fromValue( static_cast(m_molecule->totalSpinMultiplicity())); else if (key == "pointgroup") return formatPointGroup(it->second.toString()); return QString::fromStdString(it->second.toString()); } QVariant MolecularModel::headerData(int section, Qt::Orientation orientation, int role) const { // Simple lambda to convert QFlags to variant as in Qt 6 this needs help. auto toVariant = [&](auto flags) { return static_cast(flags); }; // handle text alignments if (role == Qt::TextAlignmentRole) { if (orientation == Qt::Vertical) { return toVariant(Qt::AlignLeft); } return toVariant(Qt::AlignHCenter); } if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { return tr("Property"); } else if (orientation == Qt::Vertical) { const auto map = m_propertiesCache; auto it = map.begin(); std::advance(it, section); if (it->first == " 1name") return tr("Molecule Name"); else if (it->first == " 2mass") return tr("Molecular Mass (g/mol)"); else if (it->first == " 3formula") return tr("Chemical Formula"); else if (it->first == " 4atoms") return tr("Number of Atoms"); else if (it->first == " 5bonds") return tr("Number of Bonds"); else if (it->first == " 6coordinateSets") return tr("Coordinate Sets"); else if (it->first == " 7residues") return tr("Number of Residues"); else if (it->first == " 8chains") return tr("Number of Chains"); else if (it->first == " 9totalCharge") return tr("Net Charge"); else if (it->first == " 9totalSpinMultiplicity") return tr("Net Spin Multiplicity"); else if (it->first == "dipoleMoment") return tr("Dipole Moment (Debye)"); else if (it->first == "homoEnergy") return tr("HOMO Energy (eV)", "highest occupied molecular orbital"); else if (it->first == "lumoEnergy") return tr("LUMO Energy (eV)", "lowest unoccupied molecular orbital"); else if (it->first == "somoEnergy") return tr("SOMO Energy (eV)", "singly-occupied molecular orbital"); else if (it->first == "totalEnergy") return tr("Total Energy (Hartree)", "total electronic energy in Hartrees"); else if (it->first == "zpe") return tr("Zero Point Energy (kcal/mol)", "zero point vibrational energy"); else if (it->first == "enthalpy") return tr("Enthalpy (kcal/mol)"); else if (it->first == "entropy") return tr("Entropy (kcal/mol•K)"); else if (it->first == "gibbs") return tr("Gibbs Free Energy (kcal/mol)"); else if (it->first == "pointgroup") return tr("Point Group", "point group symmetry"); else if (it != map.end()) return QString::fromStdString(it->first); return QVariant(); } else // row headers return QVariant(); return QVariant(); } Qt::ItemFlags MolecularModel::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::ItemIsEnabled; // return QAbstractItemModel::flags(index) | Qt::ItemIsEditable // for the types and columns that can be edited auto editable = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; int row = index.row(); if (row == 0) // name return editable; const auto map = m_propertiesCache; auto it = map.begin(); std::advance(it, row); auto key = it->first; if (key == " 9totalCharge" || key == " 9totalSpinMultiplicity") return editable; return QAbstractItemModel::flags(index); } bool MolecularModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) return false; if (role != Qt::EditRole) return false; int row = index.row(); if (row == 0) { // name should always be the first row m_name = value.toString(); m_autoName = false; m_molecule->setData("name", m_name.toStdString()); emit dataChanged(index, index); return true; } const auto map = m_propertiesCache; auto it = map.begin(); std::advance(it, row); auto key = it->first; if (key == " 9totalCharge") { m_molecule->setData("totalCharge", value.toInt()); emit dataChanged(index, index); return true; } else if (key == " 9totalSpinMultiplicity") { int spin = value.toInt(); if (spin < 1) return false; m_molecule->setData("totalSpinMultiplicity", value.toInt()); emit dataChanged(index, index); return true; } return false; } void MolecularModel::updateTable(unsigned int flags) { // cache all the properties m_propertiesCache.clear(); if (m_molecule == nullptr) return; m_nameUpdateNeeded = true; // we use internal key names here and // update the display names in the headerData method m_propertiesCache.setValue(" 1name", name()); m_propertiesCache.setValue(" 2mass", m_molecule->mass()); m_propertiesCache.setValue(" 3formula", m_molecule->formattedFormula()); m_propertiesCache.setValue(" 4atoms", m_molecule->atomCount()); m_propertiesCache.setValue(" 5bonds", m_molecule->bondCount()); if (m_molecule->coordinate3dCount() > 0) m_propertiesCache.setValue(" 6coordinateSets", m_molecule->coordinate3dCount()); if (m_molecule->residueCount() > 0) { m_propertiesCache.setValue(" 7residues", m_molecule->residueCount()); // figure out if we have multiple chains unsigned int chainCount = 0; unsigned int offset = 0; for (Index i = 0; i < m_molecule->residueCount(); ++i) { char chainId = m_molecule->residue(i).chainId(); if (chainId >= 'A' && chainId <= 'Z') offset = chainId - 'A'; else if (chainId >= 'a' && chainId <= 'z') offset = chainId - 'a'; else if (chainId >= '0' && chainId <= '9') offset = chainId - '0' + 15; // starts at 'P' chainCount = std::max(chainCount, offset); } m_propertiesCache.setValue(" 8chains", chainCount); } m_propertiesCache.setValue(" 9totalCharge", static_cast(m_molecule->totalCharge())); m_propertiesCache.setValue( " 9totalSpinMultiplicity", static_cast(m_molecule->totalSpinMultiplicity())); if (m_molecule->hasData("dipoleMoment")) { auto dipole = m_molecule->data("dipoleMoment").toVector3(); QString moment = QString::number(dipole.norm(), 'f', 3); m_propertiesCache.setValue("dipoleMoment", moment.toStdString()); } // TODO check for homo, lumo, or somo energies const auto* basis = m_molecule->basisSet(); const GaussianSet* gaussianSet = dynamic_cast(basis); if (gaussianSet != nullptr && gaussianSet->scfType() == Core::Rhf) { unsigned int homo = gaussianSet->homo(); unsigned int lumo = gaussianSet->lumo(); const auto moEnergies = gaussianSet->moEnergy(); if (moEnergies.size() > homo) { m_propertiesCache.setValue("homoEnergy", moEnergies[homo] * AU_TO_EV); } // look for the lumo if there's a degenerate HOMO const double threshold = 0.01 / AU_TO_EV; // 0.01 eV minimal separation while (moEnergies.size() > lumo && std::abs(moEnergies[lumo] - moEnergies[homo]) < threshold) { lumo += 1; } if (moEnergies.size() > lumo) m_propertiesCache.setValue("lumoEnergy", moEnergies[lumo] * AU_TO_EV); } // m_propertiesCache.setValue("somoEnergy", energy); // ignore potentially duplicate properties const auto& properties = m_molecule->dataMap(); for (const auto& key : properties.names()) { if (key == "formula" || key == "name" || key == "fileName" || key == "energies" || key == "totalCharge" || key == "totalSpinMultiplicity") continue; // skip these if (properties.value(key).toString().empty()) continue; // don't bother with an empty value m_propertiesCache.setValue(key, properties.value(key)); } if (flags & Molecule::Added || flags & Molecule::Removed) { // tear it down and rebuild the model beginResetModel(); endResetModel(); } else { // we can just update the current data emit dataChanged( QAbstractItemModel::createIndex(0, 0), QAbstractItemModel::createIndex(rowCount(), columnCount())); } } } // end namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/molecularmodel.h000066400000000000000000000036641506155467400274610ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef MOLECULARMODEL_H #define MOLECULARMODEL_H #include #include #include #include class QNetworkAccessManager; class QNetworkReply; #include #include namespace Avogadro { namespace QtGui { class Molecule; } enum FixedRowType { Name = 0, Mass, Formula, Atoms, Bonds // these are the fixed rows }; class MolecularModel : public QAbstractTableModel { Q_OBJECT public slots: void updateTable(unsigned int flags); void updateNameReady(QNetworkReply* reply); // reply from network void canUpdateName(); // don't do it too often public: explicit MolecularModel(QObject* parent = 0); int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void setMolecule(QtGui::Molecule* molecule); QString name() const; private: QtGui::Molecule* m_molecule = nullptr; mutable QString m_name; mutable bool m_nameRequestPending = false; bool m_autoName = true; bool m_nameUpdateNeeded = true; Core::VariantMap m_propertiesCache; QNetworkAccessManager* m_network = nullptr; }; } // end namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/molecularproperties.cpp000066400000000000000000000050501506155467400310770ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molecularproperties.h" #include "molecularview.h" #include #include #include #include #include #include #include using Avogadro::QtGui::RichTextDelegate; namespace Avogadro::QtPlugins { MolecularProperties::MolecularProperties(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr) { m_action->setEnabled(true); m_action->setText(tr("&Molecular…")); m_action->setProperty("menu priority", 990); connect(m_action, SIGNAL(triggered()), SLOT(showDialog())); } MolecularProperties::~MolecularProperties() {} QString MolecularProperties::description() const { return tr("View general properties of a molecule."); } QList MolecularProperties::actions() const { return QList() << m_action; } QStringList MolecularProperties::menuPath(QAction*) const { return QStringList() << tr("&Analyze") << tr("&Properties"); } void MolecularProperties::setMolecule(QtGui::Molecule* mol) { if (mol == m_molecule) return; m_molecule = mol; } void MolecularProperties::showDialog() { // copied from the propeties dialog auto* dialog = new QDialog(qobject_cast(parent())); auto* layout = new QVBoxLayout(dialog); dialog->setLayout(layout); // Don't show whitespace around the table view layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); auto* model = new MolecularModel(); model->setMolecule(m_molecule); // view will delete itself & model using deleteLater() auto* view = new MolecularView(dialog); view->setMolecule(m_molecule); view->setSourceModel(model); view->setModel(model); // set the headers to true QFont font = view->horizontalHeader()->font(); font.setBold(true); view->horizontalHeader()->setFont(font); view->verticalHeader()->setFont(font); view->setItemDelegateForColumn(0, new RichTextDelegate(view)); view->horizontalHeader()->setStretchLastSection(true); view->resizeColumnsToContents(); layout->addWidget(view); dialog->setWindowTitle(view->windowTitle()); dialog->setWindowFlags(Qt::Window); dialog->show(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/molecularproperties.h000066400000000000000000000024141506155467400305450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_MOLECULARPROPERTIES_H #define AVOGADRO_QTPLUGINS_MOLECULARPROPERTIES_H #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { /** * @brief The MolecularProperties class is an extension to launch * a MolecularPropertiesDialog. */ class MolecularProperties : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit MolecularProperties(QObject* parent_ = nullptr); ~MolecularProperties() override; QString name() const override { return tr("Molecular Properties"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void showDialog(); private: QAction* m_action; QtGui::Molecule* m_molecule; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_MOLECULARPROPERTIESEXTENSION_H avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/molecularview.cpp000066400000000000000000000144461506155467400276660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molecularview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro { using QtGui::Molecule; MolecularView::MolecularView(QWidget* parent) : QTableView(parent), m_molecule(nullptr), m_model(nullptr) { this->setWindowTitle(tr("Molecule Properties")); QHeaderView* horizontal = this->horizontalHeader(); horizontal->setSectionResizeMode(QHeaderView::Interactive); QHeaderView* vertical = this->verticalHeader(); vertical->setSectionResizeMode(QHeaderView::Interactive); // You can select everything (e.g., to copy, select all, etc.) setCornerButtonEnabled(true); setSelectionBehavior(QAbstractItemView::SelectRows); setSelectionMode(QAbstractItemView::ExtendedSelection); // Alternating row colors setAlternatingRowColors(true); // Don't allow sorting the table setSortingEnabled(false); } void MolecularView::selectionChanged( [[maybe_unused]] const QItemSelection& selected, const QItemSelection&) { } void MolecularView::setMolecule(Molecule* molecule) { m_molecule = molecule; } void MolecularView::hideEvent(QHideEvent*) { if (model()) { model()->deleteLater(); } this->deleteLater(); } void MolecularView::keyPressEvent(QKeyEvent* event) { // handle copy event // thanks to https://www.walletfox.com/course/qtableviewcopypaste.php if (!event->matches(QKeySequence::Copy)) { QTableView::keyPressEvent(event); return; } // get the selected rows (if any) QModelIndexList selectedRows = selectionModel()->selectedRows(); // if nothing is selected, copy everything to the clipboard QString text; if (selectedRows.isEmpty()) { // iterate through every row and column and copy the data for (int i = 0; i < model()->rowCount(); ++i) { QStringList rowContents; for (int j = 0; j < model()->columnCount(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } else { // copy the selected rows to the clipboard QItemSelectionRange range = selectionModel()->selection().first(); for (auto i = range.top(); i <= range.bottom(); ++i) { QStringList rowContents; for (auto j = range.left(); j <= range.right(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } QApplication::clipboard()->setText(text); } void MolecularView::copySelectedRowsToClipboard() { // get the selected rows (if any) QModelIndexList selectedRows = selectionModel()->selectedRows(); // if nothing is selected, copy everything to the clipboard QString text; if (selectedRows.isEmpty()) { // iterate through every row and column and copy the data for (int i = 0; i < model()->rowCount(); ++i) { QStringList rowContents; for (int j = 0; j < model()->columnCount(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } else { // copy the selected rows to the clipboard QItemSelectionRange range = selectionModel()->selection().first(); for (auto i = range.top(); i <= range.bottom(); ++i) { QStringList rowContents; for (auto j = range.left(); j <= range.right(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } QApplication::clipboard()->setText(text); } void MolecularView::openExportDialogBox() { // Create a file dialog for selecting the export location and file name QString filePath = QFileDialog::getSaveFileName(this, tr("Export CSV"), QDir::homePath(), tr("CSV Files (*.csv);;All Files (*)")); if (filePath.isEmpty()) { // User canceled the dialog or didn't provide a file name return; } // Open the file for writing QFile file(filePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { // Handle error opening the file QMessageBox::critical(this, tr("Error"), tr("Could not open the file for writing.")); return; } // Create a QTextStream to write to the file QTextStream stream(&file); // Write the header row with column names for (int col = 0; col < model()->columnCount(); ++col) { stream << model()->headerData(col, Qt::Horizontal).toString(); if (col < model()->columnCount() - 1) { stream << ","; } } stream << "\n"; // Write the data rows for (int row = 0; row < model()->rowCount(); ++row) { stream << model()->headerData(row, Qt::Vertical).toString() << ","; for (int col = 0; col < model()->columnCount(); ++col) { stream << model()->index(row, col).data().toString(); if (col < model()->columnCount() - 1) { stream << ","; } } stream << "\n"; } // Close the file file.close(); if (file.error() != QFile::NoError) { // Handle error closing the file QMessageBox::critical(this, tr("Error"), tr("Error writing to the file.")); } } void MolecularView::contextMenuEvent(QContextMenuEvent* event) { QMenu menu(this); QAction* copyAction = menu.addAction(tr("Copy")); menu.addAction(copyAction); connect(copyAction, &QAction::triggered, this, &MolecularView::copySelectedRowsToClipboard); QAction* exportAction = menu.addAction(tr("Export…")); menu.addAction(exportAction); connect(exportAction, &QAction::triggered, this, &MolecularView::openExportDialogBox); menu.exec(event->globalPos()); } } // end namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/molecularproperties/molecularview.h000066400000000000000000000023271506155467400273260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_MOLECULARVIEW_H #define AVOGADRO_QTPLUGINS_MOLECULARVIEW_H #include "molecularmodel.h" #include namespace Avogadro { namespace QtGui { class Molecule; } class MolecularView : public QTableView { Q_OBJECT public: explicit MolecularView(QWidget* parent = 0); void selectionChanged(const QItemSelection& selected, const QItemSelection& previous) override; void setMolecule(QtGui::Molecule* molecule); void setSourceModel(MolecularModel* model) { m_model = model; } void hideEvent(QHideEvent* event) override; void contextMenuEvent(QContextMenuEvent* event) override; protected: // copy the selected properties to the clipboard void keyPressEvent(QKeyEvent* event) override; private: QtGui::Molecule* m_molecule; MolecularModel* m_model; void copySelectedRowsToClipboard(); void openExportDialogBox(); }; } // end namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/navigator/000077500000000000000000000000001506155467400221655ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/navigator/CMakeLists.txt000066400000000000000000000004641506155467400247310ustar00rootroot00000000000000set(navigator_srcs navigator.cpp ) set(navigator_uis ) set(navigator_rcs navigator.qrc ) avogadro_plugin(Navigator "Navigation tool" ToolPlugin navigator.h Navigator "${navigator_srcs}" "${navigator_uis}" "${navigator_rcs}" ) target_link_libraries(Navigator PRIVATE Avogadro::QtOpenGL) avogadrolibs-1.101.0/avogadro/qtplugins/navigator/navigator.cpp000066400000000000000000000241201506155467400246620ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "navigator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { const float ZOOM_SPEED = 0.02f; const float ROTATION_SPEED = 0.005f; Navigator::Navigator(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_glWidget(nullptr), m_toolWidget(nullptr), m_renderer(nullptr), m_pressedButtons(Qt::NoButton), m_currentAction(Nothing) { QString shortcut = tr("Ctrl+1", "control-key 1"); m_activateAction->setText(tr("Navigate")); m_activateAction->setToolTip( tr("Navigation Tool\t(%1)\n\n" "Left Mouse:\tClick and drag to rotate the view.\n" "Middle Mouse:\tClick and drag to zoom in or out.\n" "Right Mouse:\tClick and drag to move the view.") .arg(shortcut)); setIcon(); QSettings settings; m_zoomDirection = settings.value("navigator/zoom", 1).toInt(); } void Navigator::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/navigator_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/navigator_light.svg")); } void Navigator::registerCommands() { emit registerCommand("rotateScene", tr("Rotate the scene along the x, y, or z axes.")); emit registerCommand("zoomScene", tr("Zoom the scene.")); emit registerCommand("translateScene", tr("Translate the scene.")); } bool Navigator::handleCommand(const QString& command, const QVariantMap& options) { if (m_renderer == nullptr) return false; // No camera if (command == "rotateScene") { float x = options.value("x").toFloat(); float y = options.value("y").toFloat(); float z = options.value("z").toFloat(); rotate(m_renderer->camera().focus(), x, y, z); m_glWidget->requestUpdate(); } else if (command == "zoomScene") { float d = options.value("delta").toFloat(); zoom(m_renderer->camera().focus(), d); m_glWidget->requestUpdate(); } else if (command == "translateScene") { float x = options.value("x").toFloat(); float y = options.value("y").toFloat(); translate(m_renderer->camera().focus(), x, y); m_glWidget->requestUpdate(); } else { qDebug() << "Unknown command: " << command; return false; } return true; } Navigator::~Navigator() {} QWidget* Navigator::toolWidget() const { if (!m_toolWidget) { m_toolWidget = new QWidget(qobject_cast(parent())); auto* layout = new QVBoxLayout; auto* swapZoom = new QCheckBox(tr("Reverse Direction of Zoom on Scroll")); swapZoom->setToolTip( tr("Default:\tScroll down to shrink, scroll up to zoom\n" "Reversed:\tScroll up to shrink, scroll down to zoom")); connect(swapZoom, SIGNAL(toggled(bool)), this, SLOT(swapZoomDirection(bool))); layout->addWidget(swapZoom); layout->addStretch(1); m_toolWidget->setLayout(layout); } return m_toolWidget; } void Navigator::swapZoomDirection(bool checked) { m_zoomDirection = (checked ? -1 : 1); QSettings settings; settings.setValue("navigator/zoom", m_zoomDirection); } QUndoCommand* Navigator::mousePressEvent(QMouseEvent* e) { updatePressedButtons(e, false); m_lastMousePosition = e->pos(); e->accept(); // Figure out what type of navigation has been requested. if (e->buttons() & Qt::LeftButton && e->modifiers() == Qt::NoModifier) { m_currentAction = Rotation; } else if (e->buttons() & Qt::MiddleButton || (e->buttons() & Qt::LeftButton && e->modifiers() == Qt::ShiftModifier)) { m_currentAction = ZoomTilt; } else if (e->buttons() & Qt::RightButton || (e->buttons() & Qt::LeftButton && (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::MetaModifier))) { m_currentAction = Translation; } return nullptr; } QUndoCommand* Navigator::mouseReleaseEvent(QMouseEvent* e) { updatePressedButtons(e, true); m_lastMousePosition = QPoint(); m_currentAction = Nothing; e->accept(); return nullptr; } QUndoCommand* Navigator::mouseMoveEvent(QMouseEvent* e) { switch (m_currentAction) { case Rotation: { QPoint delta = e->pos() - m_lastMousePosition; rotate(m_renderer->camera().focus(), delta.y(), delta.x(), 0); e->accept(); break; } case Translation: { Vector2f fromScreen(m_lastMousePosition.x(), m_lastMousePosition.y()); Vector2f toScreen(e->localPos().x(), e->localPos().y()); translate(m_renderer->camera().focus(), fromScreen, toScreen); e->accept(); break; } case ZoomTilt: { QPoint delta = e->pos() - m_lastMousePosition; // Tilt rotate(m_renderer->camera().focus(), 0, 0, delta.x()); // Zoom zoom(m_renderer->camera().focus(), delta.y()); e->accept(); break; } default:; } m_lastMousePosition = e->pos(); if (e->isAccepted()) emit updateRequested(); return nullptr; } QUndoCommand* Navigator::mouseDoubleClickEvent(QMouseEvent* e) { // Reset if (e->button() == Qt::MiddleButton) { if (m_glWidget) { m_glWidget->resetCamera(); e->accept(); emit updateRequested(); } } return nullptr; } QUndoCommand* Navigator::wheelEvent(QWheelEvent* e) { /// @todo Use scale for orthographic projections // Amount to zoom float d = 0.0f; QPoint numPixels = e->pixelDelta(); QPoint numDegrees = e->angleDelta() * 0.125; // see https://doc.qt.io/qt-5/qwheelevent.html#pixelDelta if (!numPixels.isNull() && QGuiApplication::platformName().toStdString().compare("xcb")) d = numPixels.y(); // use pixelDelta() when available, except on X11 else if (!numDegrees.isNull()) d = numDegrees.y(); // fall back to angleDelta() zoom(m_renderer->camera().focus(), m_zoomDirection * d); e->accept(); emit updateRequested(); return nullptr; } QUndoCommand* Navigator::keyPressEvent(QKeyEvent* e) { Vector3f ref = m_renderer->camera().focus(); switch (e->key()) { case Qt::Key_Left: case Qt::Key_H: case Qt::Key_A: if (e->modifiers() == Qt::NoModifier || e->modifiers() == Qt::KeypadModifier) rotate(ref, 0, -5, 0); else if (e->modifiers() == Qt::ShiftModifier) rotate(ref, 0, 0, -5); else if (e->modifiers() == Qt::ControlModifier) translate(ref, -5, 0); e->accept(); break; case Qt::Key_Right: case Qt::Key_L: case Qt::Key_D: if (e->modifiers() == Qt::NoModifier || e->modifiers() == Qt::KeypadModifier) rotate(ref, 0, 5, 0); else if (e->modifiers() == Qt::ShiftModifier) rotate(ref, 0, 0, 5); else if (e->modifiers() == Qt::ControlModifier) translate(ref, 5, 0); e->accept(); break; case Qt::Key_Up: case Qt::Key_K: case Qt::Key_W: if (e->modifiers() == Qt::NoModifier || e->modifiers() == Qt::KeypadModifier) rotate(ref, -5, 0, 0); else if (e->modifiers() == Qt::ShiftModifier) zoom(ref, -2); else if (e->modifiers() == Qt::ControlModifier) translate(ref, 0, -5); e->accept(); break; case Qt::Key_Down: case Qt::Key_J: case Qt::Key_S: if (e->modifiers() == Qt::NoModifier || e->modifiers() == Qt::KeypadModifier) rotate(ref, 5, 0, 0); else if (e->modifiers() == Qt::ShiftModifier) zoom(ref, 2); else if (e->modifiers() == Qt::ControlModifier) translate(ref, 0, 5); e->accept(); break; default: e->ignore(); } emit updateRequested(); return nullptr; } QUndoCommand* Navigator::keyReleaseEvent(QKeyEvent* e) { /// @todo e->ignore(); return nullptr; } inline void Navigator::updatePressedButtons(QMouseEvent* e, bool release) { /// @todo Use modifier keys on mac if (release) m_pressedButtons &= e->buttons(); else m_pressedButtons |= e->buttons(); } inline void Navigator::rotate(const Vector3f& ref, float x, float y, float z) { const Eigen::Affine3f& modelView = m_renderer->camera().modelView(); Vector3f xAxis = modelView.linear().row(0).transpose().normalized(); Vector3f yAxis = modelView.linear().row(1).transpose().normalized(); Vector3f zAxis = modelView.linear().row(2).transpose().normalized(); m_renderer->camera().translate(ref); m_renderer->camera().rotate(x * ROTATION_SPEED, xAxis); m_renderer->camera().rotate(y * ROTATION_SPEED, yAxis); m_renderer->camera().rotate(z * ROTATION_SPEED, zAxis); m_renderer->camera().translate(-ref); } inline void Navigator::zoom(const Vector3f& ref, float d) { const Eigen::Affine3f& modelView = m_renderer->camera().modelView(); Vector3f transformedCenter = modelView * ref; float distance = transformedCenter.norm(); float t = d * ZOOM_SPEED; float u = 2.0f / distance - 1.0f; if (t < u) t = u; if (m_renderer->camera().projectionType() == Rendering::Perspective) m_renderer->camera().preTranslate(transformedCenter * t); else m_renderer->camera().scale(t + 1.0f); } inline void Navigator::translate(const Vector3f& ref, float x, float y) { Vector2f fromScreen(0, 0); Vector2f toScreen(x, y); translate(ref, fromScreen, toScreen); } inline void Navigator::translate(const Vector3f& ref, const Vector2f& fromScr, const Vector2f& toScr) { Vector3f from(m_renderer->camera().unProject(fromScr, ref)); Vector3f to(m_renderer->camera().unProject(toScr, ref)); m_renderer->camera().translate(to - from); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/navigator/navigator.h000066400000000000000000000056771506155467400243470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_NAVIGATOR_H #define AVOGADRO_QTPLUGINS_NAVIGATOR_H #include #include #include #include // for Qt:: namespace namespace Avogadro { namespace QtPlugins { /** * @class Navigator navigator.h * @brief The Navigator tool updates the camera in response to user input. * @author Allison Vacanti */ class Navigator : public QtGui::ToolPlugin { Q_OBJECT public: explicit Navigator(QObject* parent_ = nullptr); ~Navigator() override; QString name() const override { return tr("Navigate tool"); } QString description() const override { return tr("Navigate tool"); } unsigned char priority() const override { return 10; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule* mol) override { m_molecule = mol; } void setGLWidget(QtOpenGL::GLWidget* widget) override { m_glWidget = widget; } void setGLRenderer(Rendering::GLRenderer* renderer) override { m_renderer = renderer; } QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseMoveEvent(QMouseEvent* e) override; QUndoCommand* mouseDoubleClickEvent(QMouseEvent* e) override; QUndoCommand* wheelEvent(QWheelEvent* e) override; QUndoCommand* keyPressEvent(QKeyEvent* e) override; QUndoCommand* keyReleaseEvent(QKeyEvent* e) override; bool handleCommand(const QString& command, const QVariantMap& options) override; void registerCommands() override; protected slots: void swapZoomDirection(bool checked); private: /** * Update the currently pressed buttons, accounting for modifier keys. * \todo Account for modifier keys. */ void updatePressedButtons(QMouseEvent*, bool release); void rotate(const Vector3f& ref, float x, float y, float z); void zoom(const Vector3f& ref, float d); void translate(const Vector3f& ref, float x, float y); void translate(const Vector3f& ref, const Vector2f& from, const Vector2f& to); QAction* m_activateAction; QtGui::Molecule* m_molecule; QtOpenGL::GLWidget* m_glWidget; mutable QWidget* m_toolWidget; Rendering::GLRenderer* m_renderer; Qt::MouseButtons m_pressedButtons; QPoint m_lastMousePosition; int m_zoomDirection; enum ToolAction { Nothing = 0, Rotation, Translation, ZoomTilt, Zoom }; ToolAction m_currentAction; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_NAVIGATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/navigator/navigator.qrc000066400000000000000000000002171506155467400246660ustar00rootroot00000000000000 navigator_light.svg navigator_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/navigator/navigator_dark.svg000066400000000000000000000031631506155467400257040ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/navigator/navigator_light.svg000066400000000000000000000031471506155467400260740ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/networkdatabases/000077500000000000000000000000001506155467400235345ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/networkdatabases/CMakeLists.txt000066400000000000000000000005741506155467400263020ustar00rootroot00000000000000set(srcs networkdatabases.cpp ) avogadro_plugin(NetworkDatabases "Network databases" ExtensionPlugin networkdatabases.h NetworkDatabases "${srcs}" "" ) target_link_libraries(NetworkDatabases PRIVATE Avogadro::IO Qt::Network) if(WIN32) # for https support target_link_libraries(NetworkDatabases PRIVATE OpenSSL::SSL OpenSSL::Crypto OpenSSL::applink) endif() avogadrolibs-1.101.0/avogadro/qtplugins/networkdatabases/networkdatabases.cpp000066400000000000000000000070551506155467400276100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "networkdatabases.h" #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { NetworkDatabases::NetworkDatabases(QObject* parent_) : ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr), m_network(nullptr), m_progressDialog(nullptr) { m_action->setEnabled(true); m_action->setText("Download by &Name…"); m_action->setProperty("menu priority", 190); connect(m_action, SIGNAL(triggered()), SLOT(showDialog())); } NetworkDatabases::~NetworkDatabases() {} QList NetworkDatabases::actions() const { return QList() << m_action; } QStringList NetworkDatabases::menuPath(QAction*) const { return QStringList() << tr("&File") << tr("&Import"); } void NetworkDatabases::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } bool NetworkDatabases::readMolecule(QtGui::Molecule& mol) { if (m_moleculeData.isEmpty() || m_moleculeName.isEmpty()) return false; bool readOK = Io::FileFormatManager::instance().readString( mol, m_moleculeData.data(), "sdf"); if (readOK) // worked, so set the filename mol.setData("name", m_moleculeName.toStdString()); return readOK; } void NetworkDatabases::showDialog() { if (!m_network) { m_network = new QNetworkAccessManager(this); connect(m_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); } if (!m_progressDialog) { m_progressDialog = new QProgressDialog(qobject_cast(parent())); } // Prompt for a chemical structure name bool ok; QString structureName = QInputDialog::getText( qobject_cast(parent()), tr("Chemical Name"), tr("Chemical structure to download."), QLineEdit::Normal, "", &ok); if (!ok || structureName.isEmpty()) return; // Hard coding the NIH resolver download URL - this could be used for other // services m_network->get( QNetworkRequest(QUrl("https://cactus.nci.nih.gov/chemical/structure/" + structureName + "/file?format=sdf&get3d=true"))); m_moleculeName = structureName; m_progressDialog->setLabelText(tr("Querying for %1").arg(structureName)); m_progressDialog->setRange(0, 0); m_progressDialog->show(); } void NetworkDatabases::replyFinished(QNetworkReply* reply) { m_progressDialog->hide(); // Read in all the data if (!reply->isReadable()) { QMessageBox::warning(qobject_cast(parent()), tr("Network Download Failed"), tr("Network timeout or other error.")); reply->deleteLater(); return; } m_moleculeData = reply->readAll(); // Check if the file was successfully downloaded if (m_moleculeData.contains("Error report") || m_moleculeData.contains("Page not found (404)")) { QMessageBox::warning( qobject_cast(parent()), tr("Network Download Failed"), tr("Specified molecule could not be found: %1").arg(m_moleculeName)); reply->deleteLater(); return; } emit moleculeReady(1); reply->deleteLater(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/networkdatabases/networkdatabases.h000066400000000000000000000032261506155467400272510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_NETWORKDATABASES_H #define AVOGADRO_QTPLUGINS_NETWORKDATABASES_H #include #include #include class QNetworkAccessManager; class QNetworkReply; class QProgressDialog; namespace Avogadro { namespace QtPlugins { /** * @brief Queries online databases (currently the NIH structure resolver) and * loads the returned structure if one is found. */ class NetworkDatabases : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit NetworkDatabases(QObject* parent = nullptr); ~NetworkDatabases() override; QString name() const override { return tr("Network Databases"); } QString description() const override { return tr("Interact with online databases, query structures etc."); } QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; bool readMolecule(QtGui::Molecule& mol) override; private slots: void showDialog(); void replyFinished(QNetworkReply*); private: QAction* m_action; QtGui::Molecule* m_molecule; QNetworkAccessManager* m_network; QString m_moleculeName; QByteArray m_moleculeData; QProgressDialog* m_progressDialog; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_NETWORKDATABASES_H avogadrolibs-1.101.0/avogadro/qtplugins/noncovalent/000077500000000000000000000000001506155467400225215ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/noncovalent/CMakeLists.txt000066400000000000000000000003411506155467400252570ustar00rootroot00000000000000avogadro_plugin(NonCovalent "Non-covalent interaction rendering, including hydrogen bonds" ScenePlugin noncovalent.h NonCovalent noncovalent.cpp "") target_link_libraries(NonCovalent PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/noncovalent/noncovalent.cpp000066400000000000000000000331231506155467400255550ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "noncovalent.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // bond angles #define M_TETRAHEDRAL (acosf(-1.0f / 3.0f)) #define M_TRIGONAL (2.0f * M_PI / 3.0f) namespace Avogadro::QtPlugins { using Core::Array; using Core::AtomHybridization; using Core::AtomUtilities; using Core::Bond; using Core::Elements; using Core::NeighborPerceiver; using QtGui::Molecule; using QtGui::PluginLayerManager; using Rendering::DashedLineGeometry; using Rendering::GeometryNode; using Rendering::GroupNode; NonCovalent::NonCovalent(QObject* p) : ScenePlugin(p) { m_layerManager = PluginLayerManager(m_name); QSettings settings; m_angleTolerancesDegrees = { settings.value("nonCovalent/angleTolerance0", 40.0).toDouble(), settings.value("nonCovalent/angleTolerance1", 40.0).toDouble(), settings.value("nonCovalent/angleTolerance2", 40.0).toDouble() }; m_maximumDistances = { settings.value("nonCovalent/maximumDistance0", 1.0).toDouble(), settings.value("nonCovalent/maximumDistance1", 2.0).toDouble(), settings.value("nonCovalent/maximumDistance2", 2.0).toDouble() }; auto hydrogenBColor = settings.value("nonCovalent/lineColor0", QColor(64, 192, 255)) .value(); auto halogenBColor = settings.value("nonCovalent/lineColor1", QColor(128, 255, 64)) .value(); auto chalcogenBColor = settings.value("nonCovalent/lineColor2", QColor(255, 192, 64)) .value(); m_lineColors = { Vector3ub(hydrogenBColor.red(), hydrogenBColor.green(), hydrogenBColor.blue()), Vector3ub(halogenBColor.red(), halogenBColor.green(), halogenBColor.blue()), Vector3ub(chalcogenBColor.red(), chalcogenBColor.green(), chalcogenBColor.blue()) }; m_lineWidths = { settings.value("nonCovalent/lineWidth0", 5.0).toFloat(), settings.value("nonCovalent/lineWidth1", 5.0).toFloat(), settings.value("nonCovalent/lineWidth2", 5.0).toFloat() }; } NonCovalent::~NonCovalent() {} enum InteractionTypes { NONE = -1, HYDROGEN_BOND = 0, HALOGEN_BOND = 1, CHALCOGEN_BOND = 2 }; static enum InteractionTypes getInteractionType(const Molecule& molecule, Index i) { unsigned char inum = molecule.atomicNumber(i); switch (inum) { case 1: // hydrogen bond for (const Bond* b : molecule.bonds(i)) { Index j = b->getOtherAtom(i).index(); unsigned char jnum = molecule.atomicNumber(j); switch (jnum) { case 7: case 8: case 9: // F, O, N return HYDROGEN_BOND; } } break; case 9: case 17: case 35: case 53: // halogen bond for (const Bond* b : molecule.bonds(i)) { Index j = (b->atom1().index() == i ? b->atom2() : b->atom1()).index(); unsigned char jnum = molecule.atomicNumber(j); switch (jnum) { case 6: case 7: case 8: case 9: case 16: case 17: case 35: case 53: // F, O, N, Cl, Br, C, I, S return HALOGEN_BOND; } } break; case 8: case 16: case 34: case 52: // chalcogen bond for (const Bond* b : molecule.bonds(i)) { Index j = (b->atom1().index() == i ? b->atom2() : b->atom1()).index(); unsigned char jnum = molecule.atomicNumber(j); switch (jnum) { case 6: // C return CHALCOGEN_BOND; } } break; } return NONE; } static bool checkPairDonorIsValid(const Molecule& molecule, Index n, int interactionType) { unsigned char nnum = molecule.atomicNumber(n); switch (interactionType) { case HYDROGEN_BOND: switch (nnum) { case 7: case 8: case 9: case 17: // F, O, N, Cl return true; } break; case HALOGEN_BOND: switch (nnum) { case 7: case 8: case 9: case 17: // F, O, N, Cl return true; } break; case CHALCOGEN_BOND: switch (nnum) { case 7: case 8: case 9: case 16: case 17: case 34: case 52: // F, O, N, Cl, S, Se, Te return true; } break; } return false; } static bool checkAtomPairNotBonded(const Molecule& molecule, Index i, Index n) { Array bonds = molecule.bonds(i); /* Return true if all of the bonds from i are to atoms other than n */ return std::all_of(bonds.begin(), bonds.end(), [i, n](const Bond* b) { return b->getOtherAtom(i).index() != n; }); } static float computeAngle(Vector3 a, Vector3 b) { return acos(a.normalized().dot(b.normalized())); } static bool checkHoleVector(const Molecule& molecule, Index i, const Vector3& in, float angleTolerance) { Array bonds = molecule.bonds(i); Vector3 pos = molecule.atomPosition3d(i); /* Return true if any of the bonds to i forms a small enough angle * with 'in' at the opposite side of atom 'i' */ for (const Bond* b : bonds) { Index n = b->getOtherAtom(i).index(); Vector3 npos = molecule.atomPosition3d(n); float oppositeAngle = M_PI - computeAngle(in, npos - pos); if (oppositeAngle <= angleTolerance) return true; } return false; } static bool checkPairVector(const Molecule& molecule, Index n, const Vector3& in, float angleTolerance) { AtomHybridization hybridization = AtomUtilities::perceiveHybridization(molecule.atom(n)); Array bonds = molecule.bonds(n); size_t bondCount = bonds.size(); std::vector bondVectors; Vector3 pos = molecule.atomPosition3d(n); /* Compute all bond vectors around atom n */ for (const Bond* b : bonds) bondVectors.emplace_back( molecule.atomPosition3d(b->getOtherAtom(n).index()) - pos); float pairAngle; switch (hybridization) { case Core::SP3: switch (bondCount) { case 0: pairAngle = 0.0f; break; case 1: pairAngle = fabs(computeAngle(bondVectors[0], in) - M_TETRAHEDRAL); break; case 2: { Vector3 pairVector = AtomUtilities::generateNewBondVector( molecule.atom(n), bondVectors, hybridization); pairAngle = computeAngle(pairVector, in); bondVectors.push_back(pairVector); pairVector = AtomUtilities::generateNewBondVector( molecule.atom(n), bondVectors, hybridization); pairAngle = std::min(pairAngle, computeAngle(pairVector, in)); break; } case 3: { Vector3 pairVector = AtomUtilities::generateNewBondVector( molecule.atom(n), bondVectors, hybridization); pairAngle = computeAngle(pairVector, in); break; } default: return false; } break; case Core::SP2: switch (bondCount) { case 0: pairAngle = 0.0f; break; case 1: pairAngle = fabs(computeAngle(bondVectors[0], in) - M_TRIGONAL); break; case 2: { Vector3 pairVector = AtomUtilities::generateNewBondVector( molecule.atom(n), bondVectors, hybridization); pairAngle = computeAngle(pairVector, in); break; } default: return false; } break; case Core::SP: switch (bondCount) { case 0: pairAngle = 0.0f; break; case 1: { pairAngle = fabs(computeAngle(bondVectors[0], in) - M_PI); break; } default: return false; } break; default: return true; } return pairAngle <= angleTolerance; } void NonCovalent::process(const Molecule& molecule, Rendering::GroupNode& node) { std::vector enabledAtoms; Array enabledPositions; Array atomRadii; const size_t atomCount = molecule.atomCount(); for (Index i = 0; i < atomCount; ++i) { enabledAtoms.push_back(i); enabledPositions.push_back(molecule.atomPosition3d(i)); atomRadii.push_back(Elements::radiusVDW(molecule.atomicNumber(i))); } float absoluteMaxDistance = *std::max_element(m_maximumDistances.begin(), m_maximumDistances.end()); NeighborPerceiver perceiver(enabledPositions, absoluteMaxDistance); auto* geometry = new GeometryNode; node.addChild(geometry); std::array lineGroups; for (Index type = 0; type < 3; type++) { lineGroups[type] = new DashedLineGeometry; lineGroups[type]->identifier().molecule = &molecule; lineGroups[type]->identifier().type = Rendering::BondType; lineGroups[type]->setLineWidth(m_lineWidths[type]); geometry->addDrawable(lineGroups[type]); } Array neighbors; for (Index i : enabledAtoms) { enum InteractionTypes interactionType = getInteractionType(molecule, i); if (interactionType == NONE) continue; Vector3 pos = molecule.atomPosition3d(i); double radius = atomRadii[i]; perceiver.getNeighborsInclusiveInPlace(neighbors, pos); for (Index ni : neighbors) { Index n = enabledAtoms[ni]; if (!checkPairDonorIsValid(molecule, n, interactionType)) continue; Vector3 npos = molecule.atomPosition3d(n); double nradius = atomRadii[n]; Vector3 distance_vector = npos - pos; if (distance_vector.norm() > m_maximumDistances[interactionType] + radius + nradius) continue; if (!checkAtomPairNotBonded(molecule, i, n)) continue; float angleTolerance = m_angleTolerancesDegrees[interactionType] * M_PI / 180.0; if (!checkHoleVector(molecule, i, distance_vector, angleTolerance)) continue; if (!checkPairVector(molecule, n, -distance_vector, angleTolerance)) continue; lineGroups[interactionType]->addDashedLine( pos.cast(), npos.cast(), m_lineColors[interactionType], 8); } } } QWidget* NonCovalent::setupWidget() { auto* widget = new QWidget(qobject_cast(this->parent())); auto* v = new QVBoxLayout; auto* tabs = new QTabWidget; for (Index i = 0; i < 3; i++) { // angle tolerance auto* angle_spin = new QDoubleSpinBox; angle_spin->setRange(0.0, 180.0); angle_spin->setSingleStep(1.0); angle_spin->setDecimals(0); angle_spin->setSuffix(tr(" °")); angle_spin->setValue(m_angleTolerancesDegrees[i]); QObject::connect( angle_spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this, i](float tolerance) { return setAngleTolerance(tolerance, i); }); // maximum distance auto* distance_spin = new QDoubleSpinBox; distance_spin->setRange(1.0, 10.0); distance_spin->setSingleStep(0.1); distance_spin->setDecimals(1); distance_spin->setSuffix(tr(" Å")); distance_spin->setValue(m_maximumDistances[i]); QObject::connect( distance_spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this, i](float distance) { return setMaximumDistance(distance, i); }); // line width auto* lineWidth_spin = new QDoubleSpinBox; lineWidth_spin->setRange(1.0, 10.0); lineWidth_spin->setSingleStep(0.5); lineWidth_spin->setDecimals(1); lineWidth_spin->setValue(m_lineWidths[i]); QObject::connect(lineWidth_spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this, i](float width) { return setLineWidth(width, i); }); auto* form = new QFormLayout; form->addRow(QObject::tr("Angle tolerance:"), angle_spin); form->addRow(QObject::tr("Maximum distance:"), distance_spin); form->addRow(QObject::tr("Line width:"), lineWidth_spin); auto* page = new QWidget; page->setLayout(form); tabs->addTab(page, INTERACTION_NAMES[i]); } v->addWidget(tabs); v->addStretch(1); widget->setLayout(v); return widget; } void NonCovalent::setAngleTolerance(float angleTolerance, Index index) { m_angleTolerancesDegrees[index] = float(angleTolerance); emit drawablesChanged(); QSettings settings; settings.setValue(QString("nonCovalent/angleTolerance%1").arg(index), angleTolerance); } void NonCovalent::setMaximumDistance(float maximumDistance, Index index) { m_maximumDistances[index] = float(maximumDistance); emit drawablesChanged(); QSettings settings; settings.setValue(QString("nonCovalent/maximumDistance%1").arg(index), maximumDistance); } void NonCovalent::setLineWidth(float width, Index index) { m_lineWidths[index] = width; emit drawablesChanged(); QSettings settings; settings.setValue(QString("nonCovalent/lineWidth%1").arg(index), width); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/noncovalent/noncovalent.h000066400000000000000000000036151506155467400252250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_NONCOVALENT_H #define AVOGADRO_QTPLUGINS_NONCOVALENT_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief Predict some non-covalent interactions, like hydrogen bonds. * @author Aritz Erkiaga */ class NonCovalent : public QtGui::ScenePlugin { Q_OBJECT public: explicit NonCovalent(QObject* parent = nullptr); ~NonCovalent() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Non-Covalent"); } QString description() const override { return tr("Render a few non-covalent interactions."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } public slots: void setAngleTolerance(float angleTolerance, Index index); void setMaximumDistance(float maximumDistance, Index index); void setLineWidth(float width, Index index); private: const std::string m_name = "Non-Covalent"; const std::array INTERACTION_NAMES = { tr("Hydrogen"), tr("Halogen"), tr("Chalcogen") }; std::array m_angleTolerancesDegrees; std::array m_maximumDistances; std::array m_lineColors; std::array m_lineWidths; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_NONCOVALENT_H avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/000077500000000000000000000000001506155467400221225ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/CMakeLists.txt000066400000000000000000000007041506155467400246630ustar00rootroot00000000000000set(openbabel_srcs conformersearchdialog.cpp obcharges.cpp obfileformat.cpp obforcefielddialog.cpp obprocess.cpp openbabel.cpp ) set(openbabel_uis conformersearchdialog.ui obforcefielddialog.ui ) avogadro_plugin(OpenBabel "OpenBabel extension" ExtensionPlugin openbabel.h OpenBabel "${openbabel_srcs}" "${openbabel_uis}" ) target_link_libraries(OpenBabel PRIVATE Avogadro::IO Avogadro::Calc nlohmann_json::nlohmann_json) avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/conformersearchdialog.cpp000066400000000000000000000116111506155467400271660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "conformersearchdialog.h" #include #include #include namespace Avogadro { ConformerSearchDialog::ConformerSearchDialog(QWidget* parent) : QDialog(parent) { ui.setupUi(this); connect(ui.systematicRadio, SIGNAL(toggled(bool)), this, SLOT(systematicToggled(bool))); connect(ui.randomRadio, SIGNAL(toggled(bool)), this, SLOT(randomToggled(bool))); connect(ui.weightedRadio, SIGNAL(toggled(bool)), this, SLOT(weightedToggled(bool))); connect(ui.geneticRadio, SIGNAL(toggled(bool)), this, SLOT(geneticToggled(bool))); connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); m_method = 1; // systematic m_numConformers = 100; ui.numSpin->setValue(0); ui.systematicRadio->setChecked(true); ui.randomRadio->setChecked(false); ui.weightedRadio->setChecked(false); ui.geneticRadio->setChecked(false); ui.childrenSpinBox->setEnabled(false); ui.mutabilitySpinBox->setEnabled(false); ui.convergenceSpinBox->setEnabled(false); ui.scoringComboBox->setEnabled(false); } ConformerSearchDialog::~ConformerSearchDialog() {} void ConformerSearchDialog::buttonClicked(QAbstractButton* button) { if (button == ui.buttonBox->button(QDialogButtonBox::Ok)) { emit accepted(); } close(); } QStringList ConformerSearchDialog::options() const { QStringList options; // in OB v3.2 options << "--steps" << QString::number(ui.optimizationStepsSpinBox->value()); if (ui.systematicRadio->isChecked()) options << "--systematic"; else if (ui.randomRadio->isChecked()) { options << "--random"; options << "--nconf" << QString::number(ui.numSpin->value()); } else if (ui.weightedRadio->isChecked()) { options << "--weighted"; options << "--nconf" << QString::number(ui.numSpin->value()); } else if (ui.geneticRadio->isChecked()) { // genetic is the default, no need to specify options << "--nconf" << QString::number(ui.numSpin->value()); options << "--children" << QString::number(ui.childrenSpinBox->value()); options << "--mutability" << QString::number(ui.mutabilitySpinBox->value()); options << "--convergence" << QString::number(ui.convergenceSpinBox->value()); options << "--scoring" << ui.scoringComboBox->currentText(); } return options; } void ConformerSearchDialog::systematicToggled(bool checked) { if (checked) { m_method = 1; ui.systematicRadio->setChecked(true); ui.randomRadio->setChecked(false); ui.weightedRadio->setChecked(false); ui.geneticRadio->setChecked(false); ui.childrenSpinBox->setEnabled(false); ui.mutabilitySpinBox->setEnabled(false); ui.convergenceSpinBox->setEnabled(false); ui.scoringComboBox->setEnabled(false); ui.numSpin->setEnabled(false); ui.numSpin->setValue(0); } } void ConformerSearchDialog::randomToggled(bool checked) { if (checked) { m_method = 2; ui.systematicRadio->setChecked(false); ui.randomRadio->setChecked(true); ui.weightedRadio->setChecked(false); ui.geneticRadio->setChecked(false); ui.childrenSpinBox->setEnabled(false); ui.mutabilitySpinBox->setEnabled(false); ui.convergenceSpinBox->setEnabled(false); ui.scoringComboBox->setEnabled(false); ui.numSpin->setEnabled(true); ui.numSpin->setValue(100); } } void ConformerSearchDialog::weightedToggled(bool checked) { if (checked) { m_method = 3; ui.systematicRadio->setChecked(false); ui.randomRadio->setChecked(false); ui.weightedRadio->setChecked(true); ui.geneticRadio->setChecked(false); ui.childrenSpinBox->setEnabled(false); ui.mutabilitySpinBox->setEnabled(false); ui.convergenceSpinBox->setEnabled(false); ui.scoringComboBox->setEnabled(false); ui.numSpin->setEnabled(true); ui.numSpin->setValue(100); } } void ConformerSearchDialog::geneticToggled(bool checked) { if (checked) { m_method = 4; ui.systematicRadio->setChecked(false); ui.randomRadio->setChecked(false); ui.weightedRadio->setChecked(false); ui.geneticRadio->setChecked(true); ui.childrenSpinBox->setEnabled(true); ui.mutabilitySpinBox->setEnabled(true); ui.convergenceSpinBox->setEnabled(true); ui.scoringComboBox->setEnabled(true); ui.numSpin->setEnabled(true); ui.numSpin->setValue(100); } } void ConformerSearchDialog::accept() { m_numConformers = ui.numSpin->value(); hide(); } void ConformerSearchDialog::reject() { hide(); } int ConformerSearchDialog::numConformers() { return m_numConformers; } int ConformerSearchDialog::method() { return m_method; } } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/conformersearchdialog.h000066400000000000000000000021371506155467400266360ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef CONFORMERSEARCHDIALOG_H #define CONFORMERSEARCHDIALOG_H #include #include "ui_conformersearchdialog.h" namespace Avogadro { class ConformerSearchDialog : public QDialog { Q_OBJECT public: //! Constructor explicit ConformerSearchDialog(QWidget* parent = 0); //! Deconstructor ~ConformerSearchDialog() override; int method(); int numConformers(); QStringList options() const; public slots: void accept() override; void reject() override; void systematicToggled(bool checked); void randomToggled(bool checked); void weightedToggled(bool checked); void geneticToggled(bool checked); void buttonClicked(QAbstractButton* button); signals: void accepted(); private: Ui::ConformerSearchDialog ui; int m_method; int m_numConformers; }; } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/conformersearchdialog.ui000066400000000000000000000165131506155467400270270ustar00rootroot00000000000000 ConformerSearchDialog 0 0 338 400 Conformer Search Method Number of conformers: 10000 Systematic rotor search Random rotor search Weighted rotor search Genetic algorithm search Optimization per conformer: steps 5 250 25 Genetic Algorithm Options number of children for each parent geometry Children: number of children for each parent geometry 1 9999 5 mutation frequency (lower = more frequent mutations) Mutability: mutation frequency (lower = more frequent mutations) 1 9999 5 number of identical generations before convergence is reached Convergence: number of identical generations before convergence is reached 2 999 25 Scoring method: scoring method for the genetic algorithm (RMSD = geometric distance, energy = lowest energies) RMSD Energy Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ConformerSearchDialog accept() 248 254 157 274 buttonBox rejected() ConformerSearchDialog reject() 316 260 286 274 avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obcharges.cpp000066400000000000000000000130211506155467400245600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "obcharges.h" #include "obprocess.h" #include #include #include #include #include #include namespace Avogadro { using Core::Array; using Core::Molecule; namespace QtPlugins { class OBCharges::ProcessListener : public QObject { Q_OBJECT public: ProcessListener() : QObject(), m_finished(false) {} bool waitForOutput(Array& output, int msTimeout = 120000) { if (!wait(msTimeout)) return false; // success! output = m_output; return true; } public slots: void responseReceived(const Array output) { m_finished = true; m_output = output; } private: bool wait(int msTimeout) { QTimer timer; timer.start(msTimeout); while (timer.isActive() && !m_finished) qApp->processEvents(QEventLoop::AllEvents, 500); return m_finished; } // OBProcess* m_process; bool m_finished; Array m_output; }; OBCharges::OBCharges(const std::string& id) : ChargeModel(), m_identifier(id) { // set the element mask based on our type / identifier m_elements.reset(); if (id == "eqeq") { // defined for 1-84 for (unsigned int i = 1; i <= 84; ++i) { m_elements.set(i); } } else if (id == "eem") { // H, Li, B, C, N, O, F, Na, Mg, Si, P, S, Cl m_elements.set(1); m_elements.set(3); m_elements.set(5); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(11); m_elements.set(12); m_elements.set(14); m_elements.set(15); m_elements.set(16); m_elements.set(17); } else if (id == "eem2015ba") { // H, C, N, O, F, P, S, Cl, Br, I m_elements.set(1); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(15); m_elements.set(16); m_elements.set(17); m_elements.set(35); m_elements.set(53); } else if (id == "gasteiger") { // H, C, N, O, F, P, S, Cl, Br, I, Al m_elements.set(1); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(13); m_elements.set(15); m_elements.set(16); m_elements.set(17); m_elements.set(35); m_elements.set(53); } else if (id == "mmff94") { // H, C, N, O, F, Si, P, S, Cl, Br, and I // ions - Fe, F, Cl, Br, Li, Na, K, Zn, Ca, Cu, Mg m_elements.set(1); m_elements.set(6); m_elements.set(7); m_elements.set(8); m_elements.set(9); m_elements.set(14); m_elements.set(15); m_elements.set(16); m_elements.set(17); m_elements.set(35); m_elements.set(53); // ions m_elements.set(3); m_elements.set(11); m_elements.set(12); m_elements.set(19); m_elements.set(20); m_elements.set(26); m_elements.set(29); m_elements.set(30); } } OBCharges::~OBCharges() {} std::string OBCharges::name() const { if (m_identifier == "eqeq") return "EQEq"; else if (m_identifier == "eem") return "EEM"; else if (m_identifier == "eem2015ba") return "EEM 2015"; else if (m_identifier == "gasteiger") return "Gasteiger"; else if (m_identifier == "mmff94") return "MMFF94"; else return ""; } MatrixX OBCharges::partialCharges(const Core::Molecule& molecule) const { MatrixX charges(molecule.atomCount(), 1); if (m_identifier.empty()) { // no identifier, so we can't get the charges return charges; } // check to see if we already have them in the molecule charges = molecule.partialCharges(m_identifier); // if the number of charges matches the number of atoms // and there's a non-zero charge, then we're done if (charges.rows() == static_cast(molecule.atomCount())) { for (unsigned int i = 0; i < charges.rows(); ++i) { if (abs(charges(i, 0)) > 0.00001) return charges; } } // otherwise, we're going to run obprocess to get the charges OBProcess process; ProcessListener listener; QObject::connect(&process, &OBProcess::chargesFinished, &listener, &ProcessListener::responseReceived); std::string outputString; // todo - check for failure, append errors, etc. m_cmlFormat.writeString(outputString, molecule); process.calculateCharges(QByteArray(outputString.c_str()), "cml", m_identifier); Core::Array output; if (!listener.waitForOutput(output)) { qDebug() << "Charges timed out."; return charges; } // push the output into our charges array for (unsigned int i = 0; i < output.size(); ++i) { charges(i, 0) = output[i]; } // workaround failed runs causing the code to freeze if (abs(charges(0, 0)) < 0.00001) charges(0, 0) = 0.0001; // check the size if (output.size() != molecule.atomCount()) { qDebug() << "Charges size mismatch."; return charges; } return charges; } MatrixX OBCharges::partialCharges(Core::Molecule& molecule) const { MatrixX charges = partialCharges(static_cast(molecule)); // cache the charges and allow them to show up in output molecule.setPartialCharges(m_identifier, charges); return charges; } } // namespace QtPlugins } // namespace Avogadro #include "obcharges.moc" avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obcharges.h000066400000000000000000000036341506155467400242360ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OBCHARGES_H #define AVOGADRO_QTPLUGINS_OBCHARGES_H #include #include namespace Avogadro { namespace QtPlugins { class OBCharges : public Avogadro::Calc::ChargeModel { public: OBCharges(const std::string& identifier = ""); ~OBCharges() override; /** * Create a new instance of the file format class. Ownership passes to the * caller. */ OBCharges* newInstance() const override { return new OBCharges; } /** * @brief A unique identifier defined by the file */ std::string identifier() const override { return m_identifier; } /** * @brief Set the identifier */ virtual void setIdentifier(const std::string& identifier) { m_identifier = identifier; } /** * @brief Based on the identifiers -- for the menus, etc. */ std::string name() const override; /** * @brief The element mask for a particular OB charge model (e.g., Gasteiger) * @return the mask relevant for this method */ Core::Molecule::ElementMask elements() const override { return m_elements; } /** * @brief Retrieve the relevant charges from the molecule for our defined type */ MatrixX partialCharges(Core::Molecule& mol) const override; MatrixX partialCharges(const Core::Molecule& mol) const override; /** * @brief Synchronous use of the OBProcess. */ class ProcessListener; protected: std::string m_identifier; std::string m_name; Core::Molecule::ElementMask m_elements; mutable Io::CmlFormat m_cmlFormat; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OBCHARGES_H avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obfileformat.cpp000066400000000000000000000211741506155467400253040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "obfileformat.h" #include "obprocess.h" #include #include #include #include #include #include using namespace std::string_literals; using json = nlohmann::json; namespace Avogadro::QtPlugins { /** * @brief The ProcessListener class allows synchronous use of OBProcess. */ class OBFileFormat::ProcessListener : public QObject { Q_OBJECT public: ProcessListener() : QObject(), m_finished(false) {} bool waitForOutput(QByteArray& output, int msTimeout = 120000) { if (!wait(msTimeout)) return false; // success! output = m_output; return true; } public slots: void responseReceived(const QByteArray& output) { m_finished = true; m_output = output; } private: bool wait(int msTimeout) { QTimer timer; timer.start(msTimeout); while (timer.isActive() && !m_finished) qApp->processEvents(QEventLoop::AllEvents, 500); return m_finished; } OBProcess* m_process; bool m_finished; QByteArray m_output; }; OBFileFormat::OBFileFormat(const std::string& name_, const std::string& identifier_, const std::string& description_, const std::string& specificationUrl_, const std::vector fileExtensions_, const std::vector mimeTypes_, const std::string& defaultFormat_, bool fileOnly_) : Io::FileFormat(), m_description(description_), m_fileExtensions(fileExtensions_), m_mimeTypes(mimeTypes_), m_identifier(identifier_), m_name(name_), m_specificationUrl(specificationUrl_), m_defaultFormat(defaultFormat_), m_fileOnly(fileOnly_) { } OBFileFormat::~OBFileFormat() {} bool OBFileFormat::read(std::istream& in, Core::Molecule& molecule) { json opts; if (!options().empty()) opts = json::parse(options(), nullptr, false); else opts = json::object(); // Allow blocking until the read is completed. OBProcess proc; ProcessListener listener; QObject::connect(&proc, SIGNAL(convertFinished(QByteArray)), &listener, SLOT(responseReceived(QByteArray))); // Just grab the first file extension from the list -- all extensions for a // given format map to the same parsers in OB. if (m_fileExtensions.empty()) { appendError("Internal error: No file extensions set."); return false; } // If we are reading a pure-2D format, generate 3D coordinates: QStringList options; QStringList formats2D; formats2D << "smi" << "smiles" << "can" << "inchi" << "wln"; if (formats2D.contains(QString::fromStdString(m_fileExtensions.front()))) options << "--gen3d"; // Check if we have extra arguments for open babel json extraArgs = opts.value("arguments", json::object()); if (extraArgs.is_array()) { for (const auto& arg : extraArgs) { if (arg.is_string()) options << arg.get().c_str(); } } // check if we're going to read to a different format // default is CML or CJSON QString format = QString::fromStdString(opts.value("format", m_defaultFormat)); if (!m_fileOnly) { // Determine length of data in.seekg(0, std::ios_base::end); std::istream::pos_type length = in.tellg(); in.seekg(0, std::ios_base::beg); in.clear(); // Extract char data QByteArray input; input.resize(static_cast(length)); in.read(input.data(), length); if (in.gcount() != length) { appendError("Error reading stream into buffer!"); return false; } // Perform the conversion. if (!proc.convert(input, QString::fromStdString(m_fileExtensions.front()), format, options)) { appendError("OpenBabel conversion failed!"); return false; } } else { // Can only read files. Need absolute path. QString filename = QString::fromStdString(fileName()); if (filename.isEmpty()) { // no choice but to write to a temporary file in.seekg(0, std::ios_base::end); std::istream::pos_type length = in.tellg(); in.seekg(0, std::ios_base::beg); in.clear(); // Extract data, hope it's not big QByteArray input; input.resize(static_cast(length)); in.read(input.data(), length); QTemporaryFile tmpFile; tmpFile.setAutoRemove(false); tmpFile.open(); tmpFile.write(input.data()); tmpFile.close(); filename = tmpFile.fileName(); } if (!QFileInfo(filename).isAbsolute()) { appendError("Internal error -- filename must be absolute! " + filename.toStdString()); return false; } // Perform the conversion. if (!proc.convert(filename, QString::fromStdString(m_fileExtensions.front()), format, options)) { appendError("OpenBabel conversion failed!"); return false; } } QByteArray output; if (!listener.waitForOutput(output)) { appendError("Conversion timed out."s); return false; } if (output.isEmpty()) { appendError("OpenBabel error: conversion failed."s); return false; } if (format == "cml") { if (!m_cmlFormat.readString(std::string(output.constData()), molecule)) { appendError("Error while reading OpenBabel-generated CML:"s); appendError(m_cmlFormat.error()); return false; } } else if (format == "cjson") { if (!m_cjsonFormat.readString(std::string(output.constData()), molecule)) { appendError("Error while reading OpenBabel-generated CJSON:"s); appendError(m_cjsonFormat.error()); return false; } } else if (format == "pdb") { if (!m_pdbFormat.readString(std::string(output.constData()), molecule)) { appendError("Error while reading OpenBabel-generated PDB:"s); appendError(m_pdbFormat.error()); return false; } } else { return false; // unknown format } return true; } bool OBFileFormat::write(std::ostream& out, const Core::Molecule& molecule) { json opts; if (!options().empty()) opts = json::parse(options(), nullptr, false); else opts = json::object(); // Check if we have extra arguments for open babel QStringList options; json extraArgs = opts.value("arguments", json::object()); if (extraArgs.is_array()) { for (const auto& arg : extraArgs) { if (arg.is_string()) options << arg.get().c_str(); } } #ifndef NDEBUG qDebug() << " writing to " << m_defaultFormat.c_str(); #endif // Generate CML or CJSON to give to OpenBabel std::string outputString; if (m_defaultFormat == "cml") { if (!m_cmlFormat.writeString(outputString, molecule)) { appendError("Error while writing CML:"s); appendError(m_cmlFormat.error()); return false; } } else if (m_defaultFormat == "cjson") { if (!m_cjsonFormat.writeString(outputString, molecule)) { appendError("Error while writing CJSON:"s); appendError(m_cjsonFormat.error()); return false; } } // Block until the OpenBabel conversion finishes: OBProcess proc; ProcessListener listener; QObject::connect(&proc, SIGNAL(convertFinished(QByteArray)), &listener, SLOT(responseReceived(QByteArray))); // Just grab the first file extension from the list -- all extensions for a // given format map to the same parsers in OB. if (m_fileExtensions.empty()) { appendError("Internal error: No file extensions set."); return false; } proc.convert(QByteArray(outputString.c_str()), m_defaultFormat.c_str(), QString::fromStdString(m_fileExtensions.front()), options); QByteArray output; if (!listener.waitForOutput(output)) { appendError("Conversion timed out."s); return false; } if (output.isEmpty()) { appendError("OpenBabel error: conversion failed."s); return false; } out.write(output.constData(), output.size()); return true; } void OBFileFormat::clear() { Io::FileFormat::clear(); } Io::FileFormat* OBFileFormat::newInstance() const { return new OBFileFormat(m_name, m_identifier, m_description, m_specificationUrl, m_fileExtensions, m_mimeTypes, m_defaultFormat, m_fileOnly); } } // namespace Avogadro::QtPlugins #include "obfileformat.moc" avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obfileformat.h000066400000000000000000000054301506155467400247460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OBFILEFORMAT_H #define AVOGADRO_QTPLUGINS_OBFILEFORMAT_H #include #include #include #include namespace Avogadro { namespace QtPlugins { /** * @brief The OBFileFormat class implements the FileFormat API for using an * OBProcess for file IO. */ class OBFileFormat : public Avogadro::Io::FileFormat { public: OBFileFormat(const std::string& name_, const std::string& identifier_, const std::string& description_, const std::string& specificationUrl_, const std::vector fileExtensions_, const std::vector mimeTypes_, const std::string& defaultFormat_, bool fileOnly_ = false); ~OBFileFormat() override; Operations supportedOperations() const override { return m_rwFlags | File | (m_fileOnly ? None : Stream | String); } bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; void clear() override; FileFormat* newInstance() const override; std::string description() const override { return m_description; } std::string identifier() const override { return m_identifier; } std::string name() const override { return m_name; } std::string specificationUrl() const override { return m_specificationUrl; } std::vector fileExtensions() const override { return m_fileExtensions; } std::vector mimeTypes() const override { return m_mimeTypes; } /** * Set whether this format supports read and/or write operations. */ void setReadWriteFlags(Operations ops) { m_rwFlags = ops & ReadWrite; } /** Whether or not the format supports files only. This is needed for * multifile formats. */ void setFileOnly(bool f) { m_fileOnly = f; } bool fileOnly() const { return m_fileOnly; } /** @} */ class ProcessListener; private: Operations m_rwFlags; std::string m_description; std::vector m_fileExtensions; std::vector m_mimeTypes; std::string m_identifier; std::string m_name; std::string m_specificationUrl; std::string m_defaultFormat; bool m_fileOnly; // internal format objects for interchange with obabel Io::CjsonFormat m_cjsonFormat; Io::CmlFormat m_cmlFormat; Io::PdbFormat m_pdbFormat; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OBFILEFORMAT_H avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obforcefielddialog.cpp000066400000000000000000000202761506155467400264400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "obforcefielddialog.h" #include "ui_obforcefielddialog.h" #include #include #include #include #include // for log10 namespace Avogadro::QtPlugins { enum OptimizationAlgorithm { SteepestDescent = 0, ConjugateGradient }; enum LineSearchMethod { Simple = 0, Newton }; OBForceFieldDialog::OBForceFieldDialog(const QStringList& forceFields, QWidget* parent_) : QDialog(parent_), ui(new Ui::OBForceFieldDialog) { ui->setupUi(this); ui->forceField->addItems(forceFields); updateRecommendedForceField(); connect(ui->useRecommended, SIGNAL(toggled(bool)), SLOT(useRecommendedForceFieldToggled(bool))); QSettings settings; bool autoDetect = settings.value("openbabel/optimizeGeometry/autoDetect", true).toBool(); ui->useRecommended->setChecked(autoDetect); } OBForceFieldDialog::~OBForceFieldDialog() { delete ui; } QStringList OBForceFieldDialog::prompt(QWidget* parent_, const QStringList& forceFields, const QStringList& startingOptions, const QString& recommendedForceField_) { OBForceFieldDialog dlg(forceFields, parent_); dlg.setOptions(startingOptions); dlg.setRecommendedForceField(recommendedForceField_); QStringList options; if (static_cast(dlg.exec()) == Accepted) options = dlg.options(); return options; } QStringList OBForceFieldDialog::options() const { QStringList opts; opts << "--crit" << QString::number(std::pow(10.0f, ui->energyConv->value()), 'e', 0) << "--ff" << ui->forceField->currentText() << "--steps" << QString::number(ui->stepLimit->value()) << "--rvdw" << QString::number(ui->vdwCutoff->value()) << "--rele" << QString::number(ui->eleCutoff->value()) << "--freq" << QString::number(ui->pairFreq->value()); switch (static_cast(ui->algorithm->currentIndex())) { case SteepestDescent: opts << "--sd"; break; default: case ConjugateGradient: break; } switch (static_cast(ui->lineSearch->currentIndex())) { case Newton: opts << "--newton"; break; default: case Simple: break; } if (ui->enableCutoffs->isChecked()) opts << "--cut"; return opts; } void OBForceFieldDialog::setOptions(const QStringList& opts) { // Set some defaults. These match the defaults in obabel -L minimize ui->energyConv->setValue(-6); ui->algorithm->setCurrentIndex(static_cast(ConjugateGradient)); ui->lineSearch->setCurrentIndex(static_cast(Simple)); ui->stepLimit->setValue(2500); ui->enableCutoffs->setChecked(false); ui->vdwCutoff->setValue(10.0); ui->eleCutoff->setValue(10.0); ui->pairFreq->setValue(10); for (QStringList::const_iterator it = opts.constBegin(), itEnd = opts.constEnd(); it < itEnd; ++it) { // We'll always use log: if (*it == "--log") { continue; } // Energy convergence: else if (*it == "--crit") { ++it; if (it == itEnd) { qWarning() << "OBForceFieldDialog::setOptions: " "--crit missing argument."; continue; } bool ok; float econv = it->toFloat(&ok); if (!ok) { qWarning() << "OBForceFieldDialog::setOptions: " "--crit is not numeric: " << *it; continue; } // We just show the econv as 10^(x), so calculate the nearest x int exponent = static_cast(std::floor(std::log10(econv) + 0.5)); ui->energyConv->setValue(exponent); continue; } // Use steepest descent? else if (*it == "--sd") { ui->algorithm->setCurrentIndex(SteepestDescent); continue; } // Use newton linesearch? else if (*it == "--newton") { ui->lineSearch->setCurrentIndex(Newton); continue; } // Force field? else if (*it == "--ff") { ++it; if (it == itEnd) { qWarning() << "OBForceFieldDialog::setOptions: " "--ff missing argument."; continue; } int index = ui->forceField->findText(*it); if (index < 0) { qWarning() << "OBForceFieldDialog::setOptions: " "--ff unknown: " << *it; continue; } ui->forceField->setCurrentIndex(index); continue; } // Step limit? else if (*it == "--steps") { ++it; if (it == itEnd) { qWarning() << "OBForceFieldDialog::setOptions: " "--steps missing argument."; continue; } bool ok; int numSteps = it->toInt(&ok); if (!ok) { qWarning() << "OBForceFieldDialog::setOptions: " "--steps is not numeric: " << *it; continue; } ui->stepLimit->setValue(numSteps); continue; } // Use cutoff? else if (*it == "--cut") { ui->enableCutoffs->setChecked(true); continue; } // Van der Waals cutoff else if (*it == "--rvdw") { ++it; if (it == itEnd) { qWarning() << "OBForceFieldDialog::setOptions: " "--rvdw missing argument."; continue; } bool ok; double cutoff = it->toDouble(&ok); if (!ok) { qWarning() << "OBForceFieldDialog::setOptions: " "--rvdw is not numeric: " << *it; continue; } ui->vdwCutoff->setValue(cutoff); continue; } // electrostatic cutoff else if (*it == "--rele") { ++it; if (it == itEnd) { qWarning() << "OBForceFieldDialog::setOptions: " "--rele missing argument."; continue; } bool ok; double cutoff = it->toDouble(&ok); if (!ok) { qWarning() << "OBForceFieldDialog::setOptions: " "--rele is not numeric: " << *it; continue; } ui->eleCutoff->setValue(cutoff); continue; } // Pair update frequency: else if (*it == "--freq") { ++it; if (it == itEnd) { qWarning() << "OBForceFieldDialog::setOptions: " "--freq missing argument."; continue; } bool ok; int numSteps = it->toInt(&ok); if (!ok) { qWarning() << "OBForceFieldDialog::setOptions: " "--freq is not numeric: " << *it; continue; } ui->pairFreq->setValue(numSteps); continue; } // ????? else { qWarning() << "OBForceFieldDialog::setOptions: " "Unrecognized option: " << *it; } } } void OBForceFieldDialog::setRecommendedForceField(const QString& rff) { if (rff == m_recommendedForceField) return; if (ui->forceField->findText(rff) == -1) return; m_recommendedForceField = rff; updateRecommendedForceField(); } void OBForceFieldDialog::useRecommendedForceFieldToggled(bool state) { if (!m_recommendedForceField.isEmpty()) { if (state) { int index = ui->forceField->findText(m_recommendedForceField); if (index >= 0) { ui->forceField->setCurrentIndex(index); } } } ui->forceField->setEnabled(!state); QSettings().setValue("openbabel/optimizeGeometry/autoDetect", state); } void OBForceFieldDialog::updateRecommendedForceField() { if (m_recommendedForceField.isEmpty()) { ui->useRecommended->hide(); ui->forceField->setEnabled(true); } else { ui->useRecommended->setText( tr("Autodetect (%1)").arg(m_recommendedForceField)); // Force the combo box to update if needed: useRecommendedForceFieldToggled(ui->useRecommended->isChecked()); ui->useRecommended->show(); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obforcefielddialog.h000066400000000000000000000065341506155467400261060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OBFORCEFIELDDIALOG_H #define AVOGADRO_QTPLUGINS_OBFORCEFIELDDIALOG_H #include namespace Avogadro { namespace QtPlugins { namespace Ui { class OBForceFieldDialog; } /** * @brief The OBForceFieldDialog class is used to prompt the user for parameters * to be used in an OpenBabel force field optimization. */ class OBForceFieldDialog : public QDialog { Q_OBJECT public: /** * Construct a new dialog using the forcefields in @a forceFields. */ explicit OBForceFieldDialog(const QStringList& forceFields, QWidget* parent_ = nullptr); ~OBForceFieldDialog() override; /** * Construct a new dialog using the forcefields in @a forceFields and * initialize the options to those in @a startingOptions (see setOptions). * If the user chooses the recommended force field, @a recommendedForceField_ * will be set. This is useful for preferring a specific force field for a * particular molecule. * When the user closes the dialog, the options they selected are returned. If * the user cancels the dialog, an empty list is returned. */ static QStringList prompt(QWidget* parent_, const QStringList& forceFields, const QStringList& startingOptions, const QString& recommendedForceField_ = QString()); /** * Get/set the options displayed in the dialog. The option format is a list of * strings that may be used directly as arguments in a call to * QProcess::start, with the exception of the `-i`, * `-o` and `--minimize` options, which are not used by this * class. See `obabel -L minimize` for a complete listing of available * options. * * Each option (and argument, if applicable) must be a separate string in the * list. For instance, to refer to the options in the call: @code obabel -icml -ocml --minimize --log --crit 1e-05 --ff Ghemical --sd" @endcode * * The option list should contain, in order: * - `--crit` * - `1e-05` * - `--ff` * - `Ghemical` * - `--sd` * * @note The `--log` option is always added in the list returned by * options, and is ignored by the setOptions method. * * @{ */ QStringList options() const; void setOptions(const QStringList& opts); /**@}*/ /** * Get/set the recommended forcefield for the current molecule. If an empty * string, the user will not be shown an option to use the recommended * forcefield. * If the string is non-empty (and in the forceFields list passed in the * constructor), the user will have the option of setting the forcefield to * this value. * * @{ */ QString recommendedForceField() const { return m_recommendedForceField; } void setRecommendedForceField(const QString& rff); /**@}*/ private slots: void useRecommendedForceFieldToggled(bool state); private: void updateRecommendedForceField(); Ui::OBForceFieldDialog* ui; QString m_recommendedForceField; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OBFORCEFIELDDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obforcefielddialog.ui000066400000000000000000000212541506155467400262700ustar00rootroot00000000000000 Avogadro::QtPlugins::OBForceFieldDialog 0 0 327 388 0 0 Geometry Optimization Parameters QLayout::SetFixedSize Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Optimization Method Force field: Optimization algorithm: Line search technique: Steepest Descent Conjugate Gradient Simple Newton's Method Autodetect Qt::Horizontal Limit Non-Bonded Interactions true false Van der Waals cutoff distance: Pair update frequency: electrostatic cutoff distance: Å 0.250000000000000 100.000000000000000 0.250000000000000 10.000000000000000 Å 0.250000000000000 100.000000000000000 0.250000000000000 10.000000000000000 steps 1 100 1 10 Convergence Criteria "Energy" convergence: Step limit: units 10^ -10 9 -6 steps 0 100000 250 2500 forceField useRecommended algorithm lineSearch energyConv stepLimit enableCutoffs vdwCutoff eleCutoff pairFreq buttonBox buttonBox accepted() Avogadro::QtPlugins::OBForceFieldDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::QtPlugins::OBForceFieldDialog reject() 316 260 286 274 avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obprocess.cpp000066400000000000000000000411341506155467400246300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "obprocess.h" #include #include #include #include #include #include namespace Avogadro::QtPlugins { OBProcess::OBProcess(QObject* parent_) : QObject(parent_), m_processLocked(false), m_aborted(false), m_process(new QProcess(this)), #if defined(_WIN32) m_obabelExecutable("obabel.exe") #else m_obabelExecutable("obabel") #endif { // Read the AVO_OBABEL_EXECUTABLE env var to optionally override the // executable used for obabel. QByteArray obabelExec = qgetenv("AVO_OBABEL_EXECUTABLE"); if (!obabelExec.isEmpty()) { m_obabelExecutable = obabelExec; } else { // If not overridden, look for an obabel next to the executable. QDir baseDir(QCoreApplication::applicationDirPath()); if (!baseDir.absolutePath().startsWith("/usr/") && QFileInfo(baseDir.absolutePath() + '/' + m_obabelExecutable).exists()) { m_obabelExecutable = baseDir.absolutePath() + '/' + m_obabelExecutable; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); #if defined(_WIN32) env.insert("BABEL_DATADIR", QCoreApplication::applicationDirPath() + "/data"); #else QDir dir(QCoreApplication::applicationDirPath() + "/../share/openbabel"); QStringList filters; filters << "3.*" << "2.*"; QStringList dirs = dir.entryList(filters); if (dirs.size() == 1) { env.insert("BABEL_DATADIR", QCoreApplication::applicationDirPath() + "/../share/openbabel/" + dirs[0]); } else { qDebug() << "Error, Open Babel data directory not found."; } dir.setPath(QCoreApplication::applicationDirPath() + "/../lib/openbabel"); dirs = dir.entryList(filters); if (dirs.size() == 0) { env.insert("BABEL_LIBDIR", QCoreApplication::applicationDirPath() + "/../lib/openbabel/"); } else if (dirs.size() == 1) { env.insert("BABEL_LIBDIR", QCoreApplication::applicationDirPath() + "/../lib/openbabel/" + dirs[0]); } else { qDebug() << "Error, Open Babel plugins directory not found."; } #endif m_process->setProcessEnvironment(env); } } } QString OBProcess::version() { QString result; if (!tryLockProcess()) { qWarning() << "OBProcess::version: process already in use."; return result; } executeObabel(QStringList() << "-V"); if (m_process->waitForFinished(500)) result = m_process->readAllStandardOutput().trimmed(); releaseProcess(); return result; } void OBProcess::abort() { m_aborted = true; emit aborted(); } void OBProcess::obError() { qDebug() << "Process encountered an error, and did not execute correctly."; if (m_process) { qDebug() << "\tExit code:" << m_process->exitCode(); qDebug() << "\tExit status:" << m_process->exitStatus(); qDebug() << "\tExit output:" << m_process->readAll(); } } bool OBProcess::queryReadFormats() { if (!tryLockProcess()) { qWarning() << "OBProcess::queryReadFormats: process already in use."; return false; } // Setup options QStringList options; options << "-L" << "formats" << "read"; executeObabel(options, this, SLOT(queryReadFormatsPrepare())); return true; } bool OBProcess::queryWriteFormats() { if (!tryLockProcess()) { qWarning() << "OBProcess::queryWriteFormats: process already in use."; return false; } // Setup options QStringList options; options << "-L" << "formats" << "write"; executeObabel(options, this, SLOT(queryWriteFormatsPrepare())); return true; } void OBProcess::queryReadFormatsPrepare() { if (m_aborted) { releaseProcess(); return; } QMultiMap result; QString output = QString::fromLatin1(m_process->readAllStandardOutput()); QRegularExpression parser(R"(\s*([^\s]+)\s+--\s+([^\n]+)\n)"); QRegularExpressionMatch match; int pos = 0; while ((match = parser.match(output, pos)).hasMatch()) { QString extension = match.captured(1); QString description = match.captured(2); result.insertMulti(description, extension); pos = match.capturedEnd(0); } releaseProcess(); emit queryReadFormatsFinished(result); return; } void OBProcess::queryWriteFormatsPrepare() { if (m_aborted) { releaseProcess(); return; } QMultiMap result; QString output = QString::fromLatin1(m_process->readAllStandardOutput()); QRegularExpression parser(R"(\s*([^\s]+)\s+--\s+([^\n]+)\n)"); QRegularExpressionMatch match; int pos = 0; while ((match = parser.match(output, pos)).hasMatch()) { QString extension = match.captured(1); QString description = match.captured(2); result.insertMulti(description, extension); pos = match.capturedEnd(0); } releaseProcess(); emit queryWriteFormatsFinished(result); return; } bool OBProcess::convert(const QByteArray& input, const QString& inFormat, const QString& outFormat, const QStringList& options) { if (!tryLockProcess()) { qWarning() << "OBProcess::convert: process already in use."; return false; } QStringList realOptions; realOptions << QString("-i%1").arg(inFormat) << QString("-o%1").arg(outFormat) << options; executeObabel(realOptions, this, SLOT(convertPrepareOutput()), input); return true; } bool OBProcess::convert(const QString& filename, const QString& inFormat, const QString& outFormat, const QStringList& options) { if (!tryLockProcess()) { qWarning() << "OBProcess::convert: process already in use."; return false; } QStringList realOptions; realOptions << QString("-i%1").arg(inFormat) << filename << QString("-o%1").arg(outFormat) << options; executeObabel(realOptions, this, SLOT(convertPrepareOutput())); return true; } void OBProcess::convertPrepareOutput() { if (m_aborted) { releaseProcess(); return; } // Keep this empty if an error occurs: QByteArray output; // Check for errors. QString errorOutput = QString::fromLatin1(m_process->readAllStandardError()); QRegularExpression errorChecker("\\b0 molecules converted\\b" "|" "obabel: cannot read input format!"); if (!errorOutput.contains(errorChecker)) { if (m_process->exitStatus() == QProcess::NormalExit) output = m_process->readAllStandardOutput(); } /// Print any meaningful warnings @todo This should go to a log at some point. if (!errorOutput.isEmpty() && errorOutput != "1 molecule converted\n") qWarning() << m_obabelExecutable << " stderr:\n" << errorOutput; emit convertFinished(output); releaseProcess(); } bool OBProcess::queryForceFields() { if (!tryLockProcess()) { qWarning() << "OBProcess::queryForceFields(): process already in use."; return false; } QStringList options; options << "-L" << "forcefields"; executeObabel(options, this, SLOT(queryForceFieldsPrepare())); return true; } void OBProcess::queryForceFieldsPrepare() { if (m_aborted) { releaseProcess(); return; } QMultiMap result; QString output = QString::fromLatin1(m_process->readAllStandardOutput()); QRegularExpression parser(R"(([^\s]+)\s+(\S[^\n]*[^\n\.]+)\.?\n)"); QRegularExpressionMatch match; int pos = 0; while ((match = parser.match(output, pos)).hasMatch()) { QString key = match.captured(1); QString desc = match.captured(2); result.insertMulti(key, desc); pos = match.capturedEnd(0); } releaseProcess(); emit queryForceFieldsFinished(result); } bool OBProcess::queryCharges() { if (!tryLockProcess()) { qWarning() << "OBProcess::queryCharges(): process already in use."; return false; } QStringList options; options << "-L" << "charges"; executeObabel(options, this, SLOT(queryChargesPrepare())); return true; } void OBProcess::queryChargesPrepare() { if (m_aborted) { releaseProcess(); return; } QMultiMap result; QString output = QString::fromLatin1(m_process->readAllStandardOutput()); QRegularExpression parser(R"(([^\s]+)\s+(\S[^\n]*[^\n\.]+)\.?\n)"); QRegularExpressionMatch match; int pos = 0; while ((match = parser.match(output, pos)).hasMatch()) { QString key = match.captured(1); QString desc = match.captured(2); result.insertMulti(key, desc); pos = match.capturedEnd(0); } releaseProcess(); emit queryChargesFinished(result); } bool OBProcess::calculateCharges(const QByteArray& mol, const std::string& format, const std::string& type) { if (!tryLockProcess()) { qWarning() << "OBProcess::calculateCharges(): process already in use."; return false; } QStringList realOptions; if (format == "cjson") { realOptions << "-icjson"; } else { realOptions << "-icml"; } realOptions << "-onul" // ignore the output << "--partialcharge" << type.c_str() << "--print"; // Start the optimization executeObabel(realOptions, this, SLOT(chargesPrepareOutput()), mol); return true; } void OBProcess::chargesPrepareOutput() { if (m_aborted) { releaseProcess(); return; } // Keep this empty if an error occurs: QByteArray output; // Check for errors. QString errorOutput = QString::fromLatin1(m_process->readAllStandardError()); QRegularExpression errorChecker("\\b0 molecules converted\\b" "|" "obabel: cannot read input format!"); if (!errorOutput.contains(errorChecker)) { if (m_process->exitStatus() == QProcess::NormalExit) output = m_process->readAllStandardOutput(); } /// Print any meaningful warnings @todo This should go to a log at some point. if (!errorOutput.isEmpty() && errorOutput != "1 molecule converted\n") qWarning() << m_obabelExecutable << " stderr:\n" << errorOutput; // Convert the output line-by-line to charges Core::Array charges; QTextStream stream(output); QString line; while (stream.readLineInto(&line)) { bool ok; double charge = line.toDouble(&ok); if (!ok) break; charges.push_back(charge); } emit chargesFinished(charges); releaseProcess(); } bool OBProcess::optimizeGeometry(const QByteArray& mol, const QStringList& options, const std::string format) { if (!tryLockProcess()) { qWarning() << "OBProcess::optimizeGeometry(): process already in use."; return false; } QStringList realOptions; if (format == "cjson") { realOptions << "-icjson" << "-ocjson"; } else { realOptions << "-icml" << "-ocml"; } realOptions << "--minimize" << "--noh" // new in OB 3.0.1 << "--log" << options; // We'll need to read the log (printed to stderr) to update progress connect(m_process, SIGNAL(readyReadStandardError()), SLOT(optimizeGeometryReadLog())); // Initialize the log reader ivars m_optimizeGeometryLog.clear(); m_optimizeGeometryMaxSteps = -1; // Start the optimization executeObabel(realOptions, this, SLOT(optimizeGeometryPrepare()), mol); return true; } bool OBProcess::generateConformers(const QByteArray& mol, const QStringList& options, const std::string format) { if (!tryLockProcess()) { qWarning() << "OBProcess::generateConformers(): process already in use."; return false; } QStringList realOptions; if (format == "cjson") { realOptions << "-icjson" << "-ocjson"; } else { realOptions << "-icml" << "-ocml"; } realOptions << "--conformer" << "--noh" // new in OB 3.0.1 << "--log" << options; // We'll need to read the log (printed to stderr) to update progress connect(m_process, SIGNAL(readyReadStandardError()), SLOT(conformerReadLog())); // Initialize the log reader ivars m_optimizeGeometryLog.clear(); m_maxConformers = -1; // Start the optimization executeObabel(realOptions, this, SLOT(conformerPrepare()), mol); return true; } void OBProcess::optimizeGeometryPrepare() { if (m_aborted) { releaseProcess(); return; } QByteArray result = m_process->readAllStandardOutput(); releaseProcess(); emit optimizeGeometryFinished(result); } void OBProcess::conformerPrepare() { if (m_aborted) { releaseProcess(); return; } QByteArray result = m_process->readAllStandardOutput(); releaseProcess(); emit generateConformersFinished(result); } void OBProcess::optimizeGeometryReadLog() { // Append the current stderr to the log m_optimizeGeometryLog += QString::fromLatin1(m_process->readAllStandardError()); // Search for the maximum number of steps if we haven't found it yet if (m_optimizeGeometryMaxSteps < 0) { QRegularExpression maxStepsParser("\nSTEPS = ([0-9]+)\n\n"); QRegularExpressionMatch match; if ((match = maxStepsParser.match(m_optimizeGeometryLog)).hasMatch()) { m_optimizeGeometryMaxSteps = match.captured(1).toInt(); emit optimizeGeometryStatusUpdate(0, m_optimizeGeometryMaxSteps, 0.0, 0.0); } } // Emit the last printed step if (m_optimizeGeometryMaxSteps >= 0) { QRegularExpression lastStepParser( R"(\n\s*([0-9]+)\s+([-0-9.]+)\s+([-0-9.]+)\n)"); QRegularExpressionMatchIterator matchIterator = lastStepParser.globalMatch(m_optimizeGeometryLog); QRegularExpressionMatch lastMatch; while (matchIterator.hasNext()) { lastMatch = matchIterator.next(); // Capture the last match } if (lastMatch.hasMatch()) { int step = lastMatch.captured(1).toInt(); double energy = lastMatch.captured(2).toDouble(); double lastEnergy = lastMatch.captured(3).toDouble(); emit optimizeGeometryStatusUpdate(step, m_optimizeGeometryMaxSteps, energy, lastEnergy); } } } void OBProcess::conformerReadLog() { // Append the current stderr to the log // (we're grabbing the log from the geometry optimization) m_optimizeGeometryLog += QString::fromLatin1(m_process->readAllStandardError()); // Search for the maximum number of steps if we haven't found it yet if (m_optimizeGeometryMaxSteps < 0) { QRegularExpression maxStepsParser("\nSTEPS = ([0-9]+)\n\n"); QRegularExpressionMatch match; if ((match = maxStepsParser.match(m_optimizeGeometryLog)).hasMatch()) { m_optimizeGeometryMaxSteps = match.captured(1).toInt(); emit optimizeGeometryStatusUpdate(0, m_optimizeGeometryMaxSteps, 0.0, 0.0); } } // Emit the last printed step if (m_optimizeGeometryMaxSteps >= 0) { QRegularExpression lastStepParser( R"(\n\s*([0-9]+)\s+([-0-9.]+)\s+([-0-9.]+)\n)"); QRegularExpressionMatch match; if ((match = lastStepParser.match(m_optimizeGeometryLog)).hasMatch()) { int step = match.captured(1).toInt(); double energy = match.captured(2).toDouble(); double lastEnergy = match.captured(3).toDouble(); emit optimizeGeometryStatusUpdate(step, m_optimizeGeometryMaxSteps, energy, lastEnergy); } } } void OBProcess::executeObabel(const QStringList& options, QObject* receiver, const char* slot, const QByteArray& obabelStdin) { // Setup exit handler if (receiver) { connect(m_process, SIGNAL(finished(int)), receiver, slot); connect(m_process, SIGNAL(errorOccurred(QProcess::ProcessError)), receiver, slot); connect(m_process, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(obError())); } // Start process #ifndef NDEBUG qDebug() << "OBProcess::executeObabel: " "Running" << m_obabelExecutable << options.join(" "); #endif m_process->start(m_obabelExecutable, options); if (!obabelStdin.isNull()) { m_process->write(obabelStdin); m_process->closeWriteChannel(); } } void OBProcess::resetState() { m_aborted = false; m_process->disconnect(this); disconnect(m_process); connect(this, SIGNAL(aborted()), m_process, SLOT(kill())); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/obprocess.h000066400000000000000000000333161506155467400243000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OBPROCESS_H #define AVOGADRO_QTPLUGINS_OBPROCESS_H #include #include #include #include class QProcess; namespace Avogadro { namespace QtPlugins { /** * @brief The OBProcess class provides an interface to the `obabel` executable, * which is run in a separate process. * * The `obabel` executable used by this class can be overridden by setting the * AVO_OBABEL_EXECUTABLE environment variable. */ class OBProcess : public QObject { Q_OBJECT public: explicit OBProcess(QObject* parent_ = nullptr); /** * @return The output of obabel -V. */ QString version(); /** * @name Process Management * Methods, slots, and signals used to interact with the OpenBabel process. * @{ */ public: /** * The `obabel` executable used by the process. */ QString obabelExecutable() const { return m_obabelExecutable; } /** * @return True if the process is in use, false otherwise. */ bool inUse() const { return m_processLocked; } public slots: /** * Abort any currently running processes. * * This will cause aborted() to be emitted, but not any of the * operation-specific "finished" signals. */ void abort(); /** * Called when an error in the process occurs. */ void obError(); signals: /** * Emitted when the abort() method has been called. */ void aborted(); // end Process Management doxygen group /**@}*/ /** * @name File Format Support * Query the obabel executable for supported file formats. * @{ */ public slots: /** * Request a list of all supported input formats from obabel. * * After calling this method, the queryReadFormatsFinished signal will be * emitted. This method executes * * `obabel -L formats read` * * and parses the output into a map (keys are format descriptions, values are * format extensions). * * If an error occurs, queryReadFormatsFinished will be emitted with an empty * argument. * * @return True if the process started successfully, false otherwise. */ bool queryReadFormats(); /** * Request a list of all supported output formats from obabel. * * After calling this method, the queryWriteFormatsFinished signal will be * emitted. This method executes * * `obabel -L formats write` * * and parses the output into a map (keys are format descriptions, values are * format extensions). * * If an error occurs, queryWriteFormatsFinished will be emitted with an empty * argument. * * @return True if the process started successfully, false otherwise. */ bool queryWriteFormats(); signals: /** * Triggered when the process started by queryReadFormats() completes. * @param readFormats The input file formats that OpenBabel understands. Keys * are non-translated (english), human-readable descriptions of the formats, * and the values are the corresponding file extensions. * * @note readFormats will usually contain more than one extensions per format, * so accessing the values with QMap::values() (instead of QMap::value()) is * required. * * If an error occurs, readFormats will be empty. */ void queryReadFormatsFinished(QMultiMap readFormats); /** * Triggered when the process started by queryWriteFormats() completes. * @param writeFormats The file formats that OpenBabel can write. Keys * are non-translated (english), human-readable descriptions of the formats, * and the values are the corresponding file extensions. * * @note writeFormats will usually contain more than one extensions per * format, so accessing the values with QMap::values() (instead of * QMap::value()) is required. * * If an error occurs, writeFormats will be empty. */ void queryWriteFormatsFinished(QMultiMap writeFormats); private slots: void queryReadFormatsPrepare(); void queryWriteFormatsPrepare(); // end File Format Support doxygen group /**@}*/ /** * @name Format Operations * Operations that manipulate molecular representations. * @{ */ public slots: /** * Convert the text representation in @a input from @a inFormat to * @a outFormat. * * @param input Text representation of molecule in @a inFormat format. * @param inFormat File extension corresponding to input format * (see `obabel -L formats`). * @param outFormat File extension corresponding to output format. * @param options Additional options passed to obabel. * * After calling this method, the convertFinished signal will be emitted to * indicate return status along with the requested representation of the * molecule. * * The conversion is performed as: * `obabel -i -o < input > output` * * @return True if the process started successfully, false otherwise. */ bool convert(const QByteArray& input, const QString& inFormat, const QString& outFormat, const QStringList& options = QStringList()); /** * Convert the file @a filename from @a inFormat to @a outFormat. * * @param filename File containing molecule representation in @a inFormat * format. * @param inFormat File extension corresponding to input format * (see `obabel -L formats`). * @param outFormat File extension corresponding to output format. * @param options Additional options passed to obabel. * * After calling this method, the convertFinished signal will be emitted to * indicate return status along with the requested representation of the * molecule. * * The conversion is performed as: * `obabel -i -o > output` * * @return True if the process started successfully, false otherwise. */ bool convert(const QString& filename, const QString& inFormat, const QString& outFormat, const QStringList& options = QStringList()); signals: /** * Emitted after a call to convert() finishes. * @param output The molecule in CML format, or an empty QByteArray if an e * error occurred. */ void convertFinished(const QByteArray& output); private slots: void convertPrepareOutput(); // end Format Operations doxygen group /**@}*/ /** * @name Force Fields * Methods, signals, and slots pertaining to force fields (e.g. geometry * optimizations). * @{ */ public slots: /** * Request a list of all supported force fields from obabel. * * After calling this method, the queryForceFieldsFinished signal will be * emitted. This method executes * * `obabel -L forcefields` * * and parses the output. * * If an error occurs, queryForceFieldsFinished will be emitted with an empty * argument. * * @return True if the process started successfully, false otherwise. */ bool queryForceFields(); signals: /** * Triggered when the process started by queryForceFields() completes. * @param forceFields The force fields supported by OpenBabel. Keys * are unique identifiers for the force fields, and the values are * non-translated (english), human-readable descriptions. * * If an error occurs, forceFields will be empty. */ void queryForceFieldsFinished(const QMultiMap& forceFields); private slots: void queryForceFieldsPrepare(); public slots: /** * Request that obabel optimize a molecular structure using its minimize * operation. * @param cml A Chemical Markup Language representation of the molecule. * @param options Options for the optimization. See OBForceFieldDialog::prompt * for an easy method way to get the options from the user. * * After calling this method, the optimizeGeometryStatusUpdate signal will be * emitted periodically to indicate the optimization's progress. Once the * optimization finishes, optimizeGeometryFinished will be emitted with the * result of the optimization. * * The optimization is started with, e.g. * `obabel -icml -ocml --minimize ` * * The standard output is recorded and returned by optimizeGeometryFinished. * If @a options contains `--log`, the obabel process's standard error stream * is monitored for the data used in the optimizeGeometryStatusUpdate progress * updates. * * @return True if the process started successfully, false otherwise. */ bool optimizeGeometry(const QByteArray& cml, const QStringList& options, std::string format = "cml"); bool generateConformers(const QByteArray& cml, const QStringList& options, std::string format = "cml"); signals: /** * Emitted with the standard output of the process when it finishes. * If an error occurs, the argument will not be valid CML. */ void optimizeGeometryFinished(const QByteArray& cml); void generateConformersFinished(const QByteArray& cml); /** * Emitted every 10 steps of the optimization to indicate the current * progress. * @param step The current step of the minimization algorithm. * @param maxSteps The maximum number of steps before the minimization is * aborted. * @param currentEnergy The energy of the molecule at the current step. * @param lastEnergy The energy of the molecule at the previous minimization * step. */ void optimizeGeometryStatusUpdate(int step, int maxSteps, double currentEnergy, double lastEnergy); void conformerStatusUpdate(int step, int maxSteps, double currentEnergy, double lastEnergy); private slots: void optimizeGeometryPrepare(); void optimizeGeometryReadLog(); void conformerPrepare(); void conformerReadLog(); // end Force Fields doxygen group /**@}*/ /** * @name Charge Models * Methods, signals, and slots pertaining to partial charges * @{ */ public slots: /** * Request a list of all supported charge models from obabel. * * After calling this method, the queryChargesFinished signal will be * emitted. This method executes * * `obabel -L charges` * * and parses the output. * * If an error occurs, queryChargesFinished will be emitted with an empty * argument. * * @return True if the process started successfully, false otherwise. */ bool queryCharges(); signals: /** * Triggered when the process started by queryCharges() completes. * @param charges The charge models supported by OpenBabel. Keys * are unique identifiers for the charge models, and the values are * non-translated (english), human-readable descriptions. * * If an error occurs, charges will be empty. */ void queryChargesFinished(const QMultiMap& charges); private slots: void queryChargesPrepare(); public slots: /** * Calculate the partial charges on the molecule @a input using @a type * * @param input String containing molecule representation in @a inFormat * format. * @param inFormat File extension corresponding to input format * (see `obabel -L formats`). * @param type The charge model to use (e.g., MMFF94, gasteiger, etc.) * * After calling this method, the chargesFinished signal will be emitted to * indicate return status along with the charges as text. * * The process is performed as: * `obabel -i -onul --partialcharge --print < input > * output` * * @return True if the process started successfully, false otherwise. */ bool calculateCharges(const QByteArray& input, const std::string& inFormat = "cml", const std::string& type = "mmff94"); private slots: void chargesPrepareOutput(); signals: /** * Emitted after a call to calculateCharges() finishes. * @param output the set of partial charges */ void chargesFinished(const Core::Array& charges); // end Charge Models doxygen group /**@}*/ private: /** * Internal method for launching the obabel executable. * @param options List of options to pass to QProcess::start * @param receiver A QObject subclass instance that has @a slot as a member. * @param slot The slot to call when completed. Must have no arguments. * @param obabelStdin Standard input for the obabel process (optional). * * Call this method like so: @code QStringList options; executeObabel(options, this, SLOT(mySlot())); @endcode * * @a slot will be connected to QProcess::finished(int) and * QProcess::error(QProcess::ProcessError) with @a receiver as receiver and * @a m_process as sender. @a m_process is then started using * m_obabelExecutable and options as arguments. If provided, the obabelStdin * data will be written to the obabel stdin channel. */ void executeObabel(const QStringList& options, QObject* receiver = nullptr, const char* slot = nullptr, const QByteArray& obabelStdin = QByteArray()); void resetState(); // Not thread safe -- just uses a bool. bool tryLockProcess() { if (m_processLocked) return false; m_processLocked = true; resetState(); return true; } void releaseProcess() { m_processLocked = false; } bool m_processLocked; bool m_aborted; QProcess* m_process; QString m_obabelExecutable; // Optimize geometry ivars: int m_optimizeGeometryMaxSteps; unsigned m_maxConformers; QString m_optimizeGeometryLog; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OBPROCESS_H avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/openbabel.cpp000066400000000000000000001063371506155467400245670ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "openbabel.h" #include "conformersearchdialog.h" #include "obcharges.h" #include "obfileformat.h" #include "obforcefielddialog.h" #include "obprocess.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::QtGui::Molecule; using namespace std::string_literals; namespace Avogadro::QtPlugins { OpenBabel::OpenBabel(QObject* p) : ExtensionPlugin(p), m_molecule(nullptr), m_process(new OBProcess(this)), m_readFormatsPending(true), m_writeFormatsPending(true), m_defaultFormat("cml"), m_progress(nullptr), m_conformerSearchDialog(nullptr) { auto* action = new QAction(this); action->setEnabled(true); action->setText(tr("Optimize Geometry")); connect(action, SIGNAL(triggered()), SLOT(onOptimizeGeometry())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Configure Force Field…")); connect(action, SIGNAL(triggered()), SLOT(onConfigureGeometryOptimization())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Conformer Search…")); connect(action, SIGNAL(triggered()), SLOT(onConfigureConformerSearch())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Perceive Bonds")); connect(action, SIGNAL(triggered()), SLOT(onPerceiveBonds())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Add Hydrogens")); connect(action, SIGNAL(triggered()), SLOT(onAddHydrogens())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Add Hydrogens for pH…")); connect(action, SIGNAL(triggered()), SLOT(onAddHydrogensPh())); m_actions.push_back(action); action = new QAction(this); action->setEnabled(true); action->setText(tr("Remove Hydrogens")); connect(action, SIGNAL(triggered()), SLOT(onRemoveHydrogens())); m_actions.push_back(action); refreshReadFormats(); refreshWriteFormats(); refreshForceFields(); refreshCharges(); QString info = openBabelInfo(); if (info.isEmpty()) { qWarning() << tr("%1 not found! Disabling Open Babel plugin actions.") .arg(OBProcess().obabelExecutable()); foreach (QAction* a, m_actions) a->setEnabled(false); } else { qDebug() << OBProcess().obabelExecutable() << " found: " << info; } } OpenBabel::~OpenBabel() {} QList OpenBabel::actions() const { return m_actions; } QStringList OpenBabel::menuPath(QAction*) const { return QStringList() << tr("&Extensions") << tr("&Open Babel"); } QList OpenBabel::fileFormats() const { // Return empty list if not ready yet, and print a warning. if (m_readFormatsPending || m_writeFormatsPending) { qDebug() << tr("The Open Babel file formats are not ready to be added."); return QList(); } QList result; std::string mapDesc; std::string fname; std::string fidentifier; std::string fdescription; std::string fspecificationUrl("http://openbabel.org/wiki/Category:Formats"); std::vector fexts; std::vector fmime; // Simple lambda to replace toSet in QList auto toSet = [&](const QList& list) { return QSet(list.begin(), list.end()); }; QSet formatDescriptions; formatDescriptions.unite(toSet(m_readFormats.uniqueKeys())); formatDescriptions.unite(toSet(m_writeFormats.uniqueKeys())); QSet formatExtensions; // These can only be read directly from file: QList multifileFormatDescriptions; multifileFormatDescriptions << "VASP format"; multifileFormatDescriptions << "Gaussian Output"; // Issue #571 multifileFormatDescriptions << "Generic Output file format"; // #571 and 827 foreach (const QString& qdesc, formatDescriptions) { mapDesc = qdesc.toStdString(); fname = mapDesc; fidentifier = "OpenBabel: "s + mapDesc; fdescription = mapDesc; fexts.clear(); fmime.clear(); bool fileOnly = multifileFormatDescriptions.contains(qdesc); formatExtensions.clear(); Io::FileFormat::Operations rw = Io::FileFormat::None; if (m_readFormats.contains(qdesc)) { formatExtensions.unite(toSet(m_readFormats.values(qdesc))); rw |= Io::FileFormat::Read; } if (m_writeFormats.contains(qdesc)) { formatExtensions.unite(toSet(m_writeFormats.values(qdesc))); rw |= Io::FileFormat::Write; } foreach (const QString& ext, formatExtensions) fexts.push_back(ext.toStdString()); auto* fmt = new OBFileFormat(fname, fidentifier, fdescription, fspecificationUrl, fexts, fmime, m_defaultFormat, fileOnly); fmt->setReadWriteFlags(rw); result.append(fmt); } qDebug() << "Open Babel formats ready: " << result.size(); return result; } QString OpenBabel::openBabelInfo() const { OBProcess proc; QString version = proc.version(); if (version.isEmpty()) return QString(); return QString("%1: %2").arg(proc.obabelExecutable(), version); } void OpenBabel::setMolecule(QtGui::Molecule* mol) { if (mol != m_molecule) m_molecule = mol; } bool OpenBabel::readMolecule(QtGui::Molecule& mol) { m_progress->setLabelText(tr("Loading molecule from Open Babel…")); bool result = false; if (m_moleculeQueue.isEmpty()) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("An internal error occurred: " "OpenBabel::readMolecule called, but no obabel " "output is available to parse!"), QMessageBox::Ok); } else { QByteArray output = m_moleculeQueue.takeFirst(); // Empty output means openbabel crashed, etc. if (output.isEmpty()) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("An error occurred while running Open Babel " "(%1).") .arg(m_process->obabelExecutable()), QMessageBox::Ok); } else { result = Io::FileFormatManager::instance().readString( mol, output.constData(), m_defaultFormat); if (!result) { qWarning() << "Error parsing OpenBabel output:\n" << output; QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error parsing openbabel output."), QMessageBox::Ok); } } } m_progress->reset(); return result; } void OpenBabel::refreshReadFormats() { // No need to check if the member process is in use -- we use a temporary // process for the refresh methods. auto* proc = new OBProcess(this); connect(proc, SIGNAL(queryReadFormatsFinished(QMultiMap)), SLOT(handleReadFormatUpdate(QMultiMap))); proc->queryReadFormats(); } void OpenBabel::handleReadFormatUpdate(const QMultiMap& fmts) { m_readFormatsPending = false; auto* proc = qobject_cast(sender()); if (proc) proc->deleteLater(); m_readFormats = fmts; // Emit a signal indicating the file formats are ready if read and write // formats have both returned their results. if (!m_readFormatsPending && !m_writeFormatsPending) { // Update the default format if cjson is available if (m_readFormats.contains("Chemical JSON") && m_writeFormats.contains("Chemical JSON")) { m_defaultFormat = "cjson"; qDebug() << "Setting default format to " << m_defaultFormat.c_str(); } emit fileFormatsReady(); } } void OpenBabel::refreshWriteFormats() { // No need to check if the member process is in use -- we use a temporary // process for the refresh methods. auto* proc = new OBProcess(this); connect(proc, SIGNAL(queryWriteFormatsFinished(QMultiMap)), SLOT(handleWriteFormatUpdate(QMultiMap))); proc->queryWriteFormats(); } void OpenBabel::handleWriteFormatUpdate(const QMultiMap& fmts) { m_writeFormatsPending = false; auto* proc = qobject_cast(sender()); if (proc) proc->deleteLater(); m_writeFormats = fmts; // Emit a signal indicating the file formats are ready if read and write // formats have both returned their results. if (!m_readFormatsPending && !m_writeFormatsPending) { emit fileFormatsReady(); // Update the default format if cjson is available if (m_readFormats.contains("Chemical JSON") && m_writeFormats.contains("Chemical JSON")) { m_defaultFormat = "cjson"; qDebug() << "Setting default format to cjson."; } } } void OpenBabel::refreshForceFields() { // No need to check if the member process is in use -- we use a temporary // process for the refresh methods. auto* proc = new OBProcess(this); connect(proc, SIGNAL(queryForceFieldsFinished(QMultiMap)), SLOT(handleForceFieldsUpdate(QMultiMap))); proc->queryForceFields(); } void OpenBabel::handleForceFieldsUpdate( const QMultiMap& ffMap) { auto* proc = qobject_cast(sender()); if (proc) proc->deleteLater(); m_forceFields = ffMap; } void OpenBabel::refreshCharges() { // No need to check if the member process is in use -- we use a temporary // process for the refresh methods. auto* proc = new OBProcess(this); connect(proc, SIGNAL(queryChargesFinished(QMultiMap)), SLOT(handleChargesUpdate(QMultiMap))); proc->queryCharges(); } void OpenBabel::handleChargesUpdate( const QMultiMap& chargeMap) { auto* proc = qobject_cast(sender()); if (proc) proc->deleteLater(); m_charges = chargeMap; // register the charge models foreach (const QString& key, m_charges.keys()) { // we're only picking a few select models for now if (key == "eem" || key == "eem2015ba" || key == "eqeq" || key == "gasteiger" || key == "mmff94") { auto* model = new OBCharges(key.toStdString()); Calc::ChargeManager::instance().registerModel(model); } } } void OpenBabel::onConfigureGeometryOptimization() { // If the force field map is empty, there is probably a problem with the // obabel executable. Warn the user and return. if (m_forceFields.isEmpty()) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("An error occurred while retrieving the list of " "supported forcefields. (using '%1').") .arg(m_process->obabelExecutable()), QMessageBox::Ok); return; } QSettings settings; QStringList options = settings.value("openbabel/optimizeGeometry/lastOptions").toStringList(); options = OBForceFieldDialog::prompt(qobject_cast(parent()), m_forceFields.keys(), options, autoDetectForceField()); // User cancel if (options.isEmpty()) return; settings.setValue("openbabel/optimizeGeometry/lastOptions", options); } void OpenBabel::onConfigureConformerSearch() { // If the force field map is empty, there is probably a problem with the // obabel executable. Warn the user and return. if (m_forceFields.isEmpty()) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("An error occurred while retrieving the list of " "supported forcefields. (using '%1').") .arg(m_process->obabelExecutable()), QMessageBox::Ok); return; } QSettings settings; QStringList options = settings.value("openbabel/conformerSearch/lastOptions").toStringList(); if (m_conformerSearchDialog == nullptr) { m_conformerSearchDialog = new ConformerSearchDialog(qobject_cast(parent())); connect(m_conformerSearchDialog, SIGNAL(accepted()), this, SLOT(onGenerateConformers())); } // todo set options from last run m_conformerSearchDialog->show(); } void OpenBabel::onOptimizeGeometry() { if (!m_molecule || m_molecule->atomCount() == 0) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Molecule invalid. Cannot optimize geometry."), QMessageBox::Ok); return; } // If the force field map is empty, there is probably a problem with the // obabel executable. Warn the user and return. if (m_forceFields.isEmpty()) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("An error occurred while retrieving the list of " "supported forcefields. (using '%1').") .arg(m_process->obabelExecutable()), QMessageBox::Ok); return; } // Fail here if the process is already in use if (m_process->inUse()) { showProcessInUseError(tr("Cannot optimize geometry with Open Babel.")); return; } QSettings settings; QStringList options = settings.value("openbabel/optimizeGeometry/lastOptions").toStringList(); bool autoDetect = settings.value("openbabel/optimizeGeometry/autoDetect", true).toBool(); if (autoDetect) { QString ff = autoDetectForceField(); int ffIndex = options.indexOf("--ff"); if (ffIndex >= 0) { // Shouldn't happen, but just to be safe... if (ffIndex + 1 == options.size()) options << ff; else options[ffIndex + 1] = ff; } else { options << "--ff" << ff; } } // Setup progress dialog initializeProgressDialog(tr("Optimizing Geometry (Open Babel)"), tr("Generating…"), 0, 0, 0); // Connect process disconnect(m_process); m_process->disconnect(this); connect(m_progress, SIGNAL(canceled()), m_process, SLOT(abort())); connect(m_process, SIGNAL(optimizeGeometryStatusUpdate(int, int, double, double)), SLOT(onOptimizeGeometryStatusUpdate(int, int, double, double))); connect(m_process, SIGNAL(optimizeGeometryFinished(QByteArray)), SLOT(onOptimizeGeometryFinished(QByteArray))); // Generate CML std::string mol; if (!Io::FileFormatManager::instance().writeString(*m_molecule, mol, m_defaultFormat)) { m_progress->reset(); QMessageBox::critical( qobject_cast(parent()), tr("Error"), tr("An internal error occurred while generating an " "Open Babel representation of the current molecule."), QMessageBox::Ok); return; } m_progress->setLabelText(tr("Starting %1…", "arg is an executable file.") .arg(m_process->obabelExecutable())); // Run obabel m_process->optimizeGeometry(QByteArray(mol.c_str()), options, m_defaultFormat); } void OpenBabel::onOptimizeGeometryStatusUpdate(int step, int numSteps, double energy, double lastEnergy) { QString status; if (step == 0) { status = tr("Step %1 of %2\nCurrent energy: %3\ndE: %4") .arg(step) .arg(numSteps) .arg(fabs(energy) > 1e-10 ? QString::number(energy, 'g', 5) : QString("(pending)")) .arg("(pending)"); } else { double dE = energy - lastEnergy; status = tr("Step %1 of %2\nCurrent energy: %3\ndE: %4") .arg(step) .arg(numSteps) .arg(energy, 0, 'g', 5) .arg(dE, 0, 'g', 5); } m_progress->setRange(0, numSteps); m_progress->setValue(step); m_progress->setLabelText(status); } void OpenBabel::onOptimizeGeometryFinished(const QByteArray& output) { m_progress->setLabelText(tr("Updating molecule…")); // CML --> molecule Core::Molecule mol; if (!Io::FileFormatManager::instance().readString(mol, output.constData(), m_defaultFormat)) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error interpreting Open Babel output."), QMessageBox::Ok); qDebug() << "Open Babel:" << output; return; } /// @todo cache a pointer to the current molecule in the above slot, and /// verify that we're still operating on the same molecule. // Check that the atom count hasn't changed: if (mol.atomCount() != m_molecule->atomCount()) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Number of atoms in obabel output (%1) does not " "match the number of atoms in the original " "molecule (%2).") .arg(mol.atomCount()) .arg(m_molecule->atomCount()), QMessageBox::Ok); return; } m_molecule->undoMolecule()->setAtomPositions3d(mol.atomPositions3d(), tr("Optimize Geometry")); m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Modified); m_progress->reset(); } void OpenBabel::onGenerateConformers() { if (!m_molecule || m_molecule->atomCount() == 0) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Molecule invalid. Cannot generate conformers."), QMessageBox::Ok); return; } // If the force field map is empty, there is probably a problem with the // obabel executable. Warn the user and return. if (m_forceFields.isEmpty()) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("An error occurred while retrieving the list of " "supported forcefields. (using '%1').") .arg(m_process->obabelExecutable()), QMessageBox::Ok); return; } // Fail here if the process is already in use if (m_process->inUse()) { showProcessInUseError(tr("Cannot generate conformers with Open Babel.")); return; } if (m_conformerSearchDialog == nullptr) { return; // should't happen } QSettings settings; QStringList options = m_conformerSearchDialog->options(); QStringList ffOptions = settings.value("openbabel/optimizeGeometry/lastOptions").toStringList(); bool autoDetect = settings.value("openbabel/optimizeGeometry/autoDetect", true).toBool(); if (autoDetect) { QString ff = autoDetectForceField(); int ffIndex = ffOptions.indexOf("--ff"); if (ffIndex >= 0) { // Shouldn't happen, but just to be safe... if (ffIndex + 1 == ffOptions.size()) ffOptions << ff; else ffOptions[ffIndex + 1] = ff; } else { ffOptions << "--ff" << ff; } } options << ffOptions; // Setup progress dialog initializeProgressDialog(tr("Generating Conformers (Open Babel)"), tr("Generating…"), 0, 0, 0); // Connect process disconnect(m_process); m_process->disconnect(this); connect(m_progress, SIGNAL(canceled()), m_process, SLOT(abort())); connect(m_process, SIGNAL(conformerStatusUpdate(int, int, double, double)), SLOT(onConformerStatusUpdate(int, int, double, double))); connect(m_process, SIGNAL(generateConformersFinished(QByteArray)), SLOT(onGenerateConformersFinished(QByteArray))); std::string mol; if (!Io::FileFormatManager::instance().writeString(*m_molecule, mol, m_defaultFormat)) { m_progress->reset(); QMessageBox::critical( qobject_cast(parent()), tr("Error"), tr("An internal error occurred while generating an " "Open Babel representation of the current molecule."), QMessageBox::Ok); return; } m_progress->setLabelText(tr("Starting %1…", "arg is an executable file.") .arg(m_process->obabelExecutable())); // Run obabel m_process->generateConformers(QByteArray(mol.c_str()), options, m_defaultFormat); } void OpenBabel::onConformerStatusUpdate(int step, int numSteps, double energy, double lastEnergy) { QString status; if (step == 0) { status = tr("Step %1 of %2\nCurrent energy: %3\ndE: %4") .arg(step) .arg(numSteps) .arg(fabs(energy) > 1e-10 ? QString::number(energy, 'g', 5) : QString("(pending)")) .arg("(pending)"); } else { double dE = energy - lastEnergy; status = tr("Step %1 of %2\nCurrent energy: %3\ndE: %4") .arg(step) .arg(numSteps) .arg(energy, 0, 'g', 5) .arg(dE, 0, 'g', 5); } m_progress->setRange(0, numSteps); m_progress->setValue(step); m_progress->setLabelText(status); } void OpenBabel::onGenerateConformersFinished(const QByteArray& output) { m_progress->setLabelText(tr("Updating molecule…")); // output --> molecule Core::Molecule mol; if (!Io::FileFormatManager::instance().readString(mol, output.constData(), m_defaultFormat)) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error interpreting Open Babel output."), QMessageBox::Ok); qDebug() << "Open Babel:" << output; return; } /// @todo cache a pointer to the current molecule in the above slot, and /// verify that we're still operating on the same molecule. // Check that the atom count hasn't changed: if (mol.atomCount() != m_molecule->atomCount()) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Number of atoms in obabel output (%1) does not " "match the number of atoms in the original " "molecule (%2).") .arg(mol.atomCount()) .arg(m_molecule->atomCount()), QMessageBox::Ok); return; } m_molecule->undoMolecule()->setAtomPositions3d(mol.atomPositions3d(), tr("Generate Conformers")); // copy the coordinate sets m_molecule->clearCoordinate3d(); for (size_t i = 0; i < mol.coordinate3dCount(); ++i) m_molecule->setCoordinate3d(mol.coordinate3d(i), static_cast(i)); // energy data too // TODO: check if other properties are needed m_molecule->setData("energies", mol.data("energies")); m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Modified); m_progress->reset(); } void OpenBabel::onPerceiveBonds() { // Fail here if the process is already in use if (m_process->inUse()) { showProcessInUseError(tr("Cannot open file with Open Babel.")); return; } if (!m_molecule || m_molecule->atomCount() < 2) { QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Invalid molecule: Cannot perceive bonds."), QMessageBox::Ok); return; } // Setup progress dialog initializeProgressDialog(tr("Perceiving Bonds (Open Babel)"), tr("Generating XYZ representation…"), 0, 0, 0); // Generate XYZ std::string xyz; if (!Io::FileFormatManager::instance().writeString(*m_molecule, xyz, "xyz")) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error generating XYZ string."), QMessageBox::Ok); return; } // Connect process disconnect(m_process); m_process->disconnect(this); connect(m_progress, SIGNAL(canceled()), m_process, SLOT(abort())); connect(m_process, SIGNAL(convertFinished(QByteArray)), SLOT(onPerceiveBondsFinished(QByteArray))); m_progress->setLabelText(tr("Converting XYZ to Open Babel with %1…") .arg(m_process->obabelExecutable())); // Run process m_process->convert(QByteArray(xyz.c_str(), xyz.size()), "xyz", m_defaultFormat.c_str()); } void OpenBabel::onPerceiveBondsFinished(const QByteArray& output) { m_progress->setLabelText(tr("Updating molecule from Open Babel…")); // CML --> molecule Core::Molecule mol; if (!Io::FileFormatManager::instance().readString(mol, output.constData(), m_defaultFormat)) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error interpreting Open Babel output."), QMessageBox::Ok); return; } /// @todo cache a pointer to the current molecule in the above slot, and /// verify that we're still operating on the same molecule. // Check that the atom count hasn't changed: if (mol.atomCount() != m_molecule->atomCount()) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Number of atoms in obabel output (%1) does not " "match the number of atoms in the original " "molecule (%2).") .arg(mol.atomCount()) .arg(m_molecule->atomCount()), QMessageBox::Ok); return; } // Update the undo stack Molecule newMolecule = *m_molecule; newMolecule.clearBonds(); for (size_t i = 0; i < mol.bondCount(); ++i) { Avogadro::Core::Bond bond = mol.bond(i); newMolecule.addBond(newMolecule.atom(bond.atom1().index()), newMolecule.atom(bond.atom2().index()), bond.order()); } Molecule::MoleculeChanges changes = Molecule::Bonds | Molecule::Added | Molecule::Removed | Molecule::Modified; m_molecule->undoMolecule()->modifyMolecule(newMolecule, changes, "Perceive Bonds"); m_progress->reset(); } void OpenBabel::onAddHydrogens() { if (!m_molecule || m_molecule->atomCount() == 0) return; // Nothing to do. // Fail here if the process is already in use if (m_process->inUse()) { showProcessInUseError(tr("Cannot add hydrogens with Open Babel.")); return; } // Setup progress dialog initializeProgressDialog(tr("Adding Hydrogens (Open Babel)"), tr("Generating Open Babel input…"), 0, 0, 0); // Generate MDL std::string mol; if (!Io::FileFormatManager::instance().writeString(*m_molecule, mol, m_defaultFormat)) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error generating Open Babel input."), QMessageBox::Ok); return; } // Connect process disconnect(m_process); m_process->disconnect(this); connect(m_progress, SIGNAL(canceled()), m_process, SLOT(abort())); connect(m_process, SIGNAL(convertFinished(QByteArray)), SLOT(onHydrogenOperationFinished(QByteArray))); m_progress->setLabelText( tr("Running %1…").arg(m_process->obabelExecutable())); // Run process m_process->convert(QByteArray(mol.c_str(), mol.size()), m_defaultFormat.c_str(), m_defaultFormat.c_str(), QStringList() << "-h"); } void OpenBabel::onAddHydrogensPh() { if (!m_molecule || m_molecule->atomCount() == 0) return; // Nothing to do. // Fail here if the process is already in use if (m_process->inUse()) { showProcessInUseError(tr("Cannot add hydrogens with Open Babel.")); return; } // Prompt for pH bool ok = false; double pH = QInputDialog::getDouble(qobject_cast(parent()), tr("Add hydrogens for pH"), tr("pH:"), 7.4, 0, 14, 2, &ok); if (!ok) // user cancel return; // Setup progress dialog initializeProgressDialog(tr("Adding Hydrogens (Open Babel)"), tr("Generating obabel input…"), 0, 0, 0); // Generate MDL std::string mol; if (!Io::FileFormatManager::instance().writeString(*m_molecule, mol, m_defaultFormat)) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error generating Open Babel input."), QMessageBox::Ok); return; } // Connect process disconnect(m_process); m_process->disconnect(this); connect(m_progress, SIGNAL(canceled()), m_process, SLOT(abort())); connect(m_process, SIGNAL(convertFinished(QByteArray)), SLOT(onHydrogenOperationFinished(QByteArray))); m_progress->setLabelText( tr("Running %1…").arg(m_process->obabelExecutable())); // Run process m_process->convert(QByteArray(mol.c_str(), mol.size()), m_defaultFormat.c_str(), m_defaultFormat.c_str(), QStringList() << "-p" << QString::number(pH)); } void OpenBabel::onRemoveHydrogens() { if (!m_molecule || m_molecule->atomCount() == 0) return; // Nothing to do. // Fail here if the process is already in use if (m_process->inUse()) { showProcessInUseError(tr("Cannot remove hydrogens with Open Babel.")); return; } // Setup progress dialog initializeProgressDialog(tr("Removing Hydrogens (Open Babel)"), tr("Generating obabel input…"), 0, 0, 0); // Generate MDL std::string mol; if (!Io::FileFormatManager::instance().writeString(*m_molecule, mol, m_defaultFormat)) { m_progress->reset(); QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error generating Open Babel data."), QMessageBox::Ok); return; } // Connect process disconnect(m_process); m_process->disconnect(this); connect(m_progress, SIGNAL(canceled()), m_process, SLOT(abort())); connect(m_process, SIGNAL(convertFinished(QByteArray)), SLOT(onHydrogenOperationFinished(QByteArray))); m_progress->setLabelText( tr("Running %1…").arg(m_process->obabelExecutable())); // Run process m_process->convert(QByteArray(mol.c_str(), mol.size()), m_defaultFormat.c_str(), m_defaultFormat.c_str(), QStringList() << "-d"); } void OpenBabel::onHydrogenOperationFinished(const QByteArray& mdl) { m_progress->setLabelText(tr("Reading obabel output…")); // MDL --> molecule Core::Molecule mol; if (!Io::FileFormatManager::instance().readString(mol, mdl.constData(), m_defaultFormat)) { m_progress->reset(); qWarning() << "Open Babel error: " << mdl; QMessageBox::critical(qobject_cast(parent()), tr("Error"), tr("Error interpreting Open Babel output."), QMessageBox::Ok); qDebug() << "Open Babel:" << mdl; return; } /// @todo cache a pointer to the current molecule in the above slot, and /// verify that we're still operating on the same molecule. // Update the undo stack Molecule newMolecule; for (Index i = 0; i < mol.atomCount(); ++i) { Core::Atom atom = mol.atom(i); newMolecule.addAtom(atom.atomicNumber()).setPosition3d(atom.position3d()); } for (Index i = 0; i < mol.bondCount(); ++i) { Core::Bond bond = mol.bond(i); newMolecule.addBond(newMolecule.atom(bond.atom1().index()), newMolecule.atom(bond.atom2().index()), bond.order()); } Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Bonds | Molecule::Added | Molecule::Removed | Molecule::Modified; // If the number of atoms is greater, we added hydrogens. Else, we removed // them! QString undoString = "Add Hydrogens"; if (m_molecule->atomCount() > newMolecule.atomCount()) undoString = "Remove Hydrogens"; m_molecule->undoMolecule()->modifyMolecule(newMolecule, changes, undoString); m_progress->reset(); } void OpenBabel::initializeProgressDialog(const QString& title, const QString& label, int min, int max, int value, bool showDialog) { if (!m_progress) m_progress = new QProgressDialog(qobject_cast(parent())); m_progress->setWindowTitle(title); m_progress->setLabelText(label); m_progress->setRange(min, max); m_progress->setValue(value); m_progress->setMinimumDuration(0); if (showDialog) m_progress->show(); } void OpenBabel::showProcessInUseError(const QString& title) const { QMessageBox::critical(qobject_cast(parent()), title, tr("Already running Open Babel. Wait for the other " "operation to complete and try again."), QMessageBox::Ok); } QString OpenBabel::autoDetectForceField() const { // Guess forcefield based on molecule. Preference is GAFF, MMFF94, then UFF. // See discussion at // http://forums.openbabel.org/Heuristic-for-selecting-best-forcefield-td4655917.html QString formula = QString::fromStdString(m_molecule->formula()); QStringList elementTypes = formula.split(QRegularExpression("\\d+"), Qt::SkipEmptyParts); bool mmff94Valid = true; bool gaffValid = true; QStringList::const_iterator eleIter = elementTypes.constBegin(); while (eleIter != elementTypes.constEnd() && (mmff94Valid || gaffValid)) { // These are supported by GAFF and MMFF94s if (*eleIter != "C" && *eleIter != "H" && *eleIter != "F" && *eleIter != "Cl" && *eleIter != "Br" && *eleIter != "I" && *eleIter != "N" && *eleIter != "O" && *eleIter != "P" && *eleIter != "S") { gaffValid = false; mmff94Valid = false; // MMFF94 supports isolated metal ions but it's safer to use UFF // Fixes #1324 } ++eleIter; } QStringList ffs = m_forceFields.keys(); QString result; if (gaffValid && ffs.contains("GAFF")) result = "GAFF"; else if (mmff94Valid && ffs.contains("MMFF94")) result = "MMFF94"; // MMFF94 handles nitrogens more correctly than MMFF94s, but this // can be used in a pinch. else if (mmff94Valid && ffs.contains("MMFF94s")) result = "MMFF94s"; else if (ffs.contains("UFF")) result = "UFF"; return result; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/openbabel/openbabel.h000066400000000000000000000066321506155467400242310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OPENBABEL_H #define AVOGADRO_QTPLUGINS_OPENBABEL_H #include "conformersearchdialog.h" #include #include class QAction; class QProgressDialog; namespace Avogadro { namespace QtPlugins { class OBProcess; /** * @brief The OpenBabel class implements the ExtensionPlugin interface to * expose some OpenBabel functionality. * * @todo The readMolecule method will need to be updated if we allow * multimolecule files to load with the Io::CmlReader. */ class OpenBabel : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit OpenBabel(QObject* parent = nullptr); ~OpenBabel() override; QString name() const override { return tr("OpenBabel"); } QString description() const override { return tr("Interact with OpenBabel utilities."); } QList actions() const override; QStringList menuPath(QAction*) const override; QList fileFormats() const override; QString openBabelInfo() const; public slots: void setMolecule(QtGui::Molecule* mol) override; bool readMolecule(QtGui::Molecule& mol) override; private slots: void refreshReadFormats(); void handleReadFormatUpdate(const QMultiMap& fmts); void refreshWriteFormats(); void handleWriteFormatUpdate(const QMultiMap& fmts); void refreshForceFields(); void handleForceFieldsUpdate(const QMultiMap& ffMap); void refreshCharges(); void handleChargesUpdate(const QMultiMap& chargeMap); void onConfigureGeometryOptimization(); void onConfigureConformerSearch(); void onOptimizeGeometry(); void onOptimizeGeometryStatusUpdate(int step, int numSteps, double energy, double lastEnergy); void onOptimizeGeometryFinished(const QByteArray& output); void onGenerateConformers(); void onConformerStatusUpdate(int step, int numSteps, double energy, double lastEnergy); void onGenerateConformersFinished(const QByteArray& output); void onPerceiveBonds(); void onPerceiveBondsFinished(const QByteArray& output); void onAddHydrogens(); void onAddHydrogensPh(); void onRemoveHydrogens(); void onHydrogenOperationFinished(const QByteArray& cml); private: void initializeProgressDialog(const QString& title, const QString& label, int min, int max, int value, bool showDialog = true); void showProcessInUseError(const QString& title) const; QString autoDetectForceField() const; QtGui::Molecule* m_molecule; OBProcess* m_process; QList m_actions; QList m_moleculeQueue; bool m_readFormatsPending; bool m_writeFormatsPending; QMultiMap m_readFormats; QMultiMap m_writeFormats; QMultiMap m_forceFields; QMultiMap m_charges; std::string m_defaultFormat; QProgressDialog* m_progress; ConformerSearchDialog* m_conformerSearchDialog; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OPENBABEL_H avogadrolibs-1.101.0/avogadro/qtplugins/openmminput/000077500000000000000000000000001506155467400225465ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/openmminput/CMakeLists.txt000066400000000000000000000004221506155467400253040ustar00rootroot00000000000000# Extension set(openmminput_srcs openmminputdialog.cpp openmminput.cpp ) avogadro_plugin(OpenMMInput "OpenMM input script generation" ExtensionPlugin openmminput.h OpenMMInput "${openmminput_srcs}" openmminputdialog.ui ) target_link_libraries(OpenMMInput) avogadrolibs-1.101.0/avogadro/qtplugins/openmminput/openmminput.cpp000066400000000000000000000041421506155467400256260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "openmminput.h" #include "openmminputdialog.h" #include #include #include #include #include #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { OpenMMInput::OpenMMInput(QObject* parent_) : ExtensionPlugin(parent_), m_action(new QAction(this)), m_molecule(nullptr), m_dialog(nullptr), m_outputFormat(nullptr) { m_action->setEnabled(true); m_action->setText(tr("&OpenMM Script…")); connect(m_action, SIGNAL(triggered()), SLOT(menuActivated())); } OpenMMInput::~OpenMMInput() {} QList OpenMMInput::actions() const { QList actions_; actions_.append(m_action); return actions_; } QStringList OpenMMInput::menuPath(QAction*) const { QStringList path; path << tr("&Input"); return path; } void OpenMMInput::setMolecule(QtGui::Molecule* mol) { if (m_dialog) m_dialog->setMolecule(mol); m_molecule = mol; } bool OpenMMInput::readMolecule(QtGui::Molecule& mol) { Io::FileFormat* reader = m_outputFormat->newInstance(); bool success = reader->readFile(m_outputFileName.toStdString(), mol); if (!success) { QMessageBox::information(qobject_cast(parent()), tr("Error"), tr("Error reading output file '%1':\n%2") .arg(m_outputFileName) .arg(QString::fromStdString(reader->error()))); } m_outputFormat = nullptr; m_outputFileName.clear(); return success; } void OpenMMInput::menuActivated() { if (!m_dialog) { m_dialog = new OpenMMInputDialog(qobject_cast(parent())); } m_dialog->setMolecule(m_molecule); m_dialog->show(); } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/openmminput/openmminput.h000066400000000000000000000027661506155467400253050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OPENMMINPUT_H #define AVOGADRO_QTPLUGINS_OPENMMINPUT_H #include class QAction; class QDialog; namespace Avogadro { namespace Io { class FileFormat; } namespace QtPlugins { class OpenMMInputDialog; class OpenMMInput : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit OpenMMInput(QObject* parent = nullptr); ~OpenMMInput() override; QString name() const override { return tr("OpenMM input"); } QString description() const override { return tr("Generate input for OpenMM."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; public slots: /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ // void openJobOutput(const MoleQueue::JobObject& job); bool readMolecule(QtGui::Molecule& mol) override; private slots: void menuActivated(); private: QAction* m_action; QtGui::Molecule* m_molecule; OpenMMInputDialog* m_dialog; const Io::FileFormat* m_outputFormat; QString m_outputFileName; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OPENMMINPUT_H avogadrolibs-1.101.0/avogadro/qtplugins/openmminput/openmminputdialog.cpp000066400000000000000000001173651506155467400270220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "openmminputdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { OpenMMInputDialog::OpenMMInputDialog(QWidget* parent, Qt::WindowFlags flag) : QDialog(parent, flag), m_molecule(nullptr), m_forceFieldType(amber99sbildn), m_title("Title"), m_savePath(""), m_waterModelType(tip3p), m_nonBondedType(PME), m_constraintType(HBonds), m_integratorType(Langevin), m_barostatType(NoBarostat), m_deviceIndex(1), m_openclPlatformIndex(1), m_rigidWater(0), m_temperature(298.15), m_generationTemperature(298.15), m_nonBondedCutoff(1.0), m_timeStep(2.0), m_ewaldTolerance(0.0005), m_constraintTolerance(0.00001), m_reportInterval(1000), m_equilibriationSteps(100), m_productionSteps(1000), m_errorTolerance(0.0001), m_collisionRate(1.0), m_pressure(1.0), m_barostatInterval(25), m_dumpStep(1), m_velocityDistRandom(0), m_platformType(CUDA), m_precisionType(mixedPrecision), m_thermoInterval(50), m_minimize(0), m_minimizeSteps(1000), m_DCDReporter(true), m_PDBReporter(false), m_stateDataReporter(true), m_output(), m_dirty(false), m_warned(false), readData(false), m_jobEdit(nullptr), m_moleculeEdit(nullptr) { ui.setupUi(this); m_jobFileName = (ui.jobScriptEdit->text().isEmpty() ? ui.jobScriptEdit->placeholderText() : ui.jobScriptEdit->text()) + ".py"; m_inputCoordFileName = (ui.inputCoordEdit->text().isEmpty() ? ui.inputCoordEdit->placeholderText() : ui.inputCoordEdit->text()); m_topologyFileName = (ui.inputTopEdit->text().isEmpty() ? ui.inputTopEdit->placeholderText() : ui.inputTopEdit->text()); // Connect the GUI elements to the correct slots connect(ui.jobScriptEdit, SIGNAL(textChanged(QString)), this, SLOT(setScriptName())); connect(ui.inputCoordEdit, SIGNAL(textChanged(QString)), this, SLOT(setInputCoordName())); connect(ui.forceFieldCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setForceField(int))); connect(ui.constraintsCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setConstraintType(int))); connect(ui.waterModelCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setWaterModelType(int))); connect(ui.nonBondedMethodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setNonBondedType(int))); connect(ui.integratorCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setIntegratorType(int))); connect(ui.barostatCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setBarostatType(int))); connect(ui.inputTopEdit, SIGNAL(textChanged(QString)), this, SLOT(setTopologyName())); connect(ui.rigidWaterCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setRigidWater(int))); connect(ui.temperatureSpin, SIGNAL(valueChanged(double)), this, SLOT(setTemperature(double))); connect(ui.generationTemperatureSpin, SIGNAL(valueChanged(double)), this, SLOT(setGenerationTemperature(double))); connect(ui.nonBondedCutoffSpin, SIGNAL(valueChanged(double)), this, SLOT(setNonBondedCutoff(double))); connect(ui.stepSpin, SIGNAL(valueChanged(double)), this, SLOT(setTimeStep(double))); connect(ui.ewaldToleranceSpin, SIGNAL(valueChanged(double)), this, SLOT(setEwaldTolerance(double))); connect(ui.constraintToleranceSpin, SIGNAL(valueChanged(double)), this, SLOT(setConstraintTolerance(double))); connect(ui.reportIntervalSpin, SIGNAL(valueChanged(int)), this, SLOT(setReportInterval(int))); connect(ui.equilibriationStepsSpin, SIGNAL(valueChanged(int)), this, SLOT(setEquilibriationSteps(int))); connect(ui.productionStepsSpin, SIGNAL(valueChanged(int)), this, SLOT(setProductionSteps(int))); connect(ui.deviceIndexSpin, SIGNAL(valueChanged(int)), this, SLOT(setDeviceIndex(int))); connect(ui.openCLIndexSpin, SIGNAL(valueChanged(int)), this, SLOT(setOpenCLPlatformIndex(int))); connect(ui.errorTolSpin, SIGNAL(valueChanged(double)), this, SLOT(setErrorTolerance(double))); connect(ui.collisionRateSpin, SIGNAL(valueChanged(double)), this, SLOT(setCollisionRate(double))); connect(ui.pressureSpin, SIGNAL(valueChanged(double)), this, SLOT(setPressure(double))); connect(ui.barostatIntervalSpin, SIGNAL(valueChanged(int)), this, SLOT(setBarostatInterval(int))); connect(ui.minimizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setMinimize(int))); connect(ui.minimizeStepsSpin, SIGNAL(valueChanged(int)), this, SLOT(setMinimizeSteps(int))); connect(ui.initVelCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setVelocityDistRandom(int))); connect(ui.platformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setPlatformType(int))); connect(ui.precisionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setPrecisionType(int))); connect(ui.dcdCheck, SIGNAL(toggled(bool)), this, SLOT(setDCDReporter(bool))); connect(ui.pdbCheck, SIGNAL(toggled(bool)), this, SLOT(setPDBReporter(bool))); connect(ui.stateDataCheck, SIGNAL(toggled(bool)), this, SLOT(setStateDataReporter(bool))); connect(ui.stepIndexCheck, SIGNAL(toggled(bool)), this, SLOT(setStepIndexBoolean(bool))); connect(ui.timeCheck, SIGNAL(toggled(bool)), this, SLOT(setTimeBoolean(bool))); connect(ui.speedCheck, SIGNAL(toggled(bool)), this, SLOT(setSpeedBoolean(bool))); connect(ui.progressCheck, SIGNAL(toggled(bool)), this, SLOT(setProgressBoolean(bool))); connect(ui.potentialEnergyCheck, SIGNAL(toggled(bool)), this, SLOT(setPotentialEnergyBoolean(bool))); connect(ui.kineticEnergyCheck, SIGNAL(toggled(bool)), this, SLOT(setKineticEnergyBoolean(bool))); connect(ui.totalEnergyCheck, SIGNAL(toggled(bool)), this, SLOT(setTotalEnergyBoolean(bool))); connect(ui.temperatureCheck, SIGNAL(toggled(bool)), this, SLOT(setTemperatureBoolean(bool))); connect(ui.volumeCheck, SIGNAL(toggled(bool)), this, SLOT(setVolumeBoolean(bool))); connect(ui.densityCheck, SIGNAL(toggled(bool)), this, SLOT(setDensityBoolean(bool))); connect(ui.generateButton, SIGNAL(clicked()), this, SLOT(generateClicked())); connect(ui.resetButton, SIGNAL(clicked()), this, SLOT(resetClicked())); connect(ui.enableFormButton, SIGNAL(clicked()), this, SLOT(enableFormClicked())); QSettings settings; readSettings(settings); resetClicked(); // Generate an initial preview of the input deck updatePreviewText(); addMoleculeDataTab(); } OpenMMInputDialog::~OpenMMInputDialog() { QSettings settings; writeSettings(settings); } void OpenMMInputDialog::showEvent(QShowEvent*) { updatePreviewText(); addMoleculeDataTab(); } void OpenMMInputDialog::updatePreviewText() { if (!isVisible()) return; int jobTabPosition = 0; // Store the currently displayed tab int currIndex = ui.tabWidget->currentIndex(); // Generate the input deck and display it if (m_dirty) { QString message = tr("Would you like to update the preview text, losing all " "changes made in the OpenMM input deck preview pane?"); int response = QMessageBox::question( this, tr("Overwrite modified input files?"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (static_cast(response) == QMessageBox::No) { return; } } ui.tabWidget->removeTab(jobTabPosition); m_jobEdit = new QTextEdit(this); m_jobEdit->setObjectName(m_jobFileName); m_jobEdit->setFontFamily("monospace"); connect(m_jobEdit, SIGNAL(textChanged()), this, SLOT(textEditModified())); m_jobEdit->setText(generateInputDeck()); ui.tabWidget->insertTab(jobTabPosition, m_jobEdit, m_jobFileName); deckDirty(false); // Restore current tab ui.tabWidget->setCurrentIndex(currIndex); } void OpenMMInputDialog::addMoleculeDataTab() { int molTabPosition = 1; if (m_molecule) { ui.tabWidget->removeTab(molTabPosition); std::string molOutput, extension = m_inputCoordFileName.split(".").back().toStdString(); bool writeSDF = Io::FileFormatManager::instance().writeString( *m_molecule, molOutput, extension); if (writeSDF) { m_moleculeEdit = new QTextEdit(this); m_moleculeEdit->setObjectName(m_inputCoordFileName); m_moleculeEdit->setFontFamily("monospace"); m_moleculeEdit->setText(QString::fromStdString(molOutput)); ui.tabWidget->insertTab(molTabPosition, m_moleculeEdit, m_inputCoordFileName); } } } void OpenMMInputDialog::textEditModified() { if (auto* edit = qobject_cast(sender())) { if (edit->document()->isModified()) { deckDirty(true); } else { deckDirty(false); } } } void OpenMMInputDialog::resetClicked() { // Reset the form to defaults deckDirty(false); // 1st subdivision ui.precisionCombo->setCurrentIndex(1); ui.forceFieldCombo->setCurrentIndex(2); ui.waterModelCombo->setCurrentIndex(1); ui.platformCombo->setCurrentIndex(3); setPlatformType(3); // 2nd subdivision ui.ewaldToleranceSpin->setValue(0.0005); ui.constraintToleranceSpin->setValue(0.00001); ui.nonBondedCutoffSpin->setValue(1.0); ui.generationTemperatureSpin->setValue(298.15); ui.nonBondedMethodCombo->setCurrentIndex(4); setNonBondedType(4); ui.constraintsCombo->setCurrentIndex(1); setConstraintType(1); ui.rigidWaterCombo->setCurrentIndex(0); setRigidWater(0); ui.initVelCombo->setCurrentIndex(0); setVelocityDistRandom(0); // 3rd subdivision ui.stepSpin->setValue(2.0); ui.errorTolSpin->setValue(0.0001); ui.collisionRateSpin->setValue(1.0); ui.temperatureSpin->setValue(298.15); ui.pressureSpin->setValue(1.0); ui.barostatIntervalSpin->setValue(25); ui.barostatCombo->setCurrentIndex(0); setBarostatType(0); ui.integratorCombo->setCurrentIndex(0); setIntegratorType(0); // 4th subdivision ui.minimizeCombo->setCurrentIndex(0); ui.reportIntervalSpin->setValue(1000); ui.minimizeStepsSpin->setValue(1000); ui.productionStepsSpin->setValue(1000); ui.equilibriationStepsSpin->setValue(100); ui.stepIndexCheck->setChecked(true); ui.timeCheck->setChecked(false); ui.speedCheck->setChecked(true); ui.progressCheck->setChecked(true); ui.potentialEnergyCheck->setChecked(true); ui.kineticEnergyCheck->setChecked(false); ui.totalEnergyCheck->setChecked(false); ui.temperatureCheck->setChecked(true); ui.volumeCheck->setChecked(false); ui.densityCheck->setChecked(false); // Unfortunately, setChecked() does not emit toggled() signal // So the slots have to be invoked setStepIndexBoolean(true); setTimeBoolean(false); setSpeedBoolean(true); setProgressBoolean(true); setPotentialEnergyBoolean(true); setKineticEnergyBoolean(false); setTotalEnergyBoolean(false); setTemperatureBoolean(true); setVolumeBoolean(false); setDensityBoolean(false); ui.stateDataCheck->setChecked(true); ui.dcdCheck->setChecked(true); ui.pdbCheck->setChecked(false); updatePreviewText(); addMoleculeDataTab(); } void OpenMMInputDialog::generateClicked() { QSettings settings; QString directory = settings.value("openmmInput/outputDirectory", QDir::homePath()).toString(); if (directory.isEmpty()) directory = QDir::homePath(); directory = QFileDialog::getExistingDirectory( this, tr("Select output directory"), directory); // User cancel: if (directory.isNull()) return; settings.setValue("openmmInput/outputDirectory", directory); QDir dir(directory); // Check for problems: QStringList errors; bool fatalError = false; do { // Do/while to break on fatal errors if (!dir.exists()) { errors << tr("%1: Directory does not exist!").arg(dir.absolutePath()); fatalError = true; break; } if (!dir.isReadable()) { errors << tr("%1: Directory cannot be read!").arg(dir.absolutePath()); fatalError = true; break; } QFileInfo jobFileInfo(dir.absoluteFilePath(m_jobFileName)); if (jobFileInfo.exists()) { errors << tr("%1: File will be overwritten.") .arg(jobFileInfo.absoluteFilePath()); } // Attempt to open the file for writing if (!QFile(jobFileInfo.absoluteFilePath()).open(QFile::WriteOnly)) { errors << tr("%1: File is not writable.").arg(jobFileInfo.absoluteFilePath()); fatalError = true; break; } QFileInfo molFileInfo(dir.absoluteFilePath(m_inputCoordFileName)); if (molFileInfo.exists()) { errors << tr("%1: File will be overwritten.") .arg(molFileInfo.absoluteFilePath()); } // Attempt to open the file for writing if (!QFile(molFileInfo.absoluteFilePath()).open(QFile::WriteOnly)) { errors << tr("%1: File is not writable.").arg(molFileInfo.absoluteFilePath()); fatalError = true; break; } } while (false); // only run once // Handle fatal errors: if (fatalError) { QString formattedError; switch (errors.size()) { case 0: formattedError = tr("The input files cannot be written due to an unknown error."); break; case 1: formattedError = tr("The input files cannot be written:\n\n%1").arg(errors.first()); break; default: { // If a fatal error occurred, it will be last one in the list. Pop it // off and tell the user that it was the reason we had to stop. QString fatal = errors.last(); QStringList tmp(errors); tmp.pop_back(); formattedError = tr("The input files cannot be written:\n\n%1\n\nWarnings:\n\n%2") .arg(fatal, tmp.join("\n")); break; } } QMessageBox::critical(this, tr("Output Error"), formattedError); return; } // Non-fatal errors: if (!errors.isEmpty()) { QString formattedError = tr("Warning:\n\n%1\n\nWould you like to continue?") .arg(errors.join("\n")); QMessageBox::StandardButton reply = QMessageBox::warning(this, tr("Write input files"), formattedError, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (reply != QMessageBox::Yes) return; } bool success = false; if (m_jobEdit && m_moleculeEdit) { QFile jobFile(dir.absoluteFilePath(m_jobFileName)); if (jobFile.open(QFile::WriteOnly | QFile::Text)) { if (jobFile.write(m_jobEdit->toPlainText().toLocal8Bit()) > 0) { success = true; } jobFile.close(); } if (!success) { QMessageBox::critical( this, tr("Output Error"), tr("Failed to write to file %1.").arg(jobFile.fileName())); } QFile molFile(dir.absoluteFilePath(m_inputCoordFileName)); if (molFile.open(QFile::WriteOnly | QFile::Text)) { if (molFile.write(m_moleculeEdit->toPlainText().toLocal8Bit()) > 0) { success = true; } molFile.close(); } if (!success) { QMessageBox::critical( this, tr("Output Error"), tr("Failed to write to file %1.").arg(molFile.fileName())); } } } void OpenMMInputDialog::enableFormClicked() { updatePreviewText(); } void OpenMMInputDialog::setScriptName() { m_jobFileName = (ui.jobScriptEdit->text().isEmpty() ? ui.jobScriptEdit->placeholderText() : ui.jobScriptEdit->text()) + ".py"; updatePreviewText(); } void OpenMMInputDialog::setInputCoordName() { ui.inputCoordEdit->setStyleSheet(""); m_inputCoordFileName = (ui.inputCoordEdit->text().isEmpty() ? ui.inputCoordEdit->placeholderText() : ui.inputCoordEdit->text()); QString ext = m_inputCoordFileName.split(".").back(); if (ext == tr("inpcrd") || ext == tr("gro")) { ui.forceFieldCombo->setEnabled(false); ui.inputTopEdit->setEnabled(true); if (ext == tr("inpcrd")) ui.inputTopEdit->setPlaceholderText(tr("input.prmtop")); else if (ext == tr("gro")) { ui.inputTopEdit->setPlaceholderText(tr("input.top")); } updatePreviewText(); } else if (ext == tr("pdb")) { ui.forceFieldCombo->setEnabled(true); ui.inputTopEdit->setEnabled(false); updatePreviewText(); } else { ui.forceFieldCombo->setEnabled(false); ui.inputTopEdit->setEnabled(false); ui.inputCoordEdit->setStyleSheet("color: #FF0000"); } addMoleculeDataTab(); } void OpenMMInputDialog::setTopologyName() { ui.inputCoordEdit->setStyleSheet(""); m_topologyFileName = (ui.inputTopEdit->text().isEmpty() ? ui.inputTopEdit->placeholderText() : ui.inputTopEdit->text()); QString ext = m_topologyFileName.split(".").back(); QString extCoord = m_inputCoordFileName.split(".").back(); if (extCoord == tr("inpcrd")) { if (ext != tr("prmtop")) { ui.inputCoordEdit->setStyleSheet("color: #FF0000"); } } else if (extCoord == tr("gro")) { if (ext != tr("top")) { ui.inputCoordEdit->setStyleSheet("color: #FF0000"); } } updatePreviewText(); } void OpenMMInputDialog::setForceField(int n) { m_forceFieldType = (OpenMMInputDialog::forceFieldType)n; ui.forceFieldCombo->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setConstraintType(int n) { m_constraintType = static_cast(n); ui.constraintsCombo->setEnabled(true); if (m_constraintType == None) { ui.constraintToleranceSpin->setEnabled(false); } else { ui.constraintToleranceSpin->setEnabled(true); } updatePreviewText(); } void OpenMMInputDialog::setWaterModelType(int n) { m_waterModelType = static_cast(n); ui.waterModelCombo->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setNonBondedType(int n) { m_nonBondedType = static_cast(n); ui.nonBondedMethodCombo->setEnabled(true); if (m_nonBondedType == NoCutoff) { ui.nonBondedCutoffSpin->setEnabled(false); ui.ewaldToleranceSpin->setEnabled(false); } else if (m_nonBondedType == CutoffPeriodic || m_nonBondedType == CutoffNonPeriodic) { ui.nonBondedCutoffSpin->setEnabled(true); ui.ewaldToleranceSpin->setEnabled(false); } else if (m_nonBondedType == Ewald || m_nonBondedType == PME) { ui.nonBondedCutoffSpin->setEnabled(true); ui.ewaldToleranceSpin->setEnabled(true); } updatePreviewText(); } void OpenMMInputDialog::setIntegratorType(int n) { m_integratorType = static_cast(n); ui.integratorCombo->setEnabled(true); if (m_integratorType == Langevin || m_integratorType == Brownian || m_integratorType == VariableLangevin) { if (m_integratorType == Langevin || m_integratorType == Brownian) { ui.stepSpin->setEnabled(true); ui.errorTolSpin->setEnabled(false); } else if (m_integratorType == VariableLangevin) { ui.stepSpin->setEnabled(false); ui.errorTolSpin->setEnabled(true); } ui.collisionRateSpin->setEnabled(true); ui.temperatureSpin->setEnabled(true); ui.barostatCombo->setEnabled(true); if (m_barostatType == NoBarostat) { ui.pressureSpin->setEnabled(false); ui.barostatIntervalSpin->setEnabled(false); } else { ui.pressureSpin->setEnabled(true); ui.barostatIntervalSpin->setEnabled(true); } } else if (m_integratorType == Verlet || m_integratorType == VariableVerlet) { if (m_integratorType == Verlet) { ui.stepSpin->setEnabled(true); ui.errorTolSpin->setEnabled(false); } else if (m_integratorType == VariableVerlet) { ui.stepSpin->setEnabled(false); ui.errorTolSpin->setEnabled(true); } ui.collisionRateSpin->setEnabled(false); ui.temperatureSpin->setEnabled(false); ui.barostatCombo->setEnabled(false); ui.pressureSpin->setEnabled(false); ui.barostatIntervalSpin->setEnabled(false); } updatePreviewText(); } void OpenMMInputDialog::setBarostatType(int n) { m_barostatType = static_cast(n); ui.barostatCombo->setEnabled(true); if (m_barostatType == NoBarostat) { ui.pressureSpin->setEnabled(false); ui.barostatIntervalSpin->setEnabled(false); } else { ui.pressureSpin->setEnabled(true); ui.barostatIntervalSpin->setEnabled(true); } updatePreviewText(); } void OpenMMInputDialog::setMolecule(QtGui::Molecule* molecule) { // Disconnect the old molecule first... if (molecule == m_molecule) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = molecule; // Update the preview text whenever primitives are changed connect(molecule, SIGNAL(changed(unsigned int)), SLOT(updatePreviewText())); updatePreviewText(); } void OpenMMInputDialog::setRigidWater(int n) { m_rigidWater = n; ui.rigidWaterCombo->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setMinimize(int n) { m_minimize = n; ui.minimizeCombo->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setMinimizeSteps(int n) { m_minimizeSteps = n; ui.minimizeStepsSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setTemperature(double n) { m_temperature = n; ui.temperatureSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setNonBondedCutoff(double n) { m_nonBondedCutoff = n; ui.nonBondedCutoffSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setTimeStep(double n) { m_timeStep = n; ui.stepSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setEwaldTolerance(double n) { m_ewaldTolerance = n; ui.ewaldToleranceSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setConstraintTolerance(double n) { m_constraintTolerance = n; ui.constraintToleranceSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setReportInterval(int n) { m_reportInterval = n; ui.reportIntervalSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setEquilibriationSteps(int n) { m_equilibriationSteps = n; ui.equilibriationStepsSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setProductionSteps(int n) { m_productionSteps = n; ui.productionStepsSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setDeviceIndex(int n) { m_deviceIndex = n; ui.deviceIndexSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setOpenCLPlatformIndex(int n) { m_openclPlatformIndex = n; ui.openCLIndexSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setErrorTolerance(double n) { m_errorTolerance = n; ui.errorTolSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setCollisionRate(double n) { m_collisionRate = n; ui.collisionRateSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setPressure(double n) { m_pressure = n; ui.pressureSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setBarostatInterval(int n) { m_barostatInterval = n; ui.barostatIntervalSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setVelocityDistRandom(int n) { m_velocityDistRandom = n; ui.initVelCombo->setEnabled(true); if (m_velocityDistRandom == 1) { ui.generationTemperatureSpin->setEnabled(false); } else { ui.generationTemperatureSpin->setEnabled(true); } updatePreviewText(); } void OpenMMInputDialog::setGenerationTemperature(double n) { m_generationTemperature = n; ui.generationTemperatureSpin->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setDCDReporter(bool state) { m_DCDReporter = state; ui.dcdCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setPDBReporter(bool state) { m_PDBReporter = state; ui.pdbCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setStepIndexBoolean(bool state) { m_stepIndex = state; ui.stepIndexCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setTimeBoolean(bool state) { m_time = state; ui.timeCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setSpeedBoolean(bool state) { m_speed = state; ui.speedCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setProgressBoolean(bool state) { m_progress = state; ui.progressCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setPotentialEnergyBoolean(bool state) { m_potentialEnergy = state; ui.potentialEnergyCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setKineticEnergyBoolean(bool state) { m_kineticEnergy = state; ui.kineticEnergyCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setTotalEnergyBoolean(bool state) { m_totalEnergy = state; ui.totalEnergyCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setTemperatureBoolean(bool state) { m_temperature = state; ui.temperatureCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setVolumeBoolean(bool state) { m_volume = state; ui.volumeCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setDensityBoolean(bool state) { m_density = state; ui.densityCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setStateDataReporter(bool state) { ui.stepIndexCheck->setEnabled(state); ui.timeCheck->setEnabled(state); ui.speedCheck->setEnabled(state); ui.progressCheck->setEnabled(state); ui.potentialEnergyCheck->setEnabled(state); ui.kineticEnergyCheck->setEnabled(state); ui.totalEnergyCheck->setEnabled(state); ui.temperatureCheck->setEnabled(state); ui.volumeCheck->setEnabled(state); ui.densityCheck->setEnabled(state); m_stateDataReporter = state; ui.stateDataCheck->setEnabled(true); updatePreviewText(); } void OpenMMInputDialog::setPlatformType(int n) { m_platformType = static_cast(n); ui.platformCombo->setEnabled(true); if (m_platformType == Reference || m_platformType == CPU) { ui.precisionCombo->setEnabled(false); ui.deviceIndexSpin->setEnabled(false); ui.openCLIndexSpin->setEnabled(false); } else { ui.precisionCombo->setEnabled(true); ui.deviceIndexSpin->setEnabled(true); if (m_platformType == OpenCL) ui.openCLIndexSpin->setEnabled(true); else ui.openCLIndexSpin->setEnabled(false); } updatePreviewText(); } void OpenMMInputDialog::setPrecisionType(int n) { m_precisionType = static_cast(n); ui.precisionCombo->setEnabled(true); updatePreviewText(); } QString OpenMMInputDialog::generateInputDeck() { // Generate an input deck based on the settings of the dialog QString buffer; QTextStream scriptStream(&buffer); scriptStream << "############################################################" "##############\n"; scriptStream << "# OpenMM input script generated by Avogadro.\n"; scriptStream << "# Builder adapted from OpenMM script builder " "http://builder.openmm.org.\n"; scriptStream << "############################################################" "##############\n\n"; scriptStream << "from __future__ import print_function\n"; scriptStream << "from simtk.openmm import app\n"; scriptStream << "import simtk.openmm as mm\n"; scriptStream << "from simtk import unit\n"; scriptStream << "from sys import stdout\n"; // first two or three lines, that load up the FF and the pdb // these lines end with the start of the function something.createSystem( QString ext = m_inputCoordFileName.split(".").back(); if (ext == tr("pdb")) { scriptStream << "\npdb = app.PDBFile(\'" << m_inputCoordFileName << "\')\n"; scriptStream << "forcefield = app.ForceField(\'" << getForceFieldType(m_forceFieldType) << ".xml\'"; scriptStream << ", \'" << getWaterModelType(m_waterModelType) << ".xml\'"; scriptStream << ")\n\n"; scriptStream << "system = forcefield.createSystem(pdb.topology, "; } else if (ext == tr("inpcrd")) { scriptStream << "\nprmtop = app.AmberPrmtopFile(\'" << m_topologyFileName << "\')\n"; scriptStream << "inpcrd = app.AmberInpcrdFile(\'" << m_inputCoordFileName << "\')\n\n"; scriptStream << "system = prmtop.createSystem("; if (m_waterModelType == implicit) { scriptStream << "implicitSolvent=app.OBC2, "; } } else if (ext == tr("gro")) { scriptStream << "\ngro = app.GromacsGroFile(\'" << m_inputCoordFileName << "\')\n"; scriptStream << "top = app.GromacsTopFile(\'" << m_topologyFileName << "\')\n\n"; scriptStream << "system = top.createSystem("; if (m_waterModelType == implicit) { scriptStream << "implicitSolvent=app.OBC2, "; } } else { // TODO } // options for the system scriptStream << "nonbondedMethod=" << "app." << getNonBondedType(m_nonBondedType) << ","; if (m_nonBondedCutoff > 0) { scriptStream << " nonbondedCutoff=" << Qt::fixed << qSetRealNumberPrecision(4) << m_nonBondedCutoff << "*unit.nanometers,"; } if (m_constraintType == None) { scriptStream << " constraints=" << getConstraintType(m_constraintType); } else { scriptStream << " constraints=" << "app." << getConstraintType(m_constraintType); } scriptStream << ", rigidWater=" << getRigidWater(m_rigidWater); if (m_nonBondedType == Ewald || m_nonBondedType == PME) { scriptStream << ", ewaldErrorTolerance=" << Qt::fixed << qSetRealNumberPrecision(5) << m_ewaldTolerance; } scriptStream << ")\n"; // set the integrator scriptStream << "integrator = mm." << getIntegratorType(m_integratorType) << "Integrator("; if (m_integratorType == Langevin || m_integratorType == Brownian) { scriptStream << m_temperature << "*unit.kelvin, "; scriptStream << m_collisionRate << "/unit.picoseconds, "; } if (m_integratorType == VariableLangevin || m_integratorType == VariableVerlet) { scriptStream << m_errorTolerance << ")\n"; } else { scriptStream << m_timeStep << "*unit.femtoseconds)\n"; } if (m_constraintType != None && m_constraintTolerance > 0) { scriptStream << "integrator.setConstraintTolerance(" << m_constraintTolerance << ")\n"; } // add a barostat if (m_barostatType == MonteCarlo) { scriptStream << "system.addForce(mm.MonteCarloBarostat(" << m_pressure << "*unit.atmospheres"; scriptStream << ", " << m_temperature << "*unit.kelvin"; if (m_barostatInterval > 0) { scriptStream << ", " << m_barostatInterval; } scriptStream << "))\n"; } // // add a thermostat // if (m_thermostatType == Andersen) { // scriptStream << "system.addForce(mm.AndersenThermostat(" << // m_temperature << "*unit.kelvin"; scriptStream << ", " << // m_collisionRate << "/unit.picoseconds" << "))\n"; // } scriptStream << "\n"; // set the platform options scriptStream << "platform = mm.Platform.getPlatformByName(\'" << getPlatformType(m_platformType) << "\')\n"; if (m_platformType == CUDA) { scriptStream << R"(properties = {'CudaPrecision': ')" << getPrecisionType(m_precisionType) << "\'"; if (m_deviceIndex > 0) { scriptStream << R"(, 'CudaDeviceIndex': ')" << m_deviceIndex << "\'"; } scriptStream << "}\n"; } else if (m_platformType == OpenCL) { scriptStream << R"(properties = {'OpenCLPrecision': ')" << getPrecisionType(m_precisionType) << "\'"; if (m_openclPlatformIndex > 0) { scriptStream << R"(, 'OpenCLPlatformIndex': ')" << m_openclPlatformIndex << "\'"; } if (m_deviceIndex > 0) { scriptStream << ", "; if (m_openclPlatformIndex > 0) { scriptStream << "\n "; } scriptStream << R"('OpenCLDeviceIndex': ')" << m_deviceIndex << "\'"; } scriptStream << "}\n"; } // create the simulation object scriptStream << "simulation = app.Simulation(" << (ext == tr("pdb") ? "pdb" : "prmtop") << ".topology, system, integrator, platform"; if (m_platformType == CUDA || m_platformType == OpenCL) { scriptStream << ", properties"; } scriptStream << ")\n"; if (ext == tr("pdb")) { scriptStream << "simulation.context.setPositions(pdb.positions)\n\n"; } else if (ext == tr("inpcrd")) { scriptStream << "simulation.context.setPositions(inpcrd.positions)\n\n"; } else if (ext == tr("gro")) { scriptStream << "simulation.context.setPositions(gro.positions)\n\n"; } else { // TODO } // minimize if (getMinimize(m_minimize) == tr("True")) { scriptStream << "print('Minimizing...\')\n"; if (m_minimizeSteps == 0) { scriptStream << "simulation.minimizeEnergy()\n"; } else { scriptStream << "simulation.minimizeEnergy(maxIterations=" << m_minimizeSteps << ")\n"; } } if (getVelocityDistRandom(m_velocityDistRandom) == tr("True")) { scriptStream << "\nsimulation.context.setVelocitiesToTemperature(" << m_generationTemperature << "*unit.kelvin)\n"; } // equilibrate if (m_equilibriationSteps > 0) { scriptStream << "print(\'Equilibrating...\')\n"; scriptStream << "simulation.step(" << m_equilibriationSteps << ")\n\n"; } // add reporters // if (d.simulation.dcd_reporter == 'True' && // d.simulation.statedata_opts.length > 0) { if (m_DCDReporter) { scriptStream << "simulation.reporters.append(app.DCDReporter(\'trajectory.dcd\'"; scriptStream << ", " << m_reportInterval << "))\n"; scriptStream << "\n"; } if (m_PDBReporter) { scriptStream << "simulation.reporters.append(app.PDBReporter(\'trajectory.pdb\'"; scriptStream << ", " << m_reportInterval << "))\n"; scriptStream << "\n"; } if (m_stateDataReporter) { scriptStream << "simulation.reporters.append(app.StateDataReporter("; scriptStream << "stdout"; scriptStream << ", " << m_reportInterval; if (m_stepIndex) scriptStream << ", step=True"; if (m_time) scriptStream << ", time=True"; if (m_potentialEnergy) scriptStream << ", potentialEnergy=True"; if (m_kineticEnergy) scriptStream << ", kineticEnergy=True"; if (m_totalEnergy) scriptStream << ", totalEnergy=True"; if (m_temperatureCheck) scriptStream << ", temperature=True"; if (m_volume) scriptStream << ", volume=True"; if (m_density) scriptStream << ", density=True"; if (m_progress) scriptStream << ", progress=True, remainingTime=True"; if (m_speed) scriptStream << ", speed=True"; // if using progress (which also implies remaining time), totalSteps // is required. if (m_progress) scriptStream << ", totalSteps=" << m_productionSteps; scriptStream << ", separator=\'\\t\'))\n"; scriptStream << "\n"; } // run scriptStream << "print(\'Running Production...\')\n"; scriptStream << "simulation.step(" << m_productionSteps << ")\n"; scriptStream << "print(\'Done!\')\n"; return buffer; } QString OpenMMInputDialog::getForceFieldType(forceFieldType t) { // Translate the enum to text for the output generation switch (t) { case amber96: return "amber96"; case amber99sb: return "amber99sb"; case amber99sbildn: return "amber99sbildn"; case amber99sbnmr: return "amber99sbnmr"; case amber03: return "amber03"; case amber10: return "amber10"; default: return "amber99sbildn"; } } QString OpenMMInputDialog::getImplicitSolventType(forceFieldType t) { // Translate the enum to text for the output generation switch (t) { case amber96: return "amber96_obc"; case amber99sb: return "amber99_obc"; case amber99sbildn: return "amber99_obc"; case amber99sbnmr: return "amber99_obc"; case amber03: return "amber03_obc"; case amber10: return "amber10_obc"; default: return "amber99_obc"; } } QString OpenMMInputDialog::getConstraintType(constraintType t) { switch (t) { case None: return "None"; case HBonds: return "HBonds"; case AllBonds: return "AllBonds"; case HAngles: return "HAngles"; default: return "HBonds"; } } QString OpenMMInputDialog::getWaterModelType(waterModelType t) { switch (t) { case spce: return "spce"; case tip3p: return "tip3p"; case tip4pew: return "tip4pew"; case tip5p: return "tip5p"; case implicit: return getImplicitSolventType(m_forceFieldType); default: return "tip3p"; } } QString OpenMMInputDialog::getNonBondedType(nonBondedType t) { switch (t) { case NoCutoff: return "NoCutoff"; case CutoffNonPeriodic: return "CutoffNonPeriodic"; case CutoffPeriodic: return "CutoffPeriodic"; case Ewald: return "Ewald"; case PME: return "PME"; default: return "PME"; } } QString OpenMMInputDialog::getIntegratorType(integratorType t) { switch (t) { case Langevin: return "Langevin"; case Verlet: return "Verlet"; case Brownian: return "Brownian"; case VariableVerlet: return "VariableVerlet"; case VariableLangevin: return "VariableLangevin"; default: return "Langevin"; } } QString OpenMMInputDialog::getBarostatType(barostatType t) { switch (t) { case NoBarostat: return "None"; case MonteCarlo: return "MonteCarlo"; default: return "None"; } } QString OpenMMInputDialog::getRigidWater(int t) { switch (t) { case 0: return "True"; case 1: return "False"; default: return "False"; } } QString OpenMMInputDialog::getVelocityDistRandom(int t) { switch (t) { case 0: return "True"; case 1: return "False"; default: return "True"; } } QString OpenMMInputDialog::getPlatformType(platformType t) { switch (t) { case Reference: return "Reference"; case OpenCL: return "OpenCL"; case CUDA: return "CUDA"; case CPU: return "CPU"; default: return "CUDA"; } } QString OpenMMInputDialog::getPrecisionType(precisionType t) { switch (t) { case singlePrecision: return "single"; case mixedPrecision: return "mixed"; case doublePrecision: return "double"; default: return "mixed"; } } QString OpenMMInputDialog::getMinimize(int t) { switch (t) { case 0: return "True"; case 1: return "False"; default: return "True"; } } void OpenMMInputDialog::deckDirty(bool dirty) { m_dirty = dirty; // ui.titleLine->setEnabled(!dirty); // ui.calculationCombo->setEnabled(!dirty); // ui.theoryCombo->setEnabled(!dirty); // ui.basisCombo->setEnabled(!dirty); // ui.multiplicitySpin->setEnabled(!dirty); // ui.chargeSpin->setEnabled(!dirty); ui.enableFormButton->setEnabled(dirty); } void OpenMMInputDialog::readSettings(QSettings& settings) { m_savePath = settings.value("openmm/savepath").toString(); } void OpenMMInputDialog::writeSettings(QSettings& settings) const { settings.setValue("openmm/savepath", m_savePath); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/openmminput/openmminputdialog.h000066400000000000000000000126411506155467400264560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef OPENMMINPUTDIALOG_H #define OPENMMINPUTDIALOG_H #include "ui_openmminputdialog.h" #include #include class QJsonObject; class QTextEdit; namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { class OpenMMInputDialog : public QDialog { Q_OBJECT public: explicit OpenMMInputDialog(QWidget* parent = nullptr, Qt::WindowFlags flag = Qt::WindowFlags()); ~OpenMMInputDialog() override; void readSettings(QSettings&); void writeSettings(QSettings&) const; enum forceFieldType { amber96, amber99sb, amber99sbildn, amber99sbnmr, amber03, amber10 }; enum waterModelType { spce, tip3p, tip4pew, tip5p, implicit }; enum nonBondedType { NoCutoff, CutoffNonPeriodic, CutoffPeriodic, Ewald, PME }; enum constraintType { None, HBonds, AllBonds, HAngles }; enum integratorType { Langevin, Verlet, Brownian, VariableLangevin, VariableVerlet }; enum barostatType { NoBarostat, MonteCarlo }; enum platformType { Reference, OpenCL, CPU, CUDA }; enum precisionType { singlePrecision, mixedPrecision, doublePrecision }; void setMolecule(QtGui::Molecule* molecule); protected: /** * Reimplemented to update the dialog when it is shown */ void showEvent(QShowEvent* event) override; private: Ui::OpenMMInputDialog ui; QtGui::Molecule* m_molecule; // QString m_title; QString m_readData; forceFieldType m_forceFieldType; QString m_title; QString m_savePath; waterModelType m_waterModelType; nonBondedType m_nonBondedType; constraintType m_constraintType; integratorType m_integratorType; barostatType m_barostatType; int m_deviceIndex; int m_openclPlatformIndex; int m_rigidWater; double m_temperature; double m_generationTemperature; double m_nonBondedCutoff; double m_timeStep; double m_ewaldTolerance; double m_constraintTolerance; int m_reportInterval; int m_equilibriationSteps; int m_productionSteps; double m_errorTolerance; double m_collisionRate; double m_pressure; int m_barostatInterval; QString m_dumpXYZ; int m_dumpStep; int m_velocityDistRandom; platformType m_platformType; precisionType m_precisionType; int m_thermoInterval; int m_minimize; int m_minimizeSteps; bool m_DCDReporter; bool m_PDBReporter; bool m_stateDataReporter; bool m_stepIndex; bool m_time; bool m_speed; bool m_progress; bool m_potentialEnergy; bool m_kineticEnergy; bool m_totalEnergy; bool m_temperatureCheck; bool m_volume; bool m_density; QString m_output; bool m_dirty; bool m_warned; bool readData; QTextEdit* m_jobEdit; QTextEdit* m_moleculeEdit; QString m_inputCoordFileName; QString m_topologyFileName; QString m_jobFileName; // Generate an input deck as a string QString generateInputDeck(); // Translate enums to strings QString getForceFieldType(forceFieldType t); QString getImplicitSolventType(forceFieldType t); QString getConstraintType(constraintType t); QString getWaterModelType(waterModelType t); QString getNonBondedType(nonBondedType t); QString getIntegratorType(integratorType t); QString getBarostatType(barostatType t); QString getRigidWater(int t); QString getVelocityDistRandom(int t); QString getPlatformType(platformType t); QString getPrecisionType(precisionType t); QString getMinimize(int t); // Enable/disable form elements void deckDirty(bool); void addMoleculeDataTab(); public Q_SLOTS: void updatePreviewText(); private Q_SLOTS: //! Button Slots void textEditModified(); void resetClicked(); void generateClicked(); void enableFormClicked(); void setScriptName(); void setInputCoordName(); void setTopologyName(); void setForceField(int); void setConstraintType(int); void setWaterModelType(int); void setNonBondedType(int); void setIntegratorType(int); void setBarostatType(int); void setRigidWater(int); void setTemperature(double); void setGenerationTemperature(double); void setNonBondedCutoff(double); void setTimeStep(double); void setEwaldTolerance(double); void setConstraintTolerance(double); void setReportInterval(int); void setEquilibriationSteps(int); void setProductionSteps(int); void setDeviceIndex(int); void setOpenCLPlatformIndex(int); void setErrorTolerance(double); void setCollisionRate(double); void setPressure(double); void setBarostatInterval(int); void setVelocityDistRandom(int); void setDCDReporter(bool); void setPDBReporter(bool); void setStateDataReporter(bool); void setStepIndexBoolean(bool); void setTimeBoolean(bool); void setSpeedBoolean(bool); void setProgressBoolean(bool); void setPotentialEnergyBoolean(bool); void setKineticEnergyBoolean(bool); void setTotalEnergyBoolean(bool); void setTemperatureBoolean(bool); void setVolumeBoolean(bool); void setDensityBoolean(bool); void setPlatformType(int); void setPrecisionType(int); void setMinimize(int); void setMinimizeSteps(int); }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/openmminput/openmminputdialog.ui000066400000000000000000000702221506155467400266430ustar00rootroot00000000000000 OpenMMInputDialog 0 0 1074 697 0 0 OpenMM Script Builder true QLayout::SetNoConstraint QLayout::SetDefaultConstraint Job script: script Input Coords: input.pdb Input Topology: input.prmtop false Forcefield: AMBER96 AMBER99sb AMBER99sb-ildn AMBER99sb-nmr AMBER03 AMBER10 Water Model: SPC/E TIP3P TIP4P-Ew TIP5P Implicit Solvent (OBC) Platform: Reference OpenCL CPU CUDA Precision: single mixed double Device Index: 10000 1 1 OpenCL Platform Index: 10000 1 1 1 Qt::Horizontal Nonbonded method: NoCutoff CutoffNonPeriodic CutoffPeriodic Ewald PME Ewald Tolerance: 5 0.000010000000000 0.000500000000000 0.000010000000000 10000.000000000000000 Constraints: None HBonds AllBonds HAngles Constraint Tolerance: 5 0.000010000000000 1.000000000000000 0.000010000000000 10000.000000000000000 Rigid water? True False Nonbonded cutoff: 4 0.000100000000000 1.000000000000000 0.000100000000000 10000.000000000000000 nm Random initial velocity: True False Generation Temp: 2 20000.000000000000000 298.149999999999977 K 1 Qt::Horizontal Integrator: Langevin Verlet Brownian VariableLangevin VariableVerlet Time step for the simulation in units according to "Units" specification. Timestep: Time step for the simulation in units according to "Units" specification. 0.500000000000000 2.000000000000000 fs Error tolerance: 4 0.000100000000000 0.000100000000000 0.000100000000000 10000.000000000000000 Collision rate: 2 20000.000000000000000 1.50000000000 /ps Temperature: 2 20000.000000000000000 298.149999999999977 K Barostat: None Monte Carlo Pressure: 2 20000.000000000000000 1.0000000 atm Barostat Interval 10000 1 25 Qt::Horizontal Reporters: StateData true DCD true PDB false Report Interval: 1 9999999 1000 Equilibration Steps: 1 9999999 100 Production Steps: 1 9999999 1000 Minimize? True False Max. Minimize Steps: 1 9999999 1000 StateData options: Step Index true Time false Speed true Progress true Potential Energy true Kinetic Energy false Total Energy false Temperature true Volume false Density false Qt::Horizontal 40 20 0 0 QLayout::SetFixedSize Reset false Use Form Qt::Horizontal 48 26 Generate… Close generateButton closeButton resetButton enableFormButton closeButton clicked() OpenMMInputDialog close() 451 411 258 243 avogadrolibs-1.101.0/avogadro/qtplugins/overlayaxes/000077500000000000000000000000001506155467400225355ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/overlayaxes/CMakeLists.txt000066400000000000000000000004451506155467400253000ustar00rootroot00000000000000find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) avogadro_plugin(OverlayAxes "Reference Axes Overlay" ExtensionPlugin overlayaxes.h OverlayAxes overlayaxes.cpp "") target_link_libraries(OverlayAxes PRIVATE Avogadro::Rendering Avogadro::QtOpenGL OpenGL::GL GLEW::GLEW) avogadrolibs-1.101.0/avogadro/qtplugins/overlayaxes/overlayaxes.cpp000066400000000000000000000231361506155467400256100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "overlayaxes.h" #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Vector3f; using Avogadro::Core::Array; using Avogadro::Rendering::Camera; using Avogadro::Rendering::GeometryNode; using Avogadro::Rendering::GroupNode; using Avogadro::Rendering::MeshGeometry; using Eigen::Affine3f; namespace { const static float M_PI_F = 3.14159265358979323846f; // Mesh class that overrides the camera used in rendering. class CustomMesh : public MeshGeometry { public: CustomMesh() { setRenderPass(Avogadro::Rendering::Overlay3DPass); } ~CustomMesh() override {} void render(const Camera& camera) override; }; void CustomMesh::render(const Camera& camera) { // Swap in a new viewport/camera for the overlay /// @todo This is messy, it would be better to specify camera/viewport in a /// group/geometry node that the renderer could apply to all children. // Keep the rotation, lose the translation: Affine3f mv(camera.modelView()); mv.matrix().block<3, 1>(0, 3) = Vector3f::Zero(); // Save the actual viewport - works better on high resolution screens GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); // The largest window dimension, used to scale the axes // (again, grab from the actual viewport) const int maxDim = std::max(viewport[2], viewport[3]); Camera meshCamera(camera); meshCamera.setViewport(maxDim / 10, maxDim / 10); meshCamera.setModelView(mv); meshCamera.calculateOrthographic(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f); glViewport(static_cast(10), static_cast(10), static_cast(meshCamera.width()), static_cast(meshCamera.height())); MeshGeometry::render(meshCamera); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); } } // namespace namespace Avogadro::QtPlugins { class OverlayAxes::RenderImpl { public: RenderImpl(); ~RenderImpl(); CustomMesh* mesh; private: void buildMesh(); // axis must be normalized: void addAxis(const Vector3f& axis, const Vector3ub& color); }; OverlayAxes::RenderImpl::RenderImpl() : mesh(new CustomMesh) { buildMesh(); } OverlayAxes::RenderImpl::~RenderImpl() { delete mesh; } void OverlayAxes::RenderImpl::buildMesh() { addAxis(Vector3f(1.f, 0.f, 0.f), Vector3ub(255, 0, 0)); addAxis(Vector3f(0.f, 1.f, 0.f), Vector3ub(0, 255, 0)); addAxis(Vector3f(0.f, 0.f, 1.f), Vector3ub(0, 0, 255)); } void OverlayAxes::RenderImpl::addAxis(const Vector3f& axis, const Vector3ub& color) { mesh->setColor(color); // Number of angular samples: const unsigned int res = 12; const auto resf = static_cast(res); // Cylinder length: const float cylLength = .75f; // Cylinder radius: const float cylRadius = 0.0625f; // Cone length: const float coneLength = .25f; // Cone radius: const float coneRadius = .125f; // Some vectors that will simplify things later: const Vector3f origin(0.f, 0.f, 0.f); const Vector3f cylVector(axis * cylLength); const Vector3f coneVector(axis * coneLength); const Vector3f axisVector(coneVector + cylVector); const Vector3f radialUnit(axis.unitOrthogonal()); // Index offsets: const unsigned int coneBaseOffset = 0; const unsigned int coneBaseRadialsOffset = coneBaseOffset + 1; const unsigned int coneSideRadialsOffset = coneBaseRadialsOffset + res; const unsigned int coneTipsOffset = coneSideRadialsOffset + res; const unsigned int cylBaseRadialsOffset = coneTipsOffset + res; const unsigned int cylTopRadialsOffset = cylBaseRadialsOffset + res; const unsigned int numVertices = cylTopRadialsOffset + res; // Allocate arrays: Array vertices(numVertices); Array normals(numVertices); // This point doesn't change: vertices[coneBaseOffset] = origin + cylVector; normals[coneBaseOffset] = -axis; // Initial radial: Vector3f radial(radialUnit); // Create radial transform: Eigen::Affine3f xform(Eigen::AngleAxisf(2.f * M_PI_F / resf, axis)); // Build vertex list: const Vector3f coneTip(origin + axisVector); Vector3f coneRadial; Vector3f coneSideNormal; Vector3f cylRadial; for (unsigned int i = 0; i < res; ++i) { coneRadial = origin + cylVector + (radial * coneRadius); // Calculating the cone side normal: // // /| (z points out of screen) z = coneVector x coneRadial // / | a = coneVector - coneRadial // a / | coneVector n = z x a // / | // / | (n is the normal for vector a) // /_____| // coneRadial coneSideNormal = -(coneVector.cross(coneRadial)) .cross(coneVector - coneRadial) .normalized(); vertices[coneBaseRadialsOffset + i] = coneRadial; normals[coneBaseRadialsOffset + i] = -axis; vertices[coneSideRadialsOffset + i] = coneRadial; normals[coneSideRadialsOffset + i] = coneSideNormal; cylRadial = origin + (radial * cylRadius); vertices[cylBaseRadialsOffset + i] = cylRadial; normals[cylBaseRadialsOffset + i] = radial; vertices[cylTopRadialsOffset + i] = cylVector + cylRadial; normals[cylTopRadialsOffset + i] = radial; radial = xform * radial; } // Cone tip normals are averages of the side radial normals: for (unsigned int i = 0; i < res; ++i) { unsigned int ind1 = coneSideRadialsOffset + i; unsigned int ind2 = coneSideRadialsOffset + ((i + 1) % res); vertices[coneTipsOffset + i] = coneTip; normals[coneTipsOffset + i] = (normals[ind1] + normals[ind2]).normalized(); } // Add the vertices and get our index offset: const unsigned int baseOffset = mesh->addVertices(vertices, normals); // Stitch the vertices together: Array triangles(3 * 4 * res); // 3 verts * 4 tri * nsamples unsigned int* ptr = triangles.data(); for (unsigned int i = 0; i < res; ++i) { unsigned int i2 = (i + 1) % res; // Cone sides: *ptr++ = baseOffset + coneTipsOffset + i; *ptr++ = baseOffset + coneSideRadialsOffset + i; *ptr++ = baseOffset + coneSideRadialsOffset + i2; // Cone base: *ptr++ = baseOffset + coneBaseRadialsOffset + i; *ptr++ = baseOffset + coneBaseOffset; *ptr++ = baseOffset + coneBaseRadialsOffset + i2; // Cylinder side quad: *ptr++ = baseOffset + cylTopRadialsOffset + i; *ptr++ = baseOffset + cylBaseRadialsOffset + i; *ptr++ = baseOffset + cylTopRadialsOffset + i2; *ptr++ = baseOffset + cylBaseRadialsOffset + i; *ptr++ = baseOffset + cylTopRadialsOffset + i2; *ptr++ = baseOffset + cylBaseRadialsOffset + i2; } // Add the indices to the mesh mesh->addTriangles(triangles); } OverlayAxes::OverlayAxes(QObject* p) : Avogadro::QtGui::ExtensionPlugin(p), m_initialized(false), m_render(new RenderImpl), m_glWidget(nullptr), m_axesAction(new QAction(tr("Reference Axes"), this)) { connect(m_axesAction, SIGNAL(triggered()), SLOT(processAxes())); QSettings settings; m_enabled = settings.value("overlayAxes/enabled", true).toBool(); m_axesAction->setCheckable(true); m_axesAction->setChecked(m_enabled); m_axesAction->setProperty("menu priority", 80); // processAxes() will flip the value when called // so we need to invert it here m_enabled = !m_enabled; } OverlayAxes::~OverlayAxes() { delete m_render; } QList OverlayAxes::actions() const { QList result; return result << m_axesAction; } QStringList OverlayAxes::menuPath(QAction*) const { return QStringList() << tr("&View"); } void OverlayAxes::processAxes() { m_enabled = !m_enabled; QSettings settings; settings.setValue("overlayAxes/enabled", m_enabled); m_axesAction->setChecked(m_enabled); Rendering::GroupNode* engineNode = m_widgetToNode[m_glWidget]; GroupNode& node = m_scene->rootNode(); if (node.hasChild(engineNode)) { engineNode->clearUI(); m_scene->rootNode().removeChild(engineNode); delete engineNode; m_widgetToNode[m_glWidget] = nullptr; } if (m_enabled) { engineNode = new Rendering::GroupNode(&node); m_widgetToNode[m_glWidget] = engineNode; process(*m_molecule, *engineNode); } } // namespace QtPlugins void OverlayAxes::setActiveWidget(QWidget* widget) { if (widget != nullptr) { m_glWidget = widget; connect(this, SIGNAL(updateRequested()), m_glWidget, SLOT(requestUpdate())); if (m_widgetToNode.find(m_glWidget) == m_widgetToNode.end()) { m_widgetToNode[m_glWidget] = nullptr; } } } void OverlayAxes::process(const Core::Molecule&, Rendering::GroupNode& node) { auto* geo = new GeometryNode; // Since our geometry doesn't change, we just make a copy of the pre-built // set of axes. geo->addDrawable(new CustomMesh(*m_render->mesh)); node.addChild(geo, GroupNode::NodeType::UI); emit updateRequested(); } void OverlayAxes::setMolecule(QtGui::Molecule* molecule) { m_molecule = molecule; } void OverlayAxes::setScene(Rendering::Scene* scene) { m_scene = scene; if (!m_initialized) { m_initialized = true; processAxes(); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/overlayaxes/overlayaxes.h000066400000000000000000000034351506155467400252550ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OVERLAYAXES_H #define AVOGADRO_QTPLUGINS_OVERLAYAXES_H #include #include #include #include #include namespace Avogadro { namespace QtPlugins { /** * @brief Render reference axes in the corner of the display. */ class OverlayAxes : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit OverlayAxes(QObject* parent = nullptr); ~OverlayAxes() override; QString name() const override { return tr("Reference Axes Overlay"); } QString description() const override { return tr("Render reference axes in the corner of the display."); } QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* molecule) override; void setScene(Rendering::Scene* scene) override; void setActiveWidget(QWidget* widget) override; signals: void updateRequested(); private slots: void processAxes(); private: void process(const Core::Molecule& molecule, Rendering::GroupNode& node); bool m_enabled; bool m_initialized; class RenderImpl; RenderImpl* const m_render; std::map m_widgetToNode; QtGui::Molecule* m_molecule = nullptr; Rendering::Scene* m_scene; QWidget* m_glWidget; QAction* m_axesAction; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OVERLAYAXES_H avogadrolibs-1.101.0/avogadro/qtplugins/playertool/000077500000000000000000000000001506155467400223655ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/playertool/CMakeLists.txt000066400000000000000000000006771506155467400251370ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 REQUIRED COMPONENTS OpenGL OpenGLWidgets) endif() avogadro_plugin(PlayerTool "Player tool" ToolPlugin playertool.h PlayerTool "playertool.cpp" "" playertool.qrc ) target_link_libraries(PlayerTool PRIVATE gwavi gif-h Avogadro::QtOpenGL) if(QT_VERSION EQUAL 6) target_link_libraries(PlayerTool PRIVATE Qt6::OpenGL Qt6::OpenGLWidgets) endif() avogadrolibs-1.101.0/avogadro/qtplugins/playertool/player_dark.svg000066400000000000000000000012561506155467400254070ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/playertool/player_light.svg000066400000000000000000000012561506155467400255750ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/playertool/playertool.cpp000066400000000000000000000321701506155467400252660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "playertool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using QtGui::Molecule; PlayerTool::PlayerTool(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_renderer(nullptr), m_currentFrame(0), m_toolWidget(nullptr), m_frameIdx(nullptr), m_slider(nullptr) { QString shortcut = tr("Ctrl+9", "control-key 9"); m_activateAction->setText(tr("Player")); m_activateAction->setToolTip(tr("Animation Tool\t(%1)").arg(shortcut)); setIcon(); } PlayerTool::~PlayerTool() {} void PlayerTool::setIcon(bool darkTheme) { if (darkTheme) { m_activateAction->setIcon(QIcon(":/icons/player_dark.svg")); } else { m_activateAction->setIcon(QIcon(":/icons/player_light.svg")); } } QWidget* PlayerTool::toolWidget() const { if (!m_toolWidget) { m_toolWidget = new QWidget(qobject_cast(parent())); auto* layout = new QVBoxLayout; auto* controls = new QHBoxLayout; controls->addStretch(1); auto* leftButton = new QPushButton("<"); connect(leftButton, SIGNAL(clicked()), SLOT(back())); controls->addWidget(leftButton); playButton = new QPushButton(tr("Play")); connect(playButton, SIGNAL(clicked()), SLOT(play())); controls->addWidget(playButton); stopButton = new QPushButton(tr("Stop")); connect(stopButton, SIGNAL(clicked()), SLOT(stop())); controls->addWidget(stopButton); stopButton->setEnabled(false); auto* rightButton = new QPushButton(">"); connect(rightButton, SIGNAL(clicked()), SLOT(forward())); controls->addWidget(rightButton); controls->addStretch(1); layout->addLayout(controls); auto* frames = new QHBoxLayout; auto* label = new QLabel(tr("Frame rate:")); frames->addWidget(label); m_animationFPS = new QSpinBox; m_animationFPS->setValue(5); m_animationFPS->setMinimum(0); m_animationFPS->setMaximum(1000); m_animationFPS->setSuffix(tr(" FPS", "frames per second")); frames->addWidget(m_animationFPS); layout->addLayout(frames); auto* sliderLayout = new QHBoxLayout; m_slider = new QSlider(Qt::Horizontal); m_slider->setMinimum(0); m_slider->setTickInterval(1); connect(m_slider, SIGNAL(valueChanged(int)), SLOT(sliderPositionChanged(int))); sliderLayout->addWidget(m_slider); layout->addLayout(sliderLayout); if (m_molecule->coordinate3dCount() > 1) m_slider->setMaximum(m_molecule->coordinate3dCount() - 1); auto* frameLayout = new QHBoxLayout; // QHBoxLayout* leftColumn = new QHBoxLayout; // QLabel* label2 = new QLabel(tr("Timestep:")); // leftColumn->addWidget(label2); // frameLayout->addLayout(leftColumn); auto* rightColumn = new QHBoxLayout; rightColumn->addStretch(1); auto* label3 = new QLabel(tr("Frame:")); rightColumn->addWidget(label3); m_frameIdx = new QSpinBox; m_frameIdx->setValue(1); m_frameIdx->setMinimum(1); if (m_molecule->coordinate3dCount() > 1) { m_frameIdx->setMaximum(m_molecule->coordinate3dCount()); m_frameIdx->setSuffix(tr(" of %0").arg(m_molecule->coordinate3dCount())); } connect(m_frameIdx, SIGNAL(valueChanged(int)), SLOT(spinnerPositionChanged(int))); rightColumn->addWidget(m_frameIdx); frameLayout->addLayout(rightColumn); layout->addLayout(frameLayout); auto* bonding = new QHBoxLayout; bonding->addStretch(1); m_dynamicBonding = new QCheckBox(tr("Dynamic bonding?")); m_dynamicBonding->setChecked(false); bonding->addWidget(m_dynamicBonding); bonding->addStretch(1); layout->addLayout(bonding); auto* recordLayout = new QHBoxLayout; recordLayout->addStretch(1); auto* recordButton = new QPushButton(tr("Record Movie…")); connect(recordButton, SIGNAL(clicked()), SLOT(recordMovie())); recordLayout->addWidget(recordButton); recordLayout->addStretch(1); layout->addLayout(recordLayout); m_toolWidget->setLayout(layout); } connect(&m_timer, SIGNAL(timeout()), SLOT(animate())); return m_toolWidget; } QUndoCommand* PlayerTool::mousePressEvent(QMouseEvent*) { return nullptr; } QUndoCommand* PlayerTool::mouseReleaseEvent(QMouseEvent*) { return nullptr; } QUndoCommand* PlayerTool::mouseDoubleClickEvent(QMouseEvent*) { return nullptr; } void PlayerTool::setActiveWidget(QWidget* widget) { m_glWidget = qobject_cast(widget); } void PlayerTool::back() { animate(-1); } void PlayerTool::forward() { animate(1); } void PlayerTool::play() { playButton->setEnabled(false); stopButton->setEnabled(true); auto fps = static_cast(m_animationFPS->value()); if (fps < 0.00001) fps = 5; int timeOut = static_cast(1000 / fps); if (m_timer.isActive()) m_timer.stop(); m_timer.start(timeOut); } void PlayerTool::stop() { playButton->setEnabled(true); stopButton->setEnabled(false); m_timer.stop(); } void PlayerTool::animate(int advance) { if (m_molecule) { if (m_currentFrame < m_molecule->coordinate3dCount() - advance && m_currentFrame + advance >= 0) { m_currentFrame += advance; m_molecule->setCoordinate3d(m_currentFrame); } else { m_currentFrame = advance > 0 ? 0 : m_molecule->coordinate3dCount() - 1; m_molecule->setCoordinate3d(m_currentFrame); } if (m_dynamicBonding->isChecked()) { m_molecule->clearBonds(); m_molecule->perceiveBondsSimple(); } m_molecule->emitChanged(Molecule::Atoms | Molecule::Added); m_slider->setValue(m_currentFrame); m_frameIdx->setValue(m_currentFrame + 1); } } void PlayerTool::recordMovie() { // Qt 5.14 or later gives the more reliable way for multi-screen #if QT_VERSION >= 0x050E00 qreal scaling = m_glWidget->screen()->devicePixelRatio(); #else qreal scaling = qApp->devicePixelRatio(); #endif int EXPORT_WIDTH = m_glWidget->width() * scaling; int EXPORT_HEIGHT = m_glWidget->height() * scaling; if (m_timer.isActive()) m_timer.stop(); QString baseFileName; if (m_molecule) baseFileName = m_molecule->data("fileName").toString().c_str(); // TODO: check path for avconv and disable MP4 if not found // TODO: add PNG as an export (i.e., pile of PNG for later use) QString selfFilter = tr("Movie (*.mp4)"); QString baseName = QFileDialog::getSaveFileName( qobject_cast(parent()), tr("Export Bitmap Graphics"), "", tr("Movie (*.mp4);;Movie (*.avi);;GIF (*.gif)"), &selfFilter); if (baseName.isEmpty()) return; QFileInfo fileInfo(baseName); if (!fileInfo.suffix().isEmpty()) baseName = fileInfo.absolutePath() + "/" + fileInfo.baseName(); bool bonding = m_dynamicBonding->isChecked(); int numberLength = static_cast( ceil(log10(static_cast(m_molecule->coordinate3dCount()) + 1))); // m_glWidget->resize(EXPORT_WIDTH, EXPORT_HEIGHT); if (selfFilter == tr("GIF (*.gif)")) { GifWriter writer; // GIF only supports up to 100 FPS, this minimizes breakage when FPS>100 if (m_animationFPS->value() > 100) { QMessageBox::warning( qobject_cast(parent()), tr("GIF FPS support warning"), tr("The GIF file format does not support frame rates over 100 FPS.")); } GifBegin(&writer, (baseName + ".gif").toLatin1().data(), EXPORT_WIDTH, EXPORT_HEIGHT, 100 / std::min(m_animationFPS->value(), 100)); for (int i = 0; i < m_molecule->coordinate3dCount(); ++i) { qApp->processEvents(); m_molecule->setCoordinate3d(i); if (bonding) { m_molecule->clearBonds(); m_molecule->perceiveBondsSimple(); } m_molecule->emitChanged(Molecule::Atoms | Molecule::Modified); QImage exportImage; m_glWidget->raise(); m_glWidget->repaint(); if (QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) { exportImage = m_glWidget->grabFramebuffer(); } else { auto* screen = QGuiApplication::primaryScreen(); auto pixmap = screen->grabWindow(m_glWidget->winId()); exportImage = pixmap.toImage(); } int frameWidth = exportImage.width(); int frameHeight = exportImage.height(); int numbPixels = frameWidth * frameHeight; auto* imageData = new uint8_t[numbPixels * 4]; int imageIndex = 0; for (int j = 0; j < frameHeight; ++j) { for (int k = 0; k < frameWidth; ++k) { QColor color = exportImage.pixel(k, j); imageData[imageIndex] = (uint8_t)color.red(); imageData[imageIndex + 1] = (uint8_t)color.green(); imageData[imageIndex + 2] = (uint8_t)color.blue(); imageData[imageIndex + 3] = (uint8_t)color.alpha(); imageIndex += 4; } } GifWriteFrame(&writer, imageData, EXPORT_WIDTH, EXPORT_HEIGHT, 100 / std::min(m_animationFPS->value(), 100)); delete[] imageData; } GifEnd(&writer); } else if (selfFilter == tr("Movie (*.avi)")) { gwavi_t* gwavi; gwavi = gwavi_open((baseName + ".avi").toLatin1().data(), EXPORT_WIDTH, EXPORT_HEIGHT, "MJPG", m_animationFPS->value(), nullptr); for (int i = 0; i < m_molecule->coordinate3dCount(); ++i) { qApp->processEvents(); m_molecule->setCoordinate3d(i); if (bonding) { m_molecule->clearBonds(); m_molecule->perceiveBondsSimple(); } m_molecule->emitChanged(Molecule::Atoms | Molecule::Modified); QImage exportImage; m_glWidget->raise(); m_glWidget->repaint(); if (QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) { exportImage = m_glWidget->grabFramebuffer(); } else { auto* screen = QGuiApplication::primaryScreen(); auto pixmap = screen->grabWindow(m_glWidget->winId()); exportImage = pixmap.toImage(); } QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); exportImage.save(&buffer, "JPG"); if (gwavi_add_frame( gwavi, reinterpret_cast(buffer.data().data()), buffer.size()) == -1) { QMessageBox::warning(qobject_cast(parent()), tr("Avogadro"), tr("Error: cannot add frame to video.")); } } gwavi_close(gwavi); } else if (selfFilter == tr("Movie (*.mp4)")) { for (int i = 0; i < m_molecule->coordinate3dCount(); ++i) { qApp->processEvents(); m_molecule->setCoordinate3d(i); if (bonding) { m_molecule->clearBonds(); m_molecule->perceiveBondsSimple(); } m_molecule->emitChanged(Molecule::Atoms | Molecule::Modified); QString fileName = QString::number(i); while (fileName.length() < numberLength) fileName.prepend('0'); fileName.prepend(baseName); fileName.append(".png"); QImage exportImage; m_glWidget->raise(); m_glWidget->repaint(); if (QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) { exportImage = m_glWidget->grabFramebuffer(); } else { auto* screen = QGuiApplication::primaryScreen(); auto pixmap = screen->grabWindow(m_glWidget->winId()); exportImage = pixmap.toImage(); } if (!exportImage.save(fileName)) { QMessageBox::warning(qobject_cast(parent()), tr("Avogadro"), tr("Cannot save file %1.").arg(fileName)); return; } QProcess proc; QStringList args; args << "-y" << "-r" << QString::number(m_animationFPS->value()) << "-i" << baseName + "%0" + QString::number(numberLength) + "d.png" << "-c:v" << "libx264" << "-r" << "30" << "-pix_fmt" << "yuv420p" << baseName + ".mp4"; proc.execute("avconv", args); } } } void PlayerTool::sliderPositionChanged(int k) { animate(k - m_currentFrame); } void PlayerTool::spinnerPositionChanged(int k) { animate(k - m_currentFrame - 1); } void PlayerTool::setSliderLimit() { if (m_molecule->coordinate3dCount() > 1 && m_slider) m_slider->setMaximum(m_molecule->coordinate3dCount() - 1); if (m_molecule->coordinate3dCount() > 1 && m_frameIdx) { m_frameIdx->setMaximum(m_molecule->coordinate3dCount()); m_frameIdx->setSuffix(tr(" of %0").arg(m_molecule->coordinate3dCount())); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/playertool/playertool.h000066400000000000000000000050061506155467400247310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PLAYERTOOL_H #define AVOGADRO_QTPLUGINS_PLAYERTOOL_H #include #include #include class QLabel; class QSpinBox; class QCheckBox; class QOpenGLWidget; class QPushButton; class QSlider; namespace Avogadro { namespace QtPlugins { /** * @brief PlayerTool enables playback of trajectories. */ class PlayerTool : public QtGui::ToolPlugin { Q_OBJECT public: explicit PlayerTool(QObject* p = nullptr); ~PlayerTool() override; QString name() const override { return tr("Player tool"); } QString description() const override { return tr("Play back trajectories"); } unsigned char priority() const override { return 80; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseDoubleClickEvent(QMouseEvent* e) override; public slots: void setMolecule(QtGui::Molecule*) override; void setGLRenderer(Rendering::GLRenderer* renderer) override; void setActiveWidget(QWidget* widget) override; protected slots: void back(); void forward(); void play(); void stop(); void animate(int advance = 1); void recordMovie(); void sliderPositionChanged(int k); void spinnerPositionChanged(int k); void setSliderLimit(); private: QAction* m_activateAction; QtGui::Molecule* m_molecule; Rendering::GLRenderer* m_renderer; int m_currentFrame; mutable QWidget* m_toolWidget; QTimer m_timer; mutable QSpinBox* m_animationFPS; mutable QSpinBox* m_frameIdx; mutable QCheckBox* m_dynamicBonding; mutable QOpenGLWidget* m_glWidget; mutable QSlider* m_slider; mutable QPushButton* playButton; mutable QPushButton* stopButton; }; inline void PlayerTool::setMolecule(QtGui::Molecule* mol) { if (m_molecule != mol) { m_molecule = mol; m_currentFrame = 0; setSliderLimit(); } } inline void PlayerTool::setGLRenderer(Rendering::GLRenderer* renderer) { m_renderer = renderer; } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PLAYERTOOL_H avogadrolibs-1.101.0/avogadro/qtplugins/playertool/playertool.qrc000066400000000000000000000002111506155467400252600ustar00rootroot00000000000000 player_light.svg player_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/plotpdf/000077500000000000000000000000001506155467400216435ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/plotpdf/CMakeLists.txt000066400000000000000000000004461506155467400244070ustar00rootroot00000000000000set(plotpdf_srcs plotpdf.cpp pdfoptionsdialog.cpp ) set(plotpdf_uis pdfoptionsdialog.ui ) avogadro_plugin(PlotPdf "Create a pair distribution plot." ExtensionPlugin plotpdf.h PlotPdf "${plotpdf_srcs}" "${plotpdf_uis}" ) target_link_libraries(PlotPdf PRIVATE Avogadro::Vtk) avogadrolibs-1.101.0/avogadro/qtplugins/plotpdf/pdfoptionsdialog.cpp000066400000000000000000000023451506155467400257200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "pdfoptionsdialog.h" #include "ui_pdfoptionsdialog.h" #include namespace Avogadro::QtPlugins { PdfOptionsDialog::PdfOptionsDialog(QWidget* aParent) : QDialog(aParent), m_ui(new Ui::PdfOptionsDialog) { m_ui->setupUi(this); // Read the settings QSettings settings; m_ui->spin_maxRadius->setValue( settings.value("plotpdfcurveoptions/maxRadius", 10.0).toDouble()); m_ui->spin_step->setValue( settings.value("plotpdfcurveoptions/step", 0.1).toDouble()); } PdfOptionsDialog::~PdfOptionsDialog() = default; double PdfOptionsDialog::maxRadius() const { return m_ui->spin_maxRadius->value(); } double PdfOptionsDialog::step() const { return m_ui->spin_step->value(); } void PdfOptionsDialog::accept() { QSettings settings; settings.setValue("plotpdfcurveoptions/maxRadius", maxRadius()); settings.setValue("plotpdfcurveoptions/step", step()); QDialog::accept(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plotpdf/pdfoptionsdialog.h000066400000000000000000000016761506155467400253730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PDFOPTIONSDIALOG_H #define AVOGADRO_QTPLUGINS_PDFOPTIONSDIALOG_H #include namespace Avogadro { namespace QtPlugins { namespace Ui { class PdfOptionsDialog; } /** * @brief Dialog to set options for PDF curve plotting. */ class PdfOptionsDialog : public QDialog { Q_OBJECT public: explicit PdfOptionsDialog(QWidget* parent = nullptr); ~PdfOptionsDialog() override; double maxRadius() const; double step() const; protected slots: void accept() override; private: QScopedPointer m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PDFOPTIONSDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/plotpdf/pdfoptionsdialog.ui000066400000000000000000000062351506155467400255550ustar00rootroot00000000000000 Avogadro::QtPlugins::PdfOptionsDialog 0 0 324 145 PDF Plot Options Maximum Radius: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 5 0.00001 100.00000 10.00000 Step (dr): Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Å 5 0.00001 100.00000 10.00000 QDialogButtonBox::Cancel|QDialogButtonBox::Ok spin_maxRadius spin_step buttonBox accepted() Avogadro::QtPlugins::PdfOptionsDialog accept() 226 204 161 118 buttonBox rejected() Avogadro::QtPlugins::PdfOptionsDialog reject() 226 204 161 118 avogadrolibs-1.101.0/avogadro/qtplugins/plotpdf/plotpdf.cpp000066400000000000000000000132371506155467400240250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "pdfoptionsdialog.h" #include "plotpdf.h" using Avogadro::Core::CrystalTools; using Avogadro::Core::UnitCell; using Avogadro::QtGui::Molecule; using std::map; namespace Avogadro::QtPlugins { using Core::Array; PlotPdf::PlotPdf(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_actions(QList()), m_molecule(nullptr), m_pdfOptionsDialog(new PdfOptionsDialog(qobject_cast(parent()))), m_displayDialogAction(new QAction(this)) { m_displayDialogAction->setText(tr("Plot Pair Distribution Function…")); connect(m_displayDialogAction.data(), &QAction::triggered, this, &PlotPdf::displayDialog); m_actions.push_back(m_displayDialogAction.data()); m_displayDialogAction->setProperty("menu priority", 70); updateActions(); } PlotPdf::~PlotPdf() = default; QList PlotPdf::actions() const { return m_actions; } QStringList PlotPdf::menuPath(QAction*) const { return QStringList() << tr("&Crystal"); } void PlotPdf::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); } void PlotPdf::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); auto changes = static_cast(c); if (changes & Molecule::UnitCell) { if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); } } void PlotPdf::updateActions() { // Disable everything for nullptr molecules. if (!m_molecule) { foreach (QAction* action, m_actions) action->setEnabled(false); return; } // Only display the actions if there is a unit cell if (m_molecule->unitCell()) { foreach (QAction* action, m_actions) action->setEnabled(true); } else { foreach (QAction* action, m_actions) action->setEnabled(false); } } void PlotPdf::displayDialog() { // Do nothing if the user cancels if (m_pdfOptionsDialog->exec() != QDialog::Accepted) return; // Otherwise, fetch the options and perform the run double maxRadius = m_pdfOptionsDialog->maxRadius(); double step = m_pdfOptionsDialog->step(); // size_t numpoints = m_pdfOptionsDialog->numDataPoints(); // double max2theta = m_pdfOptionsDialog->max2Theta(); PdfData results; QString err; if (!generatePdfPattern(*m_molecule, results, err, maxRadius, step)) { QMessageBox::critical(qobject_cast(parent()), tr("Failed to generate PDF pattern"), tr("Error message: ") + err); return; } // Now generate a plot with the data std::vector xData; std::vector yData; for (const auto& item : results) { xData.push_back(item.first); yData.push_back(item.second); } const char* xTitle = "r (Å)"; const char* yTitle = "g(r)"; const char* windowName = "Pair Distribution Function"; if (!m_chartDialog) m_chartDialog.reset( new VTK::ChartDialog(qobject_cast(this->parent()))); m_chartDialog->setWindowTitle(windowName); auto* chart = m_chartDialog->chartWidget(); chart->clearPlots(); chart->addPlot(xData, yData, VTK::color4ub{ 255, 0, 0, 255 }); chart->setXAxisTitle(xTitle); chart->setYAxisTitle(yTitle); m_chartDialog->show(); } bool PlotPdf::generatePdfPattern(QtGui::Molecule& mol, PdfData& results, QString& err, double maxRadius, double step) { Array refAtomCoords = mol.atomPositions3d(); size_t i, j; UnitCell* uc = mol.unitCell(); if (!uc) { err = "No unit cell found."; return false; } size_t a = static_cast(maxRadius / uc->a()) + 1; size_t b = static_cast(maxRadius / uc->b()) + 1; size_t c = static_cast(maxRadius / uc->c()) + 1; Vector3 disp = a * uc->aVector() + b * uc->bVector() + c * uc->cVector(); for (i = 0; i < refAtomCoords.size(); ++i) { refAtomCoords[i] += disp; } Molecule newMolecule = mol; CrystalTools::buildSupercell(newMolecule, 2 * a + 1, 2 * b + 1, 2 * c + 1); Array newAtomCoords = newMolecule.atomPositions3d(); map pdfCount; double dist, rStep = step; size_t k, binIdx; for (i = 0; i < refAtomCoords.size(); ++i) { for (j = 0; j < newAtomCoords.size(); ++j) { dist = (refAtomCoords.at(i) - newAtomCoords.at(j)).norm(); binIdx = static_cast(dist / rStep); if (pdfCount.find(binIdx) == pdfCount.end()) { pdfCount.insert(std::make_pair(binIdx, 1)); } else { pdfCount[binIdx]++; } } } for (k = 0; k < static_cast(maxRadius / rStep); k++) { if (pdfCount.find(k) == pdfCount.end()) { results.push_back(std::make_pair(k * rStep, 0.0)); } else { results.push_back(std::make_pair( k * rStep, pdfCount[k] * newMolecule.unitCell()->volume() / (4 * M_PI * pow(k * rStep, 2) * rStep * refAtomCoords.size() * newAtomCoords.size()))); } } return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plotpdf/plotpdf.h000066400000000000000000000040401506155467400234620ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PLOTPDF_H #define AVOGADRO_QTPLUGINS_PLOTPDF_H #include namespace VTK { class ChartDialog; } namespace Avogadro { namespace QtPlugins { class PdfOptionsDialog; // First item in the pair is radius. Second is the pdf value. typedef std::vector> PdfData; /** * @brief Generate and plot a PDF curve */ class PlotPdf : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit PlotPdf(QObject* parent_ = nullptr); ~PlotPdf() override; QString name() const override { return tr("PlotPdf"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); private slots: void updateActions(); void displayDialog(); private: // Generate Pdf curve from a crystal // Writes the results to @p results, which is a vector of pairs of doubles // (see definition above). // err will be set to an error string if the function fails. // radius is in Angstroms. static bool generatePdfPattern(QtGui::Molecule& mol, PdfData& results, QString& err, double maxRadius = 10.0, double step = 0.1); QList m_actions; QtGui::Molecule* m_molecule; QScopedPointer m_pdfOptionsDialog; QScopedPointer m_displayDialogAction; QScopedPointer m_chartDialog; }; inline QString PlotPdf::description() const { return tr("Generate and plot a Pair Distribution Function curve."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PLOTPDF_H avogadrolibs-1.101.0/avogadro/qtplugins/plotrmsd/000077500000000000000000000000001506155467400220375ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/plotrmsd/CMakeLists.txt000066400000000000000000000003231506155467400245750ustar00rootroot00000000000000set(plotrmsd_srcs plotrmsd.cpp ) avogadro_plugin(PlotRmsd "Create an RMSD plot." ExtensionPlugin plotrmsd.h PlotRmsd "${plotrmsd_srcs}" "" ) target_link_libraries(PlotRmsd PRIVATE Avogadro::Vtk) avogadrolibs-1.101.0/avogadro/qtplugins/plotrmsd/plotrmsd.cpp000066400000000000000000000076171506155467400244220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "plotrmsd.h" #include #include #include #include #include #include #include #include #include #include using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { using Core::Array; PlotRmsd::PlotRmsd(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_actions(QList()), m_molecule(nullptr), m_displayDialogAction(new QAction(this)) { m_displayDialogAction->setText(tr("Plot RMSD curve…")); connect(m_displayDialogAction.get(), &QAction::triggered, this, &PlotRmsd::displayDialog); m_actions.push_back(m_displayDialogAction.get()); m_displayDialogAction->setProperty("menu priority", 80); updateActions(); } PlotRmsd::~PlotRmsd() = default; QList PlotRmsd::actions() const { return m_actions; } QStringList PlotRmsd::menuPath(QAction*) const { return QStringList() << tr("&Crystal"); } void PlotRmsd::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); } void PlotRmsd::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); auto changes = static_cast(c); if (changes & Molecule::UnitCell) { if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); } } void PlotRmsd::updateActions() { // Disable everything for nullptr molecules. if (!m_molecule) { foreach (QAction* action, m_actions) action->setEnabled(false); return; } // Only display the actions if multimolecule. if (m_molecule->coordinate3dCount() > 1) { foreach (QAction* action, m_actions) action->setEnabled(true); } else { foreach (QAction* action, m_actions) action->setEnabled(false); } } void PlotRmsd::displayDialog() { RmsdData results; generateRmsdPattern(results); // Now generate a plot with the data std::vector xData; std::vector yData; for (const auto& item : results) { xData.push_back(item.first); yData.push_back(item.second); } const char* xTitle = "Frame"; const char* yTitle = "RMSD (Angstrom)"; const char* windowName = "RMSD Curve"; if (!m_chartDialog) { m_chartDialog.reset( new VTK::ChartDialog(qobject_cast(this->parent()))); } m_chartDialog->setWindowTitle(windowName); auto* chart = m_chartDialog->chartWidget(); chart->clearPlots(); chart->addPlot(xData, yData, VTK::color4ub{ 255, 0, 0, 255 }); chart->setXAxisTitle(xTitle); chart->setYAxisTitle(yTitle); m_chartDialog->show(); } void PlotRmsd::generateRmsdPattern(RmsdData& results) { m_molecule->setCoordinate3d(0); Array ref = m_molecule->atomPositions3d(); for (int i = 0; i < m_molecule->coordinate3dCount(); ++i) { m_molecule->setCoordinate3d(i); Array positions = m_molecule->atomPositions3d(); double sum = 0; for (size_t j = 0; j < positions.size(); ++j) { sum += (positions[j][0] - ref[j][0]) * (positions[j][0] - ref[j][0]) + (positions[j][1] - ref[j][1]) * (positions[j][1] - ref[j][1]) + (positions[j][2] - ref[j][2]) * (positions[j][2] - ref[j][2]); } sum = sqrt(sum / m_molecule->coordinate3dCount()); results.push_back(std::make_pair(static_cast(i), sum)); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plotrmsd/plotrmsd.h000066400000000000000000000033731506155467400240620ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PLOTRMSD_H #define AVOGADRO_QTPLUGINS_PLOTRMSD_H #include #include namespace Avogadro { namespace VTK { class ChartDialog; } namespace QtPlugins { // First item in the pair is the frame number. Second is the RMSD value. typedef std::vector> RmsdData; /** * @brief Generate and plot an RMSD curve. */ class PlotRmsd : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit PlotRmsd(QObject* parent_ = nullptr); ~PlotRmsd() override; QString name() const override { return tr("PlotRmsd"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); private slots: void updateActions(); void displayDialog(); private: // Generate RMSD data from a coordinate set // Writes the results to @p results, which is a vector of pairs of doubles // (see definition above). void generateRmsdPattern(RmsdData& results); QList m_actions; QtGui::Molecule* m_molecule; std::unique_ptr m_displayDialogAction; QScopedPointer m_chartDialog; }; inline QString PlotRmsd::description() const { return tr("Generate and plot an RMSD curve."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PLOTRMSD_H avogadrolibs-1.101.0/avogadro/qtplugins/plotxrd/000077500000000000000000000000001506155467400216675ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/plotxrd/CMakeLists.txt000066400000000000000000000010741506155467400244310ustar00rootroot00000000000000# Download the executable if we are not to use the system one option(USE_SYSTEM_GENXRDPATTERN "Use the system genxrdpattern" ON) mark_as_advanced(USE_SYSTEM_GENXRDPATTERN) if(NOT USE_SYSTEM_GENXRDPATTERN) include(DownloadGenXrdPattern) DownloadGenXrdPattern() endif() set(plotxrd_srcs plotxrd.cpp xrdoptionsdialog.cpp ) set(plotxrd_uis xrdoptionsdialog.ui ) avogadro_plugin(PlotXrd "Use ObjCryst++ to create an XRD plot." ExtensionPlugin plotxrd.h PlotXrd "${plotxrd_srcs}" "${plotxrd_uis}" ) target_link_libraries(PlotXrd PRIVATE Avogadro::Vtk) avogadrolibs-1.101.0/avogadro/qtplugins/plotxrd/plotxrd.cpp000066400000000000000000000176511506155467400241010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plotxrd.h" #include "xrdoptionsdialog.h" using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { PlotXrd::PlotXrd(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_actions(QList()), m_molecule(nullptr), m_xrdOptionsDialog(new XrdOptionsDialog(qobject_cast(parent()))), m_displayDialogAction(new QAction(this)) { m_displayDialogAction->setText(tr("Plot Theoretical XRD Pattern…")); connect(m_displayDialogAction.get(), &QAction::triggered, this, &PlotXrd::displayDialog); m_actions.push_back(m_displayDialogAction.get()); m_displayDialogAction->setProperty("menu priority", 90); updateActions(); } PlotXrd::~PlotXrd() = default; QList PlotXrd::actions() const { return m_actions; } QStringList PlotXrd::menuPath(QAction*) const { return QStringList() << tr("&Crystal"); } void PlotXrd::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); } void PlotXrd::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); auto changes = static_cast(c); if (changes & Molecule::UnitCell) { if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); } } void PlotXrd::updateActions() { // Disable everything for nullptr molecules. if (!m_molecule) { foreach (QAction* action, m_actions) action->setEnabled(false); return; } // Only display the actions if there is a unit cell if (m_molecule->unitCell()) { foreach (QAction* action, m_actions) action->setEnabled(true); } else { foreach (QAction* action, m_actions) action->setEnabled(false); } } void PlotXrd::displayDialog() { // Do nothing if the user cancels if (m_xrdOptionsDialog->exec() != QDialog::Accepted) return; // Otherwise, fetch the options and perform the run double wavelength = m_xrdOptionsDialog->wavelength(); double peakwidth = m_xrdOptionsDialog->peakWidth(); size_t numpoints = m_xrdOptionsDialog->numDataPoints(); double max2theta = m_xrdOptionsDialog->max2Theta(); XrdData results; QString err; if (!generateXrdPattern(*m_molecule, results, err, wavelength, peakwidth, numpoints, max2theta)) { QMessageBox::critical(qobject_cast(parent()), tr("Failed to generate XRD pattern"), tr("Error message: ") + err); return; } // Now generate a plot with the data std::vector xData; std::vector yData; for (const auto& item : results) { xData.push_back(item.first); yData.push_back(item.second); } const char* xTitle = "2 Theta"; const char* yTitle = "Intensity"; const char* windowName = "Theoretical XRD Pattern"; if (!m_chartDialog) m_chartDialog.reset( new VTK::ChartDialog(qobject_cast(this->parent()))); m_chartDialog->setWindowTitle(windowName); auto* chart = m_chartDialog->chartWidget(); chart->clearPlots(); chart->addPlot(xData, yData, VTK::color4ub{ 255, 0, 0, 255 }); chart->setXAxisTitle(xTitle); chart->setYAxisTitle(yTitle); m_chartDialog->show(); } bool PlotXrd::generateXrdPattern(const QtGui::Molecule& mol, XrdData& results, QString& err, double wavelength, double peakwidth, size_t numpoints, double max2theta) { // Get the molecule as a cif file std::string cifData; if (!Io::FileFormatManager::instance().writeString(mol, cifData, "cif")) { err = tr("Failed to convert molecule to CIF format."); qDebug() << "Error in" << __FUNCTION__ << ":" << err; return false; } // Now, execute genXrdPattern with the given inputs QStringList args; args << "--read-from-stdin" << "--wavelength=" + QString::number(wavelength) << "--peakwidth=" + QString::number(peakwidth) << "--numpoints=" + QString::number(numpoints) << "--max2theta=" + QString::number(max2theta); QByteArray output; if (!executeGenXrdPattern(args, cifData.c_str(), output, err)) { qDebug() << "Error in" << __FUNCTION__ << ":" << err; return false; } // Store the results results.clear(); // Find the section of data in the output bool dataStarted = false; QStringList lines = QString(output).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); for (const auto& line : lines) { if (!dataStarted && line.contains("# 2Theta/TOF ICalc")) { dataStarted = true; continue; } if (dataStarted) { QStringList rowData = line.split(" ", Qt::SkipEmptyParts); if (rowData.size() != 2) { err = tr("Data read from genXrdPattern appears to be corrupt!"); qDebug() << "Error in" << __FUNCTION__ << err; qDebug() << "Data is:"; for (const auto& lineTmp : lines) qDebug() << lineTmp; return false; } results.push_back( std::make_pair(rowData[0].toDouble(), rowData[1].toDouble())); } } return true; } bool PlotXrd::executeGenXrdPattern(const QStringList& args, const QByteArray& input, QByteArray& output, QString& err) { QString program; // If the GENXRDPATTERN_EXECUTABLE environment variable is set, then // use that QByteArray xrdExec = qgetenv("GENXRDPATTERN_EXECUTABLE"); if (!xrdExec.isEmpty()) { program = xrdExec; } else { // Otherwise, search in the current directory, and then ../bin #ifdef _WIN32 QString executable = "genXrdPattern.exe"; #else QString executable = "genXrdPattern"; #endif QString path = QCoreApplication::applicationDirPath(); if (QFile::exists(path + "/" + executable)) program = path + "/" + executable; else if (QFile::exists(path + "/../bin/" + executable)) program = path + "/../bin/" + executable; else { err = tr("Error: could not find genXrdPattern executable!"); qDebug() << err; return false; } } QProcess p; p.start(program, args); if (!p.waitForStarted()) { err = tr("Error: " + program.toLocal8Bit() + " failed to start"); qDebug() << err; return false; } // Give it the input! p.write(input.data()); // Close the write channel p.closeWriteChannel(); if (!p.waitForFinished()) { err = tr("Error: " + program.toLocal8Bit() + " failed to finish"); qDebug() << err; output = p.readAll(); qDebug() << "Output is as follows:\n" << output; return false; } int exitStatus = p.exitStatus(); output = p.readAll(); if (exitStatus == QProcess::CrashExit) { err = tr("Error: " + program.toLocal8Bit() + " crashed!"); qDebug() << err; qDebug() << "Output is as follows:\n" << output; return false; } if (exitStatus != QProcess::NormalExit) { err = tr("Error: " + program.toLocal8Bit() + " finished abnormally with exit code " + QString::number(exitStatus).toLocal8Bit()); qDebug() << err; qDebug() << "Output is as follows:\n" << output; return false; } // We did it! return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plotxrd/plotxrd.h000066400000000000000000000054741506155467400235460ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PLOTXRD_H #define AVOGADRO_QTPLUGINS_PLOTXRD_H #include #include // Forward declarations class QByteArray; namespace VTK { class ChartDialog; } namespace Avogadro { namespace QtPlugins { class XrdOptionsDialog; // First item in the pair is 2*theta. Second is the intensity. typedef std::vector> XrdData; /** * @brief Generate and plot a theoretical XRD pattern using ObjCryst++ */ class PlotXrd : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit PlotXrd(QObject* parent_ = nullptr); ~PlotXrd() override; QString name() const override { return tr("PlotXrd"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); private slots: void updateActions(); void displayDialog(); private: // Generate an Xrd pattern from a crystal // Writes the results to @p results, which is a vector of pairs of doubles // (see definition above). // err will be set to an error string if the function fails. // wavelength is an Angstroms. // peakwidth is in degrees. // numpoints is an unsigned integer. // max2theta is in degrees. static bool generateXrdPattern(const QtGui::Molecule& mol, XrdData& results, QString& err, double wavelength = 1.505600, double peakwidth = 0.529580, size_t numpoints = 1000, double max2theta = 162.0); // Use QProcess to execute genXrdPattern // If the GENXRDPATTERN_EXECUTABLE environment variable is set, that will be // used for the executable. Otherwise, it will search for the executable in // some common places and use it if it can be found. static bool executeGenXrdPattern(const QStringList& args, const QByteArray& input, QByteArray& output, QString& err); QList m_actions; QtGui::Molecule* m_molecule; std::unique_ptr m_xrdOptionsDialog; std::unique_ptr m_displayDialogAction; QScopedPointer m_chartDialog; }; inline QString PlotXrd::description() const { return tr("Generate and plot a theoretical XRD pattern using ObjCryst++."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PLOTXRD_H avogadrolibs-1.101.0/avogadro/qtplugins/plotxrd/xrdoptionsdialog.cpp000066400000000000000000000035311506155467400257660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "xrdoptionsdialog.h" #include "ui_xrdoptionsdialog.h" #include namespace Avogadro::QtPlugins { XrdOptionsDialog::XrdOptionsDialog(QWidget* aParent) : QDialog(aParent), m_ui(new Ui::XrdOptionsDialog) { m_ui->setupUi(this); // Read the settings QSettings settings; m_ui->spin_wavelength->setValue( settings.value("plotxrdoptions/wavelength", 1.505600).toDouble()); m_ui->spin_peakWidth->setValue( settings.value("plotxrdoptions/peakWidth", 0.529580).toDouble()); m_ui->spin_numDataPoints->setValue( settings.value("plotxrdoptions/numDataPoints", 1000).toUInt()); m_ui->spin_max2Theta->setValue( settings.value("plotxrdoptions/max2Theta", 162.0).toDouble()); } XrdOptionsDialog::~XrdOptionsDialog() = default; double XrdOptionsDialog::wavelength() const { return m_ui->spin_wavelength->value(); } double XrdOptionsDialog::peakWidth() const { return m_ui->spin_peakWidth->value(); } size_t XrdOptionsDialog::numDataPoints() const { return m_ui->spin_numDataPoints->value(); } double XrdOptionsDialog::max2Theta() const { return m_ui->spin_max2Theta->value(); } void XrdOptionsDialog::accept() { // Write the settings and accept QSettings settings; settings.setValue("plotxrdoptions/wavelength", wavelength()); settings.setValue("plotxrdoptions/peakWidth", peakWidth()); settings.setValue("plotxrdoptions/numDataPoints", static_cast(numDataPoints())); settings.setValue("plotxrdoptions/max2Theta", max2Theta()); QDialog::accept(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plotxrd/xrdoptionsdialog.h000066400000000000000000000020471506155467400254340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_XRDOPTIONSDIALOG_H #define AVOGADRO_QTPLUGINS_XRDOPTIONSDIALOG_H #include #include namespace Avogadro { namespace QtPlugins { namespace Ui { class XrdOptionsDialog; } /** * @brief Dialog to set options for a theoretical XRD pattern calculation. */ class XrdOptionsDialog : public QDialog { Q_OBJECT public: explicit XrdOptionsDialog(QWidget* parent = nullptr); ~XrdOptionsDialog() override; double wavelength() const; double peakWidth() const; size_t numDataPoints() const; double max2Theta() const; protected slots: void accept() override; private: std::unique_ptr m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_XRDOPTIONSDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/plotxrd/xrdoptionsdialog.ui000066400000000000000000000136561506155467400256320ustar00rootroot00000000000000 Avogadro::QtPlugins::XrdOptionsDialog 0 0 324 237 Theoretical XRD Pattern Options <html><head/><body><p>The broadening of the peak at the base (in degrees).</p></body></html> <html><head/><body><p>The max 2theta value in degrees.</p><p>Default: 162.00°</p></body></html> ° 2 360.000000000000000 0.100000000000000 162.000000000000000 <html><head/><body><p>The broadening of the peaks at the base in degrees.</p><p>Default: 0.52958°</p></body></html> ° 5 100.000000000000000 0.100000000000000 0.529580000000000 Number of points: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Peak width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Wavelength: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter <html><head/><body><p>The wavelength of the x-ray in Angstroms. </p><p>Default: 1.50560 Å</p></body></html> Å 5 0.000000000000000 100.000000000000000 0.100000000000000 1.505600000000000 <html><head/><body><p>The number of 2theta points to generate.</p><p>Default: 1000</p></body></html> 1 100000 1000 Qt::LeftToRight Max 2*theta: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QDialogButtonBox::Cancel|QDialogButtonBox::Ok spin_wavelength spin_peakWidth spin_numDataPoints spin_max2Theta buttonBox accepted() Avogadro::QtPlugins::XrdOptionsDialog accept() 226 204 161 118 buttonBox rejected() Avogadro::QtPlugins::XrdOptionsDialog reject() 226 204 161 118 avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/000077500000000000000000000000001506155467400235505ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/CMakeLists.txt000066400000000000000000000011171506155467400263100ustar00rootroot00000000000000find_package(LibArchive REQUIRED) # Extension set(plugindownloader_srcs plugindownloader.cpp downloaderwidget.cpp zipextracter.cpp ) avogadro_plugin(PluginDownloader "Download plugins from Github repositories" ExtensionPlugin plugindownloader.h PluginDownloader "${plugindownloader_srcs}" downloaderwidget.ui "" ) target_link_libraries(PluginDownloader PRIVATE Qt::Network LibArchive::LibArchive nlohmann_json::nlohmann_json ) if(WIN32) # for https support target_link_libraries(PluginDownloader PRIVATE OpenSSL::SSL OpenSSL::Crypto OpenSSL::applink) endif() avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/downloaderwidget.cpp000066400000000000000000000447451506155467400276340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "downloaderwidget.h" #include "ui_downloaderwidget.h" #include "zipextracter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using json = nlohmann::json; namespace Avogadro::QtPlugins { void setRawHeaders(QNetworkRequest* request) { request->setRawHeader("Accept", "text/html,application/xhtml+xml,application/" "xml;q=0.9,image/webp,*/*;q=0.8"); request->setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/54.0.2840.71 Safari/537.36"); request->setRawHeader("Accept-Language", "en - US, en; q = 0.8"); return; } DownloaderWidget::DownloaderWidget(QWidget* parent) : QDialog(parent), m_ui(new Ui::DownloaderWidget) { m_filePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); m_NetworkAccessManager = new QNetworkAccessManager(this); m_ui->setupUi(this); // enable links in the readme to open an external browser m_ui->readmeBrowser->setOpenExternalLinks(true); connect(m_ui->downloadButton, SIGNAL(clicked(bool)), this, SLOT(getCheckedRepos())); connect(m_ui->repoTable, SIGNAL(cellClicked(int, int)), this, SLOT(downloadREADME(int, int))); m_ui->repoTable->setColumnCount(4); m_ui->repoTable->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui->repoTable->setHorizontalHeaderLabels( QStringList() << tr("Update") << tr("Name") << tr("Version") << tr("Description")); m_ui->repoTable->horizontalHeader()->setSectionResizeMode( QHeaderView::ResizeToContents); m_ui->repoTable->horizontalHeader()->setStretchLastSection(true); m_ui->repoTable->setRowCount(0); m_ui->repoTable->verticalHeader()->hide(); getRepoData(); } DownloaderWidget::~DownloaderWidget() { delete m_ui; } // download master plugin.json from Avogadro.cc void DownloaderWidget::getRepoData(QString url) { QNetworkRequest request; setRawHeaders(&request); request.setUrl(url); // Set the url m_reply = m_NetworkAccessManager->get(request); connect(m_reply, SIGNAL(finished()), this, SLOT(updateRepoData())); } // Process the master plugin.json hosted on Avogadro.cc void DownloaderWidget::updateRepoData() { if (m_reply->error() == QNetworkReply::NoError) { // Reading the data from the response QByteArray bytes = m_reply->readAll(); // quick check that it's not empty if (bytes.isEmpty()) { QMessageBox::warning(this, tr("Error"), tr("Error downloading plugin data.")); return; } // does it parse as JSON cleanly? if (!json::accept(bytes.data())) { QMessageBox::warning(this, tr("Error"), tr("Error parsing plugin data.")); return; } // parse the json m_root = json::parse(bytes.data()); int numRepos = m_root.size(); m_ui->repoTable->setRowCount(numRepos); m_repoList.clear(); for (int i = 0; i < numRepos; i++) { m_repoList.emplace_back(); const auto& currentRoot = m_root[i]; // Loop through the keys for (auto it = currentRoot.cbegin(); it != currentRoot.cend(); ++it) { if (it.key() == "name" && it.value().is_string()) m_repoList[i].name = it.value().get().c_str(); else if (it.key() == "description" && it.value().is_string()) m_repoList[i].description = it.value().get().c_str(); else if (it.key() == "release_version" && it.value().is_string()) m_repoList[i].releaseVersion = it.value().get().c_str(); else if (it.key() == "type" && it.value().is_string()) m_repoList[i].type = it.value().get().c_str(); else if (it.key() == "updated_at" && it.value().is_string()) { // format the date, e.g. 2021-05-21T15:25:32Z QString format("yyyy-MM-ddTHH:mm:ssZ"); QDateTime dateTime = QDateTime::fromString( it.value().get().c_str(), format); m_repoList[i].updatedAt = QLocale().toString(dateTime.date(), QLocale::ShortFormat); } else if (it.key() == "zipball_url" && it.value().is_string()) m_repoList[i].zipballUrl = it.value().get().c_str(); else if (it.key() == "has_release" && it.value().is_boolean()) m_repoList[i].hasRelease = it.value().get(); else if (it.key() == "repo_url" && it.value().is_string()) m_repoList[i].baseUrl = it.value().get().c_str(); else if (it.key() == "readme_url" && it.value().is_string()) m_repoList[i].readmeUrl = it.value().get().c_str(); } QStringList urlParts; QString readmeUrl; // If the readme wasn't supplied with the JSON, figure it out if (m_repoList[i].readmeUrl == "Error") { if (m_repoList[i].baseUrl != "Error") urlParts = m_repoList[i].baseUrl.split("/"); else { urlParts = m_repoList[i].zipballUrl.split("/"); urlParts.removeLast(); urlParts.removeLast(); // remove /zipball/(version/branch) // save this as the base URL m_repoList[i].baseUrl = urlParts.join("/"); } urlParts.append("readme"); readmeUrl = urlParts.join("/"); m_repoList[i].readmeUrl = readmeUrl; } auto* checkbox = new QTableWidgetItem(); checkbox->setCheckState(Qt::Unchecked); m_ui->repoTable->setItem(i, 0, checkbox); m_ui->repoTable->setItem(i, 1, new QTableWidgetItem(m_repoList[i].name)); if (m_repoList[i].hasRelease) m_ui->repoTable->setItem( i, 2, new QTableWidgetItem(m_repoList[i].releaseVersion)); else m_ui->repoTable->setItem(i, 2, new QTableWidgetItem(m_repoList[i].updatedAt)); m_ui->repoTable->setItem(i, 3, new QTableWidgetItem(m_repoList[i].description)); } } m_reply->deleteLater(); } // Grab README data from Github void DownloaderWidget::downloadREADME(int row, [[maybe_unused]] int col) { m_ui->readmeBrowser->clear(); QString url = m_repoList[row].readmeUrl; QNetworkRequest request; setRawHeaders(&request); request.setUrl(url); // Set the url m_reply = m_NetworkAccessManager->get(request); connect(m_reply, SIGNAL(finished()), this, SLOT(showREADME())); } // display README when the user clicks a row void DownloaderWidget::showREADME() { if (m_reply->error() == QNetworkReply::NoError) { // Reading the data from the response QByteArray bytes = m_reply->readAll(); // parse the json m_root = json::parse(bytes.data()); QByteArray content("ERROR"); if (m_root.find("content") != m_root.end() && m_root["content"].is_string()) { content = m_root["content"].get().c_str(); } #if QT_VERSION >= 0x050E00 m_ui->readmeBrowser->setMarkdown(QByteArray::fromBase64(content).data()); #else // adapt some of the text to HTML using regex QString readme(QByteArray::fromBase64(content).data()); // This isn't ideal, but works for a bunch of common markdown // adapted from Slimdown - MIT license // https://gist.github.com/jbroadway/2836900 // h3 through h1 readme.replace(QRegularExpression("### (.*)"), "

\\1

"); readme.replace(QRegularExpression("## (.*)"), "

\\1

"); readme.replace(QRegularExpression("# (.*)"), "

\\1

"); // headers using text && ----- readme.replace(QRegularExpression("\\n([a-zA-Z].*)\\n-{5,}\\n"), "

\\1

"); // headers using text && ===== readme.replace(QRegularExpression("\\n([a-zA-Z].*)\\n={5,}\\n"), "

\\1

"); // links readme.replace(QRegularExpression("\\[([^\\[]+)\\]\\(([^\\)]+)\\)"), "\\1"); // bold readme.replace(QRegularExpression("(\\*\\*|__)(.*?)\\1"), "\\2"); // italic readme.replace(QRegularExpression("(\\*|_)(.*?)\\1"), "\\2"); // code readme.replace(QRegularExpression("`(.*?)`"), "\\1"); // horizontal lines readme.replace(QRegularExpression("\\n-{5,}"), "\n
"); // bullets (e.g., * or -) readme.replace(QRegularExpression("\\n\\*(.*)"), "\n
    \n\t
  • \\1
  • \n
"); readme.replace(QRegularExpression("\\n-(.*)"), "\n
    \n\t
  • \\1
  • \n
"); // fixup multiple
    bits readme.replace(QRegularExpression("<\\/ul>\\s?
      "), ""); // paragraphs .. doesn't seem needed // readme.replace(QRegularExpression("\\n([^\\n]+)\\n"), "

      \\1

      "); m_ui->readmeBrowser->setHtml(readme); #endif } } // see which repositories the user checked void DownloaderWidget::getCheckedRepos() { m_ui->readmeBrowser->clear(); m_downloadList.clear(); for (size_t i = 0; i < m_repoList.size(); i++) { QTableWidgetItem* row = m_ui->repoTable->item(i, 0); if (row == nullptr) continue; if (row->checkState() == Qt::Checked || row->isSelected()) { downloadEntry newEntry; newEntry.url = m_repoList[i].zipballUrl; newEntry.name = m_repoList[i].name; newEntry.type = m_repoList[i].type; m_downloadList.append(newEntry); } } downloadNext(); } // Used to download one zip at a time so we know which plugin data we're getting void DownloaderWidget::downloadNext() { if (!m_downloadList.isEmpty()) { QString url = m_downloadList.last().url; // use the .tar.gz instead of .zip if (url.endsWith(".zip")) { url.chop(4); // remove the last 4 characters ".zip" url += ".tar.gz"; } QNetworkRequest request; setRawHeaders(&request); request.setUrl(url); // Set the url m_reply = m_NetworkAccessManager->get(request); connect(m_reply, SIGNAL(finished()), this, SLOT(handleRedirect())); } } bool DownloaderWidget::checkToInstall() { QSettings settings; // check if we've asked the user before bool neverInstall = settings.value("neverInstallRequirements", false).toBool(); if (neverInstall) return false; bool alwaysInstall = settings.value("alwaysInstallRequirements", false).toBool(); if (alwaysInstall) return true; // okay, ask the user before installing QMessageBox msgBox; msgBox.setText(tr("This plugin requires certain packages to be installed.\n" "Do you want to install them?")); msgBox.setIcon(QMessageBox::Question); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); // add buttons for "Yes Always" and "No, Never" QPushButton* yesAlwaysButton = msgBox.addButton(tr("Always"), QMessageBox::YesRole); QPushButton* neverButton = msgBox.addButton(tr("Never"), QMessageBox::NoRole); msgBox.exec(); if (msgBox.clickedButton() == yesAlwaysButton) { settings.setValue("alwaysInstallRequirements", true); return true; } else if (msgBox.clickedButton() == neverButton) { settings.setValue("neverInstallRequirements", true); return false; } else if (msgBox.clickedButton() == msgBox.button(QMessageBox::Yes)) { return true; } else { return false; } } // The download url for Github is always a redirect to the actual zip // Using Qt 6 the redirect gets taken care of automatically, but on Qt 5 we // have to do it manually // m_reply is a QNetworkReply void DownloaderWidget::handleRedirect() { int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (m_reply->error() == QNetworkReply::NoError) { if (statusCode == 302) { // Redirected, have to manually redirect QVariant possibleRedirectUrl = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); QUrl _urlRedirectedTo = possibleRedirectUrl.toUrl(); QNetworkRequest request; setRawHeaders(&request); request.setUrl(_urlRedirectedTo); // Set the url m_reply = m_NetworkAccessManager->get(request); // Now we have the actual zip and can extract it connect(m_reply, SIGNAL(finished()), this, SLOT(unzipPlugin())); } else if (statusCode == 200) { // Normal success response unzipPlugin(); } else { // Something went wrong QString errorString = m_reply->errorString(); m_ui->readmeBrowser->append( tr("Failed to download from %1: status code %2, %3\n", "After an HTTP request; %1 is a URL, %2 is the HTTP status code, %3 " "is the error message (if any)") .arg(m_reply->url().toString()) .arg(statusCode) .arg(errorString)); } } else { QString errorString = m_reply->errorString(); m_ui->readmeBrowser->append( tr("Failed to download from %1: status code %2, %3\n", "After an HTTP request; %1 is a URL, %2 is the HTTP status code, %3 " "is the error message (if any)") .arg(m_reply->url().toString()) .arg(statusCode) .arg(errorString)); m_reply->deleteLater(); m_downloadList.removeLast(); downloadNext(); } } // Save and unzip the plugin zipball void DownloaderWidget::unzipPlugin() { if (m_reply->error() == QNetworkReply::NoError) { // done with redirect QByteArray fileData = m_reply->readAll(); QDir().mkpath(m_filePath); // create any needed directories for the download QString repoName = m_downloadList.last().name; QString filename = repoName + ".tar.gz"; QString absolutePath = m_filePath + "/" + filename; QString extractDirectory; QString subdir = m_downloadList.last().type; extractDirectory = m_filePath + "/" + subdir + "/"; // create the destination directory if it doesn't exist QDir().mkpath(extractDirectory); m_ui->readmeBrowser->append( tr("Downloading %1 to %2\n").arg(filename).arg(m_filePath)); QFile out(absolutePath); out.open(QIODevice::WriteOnly); out.write(fileData); out.close(); std::string extractdir = extractDirectory.toStdString(); std::string absolutep = absolutePath.toStdString(); ZipExtracter unzip; m_ui->readmeBrowser->append( tr("Extracting %1 to %2\n").arg(absolutePath).arg(extractDirectory)); QList newFiles = unzip.listFiles(absolutep); m_ui->readmeBrowser->append( tr("Finished %1 files\n").arg(newFiles.length())); QList ret = unzip.extract(extractdir, absolutep); if (ret.empty()) { m_ui->readmeBrowser->append(tr("Extraction successful\n")); // get the list of files / directories we unzipped // the first one is the main directory name if (newFiles.length() > 0) // got an empty archive { // check for a previous version of this plugin and remove it // e.g. we extracted to a path like User-Repo-GitHash // OpenChemistry-crystals-a7c672d // we want to check for OpenChemistry-crystals QStringList namePieces = newFiles[0].split('-'); if (namePieces.length() >= 3) { namePieces.removeLast(); // drop the hash namePieces.removeFirst(); // drop the org QString component = namePieces.join('-'); // Check if there's a previous install QString destination(extractDirectory + '/' + component); QDir previousInstall(destination); if (previousInstall.exists()) previousInstall.removeRecursively(); // and move the directory into place, e.g. // OpenChemistry-crystals-a7c672d QDir().rename(extractDirectory + '/' + newFiles[0], destination); // check if there's a requirements.txt file // .. if so, install with conda or pip QString requirementsFile(destination + "/requirements.txt"); if (QFile::exists(requirementsFile) && checkToInstall()) { // use conda if available QSettings settings; QString condaEnv = settings.value("condaEnvironment").toString(); QString condaPath = settings.value("condaPath").toString(); if (!condaEnv.isEmpty() && !condaPath.isEmpty()) { // install with conda QStringList arguments; arguments << "install" << "-y" << "-c" << "conda-forge" << "--file" << requirementsFile << "-n" << condaEnv; QProcess* process = new QProcess(this); process->start(condaPath, arguments); process->waitForFinished(); QString output(process->readAllStandardOutput()); QString error(process->readAllStandardError()); if (!output.isEmpty()) m_ui->readmeBrowser->append(output); if (!error.isEmpty()) m_ui->readmeBrowser->append(error); } else { // use pip QStringList arguments; arguments << "-m" << "pip" << "install" << "-r" << requirementsFile; QProcess* process = new QProcess(this); QString pythonPath = settings.value("interpreters/python", "python").toString(); process->start(pythonPath, arguments); process->waitForFinished(); QString output(process->readAllStandardOutput()); QString error(process->readAllStandardError()); if (!output.isEmpty()) m_ui->readmeBrowser->append(output); if (!error.isEmpty()) m_ui->readmeBrowser->append(error); } } } } } else { m_ui->readmeBrowser->append( tr("Error while extracting: %1").arg(ret.first())); } out.remove(); // remove the ZIP file m_reply->deleteLater(); m_downloadList.removeLast(); downloadNext(); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/downloaderwidget.h000066400000000000000000000042321506155467400272640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_DOWNLOADERWIDGET_H #define AVOGADRO_DOWNLOADERWIDGET_H #include #include #include #include #include class QNetworkAccessManager; class QNetworkReply; namespace Ui { class DownloaderWidget; } namespace Avogadro { namespace QtPlugins { class DownloaderWidget : public QDialog { Q_OBJECT public: DownloaderWidget(QWidget* parent = nullptr); ~DownloaderWidget() override; public slots: void showREADME(); void downloadREADME(int, int); void updateRepoData(); void getCheckedRepos(); void handleRedirect(); void unzipPlugin(); private: struct repo { QString name; QString description; QString releaseVersion; QString type; QString updatedAt; QString zipballUrl; QString baseUrl; QString readmeUrl; bool hasRelease; // Default constructor repo() : name("Error"), description("Error"), releaseVersion("Error"), type("other"), updatedAt("Error"), zipballUrl("Error"), baseUrl("Error"), readmeUrl("Error"), hasRelease(false) { } }; struct downloadEntry { QString url; QString name; QString type; }; void downloadNextPlugin(); // for now, the default path void getRepoData(QString url = "https://avogadro.cc/plugins.json"); void downloadNext(); bool checkSHA1(QByteArray); // check if we should install requirements.txt bool checkToInstall(); std::vector m_repoList; Ui::DownloaderWidget* m_ui; QNetworkAccessManager* m_NetworkAccessManager; QNetworkReply* m_reply; /** Holds a node of JSON results */ nlohmann::json m_root; /** Used to parse JSON results */ QVariantMap m_jsonResult; QString m_filePath; QList m_downloadList; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_DOWNLOADERWIDGET_H avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/downloaderwidget.ui000066400000000000000000000015301506155467400274500ustar00rootroot00000000000000 DownloaderWidget 0 0 965 432 Download Plugins… Download Selected avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/plugindownloader.cpp000066400000000000000000000031441506155467400276330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "plugindownloader.h" #include "downloaderwidget.h" #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { PluginDownloader::PluginDownloader(QObject* parent_) : ExtensionPlugin(parent_), m_downloadAction(new QAction(this)), m_molecule(nullptr), m_network(nullptr), m_widget(nullptr) { m_downloadAction->setEnabled(true); m_downloadAction->setText(tr("Download Plugins…")); m_downloadAction->setProperty("menu priority", 520); connect(m_downloadAction, SIGNAL(triggered()), SLOT(showDialog())); } PluginDownloader::~PluginDownloader() = default; QList PluginDownloader::actions() const { return QList() << m_downloadAction; } QStringList PluginDownloader::menuPath(QAction*) const { return QStringList() << tr("&Extensions"); } void PluginDownloader::showDialog() { if (m_widget == nullptr) { m_widget = new DownloaderWidget(qobject_cast(parent())); } m_widget->show(); } void PluginDownloader::replyFinished(QNetworkReply*) {} } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/plugindownloader.h000066400000000000000000000030761506155467400273040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PLUGINDOWNLOADER_H #define AVOGADRO_QTPLUGINS_PLUGINDOWNLOADER_H #include #include #include class QNetworkAccessManager; class QNetworkReply; class QProgressDialog; namespace Avogadro { namespace QtPlugins { class DownloaderWidget; /** * @brief Downloads Github repos and extracts their contents into a Avogadro * folder for plugins, molecule data, etc.. */ class PluginDownloader : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit PluginDownloader(QObject* parent = nullptr); ~PluginDownloader() override; QString name() const override { return tr("Download Plugins"); } QString description() const override { return tr("Download plugins from GitHub repositories."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule*) override {} private slots: void showDialog(); void replyFinished(QNetworkReply*); private: QAction* m_downloadAction; QtGui::Molecule* m_molecule; QNetworkAccessManager* m_network; QString m_moleculeName; DownloaderWidget* m_widget; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PLUGINDOWNLOADER_H avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/zipextracter.cpp000066400000000000000000000117511506155467400270050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "zipextracter.h" #include namespace Avogadro::QtPlugins { ZipExtracter::ZipExtracter() {} ZipExtracter::~ZipExtracter() {} /************* ✨ Windsurf Command ⭐ *************/ /** * Copies data from one archive to another. * * This is a convenience function to copy data from one archive to another. * It will copy the data block by block until it reaches the end of the * input archive. If there is an error reading or writing the data, it * will return the error code directly. */ /******* e529b3ce-4242-4d08-ab60-874f0aa6fefc *******/ int ZipExtracter::copyData(struct archive* ar, struct archive* aw) { int r; const void* buff; size_t size; la_int64_t offset; for (;;) { r = archive_read_data_block(ar, &buff, &size, &offset); if (r == ARCHIVE_EOF) return (ARCHIVE_OK); if (r < ARCHIVE_OK) return (r); r = archive_write_data_block(aw, buff, size, offset); if (r < ARCHIVE_OK) { fprintf(stderr, "%s\n", archive_error_string(aw)); return (r); } } } char* ZipExtracter::convert(const std::string& str) { char* result = new char[str.length() + 1]; strncpy(result, str.c_str(), str.length() + 1); // make sure to nul-terminate return result; } QList ZipExtracter::listFiles(const std::string absolutepath) { struct archive* a; struct archive_entry* entry; int r; a = archive_read_new(); archive_read_support_format_zip(a); archive_read_support_filter_all(a); QList toReturn; if ((r = archive_read_open_filename(a, convert(absolutepath), 512))) { toReturn.append( QString(tr("ERROR: could not open zip file to list contents.\n(%1)", "%1 is the error message from libarchive")) .arg(archive_error_string(a))); return toReturn; } for (;;) { r = archive_read_next_header(a, &entry); if (r < ARCHIVE_OK) break; toReturn.append(archive_entry_pathname(entry)); archive_read_data_skip(a); // Note 2 } r = archive_read_free(a); return toReturn; } // Extract method from libarchive docs, changed to return QList of errors QList ZipExtracter::extract(std::string extractdir, std::string absolutepath) { struct archive* a; struct archive* ext; struct archive_entry* entry; int flags; int r; QList toReturn; const std::string& extractdirectory = extractdir; std::string newFilename; std::string currentFilename; /* Select which attributes we want to restore. */ flags = ARCHIVE_EXTRACT_TIME; flags |= ARCHIVE_EXTRACT_PERM; flags |= ARCHIVE_EXTRACT_ACL; flags |= ARCHIVE_EXTRACT_FFLAGS; a = archive_read_new(); archive_read_support_format_all(a); archive_read_support_filter_all(a); ext = archive_write_disk_new(); archive_write_disk_set_options(ext, flags); archive_write_disk_set_standard_lookup(ext); if ((r = archive_read_open_filename(a, convert(absolutepath), 10240))) { toReturn.append( QString(tr("ERROR: could not open zip file to extract files.\n(%1)", "%1 is the error message from libarchive")) .arg(archive_error_string(a))); return toReturn; } [[maybe_unused]] long itrCount = 0; for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) { break; } if (r < ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(a)); if (r < ARCHIVE_WARN) { toReturn.append( QString(tr("Warning: (%1)", "%1 is the message from libarchive")) .arg(archive_error_string(a))); return toReturn; } currentFilename = archive_entry_pathname(entry); newFilename = extractdirectory; newFilename.append(currentFilename); archive_entry_set_pathname(entry, convert(newFilename)); r = archive_write_header(ext, entry); if (r < ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(ext)); else if (archive_entry_size(entry) > 0) { r = copyData(a, ext); if (r < ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(ext)); if (r < ARCHIVE_WARN) { toReturn.append( QString(tr("Warning: (%1)", "%1 is the message from libarchive")) .arg(archive_error_string(a))); return toReturn; } } r = archive_write_finish_entry(ext); if (r < ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(ext)); if (r < ARCHIVE_WARN) { toReturn.append( QString(tr("Warning: (%1)", "%1 is the message from libarchive")) .arg(archive_error_string(a))); return toReturn; } } archive_read_close(a); archive_read_free(a); archive_write_close(ext); archive_write_free(ext); return toReturn; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/plugindownloader/zipextracter.h000066400000000000000000000016351506155467400264520ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_ZIPEXTRACTER_H #define AVOGADRO_QTPLUGINS_ZIPEXTRACTER_H #include "archive.h" #include "archive_entry.h" #include #include #include namespace Avogadro { namespace QtPlugins { class ZipExtracter : public QObject { Q_OBJECT public: ZipExtracter(); ~ZipExtracter(); char* convert(const std::string&); int copyData(struct archive* ar, struct archive* aw); QList extract(std::string extractdir, std::string absolutepath); QList listFiles(const std::string absolutepath); }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/pluginfactory.h000066400000000000000000000016361506155467400232400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PLUGINFACTORY_H #define AVOGADRO_QTPLUGINS_PLUGINFACTORY_H #include #include namespace Avogadro::QtPlugins { /** * @class PluginFactory pluginfactory.h * @brief The base class for plugin factories in Avogadro. */ template class PluginFactory { public: virtual ~PluginFactory() {} virtual T* createInstance(QObject* parent = nullptr) = 0; virtual QString identifier() const = 0; virtual QString description() const = 0; }; } // namespace Avogadro::QtPlugins #endif /* AVOGADRO_QTPLUGINS_PLUGINFACTORY_H */ avogadrolibs-1.101.0/avogadro/qtplugins/pluginmanager.cpp000066400000000000000000000101501506155467400235250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "pluginmanager.h" #include "avogadrostaticqtplugins.h" #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { PluginManager::PluginManager(QObject* p) : QObject(p), m_staticPluginsLoaded(false) { QString libDir(QtGui::Utilities::libraryDirectory()); // http://doc.qt.digia.com/qt/deployment-plugins.html#debugging-plugins bool debugPlugins = !qgetenv("QT_DEBUG_PLUGINS").isEmpty(); // The usual base directory is the parent directory of the executable's // location. (exe is in "bin" or "MacOS" and plugins are under the parent // directory at "/avogadro2/plugins"...) QDir baseDir(QCoreApplication::applicationDirPath() + "/.."); #ifdef __APPLE__ // But if NOT running from the installed bundle on the Mac, the plugins are // relative to the build directory instead: // if (!QFileInfo(baseDir.absolutePath() + "/Resources/qt.conf").exists()) { QDir buildDir(QCoreApplication::applicationDirPath() + "/../../../.."); baseDir = buildDir; if (debugPlugins) qDebug() << " using buildDir:" << buildDir.absolutePath(); } #endif // If the environment variable is set, use that as the base directory. QByteArray pluginDir = qgetenv("AVOGADRO_PLUGIN_DIR"); if (!pluginDir.isEmpty()) baseDir.setPath(pluginDir); if (debugPlugins) qDebug() << " baseDir:" << baseDir.absolutePath(); QDir pluginsDir(baseDir.absolutePath() + "/" + libDir + "/avogadro2/plugins"); m_pluginDirs.append(pluginsDir.absolutePath()); if (debugPlugins) { qDebug() << " pluginsDir:" << pluginsDir.absolutePath(); int count = 0; foreach (const QString& pluginPath, pluginsDir.entryList(QDir::Files)) { ++count; qDebug() << " " << pluginsDir.absolutePath() + "/" + pluginPath; } if (count > 0) qDebug() << " " << count << "files found in" << pluginsDir.absolutePath(); else qDebug() << " no plugin files found in" << pluginsDir.absolutePath(); } initAvogadroPluginResources(); } PluginManager::~PluginManager() {} PluginManager* PluginManager::instance() { static QMutex mutex; // Compiler initializes this static pointer to 0. static PluginManager* pluginManagerInstance; if (!pluginManagerInstance) { mutex.lock(); if (!pluginManagerInstance) pluginManagerInstance = new PluginManager(QCoreApplication::instance()); mutex.unlock(); } return pluginManagerInstance; } void PluginManager::load() { foreach (const QString& dir, m_pluginDirs) load(dir); } void PluginManager::load(const QString& path) { // Load any static plugins first. if (!m_staticPluginsLoaded) { QObjectList staticPlugins = QPluginLoader::staticInstances(); foreach (QObject* pluginInstance, staticPlugins) m_plugins.append(pluginInstance); m_staticPluginsLoaded = true; } QDir dir(path); foreach (const QString& pluginPath, dir.entryList(QDir::Files)) { QPluginLoader pluginLoader(dir.absolutePath() + "/" + pluginPath); // We only want to count plugins once, the || should not be necessary but // I found that on the Mac at least isLoaded was not always reliable (and // if it is we skip the second in the short-circuit). if (pluginLoader.isLoaded() || m_plugins.contains(pluginLoader.instance())) continue; QObject* pluginInstance = pluginLoader.instance(); // Check if the plugin loaded correctly. Keep debug output for now, should // go away once we have verified this (or added to a logger). if (!pluginInstance) { qDebug() << "Failed to load" << pluginPath << "error" << pluginLoader.errorString(); continue; } m_plugins.append(pluginInstance); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/pluginmanager.h000066400000000000000000000071761506155467400232100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTGUI_PLUGINMANAGER_H #define AVOGADRO_QTGUI_PLUGINMANAGER_H #include "avogadroqtpluginsexport.h" #include #include #include namespace Avogadro { namespace QtPlugins { class ScenePluginFactory; class ExtensionPluginFactory; /** * @class PluginManager pluginmanager.h * @brief This class takes care of finding and loading Avogadro plugins. * @author Marcus D. Hanwell * * This class will find and load Avogadro plugins. Once loaded you can use an * instance of this class to query and construct plugin instances. By default * plugins are loaded from * QApplication::applicationDirPath()..//avogadro/plugins but this can * be changed, or more paths can be added. * * The load methods can be called multiple times, and will load any new plugins * while ignoring plugins that have already been loaded. */ class AVOGADROQTPLUGINS_EXPORT PluginManager : public QObject { Q_OBJECT public: /** * Get the singleton instance of the plugin manager. This instance should not * be deleted. */ static PluginManager* instance(); /** * Get a reference to the plugin directory path list. Modifying this before * calling load will allow you to add, remove or append to the search paths. */ QStringList& pluginDirList() { return m_pluginDirs; } /** Load all plugins available in the specified plugin directories. */ void load(); void load(const QString& dir); /** * Let the user request plugins with a certain type, this must use the Qt * mechanisms as qobject_cast is used in conjunction with interfaces. * * @code * factory = * pluginManager->pluginFactories(); * @endcode */ template QList pluginFactories() const; /** * Let the user request a plugin by name, this must use the Qt * mechanisms as qobject_cast is used in conjunction with interfaces. * * @code factory = pluginManager-> pluginFactories("name"); * @endcode * * @param id The identifier of the plugin factory. * @return The plugin factory if the plugin was found, nullptr otherwise. */ template T* pluginFactory(const QString& id) const; private: // Hide the constructor, destructor, copy and assignment operator. PluginManager(QObject* parent = nullptr); ~PluginManager() override; PluginManager(const PluginManager&); // Not implemented. PluginManager& operator=(const PluginManager&); // Not implemented. QStringList m_pluginDirs; QString m_relativeToApp; bool m_staticPluginsLoaded; // Storage for the loaded plugin instances. QList m_plugins; }; template QList PluginManager::pluginFactories() const { QList factories; foreach (QObject* plugin, m_plugins) { T* factory = qobject_cast(plugin); if (factory) factories.append(factory); } return factories; } template T* PluginManager::pluginFactory(const QString& id) const { T* factory; foreach (QObject* plugin, m_plugins) { factory = qobject_cast(plugin); if (factory && factory->identifier() == id) break; } return factory; } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTGUI_PLUGINMANAGER_H avogadrolibs-1.101.0/avogadro/qtplugins/ply/000077500000000000000000000000001506155467400207775ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/ply/CMakeLists.txt000066400000000000000000000002371506155467400235410ustar00rootroot00000000000000avogadro_plugin(PLY "Render the scene using PLY." ExtensionPlugin ply.h PLY "ply.cpp" "" ) target_link_libraries(PLY PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/ply/ply.cpp000066400000000000000000000036601506155467400223140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "ply.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { PLY::PLY(QObject* p) : Avogadro::QtGui::ExtensionPlugin(p), m_molecule(nullptr), m_scene(nullptr), m_camera(nullptr), m_action(new QAction(tr("PLY Render…"), this)) { connect(m_action, SIGNAL(triggered()), SLOT(render())); } PLY::~PLY() {} QList PLY::actions() const { QList result; return result << m_action; } QStringList PLY::menuPath(QAction*) const { return QStringList() << tr("&File") << tr("&Export"); } void PLY::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void PLY::setScene(Rendering::Scene* scene) { m_scene = scene; } void PLY::setCamera(Rendering::Camera* camera) { m_camera = camera; } void PLY::render() { if (!m_scene || !m_camera) return; QString filename = QFileDialog::getSaveFileName( qobject_cast(parent()), tr("Save File"), QDir::homePath(), tr("PLY (*.ply);;Text file (*.txt)")); QFile file(filename); if (!file.open(QIODevice::WriteOnly)) return; QTextStream fileStream(&file); Rendering::PLYVisitor visitor(*m_camera); visitor.begin(); m_scene->rootNode().accept(visitor); fileStream << visitor.end().c_str(); file.close(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/ply/ply.h000066400000000000000000000024771506155467400217660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PLY_H #define AVOGADRO_QTPLUGINS_PLY_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief The PLY class performs PLY operations on demand. */ class PLY : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit PLY(QObject* p = nullptr); ~PLY() override; QString name() const override { return tr("PLY"); } QString description() const override { return tr("Render the scene using PLY."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void setScene(Rendering::Scene* scene) override; void setCamera(Rendering::Camera* camera) override; private slots: void render(); private: QtGui::Molecule* m_molecule; Rendering::Scene* m_scene; Rendering::Camera* m_camera; QAction* m_action; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PLY_H avogadrolibs-1.101.0/avogadro/qtplugins/povray/000077500000000000000000000000001506155467400215135ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/povray/CMakeLists.txt000066400000000000000000000002621506155467400242530ustar00rootroot00000000000000avogadro_plugin(POVRay "Render the scene using POV-Ray." ExtensionPlugin povray.h POVRay "povray.cpp" "" ) target_link_libraries(POVRay PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/povray/povray.cpp000066400000000000000000000037371506155467400235510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "povray.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { POVRay::POVRay(QObject* p) : Avogadro::QtGui::ExtensionPlugin(p), m_molecule(nullptr), m_scene(nullptr), m_camera(nullptr), m_action(new QAction(tr("POV-Ray Render…"), this)) { connect(m_action, SIGNAL(triggered()), SLOT(render())); } POVRay::~POVRay() {} QList POVRay::actions() const { QList result; return result << m_action; } QStringList POVRay::menuPath(QAction*) const { return QStringList() << tr("&File") << tr("&Export"); } void POVRay::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void POVRay::setScene(Rendering::Scene* scene) { m_scene = scene; } void POVRay::setCamera(Rendering::Camera* camera) { m_camera = camera; } void POVRay::render() { if (!m_scene || !m_camera) return; QString filename = QFileDialog::getSaveFileName( qobject_cast(parent()), tr("Save File"), QDir::homePath(), tr("POV-Ray (*.pov);;Text file (*.txt)")); QFile file(filename); if (!file.open(QIODevice::WriteOnly)) return; QTextStream fileStream(&file); Rendering::POVRayVisitor visitor(*m_camera); visitor.begin(); m_scene->rootNode().accept(visitor); fileStream << visitor.end().c_str(); file.close(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/povray/povray.h000066400000000000000000000025361506155467400232120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_POVRAY_H #define AVOGADRO_QTPLUGINS_POVRAY_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief The POVRay class performs POVRay operations on demand. */ class POVRay : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit POVRay(QObject* p = nullptr); ~POVRay() override; QString name() const override { return tr("POVRay"); } QString description() const override { return tr("Render the scene using POV-Ray."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void setScene(Rendering::Scene* scene) override; void setCamera(Rendering::Camera* camera) override; private slots: void render(); private: QtGui::Molecule* m_molecule; Rendering::Scene* m_scene; Rendering::Camera* m_camera; QAction* m_action; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_POVRAY_H avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/000077500000000000000000000000001506155467400232525ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/CMakeLists.txt000066400000000000000000000003771506155467400260210ustar00rootroot00000000000000avogadro_plugin(PropertyTables "Atom, Bond, Angle, Dihedral property tables." ExtensionPlugin propertytables.h PropertyTables "propertytables.cpp;propertymodel.cpp;propertyview.cpp" ) target_link_libraries(PropertyTables PRIVATE Avogadro::Calc)avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/propertymodel.cpp000066400000000000000000001023551506155467400266710ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "propertymodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro { using Avogadro::Core::Array; using Avogadro::QtGui::Molecule; using QtGui::Molecule; using QtGui::RWAtom; using QtGui::RWBond; using std::numeric_limits; using std::pair; using std::vector; using SecondaryStructure = Avogadro::Core::Residue::SecondaryStructure; // element, valence, formal charge, partial charge, x, y, z, label, color const int AtomColumns = 9; // type, atom 1, atom 2, bond order, length, label const int BondColumns = 6; // type, atom 1, atom 2, atom 3, angle const int AngleColumns = 5; // type, atom 1, atom 2, atom 3, atom 4, dihedral const int TorsionColumns = 6; // name, number, chain, secondary structure, heterogen, color const int ResidueColumns = 6; // number, rmsd, energy or more depending on available properties const int ConformerColumns = 1; // compute the RMSD between the two sets of coordinates inline double calculateRMSD(const Array& v1, const Array& v2) { // if they're not the same length, it's an error if (v1.size() != v2.size()) return numeric_limits::quiet_NaN(); double sum = 0.0; for (size_t i = 0; i < v1.size(); ++i) { Vector3 diff = v1[i] - v2[i]; sum += diff.squaredNorm(); } return sqrt(sum / v1.size()); } inline double distance(Vector3 v1, Vector3 v2) { Vector3 v3 = v1 - v2; return v3.norm(); } inline QString angleTypeString(unsigned char a, unsigned char b, unsigned char c) { return QString("%1%2%3") .arg(Core::Elements::symbol(a)) .arg(Core::Elements::symbol(b)) .arg(Core::Elements::symbol(c)); } inline QString torsionTypeString(unsigned char a, unsigned char b, unsigned char c, unsigned char d) { return QString("%1%2%3%4") .arg(Core::Elements::symbol(a)) .arg(Core::Elements::symbol(b)) .arg(Core::Elements::symbol(c)) .arg(Core::Elements::symbol(d)); } PropertyModel::PropertyModel(PropertyType type, QObject* parent) : QAbstractTableModel(parent), m_type(type), m_molecule(nullptr) { } int PropertyModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); if (!m_validCache) updateCache(); switch (m_type) { case AtomType: return m_molecule->atomCount(); case BondType: return m_molecule->bondCount(); case ResidueType: return m_molecule->residueCount(); case AngleType: return m_angles.size(); case TorsionType: return m_torsions.size(); case ConformerType: return m_molecule->coordinate3dCount(); default: return 0; } return 0; } int PropertyModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); switch (m_type) { case AtomType: return AtomColumns; // see above case BondType: return BondColumns; // see above case AngleType: return AngleColumns; // see above case TorsionType: return TorsionColumns; case ResidueType: return ResidueColumns; case ConformerType: { if (m_molecule->hasData("energies")) return ConformerColumns + 1; else return ConformerColumns; } default: return 0; } return 0; } QString partialChargeType(Molecule* molecule) { QString type; std::set types = molecule->partialChargeTypes(); if (types.size() > 0) { type = QString(types.cbegin()->c_str()); } else { // find something const auto options = Calc::ChargeManager::instance().identifiersForMolecule(*molecule); if (options.size() > 0) { // look for GFN2 or AM1BCC, then MMFF94 then Gasteiger if (options.find("GFN2") != options.end()) type = "GFN2"; else if (options.find("am1bcc") != options.end()) type = "am1bcc"; else if (options.find("mmff94") != options.end()) type = "mmff94"; else if (options.find("gasteiger") != options.end()) type = "gasteiger"; else type = *options.begin()->c_str(); } } return type; } QString formatChargeType(QString type) { if (type == "gfn2") return "GFN2"; else if (type == "am1bcc") return "AM1BCC"; else if (type == "mmff94") return "MMFF94"; else if (type == "gasteiger") return "Gasteiger"; else if (type.startsWith("eem")) return "EEM"; else if (type == "qeq") return "QEq"; else if (type.toLower() == "mulliken") return "Mulliken"; else if (type.toLower() == "lowdin") return "Lowdin"; else if (type.toLower() == "chelpg") return "CHELPG"; else if (type.toLower() == "hirshfeld") return "Hirshfeld"; else return type; } QString partialCharge(Molecule* molecule, int atom) { // TODO: we need to track type and/or calling the charge calculator float charge = 0.0; std::string type = partialChargeType(molecule).toStdString(); MatrixX charges = Calc::ChargeManager::instance().partialCharges(type, *molecule); charge = charges(atom, 0); return QString("%L1").arg(charge, 0, 'f', 3); } // Qt calls this for multiple "roles" across row / columns in the index // we also combine multiple types into this class, so lots of special cases QVariant PropertyModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); int row = index.row(); int col = index.column(); // Simple lambda to convert QFlags to variant as in Qt 6 this needs help. auto toVariant = [&](auto flags) { return static_cast(flags); }; // handle text alignments if (role == Qt::TextAlignmentRole) { if (m_type == ConformerType) { return toVariant(Qt::AlignRight | Qt::AlignVCenter); // energies } else if (m_type == AtomType) { if ((index.column() == AtomDataCharge) || (index.column() == AtomDataColor)) return toVariant(Qt::AlignRight | Qt::AlignVCenter); else return toVariant(Qt::AlignHCenter | Qt::AlignVCenter); } else if (m_type == BondType) { if (index.column() == BondDataLength) return toVariant(Qt::AlignRight | Qt::AlignVCenter); // bond length else return toVariant(Qt::AlignHCenter | Qt::AlignVCenter); } else if (m_type == AngleType) { if (index.column() == AngleDataValue) return toVariant(Qt::AlignRight | Qt::AlignVCenter); // angle else return toVariant(Qt::AlignHCenter | Qt::AlignVCenter); } else if (m_type == TorsionType) { if (index.column() == TorsionDataValue) return toVariant(Qt::AlignRight | Qt::AlignVCenter); // dihedral angle else return toVariant(Qt::AlignHCenter | Qt::AlignVCenter); } else if (m_type == ResidueType) { return toVariant(Qt::AlignHCenter | Qt::AlignVCenter); } else if (m_type == ConformerType) { return toVariant(Qt::AlignRight | Qt::AlignVCenter); // RMSD or other properties } } if (role == Qt::DecorationRole) { // color for atom and residue if (m_type == AtomType && col == AtomDataColor && row < static_cast(m_molecule->atomCount())) { auto c = m_molecule->color(row); QColor color(c[0], c[1], c[2]); return color; } else if (m_type == ResidueType && col == ResidueDataColor && row < static_cast(m_molecule->residueCount())) { auto c = m_molecule->residue(row).color(); QColor color(c[0], c[1], c[2]); return color; } } if (role != Qt::UserRole && role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); if (m_type == AtomType) { auto column = static_cast(index.column()); if (row >= static_cast(m_molecule->atomCount()) || column > AtomColumns) { return QVariant(); // invalid index } QString format("%L1"); // Return Data switch (column) { case AtomDataElement: return Core::Elements::symbol(m_molecule->atomicNumber(row)); case AtomDataValence: return QVariant::fromValue(m_molecule->bonds(row).size()); case AtomDataFormalCharge: return m_molecule->formalCharge(row); case AtomDataPartialCharge: return partialCharge(m_molecule, row); case AtomDataX: if (role == Qt::UserRole) // Return the x coordinate as a double for sorting return m_molecule->atomPosition3d(row).x(); else // format fixed to 4 decimals return QString("%L1").arg(m_molecule->atomPosition3d(row).x(), 0, 'f', 4); case AtomDataY: if (role == Qt::UserRole) // Return the y coordinate as a double for sorting return m_molecule->atomPosition3d(row).y(); else // format fixed to 4 decimals return QString("%L1").arg(m_molecule->atomPosition3d(row).y(), 0, 'f', 4); case AtomDataZ: if (role == Qt::UserRole) // Return the z coordinate as a double for sorting return m_molecule->atomPosition3d(row).z(); else // format fixed to 4 decimals return QString("%L1").arg(m_molecule->atomPosition3d(row).z(), 0, 'f', 4); case AtomDataLabel: return m_molecule->atomLabel(row).c_str(); case AtomDataColor: default: return QVariant(); // nothing to show } } else if (m_type == BondType) { auto column = static_cast(index.column()); if (row >= static_cast(m_molecule->bondCount()) || column > BondColumns) { return QVariant(); // invalid index } auto bond = m_molecule->bond(row); auto atom1 = bond.atom1(); auto atom2 = bond.atom2(); switch (column) { case BondDataType: return QString("%1-%2") .arg(Core::Elements::symbol(atom1.atomicNumber())) .arg(Core::Elements::symbol(atom2.atomicNumber())); case BondDataAtom1: return QVariant::fromValue(atom1.index() + 1); case BondDataAtom2: return QVariant::fromValue(atom2.index() + 1); case BondDataOrder: return bond.order(); case BondDataLabel: return m_molecule->bondLabel(row).c_str(); default: // length, rounded to 4 decimals if (role == Qt::UserRole) // Return the bond length as a double for sorting return distance(atom1.position3d(), atom2.position3d()); else return QString("%L1 Å").arg( distance(atom1.position3d(), atom2.position3d()), 0, 'f', 3); } } else if (m_type == ResidueType) { auto column = static_cast(index.column()); if (row >= static_cast(m_molecule->residueCount()) || column > ResidueColumns) { return QVariant(); // invalid index } auto residue = m_molecule->residue(row); // name, number, chain, secondary structure // color is handled above switch (column) { case ResidueDataName: return residue.residueName().c_str(); case ResidueDataID: return QVariant::fromValue(residue.residueId()); case ResidueDataChain: return QString(residue.chainId()); case ResidueDataSecStructure: return secStructure(residue.secondaryStructure()); case ResidueDataHeterogen: return QString(residue.isHeterogen() ? "X" : ""); default: return QVariant(); } } else if (m_type == AngleType) { auto column = static_cast(index.column()); if (row > static_cast(m_angles.size()) || column > AngleColumns) return QVariant(); // invalid index auto angle = m_angles[row]; auto atomNumber1 = m_molecule->atomicNumber(std::get<0>(angle)); auto atomNumber2 = m_molecule->atomicNumber(std::get<1>(angle)); auto atomNumber3 = m_molecule->atomicNumber(std::get<2>(angle)); Vector3 a1 = m_molecule->atomPosition3d(std::get<0>(angle)); Vector3 a2 = m_molecule->atomPosition3d(std::get<1>(angle)); Vector3 a3 = m_molecule->atomPosition3d(std::get<2>(angle)); switch (column) { case AngleDataType: return angleTypeString(atomNumber1, atomNumber2, atomNumber3); case AngleDataAtom1: return QVariant::fromValue(std::get<0>(angle) + 1); case AngleDataAtom2: return QVariant::fromValue(std::get<1>(angle) + 1); case AngleDataAtom3: return QVariant::fromValue(std::get<2>(angle) + 1); case AngleDataValue: if (role == Qt::UserRole) // Return the angle as a double for sorting return calculateAngle(a1, a2, a3); else // format fixed to 3 decimals return QString("%L1 °").arg(calculateAngle(a1, a2, a3), 0, 'f', 3); default: return QVariant(); } } else if (m_type == TorsionType) { auto column = static_cast(index.column()); if (row > static_cast(m_torsions.size()) || column > TorsionColumns) return QVariant(); // invalid index auto torsion = m_torsions[row]; auto atomNumber1 = m_molecule->atomicNumber(std::get<0>(torsion)); auto atomNumber2 = m_molecule->atomicNumber(std::get<1>(torsion)); auto atomNumber3 = m_molecule->atomicNumber(std::get<2>(torsion)); auto atomNumber4 = m_molecule->atomicNumber(std::get<3>(torsion)); Vector3 a1 = m_molecule->atomPosition3d(std::get<0>(torsion)); Vector3 a2 = m_molecule->atomPosition3d(std::get<1>(torsion)); Vector3 a3 = m_molecule->atomPosition3d(std::get<2>(torsion)); Vector3 a4 = m_molecule->atomPosition3d(std::get<3>(torsion)); switch (column) { case TorsionDataType: return torsionTypeString(atomNumber1, atomNumber2, atomNumber3, atomNumber4); case TorsionDataAtom1: return QVariant::fromValue(std::get<0>(torsion) + 1); case TorsionDataAtom2: return QVariant::fromValue(std::get<1>(torsion) + 1); case TorsionDataAtom3: return QVariant::fromValue(std::get<2>(torsion) + 1); case TorsionDataAtom4: return QVariant::fromValue(std::get<3>(torsion) + 1); case TorsionDataValue: if (role == Qt::UserRole) // Return the dihedral angle as a double for sorting return calculateDihedral(a1, a2, a3, a4); else // format fixed to 3 decimals return QString("%L1 °").arg(calculateDihedral(a1, a2, a3, a4), 0, 'f', 3); default: return QVariant(); } } else if (m_type == ConformerType) { auto column = static_cast(index.column()); if (row >= static_cast(m_molecule->coordinate3dCount()) || column > ConformerColumns) { return QVariant(); // invalid index } switch (column) { case ConformerDataRMSD: { // rmsd double rmsd = 0.0; if (row > 0) { rmsd = calculateRMSD(m_molecule->coordinate3d(row), m_molecule->coordinate3d(0)); } if (role == Qt::UserRole) // Return the RMSD as a double for sorting return rmsd; else // format fixed to 3 decimals return QString("%L1 Å").arg(rmsd, 0, 'f', 3); } case ConformerDataEnergy: { double energy = 0.0; if (m_molecule->hasData("energies")) { std::vector energies = m_molecule->data("energies").toList(); // calculate the minimum double minEnergy = std::numeric_limits::max(); for (double e : energies) { minEnergy = std::min(minEnergy, e); } if (row < static_cast(energies.size())) energy = energies[row] - minEnergy; } if (role == Qt::UserRole) // Return the energy as a double for sorting return energy; else // format fixed to 4 decimals return QString("%L1").arg(energy, 0, 'f', 4); } } } return QVariant(); } QVariant PropertyModel::headerData(int section, Qt::Orientation orientation, int role) const { // handle text alignments if (role == Qt::TextAlignmentRole) { if (orientation == Qt::Vertical) { return Qt::AlignHCenter; // XYZ coordinates } } if (role != Qt::DisplayRole) return QVariant(); if (m_type == AtomType) { if (orientation == Qt::Horizontal) { unsigned int column = static_cast(section); switch (column) { case AtomDataElement: return tr("Element"); case AtomDataValence: return tr("Valence"); case AtomDataFormalCharge: return tr("Formal Charge"); case AtomDataPartialCharge: { QString charge = tr("%1 Partial Charge", "e.g. MMFF94 Partial Charge or " "Gasteiger Partial Charge"); return charge.arg(formatChargeType(partialChargeType(m_molecule))); } case AtomDataX: return tr("X (Å)"); case AtomDataY: return tr("Y (Å)"); case AtomDataZ: return tr("Z (Å)"); case AtomDataLabel: return tr("Label"); case AtomDataColor: return tr("Color"); } } else return tr("Atom") + QString(" %1").arg(section + 1); } else if (m_type == BondType) { if (orientation == Qt::Horizontal) { unsigned int column = static_cast(section); switch (column) { case BondDataType: return tr("Type"); case BondDataAtom1: return tr("Start Atom"); case BondDataAtom2: return tr("End Atom"); case BondDataOrder: return tr("Bond Order"); case BondDataLabel: return tr("Label"); default: // A bond length return tr("Length (Å)", "in Angstrom"); } } else // Bond ordering starts at 0 return tr("Bond") + QString(" %1").arg(section + 1); } else if (m_type == ResidueType) { if (orientation == Qt::Horizontal) { unsigned int column = static_cast(section); switch (column) { case ResidueDataName: return tr("Name"); case ResidueDataID: return tr("ID"); case ResidueDataChain: return tr("Chain"); case ResidueDataSecStructure: return tr("Secondary Structure"); case ResidueDataHeterogen: return tr("Heterogen"); case ResidueDataColor: return tr("Color"); } } else // row headers return QString("%L1").arg(section + 1); } else if (m_type == AngleType) { if (orientation == Qt::Horizontal) { unsigned int column = static_cast(section); switch (column) { case AngleDataType: return tr("Type"); case AngleDataAtom1: return tr("Atom 1"); case AngleDataAtom2: return tr("Vertex"); case AngleDataAtom3: return tr("Atom 3"); case AngleDataValue: return tr("Angle (°)"); } } else // row headers return QString("%L1").arg(section + 1); } else if (m_type == TorsionType) { if (orientation == Qt::Horizontal) { unsigned int column = static_cast(section); switch (column) { case TorsionDataType: return tr("Type"); case TorsionDataAtom1: return tr("Atom 1"); case TorsionDataAtom2: return tr("Atom 2"); case TorsionDataAtom3: return tr("Atom 3"); case TorsionDataAtom4: return tr("Atom 4"); case TorsionDataValue: return tr("Angle (°)"); } } else // row headers return QString("%L1").arg(section + 1); } else if (m_type == ConformerType) { // check if we have energies bool hasEnergies = (m_molecule->hasData("energies")); if (orientation == Qt::Horizontal) { unsigned int column = static_cast(section); switch (column) { case ConformerDataRMSD: return tr("RMSD (Å)", "root mean squared displacement in Angstrom"); case ConformerDataEnergy: // should only hit this if we have energies anyway return hasEnergies ? tr("Energy (kcal/mol)") : tr("Property"); } } else // row headers return QString("%L1").arg(section + 1); } return QVariant(); } Qt::ItemFlags PropertyModel::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::ItemIsEnabled; // return QAbstractItemModel::flags(index) | Qt::ItemIsEditable // for the types and columns that can be edited auto editable = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; if (m_type == AtomType) { if (index.column() == AtomDataElement || index.column() == AtomDataFormalCharge || index.column() == AtomDataX || index.column() == AtomDataY || index.column() == AtomDataZ || index.column() == AtomDataLabel) return editable; // TODO: Color } else if (m_type == BondType) { if (index.column() == BondDataOrder || index.column() == BondDataLength || index.column() == BondDataLabel) return editable; } else if (m_type == ResidueType) { // TODO: Color } else if (m_type == AngleType) { if (index.column() == AngleDataValue) return editable; } else if (m_type == TorsionType) { if (index.column() == TorsionDataValue) return editable; } return QAbstractItemModel::flags(index); } bool PropertyModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) return false; if (role != Qt::EditRole) return false; // If an item is actually editable, we should invalidate the cache // We can still use the cached data -- we just invalidate now // So that we can call "return" and have the cache invalid when we leave m_validCache = false; auto* undoMolecule = m_molecule->undoMolecule(); if (m_type == AtomType) { Vector3 v = m_molecule->atomPosition3d(index.row()); switch (static_cast(index.column())) { case AtomDataFormalCharge: { bool ok; int charge = value.toInt(&ok); if (ok) { undoMolecule->setFormalCharge(index.row(), charge); } break; } case AtomDataElement: { // atomic number // Try first as a number bool ok; int atomicNumber = value.toInt(&ok); if (ok) undoMolecule->setAtomicNumber(index.row(), atomicNumber); else { // try a symbol atomicNumber = Core::Elements::atomicNumberFromSymbol( value.toString().toStdString()); if (atomicNumber != Avogadro::InvalidElement) { undoMolecule->setAtomicNumber(index.row(), atomicNumber); } else return false; } // not a number break; } case AtomDataX: v[0] = value.toDouble(); break; case AtomDataY: v[1] = value.toDouble(); break; case AtomDataZ: v[2] = value.toDouble(); break; case AtomDataLabel: undoMolecule->setAtomLabel(index.row(), value.toString().toStdString()); break; default: return false; } undoMolecule->setAtomPosition3d(index.row(), v); // cleanup atom changes emit dataChanged(index, index); m_molecule->emitChanged(Molecule::Atoms); return true; } else if (m_type == BondType) { switch (static_cast(index.column())) { case BondDataOrder: undoMolecule->setBondOrder(index.row(), value.toInt()); break; case BondDataLength: setBondLength(index.row(), value.toDouble()); break; case BondDataLabel: undoMolecule->setBondLabel(index.row(), value.toString().toStdString()); break; default: return false; } emit dataChanged(index, index); m_molecule->emitChanged(Molecule::Bonds); return true; } else if (m_type == AngleType) { if (index.column() == AngleDataValue) { setAngle(index.row(), value.toDouble()); emit dataChanged(index, index); m_molecule->emitChanged(Molecule::Atoms); return true; } } else if (m_type == TorsionType) { if (index.column() == TorsionDataValue) { setTorsion(index.row(), value.toDouble()); emit dataChanged(index, index); m_molecule->emitChanged(Molecule::Atoms); return true; } } return false; } void PropertyModel::buildFragment(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom) { m_fragment.clear(); if (!fragmentRecurse(bond, startAtom, startAtom)) { // If this returns false, then a cycle has been found. Only move startAtom // in this case. m_fragment.clear(); } m_fragment.push_back(m_molecule->undoMolecule()->atomUniqueId(startAtom)); } bool PropertyModel::fragmentRecurse(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom, const QtGui::RWAtom& currentAtom) { // does our cycle include both bonded atoms? const RWAtom bondedAtom(bond.getOtherAtom(startAtom)); auto* undoMolecule = m_molecule->undoMolecule(); Core::Array bonds = undoMolecule->bonds(currentAtom); for (auto& it : bonds) { if (it != bond) { // Skip the current bond const RWAtom nextAtom = it.getOtherAtom(currentAtom); if (nextAtom != startAtom && nextAtom != bondedAtom) { // Skip atoms that have already been added. This prevents infinite // recursion on cycles in the fragments int uid = undoMolecule->atomUniqueId(nextAtom); if (!fragmentHasAtom(uid)) { m_fragment.push_back(uid); if (!fragmentRecurse(it, startAtom, nextAtom)) return false; } } else if (nextAtom == bondedAtom) { // If we've found the bonded atom, the bond is in a cycle return false; } } // *it != bond } // foreach bond return true; } inline bool PropertyModel::fragmentHasAtom(int uid) const { return std::find(m_fragment.begin(), m_fragment.end(), uid) != m_fragment.end(); } void PropertyModel::transformFragment() const { auto* undoMolecule = m_molecule->undoMolecule(); undoMolecule->beginMergeMode(tr("Adjust Fragment")); for (int it : m_fragment) { RWAtom atom = m_molecule->undoMolecule()->atomByUniqueId(it); if (atom.isValid()) { Vector3 pos = atom.position3d(); pos = m_transform * pos; atom.setPosition3d(pos); } } undoMolecule->endMergeMode(); } void PropertyModel::setBondLength(unsigned int index, double length) { if (m_molecule == nullptr) return; if (index >= m_molecule->bondCount()) return; // figure out how much to move and the vector of displacement auto bond = m_molecule->undoMolecule()->bond(index); Vector3 v1 = bond.atom1().position3d(); Vector3 v2 = bond.atom2().position3d(); Vector3 diff = v2 - v1; double currentLength = diff.norm(); diff.normalize(); Vector3 delta = diff * (length - currentLength); buildFragment(bond, bond.atom2()); m_transform.setIdentity(); m_transform.translate(delta); transformFragment(); m_molecule->emitChanged(QtGui::Molecule::Modified | QtGui::Molecule::Atoms); } void PropertyModel::setAngle(unsigned int index, double newValue) { // the index refers to the angle auto angle = m_angles[index]; auto atom1 = m_molecule->undoMolecule()->atom(std::get<0>(angle)); auto atom2 = m_molecule->undoMolecule()->atom(std::get<1>(angle)); auto atom3 = m_molecule->undoMolecule()->atom(std::get<2>(angle)); auto bond = m_molecule->undoMolecule()->bond(atom1, atom2); Vector3 a = atom1.position3d(); Vector3 b = atom2.position3d(); Vector3 c = atom3.position3d(); const double currentValue = calculateAngle(a, b, c); Vector3 ab = b - a; Vector3 bc = c - b; // Axis of rotation is the cross product of the vectors const Vector3 axis((ab.cross(bc)).normalized()); // Angle of rotation const double change = (newValue - currentValue) * M_PI / 180.0; // Build transform m_transform.setIdentity(); m_transform.translate(b); m_transform.rotate(Eigen::AngleAxis(-change, axis)); m_transform.translate(-b); // Build the fragment if needed: if (m_fragment.empty()) buildFragment(bond, atom2); // Perform transformation transformFragment(); } void PropertyModel::setTorsion(unsigned int index, double newValue) { auto torsion = m_torsions[index]; auto atom1 = m_molecule->undoMolecule()->atom(std::get<0>(torsion)); auto atom2 = m_molecule->undoMolecule()->atom(std::get<1>(torsion)); auto atom3 = m_molecule->undoMolecule()->atom(std::get<2>(torsion)); auto atom4 = m_molecule->undoMolecule()->atom(std::get<3>(torsion)); auto bond = m_molecule->undoMolecule()->bond(atom2, atom3); Vector3 a = atom1.position3d(); Vector3 b = atom2.position3d(); Vector3 c = atom3.position3d(); Vector3 d = atom4.position3d(); const double currentValue = calculateDihedral(a, b, c, d); // Axis of rotation const Vector3 axis((c - b).normalized()); // Angle of rotation const double change = (newValue - currentValue) * M_PI / 180.0; // Build transform m_transform.setIdentity(); m_transform.translate(c); m_transform.rotate(Eigen::AngleAxis(change, axis)); m_transform.translate(-c); // Build the fragment if needed: if (m_fragment.empty()) buildFragment(bond, atom3); // Perform transformation transformFragment(); } void PropertyModel::setMolecule(QtGui::Molecule* molecule) { if (molecule && molecule != m_molecule) { m_molecule = molecule; updateCache(); connect(m_molecule, SIGNAL(changed(unsigned int)), this, SLOT(updateTable(unsigned int))); } } QString PropertyModel::secStructure(unsigned int type) const { switch (type) { case SecondaryStructure::piHelix: return tr("π Helix", "pi helix"); case SecondaryStructure::bend: return tr("Bend", "protein bend secondary structure"); case SecondaryStructure::alphaHelix: return tr("α Helix", "alpha helix"); case SecondaryStructure::betaSheet: return tr("β Sheet", "beta sheet"); case SecondaryStructure::helix310: return tr("3-10 helix", "3-10 helix"); case SecondaryStructure::betaBridge: return tr("β Bridge", "beta bridge"); case SecondaryStructure::turn: return tr("Turn", "protein turn secondary structure"); case SecondaryStructure::coil: return tr("Coil", "protein coil secondary structure"); default: return QString(); // implied unknown } } void PropertyModel::updateTable(unsigned int flags) { if (flags & Molecule::Added || flags & Molecule::Removed) { // tear it down and rebuild the model updateCache(); beginResetModel(); endResetModel(); } else { // we can just update the current data emit dataChanged( QAbstractItemModel::createIndex(0, 0), QAbstractItemModel::createIndex(rowCount(), columnCount())); } } void PropertyModel::updateCache() const { m_validCache = true; m_angles.clear(); m_torsions.clear(); if (m_molecule == nullptr) return; if (m_type == AngleType) { Core::AngleIterator aIter(m_molecule); auto angle = aIter.begin(); while (angle != aIter.end()) { m_angles.push_back(angle); angle = ++aIter; } } else if (m_type == TorsionType) { Core::DihedralIterator dIter(m_molecule); auto torsion = dIter.begin(); while (torsion != dIter.end()) { m_torsions.push_back(torsion); torsion = ++dIter; } } } Core::Angle PropertyModel::getAngle(unsigned int angle) const { if (angle >= m_angles.size()) return Core::Angle(); return m_angles[angle]; } Real PropertyModel::getAngleValue(unsigned int angle) const { if (angle >= m_angles.size()) return 0.0; auto a = m_angles[angle]; auto atom1 = m_molecule->undoMolecule()->atom(std::get<0>(a)); auto atom2 = m_molecule->undoMolecule()->atom(std::get<1>(a)); auto atom3 = m_molecule->undoMolecule()->atom(std::get<2>(a)); Vector3 a1 = atom1.position3d(); Vector3 a2 = atom2.position3d(); Vector3 a3 = atom3.position3d(); return calculateAngle(a1, a2, a3); } Core::Dihedral PropertyModel::getTorsion(unsigned int torsion) const { if (torsion >= m_torsions.size()) return Core::Dihedral(); return m_torsions[torsion]; } Real PropertyModel::getTorsionValue(unsigned int torsion) const { if (torsion >= m_torsions.size()) return 0.0; auto t = m_torsions[torsion]; auto atom1 = m_molecule->undoMolecule()->atom(std::get<0>(t)); auto atom2 = m_molecule->undoMolecule()->atom(std::get<1>(t)); auto atom3 = m_molecule->undoMolecule()->atom(std::get<2>(t)); auto atom4 = m_molecule->undoMolecule()->atom(std::get<3>(t)); Vector3 a1 = atom1.position3d(); Vector3 a2 = atom2.position3d(); Vector3 a3 = atom3.position3d(); Vector3 a4 = atom4.position3d(); return calculateDihedral(a1, a2, a3, a4); } } // end namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/propertymodel.h000066400000000000000000000101521506155467400263270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef PROPMODEL_H #define PROPMODEL_H #include #include #include #include #include #include #include #include namespace Avogadro { namespace QtGui { class Molecule; } enum PropertyType { Other = 0, AtomType, BondType, AngleType, TorsionType, ConformerType, ResidueType, MoleculeType, }; class PropertyModel : public QAbstractTableModel { Q_OBJECT public slots: void updateTable(unsigned int flags); public: explicit PropertyModel(PropertyType type, QObject* parent = 0); int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void setMolecule(QtGui::Molecule* molecule); // Return what type of model this is PropertyType type() const { return m_type; }; // Generate all data pertaining to angles and torsions void updateCache() const; // Get the angle for a given index Core::Angle getAngle(unsigned int angle) const; Real getAngleValue(unsigned int angle) const; // Get the torson for a given index Core::Dihedral getTorsion(unsigned int torsion) const; Real getTorsionValue(unsigned int torsion) const; private: PropertyType m_type; QtGui::Molecule* m_molecule; mutable bool m_validCache; mutable std::vector m_angles; mutable std::vector m_torsions; QString secStructure(unsigned int type) const; std::vector m_fragment; Eigen::Affine3d m_transform; bool fragmentHasAtom(int uid) const; void buildFragment(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom); bool fragmentRecurse(const QtGui::RWBond& bond, const QtGui::RWAtom& startAtom, const QtGui::RWAtom& currentAtom); void setBondLength(unsigned int index, double value); void setAngle(unsigned int index, double newValue); void setTorsion(unsigned int index, double newValue); void transformFragment() const; QtGui::RWAtom otherBondedAtom(const QtGui::RWBond& bond, const QtGui::RWAtom& atom) const { return bond.atom1() == atom ? bond.atom2() : bond.atom1(); } /* * For each category (atom, bond etc), an enum specifies which columns hold * which data. */ // Atom Data enum AtomColumn { AtomDataElement = 0, AtomDataValence, AtomDataFormalCharge, AtomDataPartialCharge, AtomDataX, AtomDataY, AtomDataZ, AtomDataLabel, AtomDataColor, AtomDataCharge, AtomDataCustom, }; // Bond Data enum BondColumn { BondDataType = 0, BondDataAtom1, BondDataAtom2, BondDataOrder, BondDataLength, BondDataLabel }; // Angle Data enum AngleColumn { AngleDataType = 0, AngleDataAtom1, AngleDataAtom2, AngleDataAtom3, AngleDataValue }; // Torsion Data enum TorsionColumn { TorsionDataType = 0, TorsionDataAtom1, TorsionDataAtom2, TorsionDataAtom3, TorsionDataAtom4, TorsionDataValue }; // Conformer Data enum ConformerColumn { ConformerDataRMSD = 0, ConformerDataEnergy }; // Residue Data enum ResidueColumn { ResidueDataName = 0, ResidueDataID, ResidueDataChain, ResidueDataSecStructure, ResidueDataColor, ResidueDataHeterogen }; }; } // end namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/propertytables.cpp000066400000000000000000000131771506155467400270460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "propertytables.h" #include "propertymodel.h" #include "propertyview.h" #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { PropertyTables::PropertyTables(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr) { auto* action = new QAction(this); action->setText(tr("Atom Properties…")); action->setData(PropertyType::AtomType); action->setProperty("menu priority", 880); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); action = new QAction(this); action->setText(tr("Bond Properties…")); action->setData(PropertyType::BondType); action->setProperty("menu priority", 870); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); action = new QAction(this); action->setText(tr("Angle Properties…")); action->setData(PropertyType::AngleType); action->setProperty("menu priority", 860); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); action = new QAction(this); action->setText(tr("Torsion Properties…")); action->setData(PropertyType::TorsionType); action->setProperty("menu priority", 850); connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); action = new QAction(this); action->setText(tr("Residue Properties…")); action->setData(PropertyType::ResidueType); action->setProperty("menu priority", 840); action->setEnabled(false); // changed by molecule connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); action = new QAction(this); action->setText(tr("Conformer Properties…")); action->setData(PropertyType::ConformerType); action->setProperty("menu priority", 830); action->setEnabled(false); // changed by molecule connect(action, SIGNAL(triggered()), SLOT(showDialog())); m_actions.append(action); } PropertyTables::~PropertyTables() {} QString PropertyTables::description() const { return tr("Tables for displaying and editing atom, bond, angle and torsion " "properties."); } QList PropertyTables::actions() const { return m_actions; } QStringList PropertyTables::menuPath(QAction*) const { return QStringList() << tr("&Analyze") << tr("&Properties"); } void PropertyTables::setMolecule(QtGui::Molecule* mol) { if (mol == m_molecule) return; m_molecule = mol; updateActions(); // update if the molecule changes connect(m_molecule, SIGNAL(changed(unsigned int)), SLOT(updateActions())); } void PropertyTables::updateActions() { if (m_molecule == nullptr) return; // check if we enable / disable the residue and conformer actions bool haveResidues = (m_molecule->residueCount() > 0); // technically coordinate sets bool haveConformers = (m_molecule->coordinate3dCount() > 1); for (const auto& action : m_actions) { if (action->data().toInt() == PropertyType::ResidueType) action->setEnabled(haveResidues); else if (action->data().toInt() == PropertyType::ConformerType) action->setEnabled(haveConformers); } } void PropertyTables::showDialog() { auto* action = qobject_cast(sender()); if (action == nullptr || m_molecule == nullptr) return; if (action->data().toInt() == PropertyType::ResidueType && m_molecule->residueCount() == 0) return; if (action->data().toInt() == PropertyType::ConformerType && m_molecule->coordinate3dCount() < 2) return; auto* dialog = new QDialog(qobject_cast(parent())); auto* layout = new QVBoxLayout(dialog); dialog->setLayout(layout); // Don't show whitespace around the PropertiesView layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); PropertyType i = static_cast(action->data().toInt()); auto* model = new PropertyModel(i); model->setMolecule(m_molecule); // view will delete itself & model in PropertiesView::hideEvent using // deleteLater(). auto* view = new PropertyView(i, dialog); auto* proxyModel = new QSortFilterProxyModel(this); proxyModel->setSourceModel(model); proxyModel->setDynamicSortFilter(true); proxyModel->setSortLocaleAware(true); // this role will received direct floating-point numbers from the model proxyModel->setSortRole(Qt::UserRole); view->setMolecule(m_molecule); view->setModel(proxyModel); view->setSourceModel(model); view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); view->horizontalHeader()->setStretchLastSection(true); view->resizeColumnsToContents(); layout->addWidget(view); dialog->setWindowTitle(view->windowTitle()); QSize dialogSize = dialog->size(); double width = view->horizontalHeader()->length() + view->verticalHeader()->width() + 5; if (model->rowCount() < 13) { // no scrollbar dialogSize.setHeight(view->horizontalHeader()->height() + model->rowCount() * 30 + 5); dialogSize.setWidth(width); } else { // scrollbar is needed dialogSize.setHeight(width / 1.618); dialogSize.setWidth(width + view->verticalScrollBar()->width()); } dialog->resize(dialogSize); dialog->setWindowFlags(Qt::Window); dialog->show(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/propertytables.h000066400000000000000000000024061506155467400265040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PROPERTYTABLES_H #define AVOGADRO_QTPLUGINS_PROPERTYTABLES_H #include namespace Avogadro { namespace Core { class Molecule; } namespace QtPlugins { /** * @brief The PropertyTables class is an extension to launch * a "property table" views of the molecule. */ class PropertyTables : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit PropertyTables(QObject* parent_ = nullptr); ~PropertyTables() override; QString name() const override { return tr("PropertyTables"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void updateActions(); private slots: void showDialog(); private: QList m_actions; QtGui::Molecule* m_molecule; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_PROPERTYTABLES_H avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/propertyview.cpp000066400000000000000000000324561506155467400265470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "propertyview.h" #include "core/avogadrocore.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro { using QtGui::Molecule; PropertyView::PropertyView(PropertyType type, QWidget* parent) : QTableView(parent), m_type(type), m_molecule(nullptr), m_model(nullptr) { QString title; switch (type) { case AtomType: title = tr("Atom Properties"); break; case BondType: title = tr("Bond Properties"); break; case AngleType: title = tr("Angle Properties"); break; case TorsionType: title = tr("Torsion Properties"); break; case ConformerType: title = tr("Conformer Properties"); break; case ResidueType: title = tr("Residue Properties"); break; default: title = tr("Molecule Properties"); break; } this->setWindowTitle(title); QHeaderView* horizontal = this->horizontalHeader(); horizontal->setSectionResizeMode(QHeaderView::Interactive); horizontal->setMinimumSectionSize(75); QHeaderView* vertical = this->verticalHeader(); vertical->setSectionResizeMode(QHeaderView::Interactive); vertical->setMinimumSectionSize(30); vertical->setDefaultAlignment(Qt::AlignCenter); // You can select everything (e.g., to copy, select all, etc.) setCornerButtonEnabled(true); setSelectionBehavior(QAbstractItemView::SelectRows); if (type == ConformerType) setSelectionMode(QAbstractItemView::SingleSelection); else setSelectionMode(QAbstractItemView::ExtendedSelection); // Alternating row colors setAlternatingRowColors(true); // Allow sorting the table setSortingEnabled(true); } void PropertyView::selectionChanged(const QItemSelection& selected, const QItemSelection&) { bool ok = false; if (m_molecule == nullptr) return; // Start by clearing the molecule selection for (Index i = 0; i < m_molecule->atomCount(); ++i) m_molecule->undoMolecule()->setAtomSelected(i, false); foreach (const QModelIndex& index, selected.indexes()) { if (!index.isValid()) return; // Since the user can sort // we need to find the original index int rowNum = model() ->headerData(index.row(), Qt::Vertical) .toString() .split(" ") .last() .toLong(&ok) - 1; if (!ok) return; if (m_type == PropertyType::AtomType) { if (static_cast(rowNum) >= m_molecule->atomCount()) return; m_molecule->setAtomSelected(rowNum, true); } else if (m_type == PropertyType::BondType) { if (static_cast(rowNum) >= m_molecule->bondCount()) return; auto bondPair = m_molecule->bondPair(rowNum); m_molecule->undoMolecule()->setAtomSelected(bondPair.first, true); m_molecule->undoMolecule()->setAtomSelected(bondPair.second, true); } else if (m_type == PropertyType::AngleType) { if (m_model != nullptr) { auto angle = m_model->getAngle(rowNum); m_molecule->undoMolecule()->setAtomSelected(std::get<0>(angle), true); m_molecule->undoMolecule()->setAtomSelected(std::get<1>(angle), true); m_molecule->undoMolecule()->setAtomSelected(std::get<2>(angle), true); } } else if (m_type == PropertyType::TorsionType) { if (m_model != nullptr) { auto torsion = m_model->getTorsion(rowNum); m_molecule->undoMolecule()->setAtomSelected(std::get<0>(torsion), true); m_molecule->undoMolecule()->setAtomSelected(std::get<1>(torsion), true); m_molecule->undoMolecule()->setAtomSelected(std::get<2>(torsion), true); m_molecule->undoMolecule()->setAtomSelected(std::get<3>(torsion), true); } } else if (m_type == PropertyType::ResidueType) { // select all the atoms in the residue if (m_model != nullptr) { const auto residue = m_molecule->residue(rowNum); auto atoms = residue.residueAtoms(); for (Index i = 0; i < atoms.size(); ++i) { const auto atom = atoms[i]; m_molecule->undoMolecule()->setAtomSelected(atom.index(), true); } } } else if (m_type == PropertyType::ConformerType) { // selecting a row means switching to that conformer m_molecule->setCoordinate3d(rowNum); } } // end loop through selected m_molecule->emitChanged(Molecule::Atoms); } void PropertyView::setMolecule(Molecule* molecule) { m_molecule = molecule; } void PropertyView::hideEvent(QHideEvent*) { if (model()) { model()->deleteLater(); } this->deleteLater(); } void PropertyView::keyPressEvent(QKeyEvent* event) { // handle copy event // thanks to https://www.walletfox.com/course/qtableviewcopypaste.php if (!event->matches(QKeySequence::Copy)) { QTableView::keyPressEvent(event); return; } // get the selected rows (if any) QModelIndexList selectedRows = selectionModel()->selectedRows(); // if nothing is selected, copy everything to the clipboard QString text; if (selectedRows.isEmpty()) { // iterate through every row and column and copy the data for (int i = 0; i < model()->rowCount(); ++i) { QStringList rowContents; for (int j = 0; j < model()->columnCount(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } else { // copy the selected rows to the clipboard QItemSelectionRange range = selectionModel()->selection().first(); for (auto i = range.top(); i <= range.bottom(); ++i) { QStringList rowContents; for (auto j = range.left(); j <= range.right(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } QApplication::clipboard()->setText(text); } void PropertyView::copySelectedRowsToClipboard() { // get the selected rows (if any) QModelIndexList selectedRows = selectionModel()->selectedRows(); // if nothing is selected, copy everything to the clipboard QString text; if (selectedRows.isEmpty()) { // iterate through every row and column and copy the data for (int i = 0; i < model()->rowCount(); ++i) { QStringList rowContents; for (int j = 0; j < model()->columnCount(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } else { // copy the selected rows to the clipboard QItemSelectionRange range = selectionModel()->selection().first(); for (auto i = range.top(); i <= range.bottom(); ++i) { QStringList rowContents; for (auto j = range.left(); j <= range.right(); ++j) rowContents << model()->index(i, j).data().toString(); text += rowContents.join("\t"); text += "\n"; } } QApplication::clipboard()->setText(text); } void PropertyView::constrainSelectedRows() { // get the selected rows (if any) QModelIndexList selectedRows = selectionModel()->selectedRows(); // if nothing is selected, we're done if (selectedRows.isEmpty()) return; if (m_molecule == nullptr) return; // loop through the selected rows for (const auto& index : selectedRows) { if (!index.isValid()) continue; // get the row number bool ok; int rowNum = model() ->headerData(index.row(), Qt::Vertical) .toString() .split(" ") .last() .toLong(&ok) - 1; if (!ok) continue; if (m_type == PropertyType::BondType) { // get the start and end atoms and the distance from the table data auto bond = m_molecule->bond(rowNum); auto atom1 = bond.atom1(); auto atom2 = bond.atom2(); Real distance = bond.length(); m_molecule->addConstraint(distance, atom1.index(), atom2.index()); } else if (m_type == PropertyType::AngleType) { if (m_model != nullptr) { auto angle = m_model->getAngle(rowNum); auto atom1 = m_molecule->atom(std::get<0>(angle)); auto atom2 = m_molecule->atom(std::get<1>(angle)); auto atom3 = m_molecule->atom(std::get<2>(angle)); Real angleValue = m_model->getAngleValue(rowNum); m_molecule->addConstraint(angleValue, atom1.index(), atom2.index(), atom3.index()); } } else if (m_type == PropertyType::TorsionType) { if (m_model != nullptr) { auto torsion = m_model->getTorsion(rowNum); auto atom1 = m_molecule->atom(std::get<0>(torsion)); auto atom2 = m_molecule->atom(std::get<1>(torsion)); auto atom3 = m_molecule->atom(std::get<2>(torsion)); auto atom4 = m_molecule->atom(std::get<3>(torsion)); Real torsionValue = m_model->getTorsionValue(rowNum); m_molecule->addConstraint(torsionValue, atom1.index(), atom2.index(), atom3.index(), atom4.index()); } } } } void PropertyView::freezeAtom() { // get the selected rows (if any) QModelIndexList selectedRows = selectionModel()->selectedRows(); // if nothing is selected, we're done if (selectedRows.isEmpty()) return; if (m_molecule == nullptr) return; // loop through the selected rows for (const auto& index : selectedRows) { if (!index.isValid()) continue; // get the row number bool ok; int rowNum = model() ->headerData(index.row(), Qt::Vertical) .toString() .split(" ") .last() .toLong(&ok) - 1; if (!ok) continue; m_molecule->setFrozenAtom(rowNum, true); } m_molecule->emitChanged(Molecule::Atoms); } void PropertyView::openExportDialogBox() { // Create a file dialog for selecting the export location and file name QString filePath = QFileDialog::getSaveFileName(this, tr("Export CSV"), QDir::homePath(), tr("CSV Files (*.csv);;All Files (*)")); if (filePath.isEmpty()) { // User canceled the dialog or didn't provide a file name return; } // Open the file for writing QFile file(filePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { // Handle error opening the file QMessageBox::critical(this, tr("Error"), tr("Could not open the file for writing.")); return; } // Create a QTextStream to write to the file QTextStream stream(&file); // Write the header row with column names for (int col = 0; col < model()->columnCount(); ++col) { stream << model()->headerData(col, Qt::Horizontal).toString(); if (col < model()->columnCount() - 1) { stream << ","; } } stream << "\n"; // Write the data rows for (int row = 0; row < model()->rowCount(); ++row) { for (int col = 0; col < model()->columnCount(); ++col) { stream << model()->index(row, col).data().toString(); if (col < model()->columnCount() - 1) { stream << ","; } } stream << "\n"; } // Close the file file.close(); if (file.error() != QFile::NoError) { // Handle error closing the file QMessageBox::critical(this, tr("Error"), tr("Error writing to the file.")); } } void PropertyView::contextMenuEvent(QContextMenuEvent* event) { QMenu menu(this); QAction* copyAction = menu.addAction(tr("Copy")); menu.addAction(copyAction); connect(copyAction, &QAction::triggered, this, &PropertyView::copySelectedRowsToClipboard); QAction* exportAction = menu.addAction(tr("Export…")); menu.addAction(exportAction); connect(exportAction, &QAction::triggered, this, &PropertyView::openExportDialogBox); if (m_type == PropertyType::AtomType) { // freeze atom QAction* freezeAtomAction = menu.addAction(tr("Freeze Atom")); menu.addAction(freezeAtomAction); connect(freezeAtomAction, &QAction::triggered, this, &PropertyView::freezeAtom); } else { // bond angle torsion are similar QString name; if (m_type == PropertyType::BondType) name = tr("Constrain Bond"); else if (m_type == PropertyType::AngleType) name = tr("Constrain Angle"); else if (m_type == PropertyType::TorsionType) name = tr("Constrain Torsion"); QAction* constrainAction = menu.addAction(name); menu.addAction(constrainAction); connect(constrainAction, &QAction::triggered, this, &PropertyView::constrainSelectedRows); } menu.exec(event->globalPos()); } } // end namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/propertytables/propertyview.h000066400000000000000000000025061506155467400262050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_PROPERTYVIEW_H #define AVOGADRO_QTPLUGINS_PROPERTYVIEW_H #include "propertymodel.h" #include class QProgressDialog; namespace Avogadro { namespace QtGui { class Molecule; } class PropertyView : public QTableView { Q_OBJECT public: explicit PropertyView(PropertyType type, QWidget* parent = 0); void selectionChanged(const QItemSelection& selected, const QItemSelection& previous) override; void setMolecule(QtGui::Molecule* molecule); void setSourceModel(PropertyModel* model) { m_model = model; } void hideEvent(QHideEvent* event) override; void contextMenuEvent(QContextMenuEvent* event) override; protected: // copy the selected properties to the clipboard void keyPressEvent(QKeyEvent* event) override; private: PropertyType m_type; QtGui::Molecule* m_molecule; PropertyModel* m_model; void copySelectedRowsToClipboard(); void openExportDialogBox(); void constrainSelectedRows(); void freezeAtom(); }; } // end namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/000077500000000000000000000000001506155467400213065ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/CMakeLists.txt000066400000000000000000000015021506155467400240440ustar00rootroot00000000000000set(qtaimextension_SRCS qtaimextension.cpp qtaimwavefunction.cpp qtaimwavefunctionevaluator.cpp qtaimodeintegrator.cpp qtaimcriticalpointlocator.cpp qtaimmathutilities.cpp qtaimodeintegrator.cpp qtaimlsodaintegrator.cpp qtaimcubature.cpp ) avogadro_plugin(QTAIMExtension "QTAIM extension" ExtensionPlugin qtaimextension.h QTAIMExtension "${qtaimextension_SRCS}" ) target_link_libraries(QTAIMExtension PRIVATE Qt::Concurrent) # The settings widget is not built -- its settings weren't actually used by the # engine in Avogadro 1. The sources are kept for later if we decide to use it. avogadro_plugin(QTAIMScenePlugin "QTAIM scene plugin" ScenePlugin qtaimengine.h QTAIMEngine qtaimengine.cpp ) target_link_libraries(QTAIMScenePlugin PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimcriticalpointlocator.cpp000066400000000000000000000644401506155467400273060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "qtaimcriticalpointlocator.h" #include "qtaimlsodaintegrator.h" #include "qtaimmathutilities.h" #include "qtaimwavefunctionevaluator.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Eigen; #define HUGE_REAL_NUMBER 1.e20 #define SMALL_GRADIENT_NORM 1.e-4 namespace Avogadro::QtPlugins { namespace helper { template QList QTAIMLocateElectronDensityHelper(QList input) { qint64 counter = 0; const QString fileName = input.at(counter).toString(); counter++; // const qint64 nucleus=input.at(counter).toInt(); counter++ qreal x0 = input.at(counter).toReal(); counter++; qreal y0 = input.at(counter).toReal(); counter++; qreal z0 = input.at(counter).toReal(); counter++; const QVector3D x0y0z0(x0, y0, z0); QTAIMWavefunction wfn; wfn.loadFromBinaryFile(fileName); QTAIMWavefunctionEvaluator eval(wfn); /** The following logic chain was concatenated into a single statement to remove redundant branching, for readability the following table has been included. If any of the logic checks do not meet the expected result, the return value will be `false`, else it will be `true`. logic chain breakdown: | Statement | Expected | |-----------|----------| | eval.electronDensity(x0y0z0) < 1.e-1 | false | | eval.electronDensity(xyz) > 1.e-1 | true | | eval.gradientOfElectronDensityLaplacian(xyz).norm() < 1.e-3 | true | | QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix(eval.hessianOfElectronDensityLaplacian(xyz)) == ExpectedSignatureV | true | */ // Validate initial input to avoid needless costly calculations if (eval.electronDensity(Matrix(x0, y0, z0)) < 1.e-1) { return { false }; } // QTAIMODEIntegrator // ode(eval,QTAIMODEIntegrator::CMBPMinusThreeGradientInElectronDensityLaplacian); QTAIMLSODAIntegrator ode( eval, QTAIMLSODAIntegrator::CMBPMinusThreeGradientInElectronDensityLaplacian); QVector3D result = ode.integrate(x0y0z0); Matrix xyz(result.x(), result.y(), result.z()); // since we are using `&&` operator, lazy evaluation will be used if (eval.electronDensity(xyz) > 1.e-1 && eval.gradientOfElectronDensityLaplacian(xyz).norm() < 1.e-3 && QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix( eval.hessianOfElectronDensityLaplacian(xyz)) == ExpectedSignatureV) { return { true, result.x(), result.y(), result.z() }; } return { false }; } } // namespace helper QList QTAIMLocateNuclearCriticalPoint(QList input) { const QString fileName = input.at(0).toString(); const qint64 nucleus = input.at(1).toInt(); const QVector3D x0y0z0(input.at(2).toReal(), input.at(3).toReal(), input.at(4).toReal()); QTAIMWavefunction wfn; wfn.loadFromBinaryFile(fileName); QTAIMWavefunctionEvaluator eval(wfn); QVector3D result; if (wfn.nuclearCharge(nucleus) < 4) { // QTAIMODEIntegrator // ode(eval,QTAIMODEIntegrator::CMBPMinusThreeGradientInElectronDensity); QTAIMLSODAIntegrator ode( eval, QTAIMLSODAIntegrator::CMBPMinusThreeGradientInElectronDensity); result = ode.integrate(x0y0z0); } else { result = x0y0z0; } if (QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix( eval.hessianOfElectronDensity( Matrix(result.x(), result.y(), result.z()))) == -3) { return { true, result.x(), result.y(), result.z() }; } return { false }; } QList QTAIMLocateBondCriticalPoint(QList input) { const QString wfnFileName = input.at(0).toString(); const QString nuclearCriticalPointsFileName = input.at(1).toString(); const qint64 nucleusA = input.at(2).toInt(); const qint64 nucleusB = input.at(3).toInt(); const QVector3D x0y0z0(input.at(4).toReal(), input.at(5).toReal(), input.at(6).toReal()); QTAIMWavefunction wfn; wfn.loadFromBinaryFile(wfnFileName); QList nuclearCriticalPoints; QFile nuclearCriticalPointsFile(nuclearCriticalPointsFileName); nuclearCriticalPointsFile.open(QIODevice::ReadOnly); QDataStream nuclearCriticalPointsFileIn(&nuclearCriticalPointsFile); nuclearCriticalPointsFileIn >> nuclearCriticalPoints; nuclearCriticalPointsFile.close(); QList> betaSpheres; for (auto nuclearCriticalPoint : nuclearCriticalPoints) { QPair thisBetaSphere; thisBetaSphere.first = nuclearCriticalPoint; thisBetaSphere.second = 0.1; betaSpheres.append(thisBetaSphere); } QTAIMWavefunctionEvaluator eval(wfn); QList ncpList; // QTAIMODEIntegrator // ode(eval,QTAIMODEIntegrator::CMBPMinusOneGradientInElectronDensity); QTAIMLSODAIntegrator ode( eval, QTAIMLSODAIntegrator::CMBPMinusOneGradientInElectronDensity); QVector3D result = ode.integrate(x0y0z0); Matrix xyz(result.x(), result.y(), result.z()); if (!(QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix( eval.hessianOfElectronDensity(xyz)) == -1) || (eval.gradientOfElectronDensity(xyz)).norm() > SMALL_GRADIENT_NORM) { return { false, result.x(), result.y(), result.z() }; } Matrix eigenvectorsOfHessian = QTAIMMathUtilities::eigenvectorsOfASymmetricThreeByThreeMatrix( eval.hessianOfElectronDensity(xyz)); Matrix highestEigenvectorOfHessian(eigenvectorsOfHessian(0, 2), eigenvectorsOfHessian(1, 2), eigenvectorsOfHessian(2, 2)); const qreal smallStep = 0.01; QVector3D forwardStartingPoint( result.x() + smallStep * highestEigenvectorOfHessian(0), result.y() + smallStep * highestEigenvectorOfHessian(1), result.z() + smallStep * highestEigenvectorOfHessian(2)); QVector3D backwardStartingPoint( result.x() - smallStep * highestEigenvectorOfHessian(0), result.y() - smallStep * highestEigenvectorOfHessian(1), result.z() - smallStep * highestEigenvectorOfHessian(2)); // QTAIMODEIntegrator // forwardODE(eval,QTAIMODEIntegrator::SteepestAscentPathInElectronDensity); QTAIMLSODAIntegrator forwardODE( eval, QTAIMLSODAIntegrator::SteepestAscentPathInElectronDensity); forwardODE.setBetaSpheres(betaSpheres); QVector3D forwardEndpoint = forwardODE.integrate(forwardStartingPoint); QList forwardPath = forwardODE.path(); // QTAIMODEIntegrator // backwardODE(eval,QTAIMODEIntegrator::SteepestAscentPathInElectronDensity); QTAIMLSODAIntegrator backwardODE( eval, QTAIMLSODAIntegrator::SteepestAscentPathInElectronDensity); backwardODE.setBetaSpheres(betaSpheres); QVector3D backwardEndpoint = backwardODE.integrate(backwardStartingPoint); QList backwardPath = backwardODE.path(); // Find and store the forward and backward nucleus index for pair connection // check qreal minForwardDistance = HUGE_REAL_NUMBER; qreal minBackwardDistance = HUGE_REAL_NUMBER; qint64 backwardNucleusIndex = 0; qint64 forwardNucleusIndex = 0; // cache unchanged points const Matrix forwardPoint( forwardEndpoint.x(), forwardEndpoint.y(), forwardEndpoint.z()); const Matrix backwardPoint( backwardEndpoint.x(), backwardEndpoint.y(), backwardEndpoint.z()); for (qint64 n = 0; n < wfn.numberOfNuclei(); ++n) { const Matrix wavePoint(wfn.xNuclearCoordinate(n), wfn.yNuclearCoordinate(n), wfn.zNuclearCoordinate(n)); qreal fDistance = QTAIMMathUtilities::distance(forwardPoint, wavePoint); qreal bDistance = QTAIMMathUtilities::distance(backwardPoint, wavePoint); if (fDistance < minForwardDistance) { minForwardDistance = fDistance; forwardNucleusIndex = n; } if (bDistance < minBackwardDistance) { minBackwardDistance = bDistance; backwardNucleusIndex = n; } } QList value; // if statement checks if bond path connects pair if ((forwardNucleusIndex == nucleusA && backwardNucleusIndex == nucleusB) || (forwardNucleusIndex == nucleusB && backwardNucleusIndex == nucleusA)) { value.append(true); value.append(nucleusA); value.append(nucleusB); value.append(result.x()); value.append(result.y()); value.append(result.z()); const Matrix xyz_(result.x(), result.y(), result.z()); value.append(eval.laplacianOfElectronDensity(xyz_)); value.append(QTAIMMathUtilities::ellipticityOfASymmetricThreeByThreeMatrix( eval.hessianOfElectronDensity(xyz_))); value.append(1 + forwardPath.length() + 1 + backwardPath.length() + 1); value.append(forwardEndpoint.x()); for (qint64 i = forwardPath.length() - 1; i >= 0; --i) { value.append(forwardPath.at(i).x()); } value.append(result.x()); for (auto i : backwardPath) { value.append(i.x()); } value.append(backwardEndpoint.x()); value.append(forwardEndpoint.y()); for (qint64 i = forwardPath.length() - 1; i >= 0; --i) { value.append(forwardPath.at(i).y()); } value.append(result.y()); for (auto i : backwardPath) { value.append(i.y()); } value.append(backwardEndpoint.y()); value.append(forwardEndpoint.z()); for (qint64 i = forwardPath.length() - 1; i >= 0; --i) { value.append(forwardPath.at(i).z()); } value.append(result.z()); for (auto i : backwardPath) { value.append(i.z()); } value.append(backwardEndpoint.z()); } else { value.append(false); // for debugging value.append(result.x()); value.append(result.y()); value.append(result.z()); } return value; } QList QTAIMLocateElectronDensitySink(QList input) { /** This function acts as a wrapper to consolidate code The primary functionality only deviates from other functions in its expected value for QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix(eval.hessianOfElectronDensityLaplacian(**Integrated Point**)) which is passed in as a template parameter to the helper function. At the time of writing this value is -3. */ return helper::QTAIMLocateElectronDensityHelper<-3>(input); } QList QTAIMLocateElectronDensitySource(QList input) { /** This function acts as a wrapper to consolidate code The primary functionality only deviates from other functions in its expected value for QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix(eval.hessianOfElectronDensityLaplacian(**Integrated Point**)) which is passed in as a template parameter to the helper function. At the time of writing this value is 3. */ return helper::QTAIMLocateElectronDensityHelper<3>(input); } QTAIMCriticalPointLocator::QTAIMCriticalPointLocator(QTAIMWavefunction& wfn) { m_wfn = &wfn; m_nuclearCriticalPoints.empty(); m_bondCriticalPoints.empty(); m_ringCriticalPoints.empty(); m_cageCriticalPoints.empty(); m_laplacianAtBondCriticalPoints.empty(); m_ellipticityAtBondCriticalPoints.empty(); m_bondPaths.empty(); m_bondedAtoms.empty(); m_electronDensitySources.empty(); m_electronDensitySinks.empty(); } void QTAIMCriticalPointLocator::locateNuclearCriticalPoints() { QString tempFileName = QTAIMCriticalPointLocator::temporaryFileName(); QList> inputList; const qint64 numberOfNuclei = m_wfn->numberOfNuclei(); for (qint64 n = 0; n < numberOfNuclei; ++n) { QList input; input.append(tempFileName); input.append(n); input.append(m_wfn->xNuclearCoordinate(n)); input.append(m_wfn->yNuclearCoordinate(n)); input.append(m_wfn->zNuclearCoordinate(n)); inputList.append(input); } m_wfn->saveToBinaryFile(tempFileName); QProgressDialog dialog; dialog.setWindowTitle("QTAIM"); dialog.setLabelText(QString("Nuclear Critical Points Search")); QFutureWatcher> futureWatcher; QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); QFuture> future = QtConcurrent::mapped(inputList, QTAIMLocateNuclearCriticalPoint); futureWatcher.setFuture(future); dialog.exec(); futureWatcher.waitForFinished(); QList> results; if (futureWatcher.future().isCanceled()) { results.clear(); } else { results = future.results(); } QFile file; file.remove(tempFileName); for (const auto& n : results) { if (n.at(0).toBool()) { QVector3D result(n.at(1).toReal(), n.at(2).toReal(), n.at(3).toReal()); m_nuclearCriticalPoints.append(result); } } } void QTAIMCriticalPointLocator::locateBondCriticalPoints() { if (m_nuclearCriticalPoints.length() < 1) { return; } const qint64 numberOfNuclei = m_wfn->numberOfNuclei(); if (numberOfNuclei < 2) { return; } QString tempFileName = QTAIMCriticalPointLocator::temporaryFileName(); QString nuclearCriticalPointsFileName = QTAIMCriticalPointLocator::temporaryFileName(); QFile nuclearCriticalPointsFile(nuclearCriticalPointsFileName); nuclearCriticalPointsFile.open(QIODevice::WriteOnly); QDataStream nuclearCriticalPointsOut(&nuclearCriticalPointsFile); nuclearCriticalPointsOut << m_nuclearCriticalPoints; nuclearCriticalPointsFile.close(); QList> inputList; for (qint64 M = 0; M < numberOfNuclei - 1; ++M) { for (qint64 N = M + 1; N < numberOfNuclei; ++N) { const qreal distanceCutoff = 8.0; Matrix a; Matrix b; a << m_wfn->xNuclearCoordinate(M), m_wfn->yNuclearCoordinate(M), m_wfn->zNuclearCoordinate(M); b << m_wfn->xNuclearCoordinate(N), m_wfn->yNuclearCoordinate(N), m_wfn->zNuclearCoordinate(N); if (QTAIMMathUtilities::distance(a, b) < distanceCutoff) { QVector3D x0y0z0( (m_wfn->xNuclearCoordinate(M) + m_wfn->xNuclearCoordinate(N)) / 2.0, (m_wfn->yNuclearCoordinate(M) + m_wfn->yNuclearCoordinate(N)) / 2.0, (m_wfn->zNuclearCoordinate(M) + m_wfn->zNuclearCoordinate(N)) / 2.0); QList input; input.append(tempFileName); input.append(nuclearCriticalPointsFileName); input.append(M); input.append(N); input.append(x0y0z0.x()); input.append(x0y0z0.y()); input.append(x0y0z0.z()); inputList.append(input); } } // end N } // end M m_wfn->saveToBinaryFile(tempFileName); QProgressDialog dialog; dialog.setWindowTitle("QTAIM"); dialog.setLabelText(QString("Bond Critical Points Search")); QFutureWatcher> futureWatcher; QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); QFuture> future = QtConcurrent::mapped(inputList, QTAIMLocateBondCriticalPoint); ; futureWatcher.setFuture(future); dialog.exec(); futureWatcher.waitForFinished(); QList> results; if (futureWatcher.future().isCanceled()) { results.clear(); } else { results = future.results(); } QFile file; file.remove(tempFileName); file.remove(nuclearCriticalPointsFileName); for (const auto& thisCriticalPoint : results) { bool success = thisCriticalPoint.at(0).toBool(); if (success) { QPair bondedAtoms_; bondedAtoms_.first = thisCriticalPoint.at(1).toInt(); bondedAtoms_.second = thisCriticalPoint.at(2).toInt(); m_bondedAtoms.append(bondedAtoms_); QVector3D coordinates(thisCriticalPoint.at(3).toReal(), thisCriticalPoint.at(4).toReal(), thisCriticalPoint.at(5).toReal()); m_bondCriticalPoints.append(coordinates); m_laplacianAtBondCriticalPoints.append(thisCriticalPoint.at(6).toReal()); m_ellipticityAtBondCriticalPoints.append( thisCriticalPoint.at(7).toReal()); qint64 pathLength = thisCriticalPoint.at(8).toInt(); QList bondPath; for (qint64 j = 0; j < pathLength; ++j) { QVector3D pathPoint( thisCriticalPoint.at(9 + j).toReal(), thisCriticalPoint.at(9 + j + pathLength).toReal(), thisCriticalPoint.at(9 + j + 2 * pathLength).toReal()); bondPath.append(pathPoint); } m_bondPaths.append(bondPath); } } } void QTAIMCriticalPointLocator::locateElectronDensitySources() { QString tempFileName = QTAIMCriticalPointLocator::temporaryFileName(); QList> inputList; qreal xmin, ymin, zmin; qreal xmax, ymax, zmax; qreal xstep, ystep, zstep; // TODO: if only we were using Eigen data structures... QList xNuclearCoordinates; QList yNuclearCoordinates; QList zNuclearCoordinates; for (qint64 i = 0; i < m_wfn->numberOfNuclei(); ++i) { xNuclearCoordinates.append(m_wfn->xNuclearCoordinate(i)); yNuclearCoordinates.append(m_wfn->yNuclearCoordinate(i)); zNuclearCoordinates.append(m_wfn->zNuclearCoordinate(i)); } xmin = xNuclearCoordinates.first(); xmax = xNuclearCoordinates.first(); for (qint64 i = 1; i < m_wfn->numberOfNuclei(); ++i) { if (xNuclearCoordinates.at(i) < xmin) { xmin = xNuclearCoordinates.at(i); } if (xNuclearCoordinates.at(i) > xmax) { xmax = xNuclearCoordinates.at(i); } } ymin = yNuclearCoordinates.first(); ymax = yNuclearCoordinates.first(); for (qint64 i = 1; i < yNuclearCoordinates.count(); ++i) { if (yNuclearCoordinates.at(i) < ymin) { ymin = yNuclearCoordinates.at(i); } if (yNuclearCoordinates.at(i) > ymax) { ymax = yNuclearCoordinates.at(i); } } zmin = zNuclearCoordinates.first(); zmax = zNuclearCoordinates.first(); for (qint64 i = 1; i < zNuclearCoordinates.count(); ++i) { if (zNuclearCoordinates.at(i) < zmin) { zmin = zNuclearCoordinates.at(i); } if (zNuclearCoordinates.at(i) > zmax) { zmax = zNuclearCoordinates.at(i); } } xmin = -2.0 + xmin; ymin = -2.0 + ymin; zmin = -2.0 + zmin; xmax = 2.0 + xmax; ymax = 2.0 + ymax; zmax = 2.0 + zmax; xstep = ystep = zstep = 0.5; for (qreal x = xmin; x < xmax + xstep; x = x + xstep) { for (qreal y = ymin; y < ymax + ystep; y = y + ystep) { for (qreal z = zmin; z < zmax + zstep; z = z + zstep) { QList input; input.append(tempFileName); // input.append( n ); input.append(x); input.append(y); input.append(z); inputList.append(input); } } } m_wfn->saveToBinaryFile(tempFileName); QProgressDialog dialog; dialog.setWindowTitle("QTAIM"); dialog.setLabelText(QString("Electron Density Sources Search")); QFutureWatcher> futureWatcher; QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); QFuture> future = QtConcurrent::mapped(inputList, QTAIMLocateElectronDensitySource); futureWatcher.setFuture(future); dialog.exec(); futureWatcher.waitForFinished(); QList> results; if (futureWatcher.future().isCanceled()) { results.clear(); } else { results = future.results(); } QFile file; file.remove(tempFileName); for (const auto& n : results) { if (n.at(0).toBool()) { qreal x = n.at(1).toReal(); qreal y = n.at(2).toReal(); qreal z = n.at(3).toReal(); if ((xmin < x && x < xmax) && (ymin < y && y < ymax) && (zmin < z && z < zmax)) { QVector3D result(x, y, z); qreal smallestDistance = HUGE_REAL_NUMBER; for (auto m_electronDensitySource : m_electronDensitySources) { Matrix a(x, y, z); Matrix b(m_electronDensitySource.x(), m_electronDensitySource.y(), m_electronDensitySource.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < smallestDistance) { smallestDistance = distance; } } if (smallestDistance > 1.e-2) { m_electronDensitySources.append(result); } } } } // qDebug() << "SOURCES" << m_electronDensitySources; } void QTAIMCriticalPointLocator::locateElectronDensitySinks() { QString tempFileName = QTAIMCriticalPointLocator::temporaryFileName(); QList> inputList; qreal xmin, ymin, zmin; qreal xmax, ymax, zmax; qreal xstep, ystep, zstep; // TODO: if only we were using Eigen data structures... QList xNuclearCoordinates; QList yNuclearCoordinates; QList zNuclearCoordinates; for (qint64 i = 0; i < m_wfn->numberOfNuclei(); ++i) { xNuclearCoordinates.append(m_wfn->xNuclearCoordinate(i)); yNuclearCoordinates.append(m_wfn->yNuclearCoordinate(i)); zNuclearCoordinates.append(m_wfn->zNuclearCoordinate(i)); } xmin = xNuclearCoordinates.first(); xmax = xNuclearCoordinates.first(); for (qint64 i = 1; i < m_wfn->numberOfNuclei(); ++i) { if (xNuclearCoordinates.at(i) < xmin) { xmin = xNuclearCoordinates.at(i); } if (xNuclearCoordinates.at(i) > xmax) { xmax = xNuclearCoordinates.at(i); } } ymin = yNuclearCoordinates.first(); ymax = yNuclearCoordinates.first(); for (qint64 i = 1; i < yNuclearCoordinates.count(); ++i) { if (yNuclearCoordinates.at(i) < ymin) { ymin = yNuclearCoordinates.at(i); } if (yNuclearCoordinates.at(i) > ymax) { ymax = yNuclearCoordinates.at(i); } } zmin = zNuclearCoordinates.first(); zmax = zNuclearCoordinates.first(); for (qint64 i = 1; i < zNuclearCoordinates.count(); ++i) { if (zNuclearCoordinates.at(i) < zmin) { zmin = zNuclearCoordinates.at(i); } if (zNuclearCoordinates.at(i) > zmax) { zmax = zNuclearCoordinates.at(i); } } xmin = -2.0 + xmin; ymin = -2.0 + ymin; zmin = -2.0 + zmin; xmax = 2.0 + xmax; ymax = 2.0 + ymax; zmax = 2.0 + zmax; xstep = ystep = zstep = 0.5; for (qreal x = xmin; x < xmax + xstep; x = x + xstep) { for (qreal y = ymin; y < ymax + ystep; y = y + ystep) { for (qreal z = zmin; z < zmax + zstep; z = z + zstep) { QList input; input.append(tempFileName); // input.append( n ); input.append(x); input.append(y); input.append(z); inputList.append(input); } } } m_wfn->saveToBinaryFile(tempFileName); QProgressDialog dialog; dialog.setWindowTitle("QTAIM"); dialog.setLabelText(QString("Electron Density Sinks Search")); QFutureWatcher> futureWatcher; QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); QFuture> future = QtConcurrent::mapped(inputList, QTAIMLocateElectronDensitySink); futureWatcher.setFuture(future); dialog.exec(); futureWatcher.waitForFinished(); QList> results; if (futureWatcher.future().isCanceled()) { results.clear(); } else { results = future.results(); } QFile file; file.remove(tempFileName); for (const auto& n : results) { if (n.at(0).toBool()) { qreal x = n.at(1).toReal(); qreal y = n.at(2).toReal(); qreal z = n.at(3).toReal(); if ((xmin < x && x < xmax) && (ymin < y && y < ymax) && (zmin < z && z < zmax)) { QVector3D result(x, y, z); qreal smallestDistance = HUGE_REAL_NUMBER; for (auto m_electronDensitySink : m_electronDensitySinks) { Matrix a(x, y, z); Matrix b(m_electronDensitySink.x(), m_electronDensitySink.y(), m_electronDensitySink.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < smallestDistance) { smallestDistance = distance; } } if (smallestDistance > 1.e-2) { m_electronDensitySinks.append(result); } } } } // qDebug() << "SINKS" << m_electronDensitySinks; } QString QTAIMCriticalPointLocator::temporaryFileName() { QTemporaryFile temporaryFile; temporaryFile.open(); QString tempFileName = temporaryFile.fileName(); temporaryFile.close(); temporaryFile.remove(); // wait for temporary file to be deleted QDir dir; do { // Nothing } while (dir.exists(tempFileName)); return tempFileName; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimcriticalpointlocator.h000066400000000000000000000050031506155467400267410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef QTAIMCRITICALPOINTLOCATOR_H #define QTAIMCRITICALPOINTLOCATOR_H #include "qtaimwavefunction.h" #include #include #include #include namespace Avogadro::QtPlugins { class QTAIMCriticalPointLocator { public: explicit QTAIMCriticalPointLocator(QTAIMWavefunction& wfn); void locateNuclearCriticalPoints(); void locateBondCriticalPoints(); void locateElectronDensitySources(); void locateElectronDensitySinks(); QList nuclearCriticalPoints() const { return m_nuclearCriticalPoints; } QList bondCriticalPoints() const { return m_bondCriticalPoints; } QList ringCriticalPoints() const { return m_ringCriticalPoints; } QList cageCriticalPoints() const { return m_cageCriticalPoints; } QList laplacianAtBondCriticalPoints() const { return m_laplacianAtBondCriticalPoints; } QList ellipticityAtBondCriticalPoints() const { return m_ellipticityAtBondCriticalPoints; } QList> bondPaths() { return m_bondPaths; } QList> bondedAtoms() { return m_bondedAtoms; } QList electronDensitySources() const { return m_electronDensitySources; } QList electronDensitySinks() const { return m_electronDensitySinks; } private: QTAIMWavefunction* m_wfn; QList m_nuclearCriticalPoints; QList m_bondCriticalPoints; QList m_ringCriticalPoints; QList m_cageCriticalPoints; QList m_laplacianAtBondCriticalPoints; QList m_ellipticityAtBondCriticalPoints; QList> m_bondedAtoms; QList> m_bondPaths; QList m_electronDensitySources; QList m_electronDensitySinks; QString temporaryFileName(); }; } // namespace Avogadro::QtPlugins #endif // QTAIMCRITICALPOINTLOCATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimcubature.cpp000066400000000000000000002063321506155467400246660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the GPL v3 or later (the "License"). You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ /* Based on */ /* http://ab-initio.mit.edu/cubature/cubature-20101018.tgz */ /* Adaptive multidimensional integration of a vector of integrands. * * Copyright (c) 2005-2010 Steven G. Johnson * * Portions (see comments) based on HIntLib (also distributed under * the GNU GPL, v2 or later), copyright (c) 2002-2005 Rudolf Schuerer. * (http://www.cosy.sbg.ac.at/~rschuer/hintlib/) * * Portions (see comments) based on GNU GSL (also distributed under * the GNU GPL, v2 or later), copyright (c) 1996-2000 Brian Gough. * (http://www.gnu.org/software/gsl/) * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "qtaimcubature.h" #include "qtaimcriticalpointlocator.h" #include "qtaimlsodaintegrator.h" #include "qtaimmathutilities.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Adaptive multidimensional integration on hypercubes (or, really, hyper-rectangles) using cubature rules. A cubature rule takes a function and a hypercube and evaluates the function at a small number of points, returning an estimate of the integral as well as an estimate of the error, and also a suggested dimension of the hypercube to subdivide. Given such a rule, the adaptive integration is simple: 1) Evaluate the cubature rule on the hypercube(s). Stop if converged. 2) Pick the hypercube with the largest estimated error, and divide it in two along the suggested dimension. 3) Goto (1). The basic algorithm is based on the adaptive cubature described in A. C. Genz and A. A. Malik, "An adaptive algorithm for numeric integration over an N-dimensional rectangular region," J. Comput. Appl. Math. 6 (4), 295-302 (1980). and subsequently extended to integrating a vector of integrands in J. Berntsen, T. O. Espelid, and A. Genz, "An adaptive algorithm for the approximate calculation of multiple integrals," ACM Trans. Math. Soft. 17 (4), 437-451 (1991). Note, however, that we do not use any of code from the above authors (in part because their code is Fortran 77, but mostly because it is under the restrictive ACM copyright license). I did make use of some GPL code from Rudolf Schuerer's HIntLib and from the GNU Scientific Library as listed in the copyright notice above, on the other hand. I am also grateful to Dmitry Turbiner , who implemented an initial prototype of the "vectorized" functionality for evaluating multiple points in a single call (as opposed to multiple functions in a single call). (Although Dmitry implemented a working version, I ended up re-implementing this feature from scratch as part of a larger code-cleanup, and in order to have a single code path for the vectorized and non-vectorized APIs. I subsequently implemented the algorithm by Gladwell to extract even more parallelism by evaluating many hypercubes at once.) TODO: * Putting these routines into the GNU GSL library would be nice. * A Python interface would be nice. (Also a Matlab interface, a GNU Octave interface, ...) * For high-dimensional integrals, it would be nice to implement a sparse-grid cubature scheme using Clenshaw-Curtis quadrature. Currently, for dimensions > 7 or so, quasi Monte Carlo methods win. * Berntsen et. al also describe a "two-level" error estimation scheme that they claim makes the algorithm more robust. It might be nice to implement this, at least as an option (although I seem to remember trying it once and it made the number of evaluations substantially worse for my test integrands). */ /* USAGE: Call adapt_integrate with your function as described in cubature.h. To compile a test program, compile cubature.c with -DTEST_INTEGRATOR as described at the end. */ using namespace Avogadro::QtPlugins; /* error return codes */ #define SUCCESS 0 #define FAILURE 1 /***************************************************************************/ /* Basic datatypes */ using esterr = struct { double val, err; }; static double relError(esterr ee) { return (ee.val == 0.0 ? HUGE_VAL : fabs(ee.err / ee.val)); } static double errMax(unsigned int fdim, const esterr* ee) { double errmax = 0; unsigned int k; for (k = 0; k < fdim; ++k) if (ee[k].err > errmax) errmax = ee[k].err; return errmax; } using hypercube = struct { unsigned int dim; double* data; /* length 2*dim = center followed by half-widths */ double vol; /* cache volume = product of widths */ }; static double compute_vol(const hypercube* h) { unsigned int i; double vol = 1; for (i = 0; i < h->dim; ++i) vol *= 2 * h->data[i + h->dim]; return vol; } static hypercube make_hypercube(unsigned int dim, const double* center, const double* halfwidth) { unsigned int i; hypercube h; h.dim = dim; h.data = (double*)malloc(sizeof(double) * dim * 2); h.vol = 0; if (h.data) { for (i = 0; i < dim; ++i) { h.data[i] = center[i]; h.data[i + dim] = halfwidth[i]; } h.vol = compute_vol(&h); } return h; } static hypercube make_hypercube_range(unsigned int dim, const double* xmin, const double* xmax) { hypercube h = make_hypercube(dim, xmin, xmax); unsigned int i; if (h.data) { for (i = 0; i < dim; ++i) { h.data[i] = 0.5 * (xmin[i] + xmax[i]); h.data[i + dim] = 0.5 * (xmax[i] - xmin[i]); } h.vol = compute_vol(&h); } return h; } static void destroy_hypercube(hypercube* h) { free(h->data); h->dim = 0; } using region = struct { hypercube h; unsigned int splitDim; unsigned int fdim; /* dimensionality of vector integrand */ esterr* ee; /* array of length fdim */ double errmax; /* max ee[k].err */ }; static region make_region(const hypercube* h, unsigned int fdim) { region R; R.h = make_hypercube(h->dim, h->data, h->data + h->dim); R.splitDim = 0; R.fdim = fdim; R.ee = R.h.data ? (esterr*)malloc(sizeof(esterr) * fdim) : nullptr; return R; } static void destroy_region(region* R) { destroy_hypercube(&R->h); free(R->ee); R->ee = nullptr; } static int cut_region(region* R, region* R2) { unsigned int d = R->splitDim, dim = R->h.dim; *R2 = *R; R->h.data[d + dim] *= 0.5; R->h.vol *= 0.5; R2->h = make_hypercube(dim, R->h.data, R->h.data + dim); if (!R2->h.data) return FAILURE; R->h.data[d] -= R->h.data[d + dim]; R2->h.data[d] += R->h.data[d + dim]; R2->ee = (esterr*)malloc(sizeof(esterr) * R2->fdim); return R2->ee == nullptr; } struct rule_s; /* forward declaration */ using evalError_func = int (*)(struct rule_s*, unsigned int, integrand_v, void*, unsigned int, region*); using destroy_func = void (*)(struct rule_s*); using rule = struct rule_s { unsigned int dim, fdim; /* the dimensionality & number of functions */ unsigned int num_points; /* number of evaluation points */ unsigned int num_regions; /* max number of regions evaluated at once */ double* pts; /* points to eval: num_regions * num_points * dim */ double* vals; /* num_regions * num_points * fdim */ evalError_func evalError; destroy_func destroy; }; static void destroy_rule(rule* r) { if (r) { if (r->destroy) r->destroy(r); free(r->pts); free(r); } } static int alloc_rule_pts(rule* r, unsigned int num_regions) { if (num_regions > r->num_regions) { free(r->pts); r->pts = r->vals = nullptr; r->num_regions = 0; num_regions *= 2; /* allocate extra so that repeatedly calling alloc_rule_pts with growing num_regions only needs a logarithmic number of allocations */ r->pts = (double*)malloc( sizeof(double) * (num_regions * r->num_points * (r->dim + r->fdim))); if (r->fdim + r->dim > 0 && !r->pts) return FAILURE; r->vals = r->pts + num_regions * r->num_points * r->dim; r->num_regions = num_regions; } return SUCCESS; } static rule* make_rule(size_t sz, /* >= sizeof(rule) */ unsigned int dim, unsigned int fdim, unsigned int num_points, evalError_func evalError, destroy_func destroy) { rule* r; if (sz < sizeof(rule)) return nullptr; r = (rule*)malloc(sz); if (!r) return nullptr; r->pts = r->vals = nullptr; r->num_regions = 0; r->dim = dim; r->fdim = fdim; r->num_points = num_points; r->evalError = evalError; r->destroy = destroy; return r; } /* note: all regions must have same fdim */ static int eval_regions(unsigned int nR, region* R, integrand_v f, void* fdata, rule* r) { unsigned int iR; if (nR == 0) return SUCCESS; /* nothing to evaluate */ if (r->evalError(r, R->fdim, f, fdata, nR, R)) return FAILURE; for (iR = 0; iR < nR; ++iR) R[iR].errmax = errMax(R->fdim, R[iR].ee); return SUCCESS; } /***************************************************************************/ /* Functions to loop over points in a hypercube. */ /* Based on orbitrule.cpp in HIntLib-0.0.10 */ /* ls0 returns the least-significant 0 bit of n (e.g. it returns 0 if the LSB is 0, it returns 1 if the 2 LSBs are 01, etcetera). */ static unsigned int ls0(unsigned int n) { #if defined(__GNUC__) && \ ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ > 3) return __builtin_ctz(~n); /* gcc builtin for version >= 3.4 */ #else const unsigned int bits[256] = { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8, }; unsigned int bit = 0; while ((n & 0xff) == 0xff) { n >>= 8; bit += 8; } return bit + bits[n & 0xff]; #endif } /** * Evaluate the integration points for all 2^n points (+/-r,...+/-r) * * A Gray-code ordering is used to minimize the number of coordinate updates * in p, although this doesn't matter as much now that we are saving all pts. */ static void evalR_Rfs(double* pts, unsigned int dim, double* p, const double* c, const double* r) { unsigned int i; unsigned int signs = 0; /* 0/1 bit = +/- for corresponding element of r[] */ /* We start with the point where r is ADDed in every coordinate (this implies signs=0). */ for (i = 0; i < dim; ++i) p[i] = c[i] + r[i]; /* Loop through the points in Gray-code ordering */ for (i = 0;; ++i) { unsigned int mask, d; memcpy(pts, p, sizeof(double) * dim); pts += dim; d = ls0(i); /* which coordinate to flip */ if (d >= dim) break; /* flip the d-th bit and add/subtract r[d] */ mask = 1U << d; signs ^= mask; p[d] = (signs & mask) ? c[d] - r[d] : c[d] + r[d]; } } static void evalRR0_0fs(double* pts, unsigned int dim, double* p, const double* c, const double* r) { unsigned int i, j; for (i = 0; i < dim - 1; ++i) { p[i] = c[i] - r[i]; for (j = i + 1; j < dim; ++j) { p[j] = c[j] - r[j]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[i] = c[i] + r[i]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[j] = c[j] + r[j]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[i] = c[i] - r[i]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[j] = c[j]; /* Done with j -> Restore p[j] */ } p[i] = c[i]; /* Done with i -> Restore p[i] */ } } static void evalR0_0fs4d(double* pts, unsigned int dim, double* p, const double* c, const double* r1, const double* r2) { unsigned int i; memcpy(pts, p, sizeof(double) * dim); pts += dim; for (i = 0; i < dim; i++) { p[i] = c[i] - r1[i]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[i] = c[i] + r1[i]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[i] = c[i] - r2[i]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[i] = c[i] + r2[i]; memcpy(pts, p, sizeof(double) * dim); pts += dim; p[i] = c[i]; } } #define num0_0(dim) (1U) #define numR0_0fs(dim) (2 * (dim)) #define numRR0_0fs(dim) (2 * (dim) * (dim - 1)) #define numR_Rfs(dim) (1U << (dim)) /***************************************************************************/ /* Based on rule75genzmalik.cpp in HIntLib-0.0.10: An embedded cubature rule of degree 7 (embedded rule degree 5) due to A. C. Genz and A. A. Malik. See: A. C. Genz and A. A. Malik, "An imbedded [sic] family of fully symmetric numerical integration rules," SIAM J. Numer. Anal. 20 (3), 580-588 (1983). */ using rule75genzmalik = struct { rule parent; /* temporary arrays of length dim */ double *widthLambda, *widthLambda2, *p; /* dimension-dependent constants */ double weight1, weight3, weight5; double weightE1, weightE3; }; #define real(x) ((double)(x)) #define to_int(n) ((int)(n)) static int isqr(int x) { return x * x; } static void destroy_rule75genzmalik(rule* r_) { auto* r = (rule75genzmalik*)r_; free(r->p); } static int rule75genzmalik_evalError(rule* r_, unsigned int fdim, integrand_v f, void* fdata, unsigned int nR, region* R) { /* lambda2 = sqrt(9/70), lambda4 = sqrt(9/10), lambda5 = sqrt(9/19) */ const double lambda2 = 0.3585685828003180919906451539079374954541; const double lambda4 = 0.9486832980505137995996680633298155601160; const double lambda5 = 0.6882472016116852977216287342936235251269; const double weight2 = 980. / 6561.; const double weight4 = 200. / 19683.; const double weightE2 = 245. / 486.; const double weightE4 = 25. / 729.; const double ratio = (lambda2 * lambda2) / (lambda4 * lambda4); auto* r = (rule75genzmalik*)r_; unsigned int i, j, iR, dim = r_->dim, npts = 0; double *diff, *pts, *vals; if (alloc_rule_pts(r_, nR)) return FAILURE; pts = r_->pts; vals = r_->vals; for (iR = 0; iR < nR; ++iR) { const double* center = R[iR].h.data; const double* halfwidth = R[iR].h.data + dim; for (i = 0; i < dim; ++i) r->p[i] = center[i]; for (i = 0; i < dim; ++i) r->widthLambda2[i] = halfwidth[i] * lambda2; for (i = 0; i < dim; ++i) r->widthLambda[i] = halfwidth[i] * lambda4; /* Evaluate points in the center, in (lambda2,0,...,0) and (lambda3=lambda4, 0,...,0). */ evalR0_0fs4d(pts + npts * dim, dim, r->p, center, r->widthLambda2, r->widthLambda); npts += num0_0(dim) + 2 * numR0_0fs(dim); /* Calculate points for (lambda4, lambda4, 0, ...,0) */ evalRR0_0fs(pts + npts * dim, dim, r->p, center, r->widthLambda); npts += numRR0_0fs(dim); /* Calculate points for (lambda5, lambda5, ..., lambda5) */ for (i = 0; i < dim; ++i) r->widthLambda[i] = halfwidth[i] * lambda5; evalR_Rfs(pts + npts * dim, dim, r->p, center, r->widthLambda); npts += numR_Rfs(dim); } /* Evaluate the integrand function(s) at all the points */ f(dim, npts, pts, fdata, fdim, vals); /* we are done with the points, and so we can re-use the pts array to store the maximum difference diff[i] in each dimension for each hypercube */ diff = pts; for (i = 0; i < dim * nR; ++i) diff[i] = 0; for (j = 0; j < fdim; ++j) { for (iR = 0; iR < nR; ++iR) { double result, res5th; double val0, sum2 = 0, sum3 = 0, sum4 = 0, sum5 = 0; unsigned int k, k0 = 0; /* accumulate j-th function values into j-th integrals NOTE: this relies on the ordering of the eval functions above, as well as on the internal structure of the evalR0_0fs4d function */ val0 = vals[0]; /* central point */ k0 += 1; for (k = 0; k < dim; ++k) { double v0 = vals[k0 + 4 * k]; double v1 = vals[(k0 + 4 * k) + 1]; double v2 = vals[(k0 + 4 * k) + 2]; double v3 = vals[(k0 + 4 * k) + 3]; sum2 += v0 + v1; sum3 += v2 + v3; diff[iR * dim + k] += fabs(v0 + v1 - 2 * val0 - ratio * (v2 + v3 - 2 * val0)); } k0 += 4 * k; for (k = 0; k < numRR0_0fs(dim); ++k) sum4 += vals[k0 + k]; k0 += k; for (k = 0; k < numR_Rfs(dim); ++k) sum5 += vals[k0 + k]; /* Calculate fifth and seventh order results */ result = R[iR].h.vol * (r->weight1 * val0 + weight2 * sum2 + r->weight3 * sum3 + weight4 * sum4 + r->weight5 * sum5); res5th = R[iR].h.vol * (r->weightE1 * val0 + weightE2 * sum2 + r->weightE3 * sum3 + weightE4 * sum4); R[iR].ee[j].val = result; R[iR].ee[j].err = fabs(res5th - result); vals += r_->num_points; } } /* figure out dimension to split: */ for (iR = 0; iR < nR; ++iR) { double maxdiff = 0; unsigned int dimDiffMax = 0; for (i = 0; i < dim; ++i) if (diff[iR * dim + i] > maxdiff) { maxdiff = diff[iR * dim + i]; dimDiffMax = i; } R[iR].splitDim = dimDiffMax; } return SUCCESS; } static rule* make_rule75genzmalik(unsigned int dim, unsigned int fdim) { rule75genzmalik* r; if (dim < 2) return nullptr; /* this rule does not support 1d integrals */ /* Because of the use of a bit-field in evalR_Rfs, we are limited to be < 32 dimensions (or however many bits are in unsigned int). This is not a practical limitation...long before you reach 32 dimensions, the Genz-Malik cubature becomes excruciatingly slow and is superseded by other methods (e.g. Monte-Carlo). */ if (dim >= sizeof(unsigned int) * 8) return nullptr; r = (rule75genzmalik*)make_rule( sizeof(rule75genzmalik), dim, fdim, num0_0(dim) + 2 * numR0_0fs(dim) + numRR0_0fs(dim) + numR_Rfs(dim), rule75genzmalik_evalError, destroy_rule75genzmalik); if (!r) return nullptr; r->weight1 = (real(12824 - 9120 * to_int(dim) + 400 * isqr(to_int(dim))) / real(19683)); r->weight3 = real(1820 - 400 * to_int(dim)) / real(19683); r->weight5 = real(6859) / real(19683) / real(1U << dim); r->weightE1 = (real(729 - 950 * to_int(dim) + 50 * isqr(to_int(dim))) / real(729)); r->weightE3 = real(265 - 100 * to_int(dim)) / real(1458); r->p = (double*)malloc(sizeof(double) * dim * 3); if (!r->p) { destroy_rule((rule*)r); return nullptr; } r->widthLambda = r->p + dim; r->widthLambda2 = r->p + 2 * dim; return (rule*)r; } /***************************************************************************/ /* 1d 15-point Gaussian quadrature rule, based on qk15.c and qk.c in GNU GSL (which in turn is based on QUADPACK). */ static int rule15gauss_evalError(rule* r, unsigned int fdim, integrand_v f, void* fdata, unsigned int nR, region* R) { /* Gauss quadrature weights and kronrod quadrature abscissae and weights as evaluated with 80 decimal digit arithmetic by L. W. Fullerton, Bell Labs, Nov. 1981. */ const unsigned int n = 8; const double xgk[8] = { /* abscissae of the 15-point kronrod rule */ 0.991455371120812639206854697526329, 0.949107912342758524526189684047851, 0.864864423359769072789712788640926, 0.741531185599394439863864773280788, 0.586087235467691130294144838258730, 0.405845151377397166906606412076961, 0.207784955007898467600689403773245, 0.000000000000000000000000000000000 /* xgk[1], xgk[3], ... abscissae of the 7-point gauss rule. xgk[0], xgk[2], ... to optimally extend the 7-point gauss rule */ }; static const double wg[4] = { /* weights of the 7-point gauss rule */ 0.129484966168869693270611432679082, 0.279705391489276667901467771423780, 0.381830050505118944950369775488975, 0.417959183673469387755102040816327 }; static const double wgk[8] = { /* weights of the 15-point kronrod rule */ 0.022935322010529224963732008058970, 0.063092092629978553290700663189204, 0.104790010322250183839876322541518, 0.140653259715525918745189590510238, 0.169004726639267902826583426598550, 0.190350578064785409913256402421014, 0.204432940075298892414161999234649, 0.209482141084727828012999174891714 }; unsigned int j, k, iR, npts = 0; double *pts, *vals; if (alloc_rule_pts(r, nR)) return FAILURE; pts = r->pts; vals = r->vals; for (iR = 0; iR < nR; ++iR) { const double center = R[iR].h.data[0]; const double halfwidth = R[iR].h.data[1]; pts[npts++] = center; for (j = 0; j < (n - 1) / 2; ++j) { int j2 = 2 * j + 1; double w = halfwidth * xgk[j2]; pts[npts++] = center - w; pts[npts++] = center + w; } for (j = 0; j < n / 2; ++j) { int j2 = 2 * j; double w = halfwidth * xgk[j2]; pts[npts++] = center - w; pts[npts++] = center + w; } R[iR].splitDim = 0; /* no choice but to divide 0th dimension */ } f(1, npts, pts, fdata, fdim, vals); for (k = 0; k < fdim; ++k) { for (iR = 0; iR < nR; ++iR) { const double halfwidth = R[iR].h.data[1]; double result_gauss = vals[0] * wg[n / 2 - 1]; double result_kronrod = vals[0] * wgk[n - 1]; double result_abs = fabs(result_kronrod); double result_asc, mean, err; /* accumulate integrals */ npts = 1; for (j = 0; j < (n - 1) / 2; ++j) { int j2 = 2 * j + 1; double v = vals[npts] + vals[npts + 1]; result_gauss += wg[j] * v; result_kronrod += wgk[j2] * v; result_abs += wgk[j2] * (fabs(vals[npts]) + fabs(vals[npts + 1])); npts += 2; } for (j = 0; j < n / 2; ++j) { int j2 = 2 * j; result_kronrod += wgk[j2] * (vals[npts] + vals[npts + 1]); result_abs += wgk[j2] * (fabs(vals[npts]) + fabs(vals[npts + 1])); npts += 2; } /* integration result */ R[iR].ee[k].val = result_kronrod * halfwidth; /* error estimate (from GSL, probably dates back to QUADPACK ... not completely clear to me why we don't just use fabs(result_kronrod - result_gauss) * halfwidth */ mean = result_kronrod * 0.5; result_asc = wgk[n - 1] * fabs(vals[0] - mean); npts = 1; for (j = 0; j < (n - 1) / 2; ++j) { int j2 = 2 * j + 1; result_asc += wgk[j2] * (fabs(vals[npts] - mean) + fabs(vals[npts + 1] - mean)); npts += 2; } for (j = 0; j < n / 2; ++j) { int j2 = 2 * j; result_asc += wgk[j2] * (fabs(vals[npts] - mean) + fabs(vals[npts + 1] - mean)); npts += 2; } err = fabs(result_kronrod - result_gauss) * halfwidth; result_abs *= halfwidth; result_asc *= halfwidth; if (result_asc != 0 && err != 0) { double scale = pow((200 * err / result_asc), 1.5); err = (scale < 1) ? result_asc * scale : result_asc; } if (result_abs > DBL_MIN / (50 * DBL_EPSILON)) { double min_err = 50 * DBL_EPSILON * result_abs; if (min_err > err) err = min_err; } R[iR].ee[k].err = err; /* increment vals to point to next batch of results */ vals += 15; } } return SUCCESS; } static rule* make_rule15gauss(unsigned int dim, unsigned int fdim) { if (dim != 1) return nullptr; /* this rule is only for 1d integrals */ return make_rule(sizeof(rule), dim, fdim, 15, rule15gauss_evalError, nullptr); } /***************************************************************************/ /* binary heap implementation (ala _Introduction to Algorithms_ by Cormen, Leiserson, and Rivest), for use as a priority queue of regions to integrate. */ using heap_item = region; #define KEY(hi) ((hi).errmax) using heap = struct { unsigned int n, nalloc; heap_item* items; unsigned int fdim; esterr* ee; /* array of length fdim of the total integrand & error */ }; static void heap_resize(heap* h, unsigned int nalloc) { h->nalloc = nalloc; h->items = (heap_item*)realloc(h->items, sizeof(heap_item) * nalloc); } static heap heap_alloc(unsigned int nalloc, unsigned int fdim) { heap h; unsigned int i; h.n = 0; h.nalloc = 0; h.items = nullptr; h.fdim = fdim; h.ee = (esterr*)malloc(sizeof(esterr) * fdim); if (h.ee) { for (i = 0; i < fdim; ++i) h.ee[i].val = h.ee[i].err = 0; heap_resize(&h, nalloc); } return h; } /* note that heap_free does not deallocate anything referenced by the items */ static void heap_free(heap* h) { h->n = 0; heap_resize(h, 0); h->fdim = 0; free(h->ee); } static int heap_push(heap* h, heap_item hi) { int insert; unsigned int i, fdim = h->fdim; for (i = 0; i < fdim; ++i) { h->ee[i].val += hi.ee[i].val; h->ee[i].err += hi.ee[i].err; } insert = h->n; if (++(h->n) > h->nalloc) { heap_resize(h, h->n * 2); if (!h->items) return FAILURE; } while (insert) { int parent = (insert - 1) / 2; if (KEY(hi) <= KEY(h->items[parent])) break; h->items[insert] = h->items[parent]; insert = parent; } h->items[insert] = hi; return SUCCESS; } static int heap_push_many(heap* h, unsigned int ni, heap_item* hi) { unsigned int i; for (i = 0; i < ni; ++i) if (heap_push(h, hi[i])) return FAILURE; return SUCCESS; } static heap_item heap_pop(heap* h) { heap_item ret; int i, n, child; if (!(h->n)) { fprintf(stderr, "attempted to pop an empty heap\n"); return ret; // error } ret = h->items[0]; h->items[i = 0] = h->items[n = --(h->n)]; while ((child = i * 2 + 1) < n) { int largest; heap_item swap; if (KEY(h->items[child]) <= KEY(h->items[i])) largest = i; else largest = child; if (++child < n && KEY(h->items[largest]) < KEY(h->items[child])) largest = child; if (largest == i) break; swap = h->items[i]; h->items[i] = h->items[largest]; h->items[i = largest] = swap; } { unsigned int i_, fdim = h->fdim; for (i_ = 0; i_ < fdim; ++i_) { h->ee[i_].val -= ret.ee[i_].val; h->ee[i_].err -= ret.ee[i_].err; } } return ret; } /***************************************************************************/ /* adaptive integration, analogous to adaptintegrator.cpp in HIntLib */ static int ruleadapt_integrate(rule* r, unsigned int fdim, integrand_v f, void* fdata, const hypercube* h, unsigned int maxEval, double reqAbsError, double reqRelError, double* val, double* err, int parallel) { unsigned int numEval = 0; heap regions; unsigned int i, j; region* R = nullptr; /* array of regions to evaluate */ unsigned int nR_alloc = 0; esterr* ee = nullptr; regions = heap_alloc(1, fdim); if (!regions.ee || !regions.items) goto bad; ee = (esterr*)malloc(sizeof(esterr) * fdim); if (!ee) goto bad; nR_alloc = 2; R = (region*)malloc(sizeof(region) * nR_alloc); if (!R) goto bad; R[0] = make_region(h, fdim); if (!R[0].ee || eval_regions(1, R, f, fdata, r) || heap_push(®ions, R[0])) goto bad; numEval += r->num_points; while (numEval < maxEval || !maxEval) { for (j = 0; j < fdim && (regions.ee[j].err <= reqAbsError || relError(regions.ee[j]) <= reqRelError); ++j) ; if (j == fdim) break; /* convergence */ if (parallel) { /* maximize potential parallelism */ /* adapted from I. Gladwell, "Vectorization of one dimensional quadrature codes," pp. 230--238 in _Numerical Integration. Recent Developments, Software and Applications_, G. Fairweather and P. M. Keast, eds., NATO ASI Series C203, Dordrecht (1987), as described in J. M. Bull and T. L. Freeman, "Parallel Globally Adaptive Algorithms for Multi-dimensional Integration," http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.42.6638 (1994). Basically, this evaluates in one shot all regions that *must* be evaluated in order to reduce the error to the requested bound: the minimum set of largest-error regions whose errors push the total error over the bound. [Note: Bull and Freeman claim that the Gladwell approach is intrinsically inefficient because it "requires sorting", and propose an alternative algorithm that "only" requires three passes over the entire set of regions. Apparently, they didn't realize that one could use a heap data structure, in which case the time to pop K biggest-error regions out of N is only O(K log N), much better than the O(N) cost of the Bull and Freeman algorithm if K << N, and it is also much simpler.] */ unsigned int nR = 0; for (j = 0; j < fdim; ++j) ee[j] = regions.ee[j]; do { if (nR + 2 > nR_alloc) { nR_alloc = (nR + 2) * 2; R = (region*)realloc(R, nR_alloc * sizeof(region)); if (!R) goto bad; } R[nR] = heap_pop(®ions); for (j = 0; j < fdim; ++j) ee[j].err -= R[nR].ee[j].err; if (cut_region(R + nR, R + nR + 1)) goto bad; numEval += r->num_points * 2; nR += 2; for (j = 0; j < fdim && (ee[j].err <= reqAbsError || relError(ee[j]) <= reqRelError); ++j) ; if (j == fdim) break; /* other regions have small errs */ } while (regions.n > 0 && (numEval < maxEval || !maxEval)); if (eval_regions(nR, R, f, fdata, r) || heap_push_many(®ions, nR, R)) goto bad; } else { /* minimize number of function evaluations */ R[0] = heap_pop(®ions); /* get worst region */ if (cut_region(R, R + 1) || eval_regions(2, R, f, fdata, r) || heap_push_many(®ions, 2, R)) goto bad; numEval += r->num_points * 2; } } /* re-sum integral and errors */ for (j = 0; j < fdim; ++j) val[j] = err[j] = 0; for (i = 0; i < regions.n; ++i) { for (j = 0; j < fdim; ++j) { val[j] += regions.items[i].ee[j].val; err[j] += regions.items[i].ee[j].err; } destroy_region(®ions.items[i]); } /* printf("regions.nalloc = %d\n", regions.nalloc); */ free(ee); heap_free(®ions); free(R); return SUCCESS; bad: free(ee); heap_free(®ions); free(R); return FAILURE; } static int integrate(unsigned int fdim, integrand_v f, void* fdata, unsigned int dim, const double* xmin, const double* xmax, unsigned int maxEval, double reqAbsError, double reqRelError, double* val, double* err, int parallel) { rule* r; hypercube h; int status; unsigned int i; if (fdim == 0) /* nothing to do */ return SUCCESS; if (dim == 0) { /* trivial integration */ f(0, 1, xmin, fdata, fdim, val); for (i = 0; i < fdim; ++i) err[i] = 0; return SUCCESS; } r = dim == 1 ? make_rule15gauss(dim, fdim) : make_rule75genzmalik(dim, fdim); if (!r) { for (i = 0; i < fdim; ++i) { val[i] = 0; err[i] = HUGE_VAL; } return FAILURE; } h = make_hypercube_range(dim, xmin, xmax); status = !h.data ? FAILURE : ruleadapt_integrate(r, fdim, f, fdata, &h, maxEval, reqAbsError, reqRelError, val, err, parallel); destroy_hypercube(&h); destroy_rule(r); return status; } int adapt_integrate_v(unsigned int fdim, integrand_v f, void* fdata, unsigned int dim, const double* xmin, const double* xmax, unsigned int maxEval, double reqAbsError, double reqRelError, double* val, double* err) { return integrate(fdim, f, fdata, dim, xmin, xmax, maxEval, reqAbsError, reqRelError, val, err, 1); } /* wrapper around non-vectorized integrand */ using fv_data = struct fv_data_s { integrand f; void* fdata; double* fval1; }; static void fv(unsigned int ndim, unsigned int npt, const double* x, void* d_, unsigned int fdim, double* fval) { auto* d = (fv_data*)d_; double* fval1 = d->fval1; unsigned int i, k; /* printf("npt = %u\n", npt); */ for (i = 0; i < npt; ++i) { d->f(ndim, x + i * ndim, d->fdata, fdim, fval1); for (k = 0; k < fdim; ++k) fval[k * npt + i] = fval1[k]; } } int adapt_integrate(unsigned int fdim, integrand f, void* fdata, unsigned int dim, const double* xmin, const double* xmax, unsigned int maxEval, double reqAbsError, double reqRelError, double* val, double* err) { int ret; fv_data d; if (fdim == 0) return SUCCESS; /* nothing to do */ d.f = f; d.fdata = fdata; d.fval1 = (double*)malloc(sizeof(double) * fdim); if (!d.fval1) { unsigned int i; for (i = 0; i < fdim; ++i) { val[i] = 0; err[i] = HUGE_VAL; } return -2; /* ERROR */ } ret = integrate(fdim, fv, &d, dim, xmin, xmax, maxEval, reqAbsError, reqRelError, val, err, 0); free(d.fval1); return ret; } // TODO: Consider QVariantList. For now, mimic what is known to work. QList QTAIMEvaluateProperty(QList variantList) { /* Order of variantList: QString wfnFileName qreal x0 qreal y0 qreal z0 qint64 nncp qint64 xncp1 qint64 yncp1 qint64 zncp1 qint64 xncp2 qint64 yncp2 qint64 zncp2 ... qint64 nmode qint64 mode1 qint64 mode2 ... qint64 nbasin qint64 basin1 qint64 basin2 ... */ qint64 counter = 0; QString wfnFileName = variantList.at(counter).toString(); counter++; qreal x0 = variantList.at(counter).toDouble(); counter++; qreal y0 = variantList.at(counter).toDouble(); counter++; qreal z0 = variantList.at(counter).toDouble(); counter++; qint64 nncp = variantList.at(counter).toLongLong(); counter++; QList ncpList; for (qint64 n = 0; n < nncp; ++n) { qreal x = variantList.at(counter).toDouble(); counter++; qreal y = variantList.at(counter).toDouble(); counter++; qreal z = variantList.at(counter).toDouble(); counter++; ncpList.append(QVector3D(x, y, z)); } qint64 nmode = variantList.at(counter).toLongLong(); counter++; QList modeList; for (qint64 m = 0; m < nmode; ++m) { qint64 mode = variantList.at(counter).toLongLong(); counter++; modeList.append(mode); } qint64 nbasin = variantList.at(counter).toLongLong(); counter++; QList basinList; for (qint64 b = 0; b < nbasin; ++b) { qint64 basin = variantList.at(counter).toLongLong(); counter++; basinList.append(basin); } QSet basinSet(basinList.begin(), basinList.end()); QTAIMWavefunction wfn; wfn.loadFromBinaryFile(wfnFileName); QTAIMWavefunctionEvaluator eval(wfn); QList valueList; double initialElectronDensity = eval.electronDensity(Eigen::Vector3d(x0, y0, z0)); // if less than some small value, then return zero for all integrands. if (initialElectronDensity < 1.e-5) { for (qint64 m = 0; m < nmode; ++m) { qreal zero = 0.0; valueList.append(zero); } } else { QList> betaSpheres; for (qint64 i = 0; i < nncp; ++i) { QPair thisBetaSphere; thisBetaSphere.first = QVector3D(ncpList.at(i).x(), ncpList.at(i).y(), ncpList.at(i).z()); thisBetaSphere.second = 0.10; betaSpheres.append(thisBetaSphere); } QTAIMLSODAIntegrator ode(eval, 0); // Avogadro::QTAIMODEIntegrator ode(eval,0); ode.setBetaSpheres(betaSpheres); QVector3D endpoint = ode.integrate(QVector3D(x0, y0, z0)); // QList path=ode.path(); #define HUGE_REAL_NUMBER 1.e20 qreal smallestDistance = HUGE_REAL_NUMBER; qint64 smallestDistanceIndex = -1; for (qint64 n = 0; n < betaSpheres.length(); ++n) { Matrix a(endpoint.x(), endpoint.y(), endpoint.z()); Matrix b(betaSpheres.at(n).first.x(), betaSpheres.at(n).first.y(), betaSpheres.at(n).first.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < smallestDistance) { smallestDistance = distance; smallestDistanceIndex = n; } } qint64 nucleusIndex = smallestDistanceIndex; if (basinSet.contains(nucleusIndex)) { // if(nucleusIndex==0) // { // QFile file("/scratch/brown/0.txt"); // file.open(QIODevice::WriteOnly | QIODevice::Append); // QTextStream out(&file); // out << x0 << " " << y0 << " " << z0 << "\n"; // file.close(); // } for (qint64 m = 0; m < nmode; ++m) { if (modeList.at(m) == 0) { valueList.append(eval.electronDensity(Eigen::Vector3d(x0, y0, z0))); } else { qDebug() << "mode not defined"; qreal zero = 0.0; valueList.append(zero); } } } else { for (qint64 m = 0; m < nmode; ++m) { qreal zero = 0.0; valueList.append(zero); } } } return valueList; } void property_v(unsigned int /* ndim */, unsigned int npts, const double* xyz, void* param, unsigned int /* dim */, double* fval) { auto* paramVariantListPtr = (QVariantList*)param; QVariantList paramVariantList = *paramVariantListPtr; qint64 counter = 0; QString wfnFileName = paramVariantList.at(counter).toString(); counter++; qint64 nncp = paramVariantList.at(counter).toLongLong(); counter++; QList ncpList; for (qint64 i = 0; i < nncp; ++i) { qreal x = paramVariantList.at(counter).toDouble(); counter++; qreal y = paramVariantList.at(counter).toDouble(); counter++; qreal z = paramVariantList.at(counter).toDouble(); counter++; ncpList.append(QVector3D(x, y, z)); } qint64 nmode = 1; qint64 mode = paramVariantList.at(counter).toLongLong(); counter++; QList basinList; for (qint64 i = counter; i < paramVariantList.length(); ++i) { basinList.append(paramVariantList.at(i).toLongLong()); counter++; } // prepare input QList> inputList; for (unsigned int i = 0; i < npts; ++i) { double x0 = xyz[i * 3 + 0]; double y0 = xyz[i * 3 + 1]; double z0 = xyz[i * 3 + 2]; QList variantList; variantList.append(wfnFileName); variantList.append(x0); variantList.append(y0); variantList.append(z0); variantList.append(nncp); for (qint64 n = 0; n < nncp; ++n) { variantList.append(ncpList.at(n).x()); variantList.append(ncpList.at(n).y()); variantList.append(ncpList.at(n).z()); } variantList.append(nmode); // for now, one mode for (qint64 m = 0; m < nmode; ++m) { variantList.append(mode); } qint64 nbasin = basinList.length(); variantList.append(nbasin); for (long long b : basinList) { variantList.append(b); } inputList.append(variantList); } // calculate QProgressDialog dialog; dialog.setWindowTitle("QTAIM"); dialog.setLabelText(QString("Atomic Basin Integration")); QFutureWatcher> futureWatcher; QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); QFuture> future = QtConcurrent::mapped(inputList, QTAIMEvaluateProperty); futureWatcher.setFuture(future); dialog.exec(); futureWatcher.waitForFinished(); QList> results; if (futureWatcher.future().isCanceled()) { results.clear(); } else { results = future.results(); } // harvest results for (qint64 i = 0; i < npts; ++i) { for (qint64 m = 0; m < nmode; ++m) { fval[m * nmode + i] = results.at(i).at(m).toDouble(); } } } // TODO: Consider QVariantList. For now, mimic what is known to work. // This version performs integration in Spherical Polar Coordinates. // Note that the basin limits are not explicitly determined. QList QTAIMEvaluatePropertyRTP(QList variantList) { /* Order of variantList: QString wfnFileName qreal r0 qreal t0 qreal p0 qint64 nncp qint64 xncp1 qint64 yncp1 qint64 zncp1 qint64 xncp2 qint64 yncp2 qint64 zncp2 ... qint64 nmode qint64 mode1 qint64 mode2 ... qint64 nbasin qint64 basin1 qint64 basin2 ... */ qint64 counter = 0; QString wfnFileName = variantList.at(counter).toString(); counter++; qreal r0 = variantList.at(counter).toDouble(); counter++; qreal t0 = variantList.at(counter).toDouble(); counter++; qreal p0 = variantList.at(counter).toDouble(); counter++; qint64 nncp = variantList.at(counter).toLongLong(); counter++; QList ncpList; for (qint64 n = 0; n < nncp; ++n) { qreal x = variantList.at(counter).toDouble(); counter++; qreal y = variantList.at(counter).toDouble(); counter++; qreal z = variantList.at(counter).toDouble(); counter++; ncpList.append(QVector3D(x, y, z)); } qint64 nmode = variantList.at(counter).toLongLong(); counter++; QList modeList; for (qint64 m = 0; m < nmode; ++m) { qint64 mode = variantList.at(counter).toLongLong(); counter++; modeList.append(mode); } qint64 nbasin = variantList.at(counter).toLongLong(); counter++; QList basinList; for (qint64 b = 0; b < nbasin; ++b) { qint64 basin = variantList.at(counter).toLongLong(); counter++; basinList.append(basin); } QSet basinSet(basinList.begin(), basinList.end()); Matrix r0t0p0; r0t0p0 << r0, t0, p0; Matrix origin; origin << ncpList.at(basinList.at(0)).x(), ncpList.at(basinList.at(0)).y(), ncpList.at(basinList.at(0)).z(); Matrix x0y0z0 = QTAIMMathUtilities::sphericalToCartesian(r0t0p0, origin); qreal x0 = x0y0z0(0); qreal y0 = x0y0z0(1); qreal z0 = x0y0z0(2); QTAIMWavefunction wfn; wfn.loadFromBinaryFile(wfnFileName); QTAIMWavefunctionEvaluator eval(wfn); QList valueList; double initialElectronDensity = eval.electronDensity(Eigen::Vector3d(x0, y0, z0)); // if less than some small value, then return zero for all integrands. if (initialElectronDensity < 1.e-5) { for (qint64 m = 0; m < nmode; ++m) { qreal zero = 0.0; valueList.append(zero); } } else { QList> betaSpheres; for (qint64 i = 0; i < nncp; ++i) { QPair thisBetaSphere; thisBetaSphere.first = QVector3D(ncpList.at(i).x(), ncpList.at(i).y(), ncpList.at(i).z()); thisBetaSphere.second = 0.10; betaSpheres.append(thisBetaSphere); } QTAIMLSODAIntegrator ode(eval, 0); // Avogadro::QTAIMODEIntegrator ode(eval,0); ode.setBetaSpheres(betaSpheres); QVector3D endpoint = ode.integrate(QVector3D(x0, y0, z0)); // QList path=ode.path(); #define HUGE_REAL_NUMBER 1.e20 qreal smallestDistance = HUGE_REAL_NUMBER; qint64 smallestDistanceIndex = -1; for (qint64 n = 0; n < betaSpheres.length(); ++n) { Matrix a(endpoint.x(), endpoint.y(), endpoint.z()); Matrix b(betaSpheres.at(n).first.x(), betaSpheres.at(n).first.y(), betaSpheres.at(n).first.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < smallestDistance) { smallestDistance = distance; smallestDistanceIndex = n; } } qint64 nucleusIndex = smallestDistanceIndex; if (basinSet.contains(nucleusIndex)) { // if(nucleusIndex==0) // { // QFile file("/scratch/brown/0.txt"); // file.open(QIODevice::WriteOnly | QIODevice::Append); // QTextStream out(&file); // out << x0 << " " << y0 << " " << z0 << "\n"; //// out << r0 << " " << t0 << " " << p0 << "\n"; // file.close(); // } for (qint64 m = 0; m < nmode; ++m) { if (modeList.at(m) == 0) { valueList.append( r0 * r0 * sin(t0) * eval.electronDensity(Eigen::Vector3d(x0, y0, z0)) ); } else { qDebug() << "mode not defined"; qreal zero = 0.0; valueList.append(zero); } } } else { for (qint64 m = 0; m < nmode; ++m) { qreal zero = 0.0; valueList.append(zero); } } } return valueList; } void property_v_rtp(unsigned int /* ndim */, unsigned int npts, const double* xyz, void* param, unsigned int /* fdim */, double* fval) { auto* paramVariantListPtr = (QVariantList*)param; QVariantList paramVariantList = *paramVariantListPtr; qint64 counter = 0; QString wfnFileName = paramVariantList.at(counter).toString(); counter++; qint64 nncp = paramVariantList.at(counter).toLongLong(); counter++; QList ncpList; for (qint64 i = 0; i < nncp; ++i) { qreal x = paramVariantList.at(counter).toDouble(); counter++; qreal y = paramVariantList.at(counter).toDouble(); counter++; qreal z = paramVariantList.at(counter).toDouble(); counter++; ncpList.append(QVector3D(x, y, z)); } qint64 nmode = 1; qint64 mode = paramVariantList.at(counter).toLongLong(); counter++; QList basinList; for (qint64 i = counter; i < paramVariantList.length(); ++i) { basinList.append(paramVariantList.at(i).toLongLong()); counter++; } // prepare input QList> inputList; for (unsigned int i = 0; i < npts; ++i) { double x0 = xyz[i * 3 + 0]; double y0 = xyz[i * 3 + 1]; double z0 = xyz[i * 3 + 2]; QList variantList; variantList.append(wfnFileName); variantList.append(x0); variantList.append(y0); variantList.append(z0); variantList.append(nncp); for (qint64 n = 0; n < nncp; ++n) { variantList.append(ncpList.at(n).x()); variantList.append(ncpList.at(n).y()); variantList.append(ncpList.at(n).z()); } variantList.append(nmode); // for now, one mode for (qint64 m = 0; m < nmode; ++m) { variantList.append(mode); } qint64 nbasin = basinList.length(); variantList.append(nbasin); for (long long b : basinList) { variantList.append(b); } inputList.append(variantList); } // calculate QProgressDialog dialog; dialog.setWindowTitle("QTAIM"); dialog.setLabelText(QString("Atomic Basin Integration")); QFutureWatcher> futureWatcher; QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); QFuture> future = QtConcurrent::mapped(inputList, QTAIMEvaluatePropertyRTP); futureWatcher.setFuture(future); dialog.exec(); futureWatcher.waitForFinished(); QList> results; if (futureWatcher.future().isCanceled()) { results.clear(); } else { results = future.results(); } // harvest results for (qint64 i = 0; i < npts; ++i) { for (qint64 m = 0; m < nmode; ++m) { fval[m * nmode + i] = results.at(i).at(m).toDouble(); } } } void property_r(unsigned int ndim, const double* xyz, void* param, unsigned int fdim, double* fval) { ndim = ndim; fdim = fdim; auto* paramVariantListPtr = (QVariantList*)param; QVariantList paramVariantList = *paramVariantListPtr; qint64 counter = 0; QString wfnFileName = paramVariantList.at(counter).toString(); counter++; qreal r = xyz[0]; qreal t = paramVariantList.at(counter).toDouble(); counter++; qreal p = paramVariantList.at(counter).toDouble(); counter++; qint64 nncp = paramVariantList.at(counter).toLongLong(); counter++; QList ncpList; for (qint64 i = 0; i < nncp; ++i) { qreal x = paramVariantList.at(counter).toDouble(); counter++; qreal y = paramVariantList.at(counter).toDouble(); counter++; qreal z = paramVariantList.at(counter).toDouble(); counter++; ncpList.append(QVector3D(x, y, z)); } qint64 nmode = 1; qint64 mode = paramVariantList.at(counter).toLongLong(); counter++; QList basinList; for (qint64 i = counter; i < paramVariantList.length(); ++i) { basinList.append(paramVariantList.at(i).toLongLong()); counter++; } Matrix rtp; rtp << r, t, p; Matrix origin; origin << ncpList.at(basinList.at(0)).x(), ncpList.at(basinList.at(0)).y(), ncpList.at(basinList.at(0)).z(); Matrix XYZ = QTAIMMathUtilities::sphericalToCartesian(rtp, origin); qreal x = XYZ(0); qreal y = XYZ(1); qreal z = XYZ(2); // This routine reads the wavefunction file repeatedly. // Let's hope that this time is dwarfed by the time // taken to delineate the atomic basins in the calling routine. QTAIMWavefunction wfn; wfn.loadFromBinaryFile(wfnFileName); QTAIMWavefunctionEvaluator eval(wfn); for (qint64 m = 0; m < nmode; ++m) { if (mode == 0) { fval[m] = r * r * eval.electronDensity(Eigen::Vector3d(x, y, z)); } } } QList QTAIMEvaluatePropertyTP(QList variantList) { /* Order of variantList: QString wfnFileName qreal t qreal p qint64 nncp qint64 xncp1 qint64 yncp1 qint64 zncp1 qint64 xncp2 qint64 yncp2 qint64 zncp2 ... qint64 nmode qint64 mode1 qint64 mode2 ... qint64 nbasin qint64 basin1 qint64 basin2 ... */ qint64 counter = 0; QString wfnFileName = variantList.at(counter).toString(); counter++; qreal t = variantList.at(counter).toDouble(); counter++; qreal p = variantList.at(counter).toDouble(); counter++; qint64 nncp = variantList.at(counter).toLongLong(); counter++; QList ncpList; for (qint64 n = 0; n < nncp; ++n) { qreal x = variantList.at(counter).toDouble(); counter++; qreal y = variantList.at(counter).toDouble(); counter++; qreal z = variantList.at(counter).toDouble(); counter++; ncpList.append(QVector3D(x, y, z)); } qint64 nmode = variantList.at(counter).toLongLong(); counter++; QList modeList; for (qint64 m = 0; m < nmode; ++m) { qint64 mode = variantList.at(counter).toLongLong(); counter++; modeList.append(mode); } qint64 nbasin = variantList.at(counter).toLongLong(); counter++; QList basinList; for (qint64 b = 0; b < nbasin; ++b) { qint64 basin = variantList.at(counter).toLongLong(); counter++; basinList.append(basin); } QSet basinSet(basinList.begin(), basinList.end()); QTAIMWavefunction wfn; wfn.loadFromBinaryFile(wfnFileName); QTAIMWavefunctionEvaluator eval(wfn); // Set up steepest ascent integrator and beta spheres QList> betaSpheres; for (qint64 i = 0; i < nncp; ++i) { QPair thisBetaSphere; thisBetaSphere.first = QVector3D(ncpList.at(i).x(), ncpList.at(i).y(), ncpList.at(i).z()); thisBetaSphere.second = 0.10; betaSpheres.append(thisBetaSphere); } QTAIMLSODAIntegrator ode(eval, 0); // Avogadro::QTAIMODEIntegrator ode(eval,0); ode.setBetaSpheres(betaSpheres); // Determine radial basin limit via bisection // Bisection Algorithm courtesy of Wikipedia qint64 thisBasin = basinList.at(0); Matrix origin; origin << ncpList.at(thisBasin).x(), ncpList.at(thisBasin).y(), ncpList.at(thisBasin).z(); const qreal rmin = betaSpheres.at(thisBasin).second; const qreal rmax = 8.0; const qreal epsilon = 1.e-3; qreal left = rmin; qreal right = rmax; Matrix rtpl; rtpl << left, t, p; Matrix xyzl = QTAIMMathUtilities::sphericalToCartesian(rtpl, origin); qreal fleft; qreal x = xyzl(0); qreal y = xyzl(1); qreal z = xyzl(2); qreal leftElectronDensity = eval.electronDensity(Eigen::Vector3d(x, y, z)); if (leftElectronDensity < 1.e-5) { fleft = -1.0; } else { QVector3D endpoint = ode.integrate(QVector3D(x, y, z)); #define HUGE_REAL_NUMBER 1.e20 qreal smallestDistance = HUGE_REAL_NUMBER; qint64 smallestDistanceIndex = -1; for (qint64 n = 0; n < betaSpheres.length(); ++n) { Matrix a(endpoint.x(), endpoint.y(), endpoint.z()); Matrix b(betaSpheres.at(n).first.x(), betaSpheres.at(n).first.y(), betaSpheres.at(n).first.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < smallestDistance) { smallestDistance = distance; smallestDistanceIndex = n; } } qint64 nucleusIndex = smallestDistanceIndex; if (thisBasin == nucleusIndex) { fleft = leftElectronDensity; } else { fleft = -1.0; } } Matrix rtpr; rtpr << right, t, p; Matrix xyzr = QTAIMMathUtilities::sphericalToCartesian(rtpr, origin); qreal fright; x = xyzr(0); y = xyzr(1); z = xyzr(2); qreal rightElectronDensity = eval.electronDensity(Eigen::Vector3d(x, y, z)); if (rightElectronDensity < 1.e-5) { fright = -1.0; } else { QVector3D endpoint = ode.integrate(QVector3D(x, y, z)); #define HUGE_REAL_NUMBER 1.e20 qreal smallestDistance = HUGE_REAL_NUMBER; qint64 smallestDistanceIndex = -1; for (qint64 n = 0; n < betaSpheres.length(); ++n) { Matrix a(endpoint.x(), endpoint.y(), endpoint.z()); Matrix b(betaSpheres.at(n).first.x(), betaSpheres.at(n).first.y(), betaSpheres.at(n).first.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < smallestDistance) { smallestDistance = distance; smallestDistanceIndex = n; } } qint64 nucleusIndex = smallestDistanceIndex; if (thisBasin == nucleusIndex) { fright = rightElectronDensity; } else { fright = -1.0; } } if (fleft > 0.0 && fright > 0.0) { qDebug() << "error in bisection: both values positive."; } qreal rf(0.0); while (fabs(right - left) > 2.0 * epsilon) { qreal midpoint = (right + left) / 2.0; rf = midpoint; // qDebug() << left << midpoint << right ; Matrix rtpm; rtpm << midpoint, t, p; Matrix xyzm = QTAIMMathUtilities::sphericalToCartesian(rtpm, origin); qreal fmidpoint; x = xyzm(0); y = xyzm(1); z = xyzm(2); qreal midpointElectronDensity = eval.electronDensity(Eigen::Vector3d(x, y, z)); if (midpointElectronDensity < 1.e-5) { fmidpoint = -1.0; } else { QVector3D endpoint = ode.integrate(QVector3D(x, y, z)); #define HUGE_REAL_NUMBER 1.e20 qreal smallestDistance = HUGE_REAL_NUMBER; qint64 smallestDistanceIndex = -1; for (qint64 n = 0; n < betaSpheres.length(); ++n) { Matrix a(endpoint.x(), endpoint.y(), endpoint.z()); Matrix b(betaSpheres.at(n).first.x(), betaSpheres.at(n).first.y(), betaSpheres.at(n).first.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < smallestDistance) { smallestDistance = distance; smallestDistanceIndex = n; } } qint64 nucleusIndex = smallestDistanceIndex; if (thisBasin == nucleusIndex) { fmidpoint = midpointElectronDensity; } else { fmidpoint = -1.0; } } if ((fleft * fmidpoint) < 0) { right = midpoint; fright = fmidpoint; } else if ((fright * fmidpoint) < 0) { left = midpoint; fleft = fmidpoint; } else { goto endOfBisection; } } endOfBisection: // Integration over r unsigned int fdim = 1; double* val; double* err; val = (double*)malloc(sizeof(double) * fdim); err = (double*)malloc(sizeof(double) * fdim); double tol = 1.e-6; unsigned int maxEval = 0; unsigned int dim = 1; double* xmin; double* xmax; xmin = (double*)malloc(dim * sizeof(double)); xmax = (double*)malloc(dim * sizeof(double)); xmin[0] = 0.0; xmax[0] = rf; QVariantList paramVariantList; paramVariantList.append(wfnFileName); paramVariantList.append(t); paramVariantList.append(p); paramVariantList.append( ncpList.length()); // number of nuclear critical points for (auto j : ncpList) { paramVariantList.append(j.x()); paramVariantList.append(j.y()); paramVariantList.append(j.z()); } paramVariantList.append(0); // mode paramVariantList.append(basinList.at(0)); // basin // qDebug() << "Into R with rf=" << rf; adapt_integrate(fdim, property_r, ¶mVariantList, dim, xmin, xmax, maxEval, tol, 0, val, err); // qDebug() << "Out of R with val=" << val[0] << "err=" << err[0]; qreal Rval = val[0]; free(xmin); free(xmax); free(val); free(err); // QList variantList; variantList.append(sin(t) * Rval); // qDebug() << rf << t << p << sin(t) * Rval; return variantList; } void property_v_tp(unsigned int /* ndim */, unsigned int npts, const double* xyz, void* param, unsigned int /* fdim */, double* fval) { auto* paramVariantListPtr = (QVariantList*)param; QVariantList paramVariantList = *paramVariantListPtr; qint64 counter = 0; QString wfnFileName = paramVariantList.at(counter).toString(); counter++; qint64 nncp = paramVariantList.at(counter).toLongLong(); counter++; QList ncpList; for (qint64 i = 0; i < nncp; ++i) { qreal x = paramVariantList.at(counter).toDouble(); counter++; qreal y = paramVariantList.at(counter).toDouble(); counter++; qreal z = paramVariantList.at(counter).toDouble(); counter++; ncpList.append(QVector3D(x, y, z)); } qint64 nmode = 1; qint64 mode = paramVariantList.at(counter).toLongLong(); counter++; QList basinList; for (qint64 i = counter; i < paramVariantList.length(); ++i) { basinList.append(paramVariantList.at(i).toLongLong()); counter++; } // prepare input QList> inputList; for (unsigned int i = 0; i < npts; ++i) { double t = xyz[i * 2 + 0]; double p = xyz[i * 2 + 1]; QList variantList; variantList.append(wfnFileName); variantList.append(t); variantList.append(p); variantList.append(nncp); for (qint64 n = 0; n < nncp; ++n) { variantList.append(ncpList.at(n).x()); variantList.append(ncpList.at(n).y()); variantList.append(ncpList.at(n).z()); } variantList.append(nmode); // for now, one mode for (qint64 m = 0; m < nmode; ++m) { variantList.append(mode); } qint64 nbasin = basinList.length(); variantList.append(nbasin); for (long long b : basinList) { variantList.append(b); } inputList.append(variantList); } // calculate QProgressDialog dialog; dialog.setWindowTitle("QTAIM"); dialog.setLabelText(QString("Atomic Basin Integration")); QFutureWatcher> futureWatcher; QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); QFuture> future = QtConcurrent::mapped(inputList, QTAIMEvaluatePropertyTP); futureWatcher.setFuture(future); dialog.exec(); futureWatcher.waitForFinished(); QList> results; if (futureWatcher.future().isCanceled()) { results.clear(); } else { results = future.results(); } // harvest results // qDebug() << "results=" << results; for (qint64 i = 0; i < npts; ++i) { for (qint64 m = 0; m < nmode; ++m) { fval[m * nmode + i] = results.at(i).at(m).toDouble(); } } } namespace Avogadro::QtPlugins { QTAIMCubature::QTAIMCubature(QTAIMWavefunction& wfn) { m_wfn = &wfn; m_temporaryFileName = QTAIMCubature::temporaryFileName(); m_wfn->saveToBinaryFile(m_temporaryFileName); // Instantiate a Critical Point Locator QTAIMCriticalPointLocator cpl(wfn); // Locate the Nuclear Critical Points cpl.locateNuclearCriticalPoints(); // QLists of results m_ncpList = cpl.nuclearCriticalPoints(); } QList> QTAIMCubature::integrate(qint64 mode, QList basins) { QList> value; m_mode = mode; m_basins = basins; double tol = 1.e-2; unsigned int maxEval = 0; bool threeDimensionalIntegration = false; bool cartesianIntegrationLimits = false; unsigned int fdim = 1; double* val; double* err; val = (double*)malloc(sizeof(double) * fdim); err = (double*)malloc(sizeof(double) * fdim); for (qint64 i = 0; i < m_basins.length(); ++i) { if (threeDimensionalIntegration) { unsigned int dim = 3; double* xmin; double* xmax; xmin = (double*)malloc(dim * sizeof(double)); xmax = (double*)malloc(dim * sizeof(double)); if (cartesianIntegrationLimits) { // shift origin of the integration to the nuclear coordinates of the ith // nucleus. xmin[0] = -8. + m_ncpList.at(i).x(); xmax[0] = 8. + m_ncpList.at(i).x(); xmin[1] = -8. + m_ncpList.at(i).y(); xmax[1] = 8. + m_ncpList.at(i).y(); xmin[2] = -8. + m_ncpList.at(i).z(); xmax[2] = 8. + m_ncpList.at(i).z(); QVariantList paramVariantList; paramVariantList.append(m_temporaryFileName); paramVariantList.append( m_ncpList.length()); // number of nuclear critical points for (auto j : m_ncpList) { paramVariantList.append(j.x()); paramVariantList.append(j.y()); paramVariantList.append(j.z()); } paramVariantList.append(0); // mode paramVariantList.append(basins.at(i)); // basin adapt_integrate_v(fdim, property_v, ¶mVariantList, dim, xmin, xmax, maxEval, tol, 0, val, err); } else { const qreal pi = 4.0 * atan(1.0); xmin[0] = 0.; xmax[0] = 8.; xmin[1] = 0.; xmax[1] = pi; xmin[2] = 0.; xmax[2] = 2.0 * pi; QVariantList paramVariantList; paramVariantList.append(m_temporaryFileName); paramVariantList.append( m_ncpList.length()); // number of nuclear critical points for (auto j : m_ncpList) { paramVariantList.append(j.x()); paramVariantList.append(j.y()); paramVariantList.append(j.z()); } paramVariantList.append(0); // mode paramVariantList.append(basins.at(i)); // basin adapt_integrate_v(fdim, property_v_rtp, ¶mVariantList, dim, xmin, xmax, maxEval, tol, 0, val, err); } free(xmin); free(xmax); } else { unsigned int dim = 2; double* xmin; double* xmax; xmin = (double*)malloc(dim * sizeof(double)); xmax = (double*)malloc(dim * sizeof(double)); const qreal pi = 4.0 * atan(1.0); xmin[0] = 0.; xmax[0] = pi; xmin[1] = 0.; xmax[1] = 2.0 * pi; QVariantList paramVariantList; paramVariantList.append(m_temporaryFileName); paramVariantList.append( m_ncpList.length()); // number of nuclear critical points for (auto j : m_ncpList) { paramVariantList.append(j.x()); paramVariantList.append(j.y()); paramVariantList.append(j.z()); } paramVariantList.append(0); // mode paramVariantList.append(basins.at(i)); // basin adapt_integrate_v(fdim, property_v_tp, ¶mVariantList, dim, xmin, xmax, maxEval, tol, 0, val, err); free(xmin); free(xmax); } qDebug() << "basin=" << basins.at(i) + 1 << "value= " << val[0] << "err=" << err[0]; QPair thisPair; thisPair.first = val[0]; thisPair.second = err[0]; value.append(thisPair); } free(val); free(err); return value; } QTAIMCubature::~QTAIMCubature() { if (QFile::exists(m_temporaryFileName)) { QFile::remove(m_temporaryFileName); } } void QTAIMCubature::setMode(qint64 mode) { m_mode = mode; } QString QTAIMCubature::temporaryFileName() { QTemporaryFile temporaryFile; temporaryFile.open(); QString tempFileName = temporaryFile.fileName(); temporaryFile.close(); temporaryFile.remove(); // wait for temporary file to be deleted QDir dir; do { // Nothing } while (dir.exists(tempFileName)); return tempFileName; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimcubature.h000066400000000000000000000114521506155467400243300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the GPL v3 or later (the "License"). You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ /* Based on */ /* Adaptive multidimensional integration of a vector of integrands. * * Copyright (c) 2005-2009 Steven G. Johnson * * Portions (see comments) based on HIntLib (also distributed under * the GNU GPL, v2 or later), copyright (c) 2002-2005 Rudolf Schuerer. * (http://www.cosy.sbg.ac.at/~rschuer/hintlib/) * * Portions (see comments) based on GNU GSL (also distributed under * the GNU GPL, v2 or later), copyright (c) 1996-2000 Brian Gough. * (http://www.gnu.org/software/gsl/) * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef QTAIMCUBATURE_H #define QTAIMCUBATURE_H #include // #ifdef __cplusplus // extern "C" //{ // #endif /* __cplusplus */ /* USAGE: Call adapt_integrate with your function as described below. To compile a test program, compile cubature.c with -DTEST_INTEGRATOR as described at the end. */ /* a vector integrand - evaluates the function at the given point x (an array of length ndim) and returns the result in fval (an array of length fdim). The void* parameter is there in case you have to pass any additional data through to your function (it corresponds to the fdata parameter you pass to adapt_integrate). */ using integrand = void (*)(unsigned int, const double*, void*, unsigned int, double*); /* a vector integrand of a vector of npt points: x[i*ndim + j] is the j-th coordinate of the i-th point, and the k-th function evaluation for the i-th point is returned in fval[k*npt + i]. */ using integrand_v = void (*)(unsigned int, unsigned int, const double*, void*, unsigned int, double*); /* Integrate the function f from xmin[dim] to xmax[dim], with at most maxEval function evaluations (0 for no limit), until the given absolute or relative error is achieved. val returns the integral, and err returns the estimate for the absolute error in val; both of these are arrays of length fdim, the dimension of the vector integrand f(x). The return value of the function is 0 on success and non-zero if there was an error. */ int adapt_integrate(unsigned int fdim, integrand f, void* fdata, unsigned int dim, const double* xmin, const double* xmax, unsigned int maxEval, double reqAbsError, double reqRelError, double* val, double* err); /* as adapt_integrate, but vectorized integrand */ int adapt_integrate_v(unsigned int fdim, integrand_v f, void* fdata, unsigned int dim, const double* xmin, const double* xmax, unsigned int maxEval, double reqAbsError, double reqRelError, double* val, double* err); // #ifdef __cplusplus // } /* extern "C" */ // #endif /* __cplusplus */ #include "qtaimwavefunction.h" namespace Avogadro::QtPlugins { class QTAIMCubature { public: enum { ElectronDensity = 0, ElectronDensityLaplacian = 1 }; explicit QTAIMCubature(QTAIMWavefunction& wfn); ~QTAIMCubature(); QList> integrate(qint64 mode, QList basins); void setMode(qint64 mode); private: QTAIMWavefunction* m_wfn; qint64 m_mode; QList m_basins; QString m_temporaryFileName; QString temporaryFileName(); QList m_ncpList; }; } // namespace Avogadro::QtPlugins #endif /* QTAIMCUBATURE_H */ avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimengine.cpp000066400000000000000000000221021506155467400243100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "qtaimengine.h" #include #include #include #include #include #include #include using namespace Avogadro; using namespace Avogadro::Rendering; namespace Avogadro::QtPlugins { QTAIMEngine::QTAIMEngine(QObject* aParent) : QtGui::ScenePlugin(aParent), m_enabled(false) { m_layerManager = QtGui::PluginLayerManager(m_name); } void QTAIMEngine::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { // Create sphere/cylinder nodes. auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; geometry->addDrawable(spheres); auto* cylinders = new CylinderGeometry; geometry->addDrawable(cylinders); // Render the bond paths if (molecule.property("QTAIMFirstNCPIndexVariantList").isValid() && molecule.property("QTAIMSecondNCPIndexVariantList").isValid() && molecule.property("QTAIMLaplacianAtBondCriticalPoints").isValid() && molecule.property("QTAIMEllipticityAtBondCriticalPoints").isValid() && molecule.property("QTAIMBondPathSegmentStartIndex").isValid() && molecule.property("QTAIMBondPathSegmentEndIndex").isValid() && molecule.property("QTAIMXBondPaths").isValid() && molecule.property("QTAIMYBondPaths").isValid() && molecule.property("QTAIMZBondPaths").isValid()) { QVariant firstNCPIndexVariant = molecule.property("QTAIMFirstNCPIndexVariantList"); QVariant secondNCPIndexVariant = molecule.property("QTAIMSecondNCPIndexVariantList"); QVariant laplacianAtBondCriticalPointsVariant = molecule.property("QTAIMLaplacianAtBondCriticalPoints"); QVariant ellipticityAtBondCriticalPointsVariant = molecule.property("QTAIMEllipticityAtBondCriticalPoints"); QVariant bondPathSegmentStartIndexVariant = molecule.property("QTAIMBondPathSegmentStartIndex"); QVariant bondPathSegmentEndIndexVariant = molecule.property("QTAIMBondPathSegmentEndIndex"); QVariant xBondPathsVariant = molecule.property("QTAIMXBondPaths"); QVariant yBondPathsVariant = molecule.property("QTAIMYBondPaths"); QVariant zBondPathsVariant = molecule.property("QTAIMZBondPaths"); QVariantList firstNCPIndexVariantList = firstNCPIndexVariant.toList(); QVariantList secondNCPIndexVariantList = secondNCPIndexVariant.toList(); QVariantList laplacianAtBondCriticalPointsVariantList = laplacianAtBondCriticalPointsVariant.toList(); QVariantList ellipticityAtBondCriticalPointsVariantList = ellipticityAtBondCriticalPointsVariant.toList(); QVariantList bondPathSegmentStartIndexVariantList = bondPathSegmentStartIndexVariant.toList(); QVariantList bondPathSegmentEndIndexVariantList = bondPathSegmentEndIndexVariant.toList(); QVariantList xBondPathsVariantList = xBondPathsVariant.toList(); QVariantList yBondPathsVariantList = yBondPathsVariant.toList(); QVariantList zBondPathsVariantList = zBondPathsVariant.toList(); for (qint64 i = 0; i < firstNCPIndexVariantList.length(); ++i) { qint64 start = bondPathSegmentStartIndexVariantList.at(i).toLongLong(); qint64 end = bondPathSegmentEndIndexVariantList.at(i).toLongLong(); if (laplacianAtBondCriticalPointsVariantList.at(i).toReal() > 0.0) { const qint64 step = 4; Vector3f xyz; Vector3ub color(255, 255, 255); for (qint64 j = start; j < end - 1; j = j + step) { xyz << xBondPathsVariantList.at(j).toFloat(), yBondPathsVariantList.at(j).toFloat(), zBondPathsVariantList.at(j).toFloat(); spheres->addSphere(xyz, color, 0.025f); } } else { const qint64 step = 1; Vector3ub color(255, 255, 255); double radius = 0.025; Vector3f v1; Vector3f v2; Vector3f direction; for (qint64 j = start; j < end - 1; j = j + step) { v1 << xBondPathsVariantList.at(j).toFloat(), yBondPathsVariantList.at(j).toFloat(), zBondPathsVariantList.at(j).toFloat(); v2 << xBondPathsVariantList.at(j + 1).toFloat(), yBondPathsVariantList.at(j + 1).toFloat(), zBondPathsVariantList.at(j + 1).toFloat(); direction = v2 - v1; float length = direction.norm(); direction /= length; cylinders->addCylinder(v1, v2, radius, color); } } } // bond path } if (molecule.property("QTAIMXNuclearCriticalPoints").isValid() && molecule.property("QTAIMYNuclearCriticalPoints").isValid() && molecule.property("QTAIMZNuclearCriticalPoints").isValid()) { QVariant xNuclearCriticalPointsVariant = molecule.property("QTAIMXNuclearCriticalPoints"); QVariant yNuclearCriticalPointsVariant = molecule.property("QTAIMYNuclearCriticalPoints"); QVariant zNuclearCriticalPointsVariant = molecule.property("QTAIMZNuclearCriticalPoints"); QVariantList xNuclearCriticalPointsVariantList = xNuclearCriticalPointsVariant.toList(); QVariantList yNuclearCriticalPointsVariantList = yNuclearCriticalPointsVariant.toList(); QVariantList zNuclearCriticalPointsVariantList = zNuclearCriticalPointsVariant.toList(); if (xNuclearCriticalPointsVariantList.length() == yNuclearCriticalPointsVariantList.length() && xNuclearCriticalPointsVariantList.length() == zNuclearCriticalPointsVariantList.length()) { Vector3f xyz; Vector3ub color(255, 64, 255); for (qint64 i = 0; i < xNuclearCriticalPointsVariantList.length(); ++i) { xyz << xNuclearCriticalPointsVariantList.at(i).toFloat(), yNuclearCriticalPointsVariantList.at(i).toFloat(), zNuclearCriticalPointsVariantList.at(i).toFloat(); // map->setFromPrimitive(ncp); spheres->addSphere(xyz, color, 0.1f); } } } if (molecule.property("QTAIMXBondCriticalPoints").isValid() && molecule.property("QTAIMYBondCriticalPoints").isValid() && molecule.property("QTAIMZBondCriticalPoints").isValid()) { QVariant xBondCriticalPointsVariant = molecule.property("QTAIMXBondCriticalPoints"); QVariant yBondCriticalPointsVariant = molecule.property("QTAIMYBondCriticalPoints"); QVariant zBondCriticalPointsVariant = molecule.property("QTAIMZBondCriticalPoints"); QVariantList xBondCriticalPointsVariantList = xBondCriticalPointsVariant.toList(); QVariantList yBondCriticalPointsVariantList = yBondCriticalPointsVariant.toList(); QVariantList zBondCriticalPointsVariantList = zBondCriticalPointsVariant.toList(); if (xBondCriticalPointsVariantList.length() == yBondCriticalPointsVariantList.length() && xBondCriticalPointsVariantList.length() == zBondCriticalPointsVariantList.length()) { Vector3ub color(255, 255, 32); Vector3f xyz; for (qint64 i = 0; i < xBondCriticalPointsVariantList.length(); ++i) { xyz << xBondCriticalPointsVariantList.at(i).toFloat(), yBondCriticalPointsVariantList.at(i).toFloat(), zBondCriticalPointsVariantList.at(i).toFloat(); // map->setFromPrimitive(ncp); spheres->addSphere(xyz, color, 0.1f); } } } if (molecule.property("QTAIMXElectronDensitySources").isValid() && molecule.property("QTAIMYElectronDensitySources").isValid() && molecule.property("QTAIMZElectronDensitySources").isValid()) { QVariant xElectronDensitySourcesVariant = molecule.property("QTAIMXElectronDensitySources"); QVariant yElectronDensitySourcesVariant = molecule.property("QTAIMYElectronDensitySources"); QVariant zElectronDensitySourcesVariant = molecule.property("QTAIMZElectronDensitySources"); QVariantList xElectronDensitySourcesVariantList = xElectronDensitySourcesVariant.toList(); QVariantList yElectronDensitySourcesVariantList = yElectronDensitySourcesVariant.toList(); QVariantList zElectronDensitySourcesVariantList = zElectronDensitySourcesVariant.toList(); if (xElectronDensitySourcesVariantList.length() == yElectronDensitySourcesVariantList.length() && xElectronDensitySourcesVariantList.length() == zElectronDensitySourcesVariantList.length()) { Vector3ub color(64, 64, 255); Vector3f xyz; for (qint64 i = 0; i < xElectronDensitySourcesVariantList.length(); ++i) { xyz << xElectronDensitySourcesVariantList.at(i).toFloat(), yElectronDensitySourcesVariantList.at(i).toFloat(), zElectronDensitySourcesVariantList.at(i).toFloat(); // map->setFromPrimitive(ncp); spheres->addSphere(xyz, color, 0.1f); } } } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimengine.h000066400000000000000000000017421506155467400237640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef QTAIMENGINE_H #define QTAIMENGINE_H #include #include namespace Avogadro::QtPlugins { class QTAIMEngine : public QtGui::ScenePlugin { Q_OBJECT public: explicit QTAIMEngine(QObject* parent = nullptr); virtual ~QTAIMEngine() override = default; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("QTAIM"); } QString description() const override { return tr("Renders primitives using QTAIM properties"); } private: bool m_enabled; std::string m_name = "QTAIM"; }; } // end namespace Avogadro::QtPlugins #endif avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimextension.cpp000066400000000000000000000663451506155467400251000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "qtaimextension.h" #include "qtaimcriticalpointlocator.h" #include "qtaimcubature.h" #include "qtaimwavefunctionevaluator.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Eigen; namespace Avogadro::QtPlugins { enum QTAIMExtensionIndex { FirstAction = 0, SecondAction, ThirdAction }; QTAIMExtension::QTAIMExtension(QObject* aParent) : QtGui::ExtensionPlugin(aParent) { // create an action for our first action auto* action = new QAction(this); action->setText(tr("Molecular Graph…")); m_actions.append(action); action->setData(FirstAction); connect(action, SIGNAL(triggered()), SLOT(triggered())); // create an action for our second action action = new QAction(this); action->setText(tr("Molecular Graph with Lone Pairs…")); m_actions.append(action); action->setData(SecondAction); connect(action, SIGNAL(triggered()), SLOT(triggered())); // create an action for our third action action = new QAction(this); action->setText(tr("Atomic Charge…")); m_actions.append(action); action->setData(ThirdAction); connect(action, SIGNAL(triggered()), SLOT(triggered())); } QList QTAIMExtension::actions() const { return m_actions; } QStringList QTAIMExtension::menuPath(QAction*) const { return QStringList() << tr("&Analyze") << tr("QTAIM"); } void QTAIMExtension::setMolecule(QtGui::Molecule* molecule) { m_molecule = molecule; } void QTAIMExtension::triggered() { auto* action = qobject_cast(sender()); if (!action) return; bool wavefunctionAlreadyLoaded = m_molecule->property("QTAIMComment").isValid(); int i = action->data().toInt(); QString fileName; if (!wavefunctionAlreadyLoaded) { fileName = QFileDialog::getOpenFileName( new QWidget, tr("Open WFN File"), QDir::homePath(), tr("WFN files (*.wfn);;All files (*.*)")); if (fileName.isNull()) { qDebug() << "No such file."; return; } } // Instantiate a Wavefunction bool success = false; QTAIMWavefunction wfn; if (wavefunctionAlreadyLoaded) { success = wfn.initializeWithMoleculeProperties(m_molecule); } else { success = wfn.initializeWithWFNFile(fileName); } if (!success) { if (wavefunctionAlreadyLoaded) { qDebug() << "Error initializing wavefunction."; } else { qDebug() << "Error reading WFN file."; } return; } QtGui::Molecule::MoleculeChanges changes; if (m_molecule->atomCount() > 0) changes |= QtGui::Molecule::Atoms | QtGui::Molecule::Removed; if (m_molecule->bondCount() > 0) changes |= QtGui::Molecule::Bonds | QtGui::Molecule::Removed; m_molecule->clearAtoms(); m_molecule->emitChanged(static_cast(changes)); // Instantiate an Evaluator QTAIMWavefunctionEvaluator eval(wfn); switch (i) { case FirstAction: // Molecular Graph { // Instantiate a Critical Point Locator QTAIMCriticalPointLocator cpl(wfn); // Locate the Nuclear Critical Points cpl.locateNuclearCriticalPoints(); // QLists of results QList nucChargeList = wfn.nuclearChargesList(); QList ncpList = cpl.nuclearCriticalPoints(); QVariantList xNCPsVariantList; QVariantList yNCPsVariantList; QVariantList zNCPsVariantList; QVariantList nuclearChargesVariantList; const qreal convertBohrToAngstrom = 0.529177249; // Nuclear Critical Points for (qint64 n = 0; n < ncpList.length(); ++n) { QVector3D thisNuclearCriticalPoint = ncpList.at(n); qreal x = thisNuclearCriticalPoint.x() * convertBohrToAngstrom; qreal y = thisNuclearCriticalPoint.y() * convertBohrToAngstrom; qreal z = thisNuclearCriticalPoint.z() * convertBohrToAngstrom; xNCPsVariantList.append(x); yNCPsVariantList.append(y); zNCPsVariantList.append(z); nuclearChargesVariantList.append(wfn.nuclearCharge(n)); } m_molecule->setProperty("QTAIMXNuclearCriticalPoints", xNCPsVariantList); m_molecule->setProperty("QTAIMYNuclearCriticalPoints", yNCPsVariantList); m_molecule->setProperty("QTAIMZNuclearCriticalPoints", zNCPsVariantList); m_molecule->setProperty("QTAIMNuclearCharges", nuclearChargesVariantList); // Nuclei stored as Atoms for (qint64 n = 0; n < wfn.numberOfNuclei(); ++n) { qreal x = wfn.xNuclearCoordinate(n) * convertBohrToAngstrom; qreal y = wfn.yNuclearCoordinate(n) * convertBohrToAngstrom; qreal z = wfn.zNuclearCoordinate(n) * convertBohrToAngstrom; int Z = (int)wfn.nuclearCharge(n); m_molecule->addAtom(static_cast(Z)) .setPosition3d(Vector3(static_cast(x), static_cast(y), static_cast(z))); } if (m_molecule->atomCount() > 0) { m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } // Locate the Bond Critical Points and Trace Bond Paths cpl.locateBondCriticalPoints(); // BCP and Bond Path Results QList bcpList = cpl.bondCriticalPoints(); QList> bondPathList = cpl.bondPaths(); QList> bondedAtomsList = cpl.bondedAtoms(); QList laplacianAtBondCriticalPoints = cpl.laplacianAtBondCriticalPoints(); QList ellipticityAtBondCriticalPoints = cpl.ellipticityAtBondCriticalPoints(); QVariantList xBCPsVariantList; QVariantList yBCPsVariantList; QVariantList zBCPsVariantList; QVariantList firstNCPIndexVariantList; QVariantList secondNCPIndexVariantList; QVariantList laplacianAtBondCriticalPointsVariantList; QVariantList ellipticityAtBondCriticalPointsVariantList; QVariantList bondPathSegmentStartIndexVariantList; QVariantList bondPathSegmentEndIndexVariantList; QVariantList xBondPathsVariantList; QVariantList yBondPathsVariantList; QVariantList zBondPathsVariantList; // Connectivity stored as Bonds qint64 bpCtr = 0; auto numAtoms = static_cast(m_molecule->atomCount()); for (qint64 atom0 = 0; atom0 < numAtoms - 1; ++atom0) { for (qint64 atom1 = atom0 + 1; atom1 < numAtoms; ++atom1) { bool areBonded = false; for (qint64 bondPair = 0; bondPair < bondedAtomsList.length(); ++bondPair) { if (atom0 == bondedAtomsList.at(bondPair).first && atom1 == bondedAtomsList.at(bondPair).second) { areBonded = true; if (areBonded) { if ((wfn.nuclearCharge(atom0) == 1 || wfn.nuclearCharge(atom1) == 1) && laplacianAtBondCriticalPoints.at(bondPair) > 0.0) { // do not draw Bond because it looks like hydrogen bond } else { m_molecule->addBond( m_molecule->atom(static_cast(atom0)), m_molecule->atom(static_cast(atom1))); // bond->setAromaticity(isAromatic); // bond->setOrder( (int) order); } qreal x = bcpList.at(bondPair).x() * convertBohrToAngstrom; qreal y = bcpList.at(bondPair).y() * convertBohrToAngstrom; qreal z = bcpList.at(bondPair).z() * convertBohrToAngstrom; xBCPsVariantList.append(x); yBCPsVariantList.append(y); zBCPsVariantList.append(z); firstNCPIndexVariantList.append(atom0); secondNCPIndexVariantList.append(atom1); laplacianAtBondCriticalPointsVariantList.append( laplacianAtBondCriticalPoints.at(bondPair)); ellipticityAtBondCriticalPointsVariantList.append( ellipticityAtBondCriticalPoints.at(bondPair)); bondPathSegmentStartIndexVariantList.append(bpCtr); for (auto j : bondPathList.at(bondPair)) { x = j.x() * convertBohrToAngstrom; y = j.y() * convertBohrToAngstrom; z = j.z() * convertBohrToAngstrom; xBondPathsVariantList.append(x); yBondPathsVariantList.append(y); zBondPathsVariantList.append(z); bpCtr++; } bondPathSegmentEndIndexVariantList.append(bpCtr); } } } // bond pairs } // atom1 } // atom 0 m_molecule->setProperty("QTAIMXBondCriticalPoints", xBCPsVariantList); m_molecule->setProperty("QTAIMYBondCriticalPoints", yBCPsVariantList); m_molecule->setProperty("QTAIMZBondCriticalPoints", zBCPsVariantList); m_molecule->setProperty("QTAIMFirstNCPIndexVariantList", firstNCPIndexVariantList); m_molecule->setProperty("QTAIMSecondNCPIndexVariantList", secondNCPIndexVariantList); m_molecule->setProperty("QTAIMLaplacianAtBondCriticalPoints", laplacianAtBondCriticalPointsVariantList); m_molecule->setProperty("QTAIMEllipticityAtBondCriticalPoints", ellipticityAtBondCriticalPointsVariantList); m_molecule->setProperty("QTAIMBondPathSegmentStartIndex", bondPathSegmentStartIndexVariantList); m_molecule->setProperty("QTAIMBondPathSegmentEndIndex", bondPathSegmentEndIndexVariantList); m_molecule->setProperty("QTAIMXBondPaths", xBondPathsVariantList); m_molecule->setProperty("QTAIMYBondPaths", yBondPathsVariantList); m_molecule->setProperty("QTAIMZBondPaths", zBondPathsVariantList); if (m_molecule->bondCount()) { m_molecule->emitChanged(QtGui::Molecule::Bonds | QtGui::Molecule::Added); } } break; case SecondAction: // Molecular Graph with Lone Pairs { // Instantiate a Critical Point Locator QTAIMCriticalPointLocator cpl(wfn); // Locate the Nuclear Critical Points cpl.locateNuclearCriticalPoints(); // QLists of results QList nucChargeList = wfn.nuclearChargesList(); QList ncpList = cpl.nuclearCriticalPoints(); QVariantList xNCPsVariantList; QVariantList yNCPsVariantList; QVariantList zNCPsVariantList; QVariantList nuclearChargesVariantList; const qreal convertBohrToAngstrom = 0.529177249; // Nuclear Critical Points for (qint64 n = 0; n < ncpList.length(); ++n) { QVector3D thisNuclearCriticalPoint = ncpList.at(n); qreal x = thisNuclearCriticalPoint.x() * convertBohrToAngstrom; qreal y = thisNuclearCriticalPoint.y() * convertBohrToAngstrom; qreal z = thisNuclearCriticalPoint.z() * convertBohrToAngstrom; xNCPsVariantList.append(x); yNCPsVariantList.append(y); zNCPsVariantList.append(z); nuclearChargesVariantList.append(wfn.nuclearCharge(n)); } m_molecule->setProperty("QTAIMXNuclearCriticalPoints", xNCPsVariantList); m_molecule->setProperty("QTAIMYNuclearCriticalPoints", yNCPsVariantList); m_molecule->setProperty("QTAIMZNuclearCriticalPoints", zNCPsVariantList); m_molecule->setProperty("QTAIMNuclearCharges", nuclearChargesVariantList); // Nuclei stored as Atoms for (qint64 n = 0; n < wfn.numberOfNuclei(); ++n) { qreal x = wfn.xNuclearCoordinate(n) * convertBohrToAngstrom; qreal y = wfn.yNuclearCoordinate(n) * convertBohrToAngstrom; qreal z = wfn.zNuclearCoordinate(n) * convertBohrToAngstrom; int Z = (int)wfn.nuclearCharge(n); m_molecule->addAtom(static_cast(Z)) .setPosition3d(Vector3(static_cast(x), static_cast(y), static_cast(z))); } if (m_molecule->atomCount() > 0) { m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } // Locate the Bond Critical Points and Trace Bond Paths cpl.locateBondCriticalPoints(); // BCP and Bond Path Results QList bcpList = cpl.bondCriticalPoints(); QList> bondPathList = cpl.bondPaths(); QList> bondedAtomsList = cpl.bondedAtoms(); QList laplacianAtBondCriticalPoints = cpl.laplacianAtBondCriticalPoints(); QList ellipticityAtBondCriticalPoints = cpl.ellipticityAtBondCriticalPoints(); QVariantList xBCPsVariantList; QVariantList yBCPsVariantList; QVariantList zBCPsVariantList; QVariantList firstNCPIndexVariantList; QVariantList secondNCPIndexVariantList; QVariantList laplacianAtBondCriticalPointsVariantList; QVariantList ellipticityAtBondCriticalPointsVariantList; QVariantList bondPathSegmentStartIndexVariantList; QVariantList bondPathSegmentEndIndexVariantList; QVariantList xBondPathsVariantList; QVariantList yBondPathsVariantList; QVariantList zBondPathsVariantList; // Connectivity stored as Bonds qint64 bpCtr = 0; auto numAtoms = static_cast(m_molecule->atomCount()); for (qint64 atom0 = 0; atom0 < numAtoms - 1; ++atom0) { for (qint64 atom1 = atom0 + 1; atom1 < numAtoms; ++atom1) { bool areBonded = false; for (qint64 bondPair = 0; bondPair < bondedAtomsList.length(); ++bondPair) { if (atom0 == bondedAtomsList.at(bondPair).first && atom1 == bondedAtomsList.at(bondPair).second) { areBonded = true; if (areBonded) { if ((wfn.nuclearCharge(atom0) == 1 || wfn.nuclearCharge(atom1) == 1) && laplacianAtBondCriticalPoints.at(bondPair) > 0.0) { // do not draw Bond because it looks like hydrogen bond } else { m_molecule->addBond( m_molecule->atom(static_cast(atom0)), m_molecule->atom(static_cast(atom1))); // bond->setAromaticity(isAromatic); // bond->setOrder( (int) order); } qreal x = bcpList.at(bondPair).x() * convertBohrToAngstrom; qreal y = bcpList.at(bondPair).y() * convertBohrToAngstrom; qreal z = bcpList.at(bondPair).z() * convertBohrToAngstrom; xBCPsVariantList.append(x); yBCPsVariantList.append(y); zBCPsVariantList.append(z); firstNCPIndexVariantList.append(atom0); secondNCPIndexVariantList.append(atom1); laplacianAtBondCriticalPointsVariantList.append( laplacianAtBondCriticalPoints.at(bondPair)); ellipticityAtBondCriticalPointsVariantList.append( ellipticityAtBondCriticalPoints.at(bondPair)); bondPathSegmentStartIndexVariantList.append(bpCtr); for (auto j : bondPathList.at(bondPair)) { x = j.x() * convertBohrToAngstrom; y = j.y() * convertBohrToAngstrom; z = j.z() * convertBohrToAngstrom; xBondPathsVariantList.append(x); yBondPathsVariantList.append(y); zBondPathsVariantList.append(z); bpCtr++; } bondPathSegmentEndIndexVariantList.append(bpCtr); } } } // bond pairs } // atom1 } // atom 0 m_molecule->setProperty("QTAIMXBondCriticalPoints", xBCPsVariantList); m_molecule->setProperty("QTAIMYBondCriticalPoints", yBCPsVariantList); m_molecule->setProperty("QTAIMZBondCriticalPoints", zBCPsVariantList); m_molecule->setProperty("QTAIMFirstNCPIndexVariantList", firstNCPIndexVariantList); m_molecule->setProperty("QTAIMSecondNCPIndexVariantList", secondNCPIndexVariantList); m_molecule->setProperty("QTAIMLaplacianAtBondCriticalPoints", laplacianAtBondCriticalPointsVariantList); m_molecule->setProperty("QTAIMEllipticityAtBondCriticalPoints", ellipticityAtBondCriticalPointsVariantList); m_molecule->setProperty("QTAIMBondPathSegmentStartIndex", bondPathSegmentStartIndexVariantList); m_molecule->setProperty("QTAIMBondPathSegmentEndIndex", bondPathSegmentEndIndexVariantList); m_molecule->setProperty("QTAIMXBondPaths", xBondPathsVariantList); m_molecule->setProperty("QTAIMYBondPaths", yBondPathsVariantList); m_molecule->setProperty("QTAIMZBondPaths", zBondPathsVariantList); if (m_molecule->bondCount()) { m_molecule->emitChanged(QtGui::Molecule::Bonds | QtGui::Molecule::Added); } // Locate Electron Density Sources / Lone Pairs cpl.locateElectronDensitySources(); QList electronDensitySourcesList = cpl.electronDensitySources(); QVariantList xElectronDensitySourcesVariantList; QVariantList yElectronDensitySourcesVariantList; QVariantList zElectronDensitySourcesVariantList; for (auto thisCriticalPoint : electronDensitySourcesList) { qreal x = thisCriticalPoint.x() * convertBohrToAngstrom; qreal y = thisCriticalPoint.y() * convertBohrToAngstrom; qreal z = thisCriticalPoint.z() * convertBohrToAngstrom; xElectronDensitySourcesVariantList.append(x); yElectronDensitySourcesVariantList.append(y); zElectronDensitySourcesVariantList.append(z); } m_molecule->setProperty("QTAIMXElectronDensitySources", xElectronDensitySourcesVariantList); m_molecule->setProperty("QTAIMYElectronDensitySources", yElectronDensitySourcesVariantList); m_molecule->setProperty("QTAIMZElectronDensitySources", zElectronDensitySourcesVariantList); // TODO need some way to indicate that the properties have changed: // m_molecule->update(); } break; case ThirdAction: // perform third action { // Instantiate a Critical Point Locator QTAIMCriticalPointLocator cpl(wfn); // Locate the Nuclear Critical Points cpl.locateNuclearCriticalPoints(); // QLists of results QList nucChargeList = wfn.nuclearChargesList(); QList ncpList = cpl.nuclearCriticalPoints(); QVariantList xNCPsVariantList; QVariantList yNCPsVariantList; QVariantList zNCPsVariantList; QVariantList nuclearChargesVariantList; const qreal convertBohrToAngstrom = 0.529177249; // Nuclear Critical Points for (qint64 n = 0; n < ncpList.length(); ++n) { QVector3D thisNuclearCriticalPoint = ncpList.at(n); qreal x = thisNuclearCriticalPoint.x() * convertBohrToAngstrom; qreal y = thisNuclearCriticalPoint.y() * convertBohrToAngstrom; qreal z = thisNuclearCriticalPoint.z() * convertBohrToAngstrom; xNCPsVariantList.append(x); yNCPsVariantList.append(y); zNCPsVariantList.append(z); nuclearChargesVariantList.append(wfn.nuclearCharge(n)); } m_molecule->setProperty("QTAIMXNuclearCriticalPoints", xNCPsVariantList); m_molecule->setProperty("QTAIMYNuclearCriticalPoints", yNCPsVariantList); m_molecule->setProperty("QTAIMZNuclearCriticalPoints", zNCPsVariantList); m_molecule->setProperty("QTAIMNuclearCharges", nuclearChargesVariantList); // Nuclei stored as Atoms for (qint64 n = 0; n < wfn.numberOfNuclei(); ++n) { qreal x = wfn.xNuclearCoordinate(n) * convertBohrToAngstrom; qreal y = wfn.yNuclearCoordinate(n) * convertBohrToAngstrom; qreal z = wfn.zNuclearCoordinate(n) * convertBohrToAngstrom; int Z = (int)wfn.nuclearCharge(n); m_molecule->addAtom(static_cast(Z)) .setPosition3d(Vector3(static_cast(x), static_cast(y), static_cast(z))); } if (m_molecule->atomCount() > 0) { m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } // Locate the Bond Critical Points and Trace Bond Paths cpl.locateBondCriticalPoints(); // BCP and Bond Path Results QList bcpList = cpl.bondCriticalPoints(); QList> bondPathList = cpl.bondPaths(); QList> bondedAtomsList = cpl.bondedAtoms(); QList laplacianAtBondCriticalPoints = cpl.laplacianAtBondCriticalPoints(); QList ellipticityAtBondCriticalPoints = cpl.ellipticityAtBondCriticalPoints(); QVariantList xBCPsVariantList; QVariantList yBCPsVariantList; QVariantList zBCPsVariantList; QVariantList firstNCPIndexVariantList; QVariantList secondNCPIndexVariantList; QVariantList laplacianAtBondCriticalPointsVariantList; QVariantList ellipticityAtBondCriticalPointsVariantList; QVariantList bondPathSegmentStartIndexVariantList; QVariantList bondPathSegmentEndIndexVariantList; QVariantList xBondPathsVariantList; QVariantList yBondPathsVariantList; QVariantList zBondPathsVariantList; // Connectivity stored as Bonds qint64 bpCtr = 0; auto numAtoms = static_cast(m_molecule->atomCount()); for (qint64 atom0 = 0; atom0 < numAtoms - 1; ++atom0) { for (qint64 atom1 = atom0 + 1; atom1 < numAtoms; ++atom1) { bool areBonded = false; for (qint64 bondPair = 0; bondPair < bondedAtomsList.length(); ++bondPair) { if (atom0 == bondedAtomsList.at(bondPair).first && atom1 == bondedAtomsList.at(bondPair).second) { areBonded = true; if (areBonded) { if ((wfn.nuclearCharge(atom0) == 1 || wfn.nuclearCharge(atom1) == 1) && laplacianAtBondCriticalPoints.at(bondPair) > 0.0) { // do not draw Bond because it looks like hydrogen bond } else { m_molecule->addBond( m_molecule->atom(static_cast(atom0)), m_molecule->atom(static_cast(atom1))); // bond->setAromaticity(isAromatic); // bond->setOrder( (int) order); } qreal x = bcpList.at(bondPair).x() * convertBohrToAngstrom; qreal y = bcpList.at(bondPair).y() * convertBohrToAngstrom; qreal z = bcpList.at(bondPair).z() * convertBohrToAngstrom; xBCPsVariantList.append(x); yBCPsVariantList.append(y); zBCPsVariantList.append(z); firstNCPIndexVariantList.append(atom0); secondNCPIndexVariantList.append(atom1); laplacianAtBondCriticalPointsVariantList.append( laplacianAtBondCriticalPoints.at(bondPair)); ellipticityAtBondCriticalPointsVariantList.append( ellipticityAtBondCriticalPoints.at(bondPair)); bondPathSegmentStartIndexVariantList.append(bpCtr); for (auto j : bondPathList.at(bondPair)) { x = j.x() * convertBohrToAngstrom; y = j.y() * convertBohrToAngstrom; z = j.z() * convertBohrToAngstrom; xBondPathsVariantList.append(x); yBondPathsVariantList.append(y); zBondPathsVariantList.append(z); bpCtr++; } bondPathSegmentEndIndexVariantList.append(bpCtr); } } } // bond pairs } // atom1 } // atom 0 m_molecule->setProperty("QTAIMXBondCriticalPoints", xBCPsVariantList); m_molecule->setProperty("QTAIMYBondCriticalPoints", yBCPsVariantList); m_molecule->setProperty("QTAIMZBondCriticalPoints", zBCPsVariantList); m_molecule->setProperty("QTAIMFirstNCPIndexVariantList", firstNCPIndexVariantList); m_molecule->setProperty("QTAIMSecondNCPIndexVariantList", secondNCPIndexVariantList); m_molecule->setProperty("QTAIMLaplacianAtBondCriticalPoints", laplacianAtBondCriticalPointsVariantList); m_molecule->setProperty("QTAIMEllipticityAtBondCriticalPoints", ellipticityAtBondCriticalPointsVariantList); m_molecule->setProperty("QTAIMBondPathSegmentStartIndex", bondPathSegmentStartIndexVariantList); m_molecule->setProperty("QTAIMBondPathSegmentEndIndex", bondPathSegmentEndIndexVariantList); m_molecule->setProperty("QTAIMXBondPaths", xBondPathsVariantList); m_molecule->setProperty("QTAIMYBondPaths", yBondPathsVariantList); m_molecule->setProperty("QTAIMZBondPaths", zBondPathsVariantList); if (m_molecule->bondCount() > 0) { m_molecule->emitChanged(QtGui::Molecule::Bonds | QtGui::Molecule::Added); } // Electron Density qint64 mode = 0; // All Atomic Basins QList basins; for (qint64 j = 0; j < wfn.numberOfNuclei(); ++j) { basins.append(j); } QTAIMCubature cub(wfn); // QTime time; // time.start(); QList> results = cub.integrate(mode, basins); // qDebug() << "Time Elapsed:" << time.elapsed(); for (qint64 j = 0; j < results.length(); ++j) { qDebug() << "basin" << j << results.at(j).first << results.at(j).second; } // TODO: Set the properties of the atoms. // I don't know why this bombs. for (qint64 j = 0; static_cast(m_molecule->atomCount()); ++j) { // Atom *atom=m_molecule->atoms().at(i); // const qreal charge=results.at(i).first; // atom->setPartialCharge( charge ); } } break; } emit requestActiveTool("Navigator"); emit requestActiveDisplayTypes(QStringList() << "QTAIMScenePlugin"); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimextension.h000066400000000000000000000021251506155467400245270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef QTAIMEXTENSION_H #define QTAIMEXTENSION_H #include #include namespace Avogadro::QtPlugins { class QTAIMExtension : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit QTAIMExtension(QObject* parent = nullptr); ~QTAIMExtension() override = default; QString name() const override { return tr("QTAIM"); } QString description() const override { return tr("QTAIM extension"); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* molecule) override; private slots: void triggered(); private: QList m_actions; QtGui::Molecule* m_molecule; }; } // end namespace Avogadro::QtPlugins #endif // QTAIMEXTENSION_H avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimlsodaintegrator.cpp000066400000000000000000002137021506155467400262540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ /* From tam@dragonfly.wri.com Wed Apr 24 01:35:52 1991 Return-Path: Date: Wed, 24 Apr 91 03:35:24 CDT From: tam@dragonfly.wri.com To: whitbeck@wheeler.wrc.unr.edu Subject: lsoda.c Cc: augenbau@sparc0.brc.uconn.edu I'm told by Steve Nichols at Georgia Tech that you are interested in a stiff integrator. Here's a translation of the fortran code LSODA. Please note that there is no comment. The interface is the same as the FORTRAN code and I believe the documentation in LSODA will suffice. As usual, a free software comes with no guarantee. Hon Wah Tam Wolfram Research, Inc. tam@wri.com */ #include "qtaimlsodaintegrator.h" #include "qtaimmathutilities.h" namespace Avogadro::QtPlugins { QTAIMLSODAIntegrator::QTAIMLSODAIntegrator(QTAIMWavefunctionEvaluator& eval, const qint64 mode) : m_eval(&eval), m_mode(mode), m_associatedSphere(0) { m_betaSpheres.empty(); } QVector3D QTAIMLSODAIntegrator::integrate(QVector3D x0y0z0) { qreal x0 = x0y0z0.x(); qreal y0 = x0y0z0.y(); qreal z0 = x0y0z0.z(); mord[0] = 0; mord[1] = 12; mord[2] = 5; sm1[0] = 0.; sm1[1] = 0.5; sm1[2] = 0.575; sm1[3] = 0.55; sm1[4] = 0.45; sm1[5] = 0.35; sm1[6] = 0.25; sm1[7] = 0.2; sm1[8] = 0.15; sm1[9] = 0.1; sm1[10] = 0.075; sm1[11] = 0.05; sm1[12] = 0.025; illin = 0; init = 0; ntrep = 0; ixpr = 0; mesflg = 1; double rwork1, rwork5, rwork6, rwork7; double atol[4], rtol[4], t, tout, y[4]; int iwork1, iwork2, iwork5, iwork6, iwork7, iwork8, iwork9; int neq = 3; int itol, itask, istate, iopt, jt; y[1] = x0; y[2] = y0; y[3] = z0; double t0; double tf = 100.0; double dt = 0.1; m_path.clear(); m_path.append(QVector3D(y[1], y[2], y[3])); t = 0.0; for (t0 = 0.0; t0 < tf; t0 = t0 + dt) { iwork1 = iwork2 = iwork5 = iwork6 = iwork7 = iwork8 = iwork9 = 0; rwork1 = rwork5 = rwork6 = rwork7 = 0.0; t = t0; tout = t0 + dt; itol = 2; rtol[0] = 0.0; atol[0] = 0.0; rtol[1] = 0.0; rtol[2] = 0.0; rtol[3] = 0.0; atol[1] = 5.0E-5; atol[2] = 5.0E-5; atol[3] = 5.0E-5; itask = 1; istate = 1; iopt = 0; jt = 2; // if( t0 + dt > tf ) qDebug() << "END"; // beta spheres if (m_mode == QTAIMLSODAIntegrator::SteepestAscentPathInElectronDensity) { if (m_betaSpheres.length() > 0) { for (qint64 n_ = 0; n_ < m_betaSpheres.length(); ++n_) { Matrix a(y[1], y[2], y[3]); Matrix b(m_betaSpheres.at(n_).first.x(), m_betaSpheres.at(n_).first.y(), m_betaSpheres.at(n_).first.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < m_betaSpheres.at(n_).second) { m_status = 0; m_associatedSphere = n_; return QVector3D(m_betaSpheres.at(n_).first.x(), m_betaSpheres.at(n_).first.y(), m_betaSpheres.at(n_).first.z()); } } } } // beta spheres lsoda(neq, y, &t, tout, itol, rtol, atol, itask, &istate, iopt, jt, iwork1, iwork2, iwork5, iwork6, iwork7, iwork8, iwork9, rwork1, rwork5, rwork6, rwork7); m_path.append(QVector3D(y[1], y[2], y[3])); // qDebug(" at t= %12.4e y= %14.6e %14.6e %14.6e", t,y[1],y[2],y[3]); if (istate <= 0) { // qDebug("error istate = %d",istate); // qDebug(" at t= %12.4e y= %14.6e %14.6e %14.6e", // t,y[1],y[2],y[3]); return QVector3D(y[1], y[2], y[3]); } } // ode step return QVector3D(y[1], y[2], y[3]); } void QTAIMLSODAIntegrator::f(int neq, double t, double* y, double* ydot) { neq = neq; // suppress warning t = t; // suppress warning Matrix gradient; Matrix gH; Matrix g; Matrix H; Matrix xyz; xyz << y[1], y[2], y[3]; if (m_mode == SteepestAscentPathInElectronDensity) { g = m_eval->gradientOfElectronDensity(xyz); } else { if (m_mode == 1 || m_mode == 2 || m_mode == 3 || m_mode == 4) { gH = m_eval->gradientAndHessianOfElectronDensity(xyz); } else { gH = m_eval->gradientAndHessianOfElectronDensityLaplacian(xyz); } g(0) = gH(0, 0); g(1) = gH(1, 0); g(2) = gH(2, 0); H(0, 0) = gH(0, 1); H(1, 0) = gH(1, 1); H(2, 0) = gH(2, 1); H(0, 1) = gH(0, 2); H(1, 1) = gH(1, 2); H(2, 1) = gH(2, 2); H(0, 2) = gH(0, 3); H(1, 2) = gH(1, 3); H(2, 2) = gH(2, 3); } switch (m_mode) { case SteepestAscentPathInElectronDensity: gradient = g; break; case CMBPMinusThreeGradientInElectronDensity: gradient = QTAIMMathUtilities::minusThreeSignatureLocatorGradient(g, H); break; case CMBPMinusOneGradientInElectronDensity: gradient = QTAIMMathUtilities::minusOneSignatureLocatorGradient(g, H); break; case CMBPPlusOneGradientInElectronDensity: gradient = QTAIMMathUtilities::plusOneSignatureLocatorGradient(g, H); break; case CMBPPlusThreeGradientInElectronDensity: gradient = QTAIMMathUtilities::plusThreeSignatureLocatorGradient(g, H); break; case CMBPMinusThreeGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::minusThreeSignatureLocatorGradient(g, H); break; case CMBPMinusOneGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::minusOneSignatureLocatorGradient(g, H); break; case CMBPPlusOneGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::plusOneSignatureLocatorGradient(g, H); break; case CMBPPlusThreeGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::plusThreeSignatureLocatorGradient(g, H); break; default: qDebug() << "Catastrophic: No ODE parameters for this property."; break; } qreal normGradient = std::hypot(gradient(0), gradient(1), gradient(2)); ydot[1] = gradient(0) / normGradient; ydot[2] = gradient(1) / normGradient; ydot[3] = gradient(2) / normGradient; } void QTAIMLSODAIntegrator::daxpy(int n_, double da, double* dx, int incx, double* dy, int incy) /* Purpose : To compute dy = da * dx + dy --- Input --- n : number of elements in input vector(s) da : double scalar multiplier dx : double vector with n+1 elements, dx[0] is not used incx : storage spacing between elements of dx dy : double vector with n+1 elements, dy[0] is not used incy : storage spacing between elements of dy --- Output --- dy = da * dx + dy, unchanged if n <= 0 For i = 0 to n-1, replace dy[ly+i*incy] with da*dx[lx+i*incx] + dy[ly+i*incy], where lx = 1 if incx >= 0, else lx = (-incx)*(n-1)+1 and ly is defined in a similar way using incy. */ { int ix, iy, i, m; if (n_ < 0 || da == 0.) return; /* Code for nonequal or nonpositive increments. */ if (incx != incy || incx < 1) { ix = 1; iy = 1; if (incx < 0) ix = (-n_ + 1) * incx + 1; if (incy < 0) iy = (-n_ + 1) * incy + 1; for (i = 1; i <= n_; i++) { dy[iy] = dy[iy] + da * dx[ix]; ix = ix + incx; iy = iy + incy; } return; } /* Code for both increments equal to 1. */ /* Clean-up loop so remaining vector length is a multiple of 4. */ if (incx == 1) { m = n_ % 4; if (m != 0) { for (i = 1; i <= m; i++) dy[i] = dy[i] + da * dx[i]; if (n_ < 4) return; } for (i = m + 1; i <= n_; i = i + 4) { dy[i] = dy[i] + da * dx[i]; dy[i + 1] = dy[i + 1] + da * dx[i + 1]; dy[i + 2] = dy[i + 2] + da * dx[i + 2]; dy[i + 3] = dy[i + 3] + da * dx[i + 3]; } return; } /* Code for equal, positive, nonunit increments. */ for (i = 1; i <= n_ * incx; i = i + incx) dy[i] = da * dx[i] + dy[i]; } double QTAIMLSODAIntegrator::ddot(int n_, double* dx, int incx, double* dy, int incy) /* Purpose : Inner product dx . dy --- Input --- n : number of elements in input vector(s) dx : double vector with n+1 elements, dx[0] is not used incx : storage spacing between elements of dx dy : double vector with n+1 elements, dy[0] is not used incy : storage spacing between elements of dy --- Output --- ddot : dot product dx . dy, 0 if n <= 0 ddot = sum for i = 0 to n-1 of dx[lx+i*incx] * dy[ly+i*incy] where lx = 1 if incx >= 0, else lx = (-incx)*(n-1)+1, and ly is defined in a similar way using incy. */ { double dotprod; int ix, iy, i, m; dotprod = 0.; if (n_ <= 0) return dotprod; /* Code for unequal or nonpositive increments. */ if (incx != incy || incx < 1) { ix = 1; iy = 1; if (incx < 0) ix = (-n_ + 1) * incx + 1; if (incy < 0) iy = (-n_ + 1) * incy + 1; for (i = 1; i <= n_; i++) { dotprod = dotprod + dx[ix] * dy[iy]; ix = ix + incx; iy = iy + incy; } return dotprod; } /* Code for both increments equal to 1. */ /* Clean-up loop so remaining vector length is a multiple of 5. */ if (incx == 1) { m = n_ % 5; if (m != 0) { for (i = 1; i <= m; i++) dotprod = dotprod + dx[i] * dy[i]; if (n_ < 5) return dotprod; } for (i = m + 1; i <= n_; i = i + 5) dotprod = dotprod + dx[i] * dy[i] + dx[i + 1] * dy[i + 1] + dx[i + 2] * dy[i + 2] + dx[i + 3] * dy[i + 3] + dx[i + 4] * dy[i + 4]; return dotprod; } /* Code for positive equal nonunit increments. */ for (i = 1; i <= n_ * incx; i = i + incx) dotprod = dotprod + dx[i] * dy[i]; return dotprod; } void QTAIMLSODAIntegrator::dgefa(double** a, int n_, int* ipvt_, int* info) /* Purpose : dgefa factors a double matrix by Gaussian elimination. dgefa is usually called by dgeco, but it can be called directly with a saving in time if rcond is not needed. (Time for dgeco) = (1+9/n)*(time for dgefa). This c version uses algorithm kji rather than the kij in dgefa.f. Note that the fortran version input variable lda is not needed. On Entry : a : double matrix of dimension ( n+1, n+1 ), the 0-th row and column are not used. a is created using NewDoubleMatrix, hence lda is unnecessary. n : the row dimension of a. On Return : a : a lower triangular matrix and the multipliers which were used to obtain it. The factorization can be written a = L * U where U is a product of permutation and unit upper triangular matrices and L is lower triangular. ipvt : an n+1 integer vector of pivot indices. *info : = 0 normal value, = k if U[k][k] == 0. This is not an error condition for this subroutine, but it does indicate that dgesl or dgedi will divide by zero if called. Use rcond in dgeco for a reliable indication of singularity. Notice that the calling program must use &info. BLAS : daxpy, dscal, idamax */ { int j, k, i; double t; /* Gaussian elimination with partial pivoting. */ *info = 0; for (k = 1; k <= n_ - 1; k++) { /* Find j = pivot index. Note that a[k]+k-1 is the address of the 0-th element of the row vector whose 1st element is a[k][k]. */ j = idamax(n_ - k + 1, a[k] + k - 1, 1) + k - 1; ipvt_[k] = j; /* Zero pivot implies this row already triangularized. */ if (a[k][j] == 0.) { *info = k; continue; } /* Interchange if necessary. */ if (j != k) { t = a[k][j]; a[k][j] = a[k][k]; a[k][k] = t; } /* Compute multipliers. */ t = -1. / a[k][k]; dscal(n_ - k, t, a[k] + k, 1); /* Column elimination with row indexing. */ for (i = k + 1; i <= n_; i++) { t = a[i][j]; if (j != k) { a[i][j] = a[i][k]; a[i][k] = t; } daxpy(n_ - k, t, a[k] + k, 1, a[i] + k, 1); } } /* end k-loop */ ipvt_[n_] = n_; if (a[n_][n_] == 0.) *info = n_; } void QTAIMLSODAIntegrator::dgesl(double** a, int n_, int* ipvt_, double* b, int job) /* Purpose : dgesl solves the linear system a * x = b or Transpose(a) * x = b using the factors computed by dgeco or degfa. On Entry : a : double matrix of dimension ( n+1, n+1 ), the output from dgeco or dgefa. The 0-th row and column are not used. n : the row dimension of a. ipvt : the pivot vector from degco or dgefa. b : the right hand side vector. job : = 0 to solve a * x = b, = nonzero to solve Transpose(a) * x = b. On Return : b : the solution vector x. Error Condition : A division by zero will occur if the input factor contains a zero on the diagonal. Technically this indicates singularity but it is often caused by improper arguments or improper setting of the pointers of a. It will not occur if the subroutines are called correctly and if dgeco has set rcond > 0 or dgefa has set info = 0. BLAS : daxpy, ddot */ { int k, j; double t; /* Job = 0, solve a * x = b. */ if (job == 0) { /* First solve L * y = b. */ for (k = 1; k <= n_; k++) { t = ddot(k - 1, a[k], 1, b, 1); b[k] = (b[k] - t) / a[k][k]; } /* Now solve U * x = y. */ for (k = n_ - 1; k >= 1; k--) { b[k] = b[k] + ddot(n_ - k, a[k] + k, 1, b + k, 1); j = ipvt_[k]; if (j != k) { t = b[j]; b[j] = b[k]; b[k] = t; } } return; } /* Job = nonzero, solve Transpose(a) * x = b. First solve Transpose(U) * y = b. */ for (k = 1; k <= n_ - 1; k++) { j = ipvt_[k]; t = b[j]; if (j != k) { b[j] = b[k]; b[k] = t; } daxpy(n_ - k, t, a[k] + k, 1, b + k, 1); } /* Now solve Transpose(L) * x = y. */ for (k = n_; k >= 1; k--) { b[k] = b[k] / a[k][k]; t = -b[k]; daxpy(k - 1, t, a[k], 1, b, 1); } } void QTAIMLSODAIntegrator::dscal(int n_, double da, double* dx, int incx) /* Purpose : scalar vector multiplication dx = da * dx --- Input --- n : number of elements in input vector da : double scale factor dx : double vector with n+1 elements, dx[0] is not used incx : storage spacing between elements of dx --- Output --- dx = da * dx, unchanged if n <= 0 For i = 0 to n-1, replace dx[1+i*incx] with da * dx[1+i*incx]. */ { int m, i; if (n_ <= 0) return; /* Code for increments not equal to 1. */ if (incx != 1) { for (i = 1; i <= n_ * incx; i = i + incx) dx[i] = da * dx[i]; return; } /* Code for increments equal to 1. */ /* Clean-up loop so remaining vector length is a multiple of 5. */ m = n_ % 5; if (m != 0) { for (i = 1; i <= m; i++) dx[i] = da * dx[i]; if (n_ < 5) return; } for (i = m + 1; i <= n_; i = i + 5) { dx[i] = da * dx[i]; dx[i + 1] = da * dx[i + 1]; dx[i + 2] = da * dx[i + 2]; dx[i + 3] = da * dx[i + 3]; dx[i + 4] = da * dx[i + 4]; } } int QTAIMLSODAIntegrator::idamax(int n_, double* dx, int incx) /* Purpose : Find largest component of double vector dx --- Input --- n : number of elements in input vector dx : double vector with n+1 elements, dx[0] is not used incx : storage spacing between elements of dx --- Output --- idamax : smallest index, 0 if n <= 0 Find smallest index of maximum magnitude of dx. idamax = first i, i=1 to n, to minimize fabs( dx[1-incx+i*incx] ). */ { double dmax, xmag; int i, ii, xindex; xindex = 0; if (n_ <= 0) return xindex; xindex = 1; if (n_ <= 1 || incx <= 0) return xindex; /* Code for increments not equal to 1. */ if (incx != 1) { dmax = fabs(dx[1]); ii = 2; for (i = 1 + incx; i <= n_ * incx; i = i + incx) { xmag = fabs(dx[i]); if (xmag > dmax) { xindex = ii; dmax = xmag; } ii++; } return xindex; } /* Code for increments equal to 1. */ dmax = fabs(dx[1]); for (i = 2; i <= n_; i++) { xmag = fabs(dx[i]); if (xmag > dmax) { xindex = i; dmax = xmag; } } return xindex; } // lsoda.c #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) #define ETA 2.2204460492503131e-16 /* static void prja(), solsy(), stoda(), cfode(), ewset(), intdy(), terminate(), terminate2(), successreturn(), scaleh(), correction(), methodswitch(), orderswitch(), endstoda(), resetcoeff(), freevectors(), corfailure(); static double vmnorm(), bnorm(), fnorm(); */ /* The following are useful statistics. hu, h, tn, tolsf, tsw, nst, nfe, nje, nqu, nq, imxer, mused, meth */ /* Terminate lsoda due to illegal input. */ void QTAIMLSODAIntegrator::terminate(int* istate) { if (illin == 5) { qDebug("lsoda -- repeated occurrence of illegal input"); qDebug(" run aborted.. apparent infinite loop"); } else { illin++; *istate = -3; } } /* end terminate */ /* Terminate lsoda due to various error conditions. */ void QTAIMLSODAIntegrator::terminate2(double* y, double* t) { int i; yp1 = yh[1]; for (i = 1; i <= n; i++) y[i] = yp1[i]; *t = tn; illin = 0; freevectors(); } /* end terminate2 */ /* The following block handles all successful returns from lsoda. If itask != 1, y is loaded from yh and t is set accordingly. *Istate is set to 2, the illegal input counter is zeroed, and the optional outputs are loaded into the work arrays before returning. */ void QTAIMLSODAIntegrator::successreturn(double* y, double* t, int itask, int ihit, double tcrit, int* istate) { int i; yp1 = yh[1]; for (i = 1; i <= n; i++) y[i] = yp1[i]; *t = tn; if (itask == 4 || itask == 5) if (ihit) *t = tcrit; *istate = 2; illin = 0; freevectors(); } /* end successreturn */ /* In this version all memory allocated using malloc is freed upon exit. Therefore *istate = 2 and *istate = 3 will not work. */ void QTAIMLSODAIntegrator::lsoda(int neq, double* y, double* t, double tout, int itol, double* rtol, double* atol, int itask, int* istate, int iopt, int jt, int iwork1, int iwork2, int iwork5, int iwork6, int iwork7, int iwork8, int iwork9, double rwork1, double rwork5, double rwork6, double rwork7) /* If the user does not supply any of these values, the calling program should initialize those untouched working variables to zero. ml = iwork1 mu = iwork2 ixpr = iwork5 mxstep = iwork6 mxhnil = iwork7 mxordn = iwork8 mxords = iwork9 tcrit = rwork1 h0 = rwork5 hmax = rwork6 hmin = rwork7 */ { int mxstp0 = 500, mxhnl0 = -1 /* 10 */; int i, iflag, lenyh; double atoli, ayi, big, hmax, hmx, rh, rtoli, tdist, tnext, tol, tolsf, tp, size, sum, w0; /* Block a. This code block is executed on every call. It tests *istate and itask for legality and branches appropriately. If *istate > 1 but the flag init shows that initialization has not yet been done, an error return occurs. If *istate = 1 and tout = t, return immediately. */ if (*istate < 1 || *istate > 3) { qDebug("lsoda -- illegal istate = %d", *istate); terminate(istate); return; } if (itask < 1 || itask > 5) { qDebug("lsoda -- illegal itask = %d", itask); terminate(istate); return; } if (init == 0 && (*istate == 2 || *istate == 3)) { qDebug("lsoda -- istate > 1 but lsoda not initialized"); terminate(istate); return; } if (*istate == 1) { init = 0; if (tout == *t) { ntrep++; if (ntrep < 5) return; qDebug("lsoda -- repeated calls with istate = 1 and tout = t"); qDebug(" run aborted.. apparent infinite loop"); return; } } /* Block b. The next code block is executed for the initial call ( *istate = 1 ), or for a continuation call with parameter changes ( *istate = 3 ). It contains checking of all inputs and various initializations. First check legality of the non-optional inputs neq, itol, iopt, jt, ml, and mu. */ double h0(0.0); if (*istate == 1 || *istate == 3) { ntrep = 0; if (neq <= 0) { qDebug("lsoda -- neq = %d is less than 1", neq); terminate(istate); return; } if (*istate == 3 && neq > n) { qDebug("lsoda -- istate = 3 and neq increased"); terminate(istate); return; } n = neq; if (itol < 1 || itol > 4) { qDebug("lsoda -- itol = %d illegal", itol); terminate(istate); return; } if (iopt < 0 || iopt > 1) { qDebug("lsoda -- iopt = %d illegal", iopt); terminate(istate); return; } if (jt == 3 || jt < 1 || jt > 5) { qDebug("lsoda -- jt = %d illegal", jt); terminate(istate); return; } jtyp = jt; if (jt > 2) { ml = iwork1; mu = iwork2; if (ml < 0 || ml >= n) { qDebug("lsoda -- ml = %d not between 1 and neq", ml); terminate(istate); return; } if (mu < 0 || mu >= n) { qDebug("lsoda -- mu = %d not between 1 and neq", mu); terminate(istate); return; } } /* Next process and check the optional inpus. */ /* Default options. */ if (iopt == 0) { ixpr = 0; mxstep = mxstp0; mxhnil = mxhnl0; hmxi = 0.; hmin = 0.; if (*istate == 1) { h0 = 0.; mxordn = mord[1]; mxords = mord[2]; } } /* end if ( iopt == 0 ) */ /* Optional inputs. */ else { /* if ( iopt = 1 ) */ ixpr = iwork5; if (ixpr < 0 || ixpr > 1) { qDebug("lsoda -- ixpr = %d is illegal", ixpr); terminate(istate); return; } mxstep = iwork6; if (mxstep < 0) { qDebug("lsoda -- mxstep < 0"); terminate(istate); return; } if (mxstep == 0) mxstep = mxstp0; mxhnil = iwork7; if (mxhnil < 0) { qDebug("lsoda -- mxhnil < 0"); terminate(istate); return; } if (*istate == 1) { h0 = rwork5; mxordn = iwork8; if (mxordn < 0) { qDebug("lsoda -- mxordn = %d is less than 0", mxordn); terminate(istate); return; } if (mxordn == 0) mxordn = 100; mxordn = min(mxordn, mord[1]); mxords = iwork9; if (mxords < 0) { qDebug("lsoda -- mxords = %d is less than 0", mxords); terminate(istate); return; } if (mxords == 0) mxords = 100; mxords = min(mxords, mord[2]); if ((tout - *t) * h0 < 0.) { qDebug("lsoda -- tout = %g behind t = %g", tout, *t); qDebug(" integration direction is given by %g", h0); terminate(istate); return; } } /* end if ( *istate == 1 ) */ hmax = rwork6; if (hmax < 0.) { qDebug("lsoda -- hmax < 0."); terminate(istate); return; } hmxi = 0.; if (hmax > 0) hmxi = 1. / hmax; hmin = rwork7; if (hmin < 0.) { qDebug("lsoda -- hmin < 0."); terminate(istate); return; } } /* end else */ /* end iopt = 1 */ } /* end if ( *istate == 1 || *istate == 3 ) */ /* If *istate = 1, meth is initialized to 1. Also allocate memory for yh, wm, ewt, savf, acor, ipvt. */ if (*istate == 1) { /* If memory were not freed, *istate = 3 need not reallocate memory. Hence this section is not executed by *istate = 3. */ sqrteta = sqrt(ETA); meth = 1; nyh = n; lenyh = 1 + max(mxordn, mxords); m_lenyh = lenyh; m_nyh = nyh; yh = (double**)malloc((1 + lenyh) * sizeof(*yh)); if (yh == nullptr) { qDebug("lsoda -- insufficient memory for your problem"); terminate(istate); return; } for (i = 1; i <= lenyh; i++) yh[i] = (double*)malloc((1 + nyh) * sizeof(double)); wm = (double**)malloc((1 + nyh) * sizeof(*wm)); if (wm == nullptr) { free(yh); qDebug("lsoda -- insufficient memory for your problem"); terminate(istate); return; } for (i = 1; i <= nyh; i++) wm[i] = (double*)malloc((1 + nyh) * sizeof(double)); ewt = (double*)malloc((1 + nyh) * sizeof(double)); if (ewt == nullptr) { free(yh); free(wm); qDebug("lsoda -- insufficient memory for your problem"); terminate(istate); return; } savf = (double*)malloc((1 + nyh) * sizeof(double)); if (savf == nullptr) { free(yh); free(wm); free(ewt); qDebug("lsoda -- insufficient memory for your problem"); terminate(istate); return; } acor = (double*)malloc((1 + nyh) * sizeof(double)); if (acor == nullptr) { free(yh); free(wm); free(ewt); free(savf); qDebug("lsoda -- insufficient memory for your problem"); terminate(istate); return; } ipvt = (int*)malloc((1 + nyh) * sizeof(int)); if (ipvt == nullptr) { free(yh); free(wm); free(ewt); free(savf); free(acor); qDebug("lsoda -- insufficient memory for your problem"); terminate(istate); return; } } /* Check rtol and atol for legality. */ if (*istate == 1 || *istate == 3) { rtoli = rtol[1]; atoli = atol[1]; for (i = 1; i <= n; i++) { if (itol >= 3) rtoli = rtol[i]; if (itol == 2 || itol == 4) atoli = atol[i]; if (rtoli < 0.) { qDebug("lsoda -- rtol = %g is less than 0.", rtoli); terminate(istate); freevectors(); return; } if (atoli < 0.) { qDebug("lsoda -- atol = %g is less than 0.", atoli); terminate(istate); freevectors(); return; } } /* end for */ } /* end if ( *istate == 1 || *istate == 3 ) */ /* If *istate = 3, set flag to signal parameter changes to stoda. */ if (*istate == 3) { jstart = -1; } /* Block c. The next block is for the initial call only ( *istate = 1 ). It contains all remaining initializations, the initial call to f, and the calculation of the initial step size. The error weights in ewt are inverted after being loaded. */ int tcrit(0); if (*istate == 1) { tn = *t; tsw = *t; maxord = mxordn; if (itask == 4 || itask == 5) { tcrit = rwork1; if ((tcrit - tout) * (tout - *t) < 0.) { qDebug("lsoda -- itask = 4 or 5 and tcrit behind tout"); terminate(istate); freevectors(); return; } if (h0 != 0. && (*t + h0 - tcrit) * h0 > 0.) h0 = tcrit - *t; } jstart = 0; nhnil = 0; nst = 0; nje = 0; nslast = 0; hu = 0.; nqu = 0; mused = 0; miter = 0; ccmax = 0.3; maxcor = 3; msbp = 20; mxncf = 10; /* Initial call to f. */ f(neq, *t, y, yh[2]); nfe = 1; /* Load the initial value vector in yh. */ yp1 = yh[1]; for (i = 1; i <= n; i++) yp1[i] = y[i]; /* Load and invert the ewt array. ( h is temporarily set to 1. ) */ nq = 1; h = 1.; ewset(itol, rtol, atol, y); for (i = 1; i <= n; i++) { if (ewt[i] <= 0.) { qDebug("lsoda -- ewt[%d] = %g <= 0.", i, ewt[i]); // ECB Comment out because wrong number of arguments. // terminate( y, yh, t, tn ); return; } ewt[i] = 1. / ewt[i]; } /* The coding below computes the step size, h0, to be attempted on the first step, unless the user has supplied a value for this. First check that tout - *t differs significantly from zero. A scalar tolerance quantity tol is computed, as max(rtol[i]) if this is positive, or max(atol[i]/fabs(y[i])) otherwise, adjusted so as to be between 100*ETA and 0.001. Then the computed value h0 is given by h0^(-2) = 1. / ( tol * w0^2 ) + tol * ( norm(f) )^2 where w0 = max( fabs(*t), fabs(tout) ), f = the initial value of the vector f(t,y), and norm() = the weighted vector norm used throughout, given by the vmnorm function routine, and weighted by the tolerances initially loaded into the ewt array. The sign of h0 is inferred from the initial values of tout and *t. fabs(h0) is made < fabs(tout-*t) in any case. */ if (h0 == 0.) { tdist = fabs(tout - *t); w0 = max(fabs(*t), fabs(tout)); if (tdist < 2. * ETA * w0) { qDebug("lsoda -- tout too close to t to start integration "); terminate(istate); freevectors(); return; } tol = rtol[1]; if (itol > 2) { for (i = 2; i <= n; i++) tol = max(tol, rtol[i]); } if (tol <= 0.) { atoli = atol[1]; for (i = 1; i <= n; i++) { if (itol == 2 || itol == 4) atoli = atol[i]; ayi = fabs(y[i]); if (ayi != 0.) tol = max(tol, atoli / ayi); } } tol = max(tol, 100. * ETA); tol = min(tol, 0.001); sum = vmnorm(n, yh[2], ewt); sum = 1. / (tol * w0 * w0) + tol * sum * sum; h0 = 1. / sqrt(sum); h0 = min(h0, tdist); h0 = h0 * ((tout - *t >= 0.) ? 1. : -1.); } /* end if ( h0 == 0. ) */ /* Adjust h0 if necessary to meet hmax bound. */ rh = fabs(h0) * hmxi; if (rh > 1.) h0 /= rh; /* Load h with h0 and scale yh[2] by h0. */ h = h0; yp1 = yh[2]; for (i = 1; i <= n; i++) yp1[i] *= h0; } /* if ( *istate == 1 ) */ /* Block d. The next code block is for continuation calls only ( *istate = 2 or 3 ) and is to check stop conditions before taking a step. */ int ihit(0); if (*istate == 2 || *istate == 3) { nslast = nst; switch (itask) { case 1: if ((tn - tout) * h >= 0.) { intdy(tout, 0, y, &iflag); if (iflag != 0) { qDebug("lsoda -- trouble from intdy, itask = %d, tout = %g", itask, tout); terminate(istate); freevectors(); return; } *t = tout; *istate = 2; illin = 0; freevectors(); return; } break; case 2: break; case 3: tp = tn - hu * (1. + 100. * ETA); if ((tp - tout) * h > 0.) { qDebug("lsoda -- itask = %d and tout behind tcur - hu", itask); terminate(istate); freevectors(); return; } if ((tn - tout) * h < 0.) break; successreturn(y, t, itask, ihit, tcrit, istate); return; case 4: tcrit = rwork1; if ((tn - tcrit) * h > 0.) { qDebug("lsoda -- itask = 4 or 5 and tcrit behind tcur"); terminate(istate); freevectors(); return; } if ((tcrit - tout) * h < 0.) { qDebug("lsoda -- itask = 4 or 5 and tcrit behind tout"); terminate(istate); freevectors(); return; } if ((tn - tout) * h >= 0.) { intdy(tout, 0, y, &iflag); if (iflag != 0) { qDebug("lsoda -- trouble from intdy, itask = %d, tout = %g", itask, tout); terminate(istate); freevectors(); return; } *t = tout; *istate = 2; illin = 0; freevectors(); return; } case 5: if (itask == 5) { tcrit = rwork1; if ((tn - tcrit) * h > 0.) { qDebug("lsoda -- itask = 4 or 5 and tcrit behind tcur"); terminate(istate); freevectors(); return; } } hmx = fabs(tn) + fabs(h); ihit = fabs(tn - tcrit) <= (100. * ETA * hmx); if (ihit) { *t = tcrit; successreturn(y, t, itask, ihit, tcrit, istate); return; } tnext = tn + h * (1. + 4. * ETA); if ((tnext - tcrit) * h <= 0.) break; h = (tcrit - tn) * (1. - 4. * ETA); if (*istate == 2) jstart = -2; break; } /* end switch */ } /* end if ( *istate == 2 || *istate == 3 ) */ /* Block e. The next block is normally executed for all calls and contains the call to the one-step core integrator stoda. This is a looping point for the integration steps. First check for too many steps being taken, update ewt ( if not at start of problem). Check for too much accuracy being requested, and check for h below the roundoff level in *t. */ while (true) { if (*istate != 1 || nst != 0) { if ((nst - nslast) >= mxstep) { // qDebug( "lsoda -- %d steps taken before reaching tout", // mxstep ); *istate = -1; terminate2(y, t); return; } ewset(itol, rtol, atol, yh[1]); for (i = 1; i <= n; i++) { if (ewt[i] <= 0.) { qDebug("lsoda -- ewt[%d] = %g <= 0.", i, ewt[i]); *istate = -6; terminate2(y, t); return; } ewt[i] = 1. / ewt[i]; } } tolsf = ETA * vmnorm(n, yh[1], ewt); if (tolsf > 0.01) { tolsf = tolsf * 200.; if (nst == 0) { qDebug("lsoda -- at start of problem, too much accuracy"); qDebug(" requested for precision of machine,"); qDebug(" suggested scaling factor = %g", tolsf); terminate(istate); freevectors(); return; } qDebug("lsoda -- at t = %g, too much accuracy requested", *t); qDebug(" for precision of machine, suggested"); qDebug(" scaling factor = %g", tolsf); *istate = -2; terminate2(y, t); return; } if ((tn + h) == tn) { nhnil++; if (nhnil <= mxhnil) { qDebug("lsoda -- warning..internal t = %g and h = %g are", tn, h); qDebug(" such that in the machine, t + h = t on the next step"); qDebug(" solver will continue anyway."); if (nhnil == mxhnil) { qDebug("lsoda -- above warning has been issued %d times,", nhnil); qDebug(" it will not be issued again for this problem"); } } } /* Call stoda */ stoda(neq, y); /* qDebug( "meth= %d, order= %d, nfe= %d, nje= %d", meth, nq, nfe, nje ); qDebug( "t= %20.15e, h= %20.15e, nst=%d", tn, h, nst ); qDebug( "y= %20.15e, %20.15e, %20.15e", yh[1][1], yh[1][2], yh[1][3] ); */ if (kflag == 0) { /* Block f. The following block handles the case of a successful return from the core integrator ( kflag = 0 ). If a method switch was just made, record tsw, reset maxord, set jstart to -1 to signal stoda to complete the switch, and do extra printing of data if ixpr = 1. Then, in any case, check for stop conditions. */ init = 1; if (meth != mused) { tsw = tn; maxord = mxordn; if (meth == 2) maxord = mxords; jstart = -1; if (ixpr) { if (meth == 2) qDebug() << "lsoda -- a switch to the stiff method has occurred"; if (meth == 1) qDebug() << "lsoda -- a switch to the nonstiff method has occurred"; qDebug() << " at t = " << tn << ", tentative step size h = " << h << ", step nst = " << nst; } } /* end if ( meth != mused ) */ /* itask = 1. If tout has been reached, interpolate. */ if (itask == 1) { if ((tn - tout) * h < 0.) continue; intdy(tout, 0, y, &iflag); *t = tout; *istate = 2; illin = 0; freevectors(); return; } /* itask = 2. */ if (itask == 2) { successreturn(y, t, itask, ihit, tcrit, istate); return; } /* itask = 3. Jump to exit if tout was reached. */ if (itask == 3) { if ((tn - tout) * h >= 0.) { successreturn(y, t, itask, ihit, tcrit, istate); return; } continue; } /* itask = 4. See if tout or tcrit was reached. Adjust h if necessary. */ if (itask == 4) { if ((tn - tout) * h >= 0.) { intdy(tout, 0, y, &iflag); *t = tout; *istate = 2; illin = 0; freevectors(); return; } else { hmx = fabs(tn) + fabs(h); ihit = fabs(tn - tcrit) <= (100. * ETA * hmx); if (ihit) { successreturn(y, t, itask, ihit, tcrit, istate); return; } tnext = tn + h * (1. + 4. * ETA); if ((tnext - tcrit) * h <= 0.) continue; h = (tcrit - tn) * (1. - 4. * ETA); jstart = -2; continue; } } /* end if ( itask == 4 ) */ /* itask = 5. See if tcrit was reached and jump to exit. */ if (itask == 5) { hmx = fabs(tn) + fabs(h); ihit = fabs(tn - tcrit) <= (100. * ETA * hmx); successreturn(y, t, itask, ihit, tcrit, istate); return; } } /* end if ( kflag == 0 ) */ /* kflag = -1, error test failed repeatedly or with fabs(h) = hmin. kflag = -2, convergence failed repeatedly or with fabs(h) = hmin. */ if (kflag == -1 || kflag == -2) { qDebug("lsoda -- at t = %g and step size h = %g, the", tn, h); if (kflag == -1) { qDebug(" error test failed repeatedly or"); qDebug(" with fabs(h) = hmin"); *istate = -4; } if (kflag == -2) { qDebug(" corrector convergence failed repeatedly or"); qDebug(" with fabs(h) = hmin"); *istate = -5; } big = 0.; imxer = 1; for (i = 1; i <= n; i++) { size = fabs(acor[i]) * ewt[i]; if (big < size) { big = size; imxer = i; } } terminate2(y, t); return; } /* end if ( kflag == -1 || kflag == -2 ) */ } /* end while */ } /* end lsoda */ void QTAIMLSODAIntegrator::stoda(int neq, double* y) { int corflag, orderflag; int i, i1, j, m, ncf; double del, delp, dup, exup, r, rh, rhup, told; double pdh, pnorm; /* stoda performs one step of the integration of an initial value problem for a system of ordinary differential equations. Note.. stoda is independent of the value of the iteration method indicator miter, when this is != 0, and hence is independent of the type of chord method used, or the Jacobian structure. Communication with stoda is done with the following variables: jstart = an integer used for input only, with the following values and meanings: 0 perform the first step, > 0 take a new step continuing from the last, -1 take the next step with a new value of h, n, meth, miter, and/or matrix parameters. -2 take the next step with a new value of h, but with other inputs unchanged. kflag = a completion code with the following meanings: 0 the step was successful, -1 the requested error could not be achieved, -2 corrector convergence could not be achieved, -3 fatal error in prja or solsy. miter = corrector iteration method: 0 functional iteration, >0 a chord method corresponding to jacobian type jt. */ kflag = 0; told = tn; ncf = 0; ierpj = 0; iersl = 0; jcur = 0; delp = 0.; /* On the first call, the order is set to 1, and other variables are initialized. rmax is the maximum ratio by which h can be increased in a single step. It is initially 1.e4 to compensate for the small initial h, but then is normally equal to 10. If a filure occurs (in corrector convergence or error test), rmax is set at 2 for the next increase. cfode is called to get the needed coefficients for both methods. */ if (jstart == 0) { lmax = maxord + 1; nq = 1; l = 2; ialth = 2; rmax = 10000.; rc = 0.; el0 = 1.; crate = 0.7; hold = h; nslp = 0; ipup = miter; /* Initialize switching parameters. meth = 1 is assumed initially. */ icount = 20; irflag = 0; pdest = 0.; pdlast = 0.; ratio = 5.; cfode(2); for (i = 1; i <= 5; i++) cm2[i] = tesco[i][2] * elco[i][i + 1]; cfode(1); for (i = 1; i <= 12; i++) cm1[i] = tesco[i][2] * elco[i][i + 1]; resetcoeff(); } /* end if ( jstart == 0 ) */ /* The following block handles preliminaries needed when jstart = -1. ipup is set to miter to force a matrix update. If an order increase is about to be considered ( ialth = 1 ), ialth is reset to 2 to postpone consideration one more step. If the caller has changed meth, cfode is called to reset the coefficients of the method. If h is to be changed, yh must be rescaled. If h or meth is being changed, ialth is reset to l = nq + 1 to prevent further changes in h for that many steps. */ if (jstart == -1) { ipup = miter; lmax = maxord + 1; if (ialth == 1) ialth = 2; if (meth != mused) { cfode(meth); ialth = l; resetcoeff(); } if (h != hold) { rh = h / hold; h = hold; scaleh(&rh, &pdh); } } /* if ( jstart == -1 ) */ if (jstart == -2) { if (h != hold) { rh = h / hold; h = hold; scaleh(&rh, &pdh); } } /* if ( jstart == -2 ) */ /* Prediction. This section computes the predicted values by effectively multiplying the yh array by the pascal triangle matrix. rc is the ratio of new to old values of the coefficient h * el[1]. When rc differs from 1 by more than ccmax, ipup is set to miter to force pjac to be called, if a jacobian is involved. In any case, prja is called at least every msbp steps. */ while (true) { while (true) { if (fabs(rc - 1.) > ccmax) ipup = miter; if (nst >= nslp + msbp) ipup = miter; tn += h; for (j = nq; j >= 1; j--) for (i1 = j; i1 <= nq; i1++) { yp1 = yh[i1]; yp2 = yh[i1 + 1]; for (i = 1; i <= n; i++) yp1[i] += yp2[i]; } pnorm = vmnorm(n, yh[1], ewt); correction(neq, y, &corflag, pnorm, &del, &delp, &told, &ncf, &rh, &m); if (corflag == 0) break; if (corflag == 1) { rh = max(rh, hmin / fabs(h)); scaleh(&rh, &pdh); continue; } if (corflag == 2) { kflag = -2; hold = h; jstart = 1; return; } } /* end inner while ( corrector loop ) */ /* The corrector has converged. jcur is set to 0 to signal that the Jacobian involved may need updating later. The local error test is done now. */ jcur = 0; double dsm(0.0); if (m == 0) dsm = del / tesco[nq][2]; if (m > 0) dsm = vmnorm(n, acor, ewt) / tesco[nq][2]; if (dsm <= 1.) { /* After a successful step, update the yh array. Decrease icount by 1, and if it is -1, consider switching methods. If a method switch is made, reset various parameters, rescale the yh array, and exit. If there is no switch, consider changing h if ialth = 1. Otherwise decrease ialth by 1. If ialth is then 1 and nq < maxord, then acor is saved for use in a possible order increase on the next step. If a change in h is considered, an increase or decrease in order by one is considered also. A change in h is made only if it is by a factor of at least 1.1. If not, ialth is set to 3 to prevent testing for that many steps. */ kflag = 0; nst++; hu = h; nqu = nq; mused = meth; for (j = 1; j <= l; j++) { yp1 = yh[j]; r = el[j]; for (i = 1; i <= n; i++) yp1[i] += r * acor[i]; } icount--; if (icount < 0) { methodswitch(dsm, pnorm, &pdh, &rh); if (meth != mused) { rh = max(rh, hmin / fabs(h)); scaleh(&rh, &pdh); rmax = 10.; endstoda(); break; } } /* No method switch is being made. Do the usual step/order selection. */ ialth--; if (ialth == 0) { rhup = 0.; if (l != lmax) { yp1 = yh[lmax]; for (i = 1; i <= n; i++) savf[i] = acor[i] - yp1[i]; dup = vmnorm(n, savf, ewt) / tesco[nq][3]; exup = 1. / (double)(l + 1); rhup = 1. / (1.4 * pow(dup, exup) + 0.0000014); } orderswitch(&rhup, dsm, &pdh, &rh, &orderflag); /* No change in h or nq. */ if (orderflag == 0) { endstoda(); break; } /* h is changed, but not nq. */ if (orderflag == 1) { rh = max(rh, hmin / fabs(h)); scaleh(&rh, &pdh); rmax = 10.; endstoda(); break; } /* both nq and h are changed. */ if (orderflag == 2) { resetcoeff(); rh = max(rh, hmin / fabs(h)); scaleh(&rh, &pdh); rmax = 10.; endstoda(); break; } } /* end if ( ialth == 0 ) */ if (ialth > 1 || l == lmax) { endstoda(); break; } yp1 = yh[lmax]; for (i = 1; i <= n; i++) yp1[i] = acor[i]; endstoda(); break; } /* end if ( dsm <= 1. ) */ /* The error test failed. kflag keeps track of multiple failures. Restore tn and the yh array to their previous values, and prepare to try the step again. Compute the optimum step size for this or one lower. After 2 or more failures, h is forced to decrease by a factor of 0.2 or less. */ else { kflag--; tn = told; for (j = nq; j >= 1; j--) for (i1 = j; i1 <= nq; i1++) { yp1 = yh[i1]; yp2 = yh[i1 + 1]; for (i = 1; i <= n; i++) yp1[i] -= yp2[i]; } rmax = 2.; if (fabs(h) <= hmin * 1.00001) { kflag = -1; hold = h; jstart = 1; break; } if (kflag > -3) { rhup = 0.; orderswitch(&rhup, dsm, &pdh, &rh, &orderflag); if (orderflag == 1 || orderflag == 0) { if (orderflag == 0) rh = min(rh, 0.2); rh = max(rh, hmin / fabs(h)); scaleh(&rh, &pdh); } if (orderflag == 2) { resetcoeff(); rh = max(rh, hmin / fabs(h)); scaleh(&rh, &pdh); } continue; } /* if ( kflag > -3 ) */ /* Control reaches this section if 3 or more failures have occurred. If 10 failures have occurred, exit with kflag = -1. It is assumed that the derivatives that have accumulated in the yh array have errors of the wrong order. Hence the first derivative is recomputed, and the order is set to 1. Then h is reduced by a factor of 10, and the step is retried, until it succeeds or h reaches hmin. */ else { if (kflag == -10) { kflag = -1; hold = h; jstart = 1; break; } else { rh = 0.1; rh = max(hmin / fabs(h), rh); h *= rh; yp1 = yh[1]; for (i = 1; i <= n; i++) y[i] = yp1[i]; f(neq, tn, y, savf); nfe++; yp1 = yh[2]; for (i = 1; i <= n; i++) yp1[i] = h * savf[i]; ipup = miter; ialth = 5; if (nq == 1) continue; nq = 1; l = 2; resetcoeff(); continue; } } /* end else -- kflag <= -3 */ } /* end error failure handling */ } /* end outer while */ } /* end stoda */ void QTAIMLSODAIntegrator::ewset(int itol, double* rtol, double* atol, double* ycur) { int i; switch (itol) { case 1: for (i = 1; i <= n; i++) ewt[i] = rtol[1] * fabs(ycur[i]) + atol[1]; break; case 2: for (i = 1; i <= n; i++) ewt[i] = rtol[1] * fabs(ycur[i]) + atol[i]; break; case 3: for (i = 1; i <= n; i++) ewt[i] = rtol[i] * fabs(ycur[i]) + atol[1]; break; case 4: for (i = 1; i <= n; i++) ewt[i] = rtol[i] * fabs(ycur[i]) + atol[i]; break; } } /* end ewset */ void QTAIMLSODAIntegrator::intdy(double t, int k, double* dky, int* iflag) /* Intdy computes interpolated values of the k-th derivative of the dependent variable vector y, and stores it in dky. This routine is called within the package with k = 0 and *t = tout, but may also be called by the user for any k up to the current order. ( See detailed instructions in the usage documentation. ) The computed values in dky are gotten by interpolation using the Nordsieck history array yh. This array corresponds uniquely to a vector-valued polynomial of degree nqcur or less, and dky is set to the k-th derivative of this polynomial at t. The formula for dky is q dky[i] = sum c[k][j] * ( t - tn )^(j-k) * h^(-j) * yh[j+1][i] j=k where c[k][j] = j*(j-1)*...*(j-k+1), q = nqcur, tn = tcur, h = hcur. The quantities nq = nqcur, l = nq+1, n = neq, tn, and h are declared static globally. The above sum is done in reverse order. *iflag is returned negative if either k or t is out of bounds. */ { int i, ic, j, jj, jp1; double c, r, s, tp; *iflag = 0; if (k < 0 || k > nq) { qDebug("intdy -- k = %d illegal", k); *iflag = -1; return; } tp = tn - hu - 100. * ETA * (tn + hu); if ((t - tp) * (t - tn) > 0.) { qDebug("intdy -- t = %g illegal", t); qDebug(" t not in interval tcur - hu to tcur"); *iflag = -2; return; } s = (t - tn) / h; ic = 1; for (jj = l - k; jj <= nq; jj++) ic *= jj; c = (double)ic; yp1 = yh[l]; for (i = 1; i <= n; i++) dky[i] = c * yp1[i]; for (j = nq - 1; j >= k; j--) { jp1 = j + 1; ic = 1; for (jj = jp1 - k; jj <= j; jj++) ic *= jj; c = (double)ic; yp1 = yh[jp1]; for (i = 1; i <= n; i++) dky[i] = c * yp1[i] + s * dky[i]; } if (k == 0) return; r = pow(h, (double)(-k)); for (i = 1; i <= n; i++) dky[i] *= r; } /* end intdy */ void QTAIMLSODAIntegrator::cfode(int meth_) { int i, nq_, nqm1, nqp1; double agamq, fnq, fnqm1, pc[13], pint, ragq, rqfac, rq1fac, tsign, xpin; /* cfode is called by the integrator routine to set coefficients needed there. The coefficients for the current method, as given by the value of meth, are set for all orders and saved. The maximum order assumed here is 12 if meth = 1 and 5 if meth = 2. ( A smaller value of the maximum order is also allowed. ) cfode is called once at the beginning of the problem, and is not called again unless and until meth is changed. The elco array contains the basic method coefficients. The coefficients el[i], 1 < i < nq+1, for the method of order nq are stored in elco[nq][i]. They are given by a generating polynomial, i.e., l(x) = el[1] + el[2]*x + ... + el[nq+1]*x^nq. For the implicit Adams method, l(x) is given by dl/dx = (x+1)*(x+2)*...*(x+nq-1)/factorial(nq-1), l(-1) = 0. For the bdf methods, l(x) is given by l(x) = (x+1)*(x+2)*...*(x+nq)/k, where k = factorial(nq)*(1+1/2+...+1/nq). The tesco array contains test constants used for the local error test and the selection of step size and/or order. At order nq, tesco[nq][k] is used for the selection of step size at order nq-1 if k = 1, at order nq if k = 2, and at order nq+1 if k = 3. */ if (meth_ == 1) { elco[1][1] = 1.; elco[1][2] = 1.; tesco[1][1] = 0.; tesco[1][2] = 2.; tesco[2][1] = 1.; tesco[12][3] = 0.; pc[1] = 1.; rqfac = 1.; for (nq_ = 2; nq_ <= 12; nq_++) { /* The pc array will contain the coefficients of the polynomial p(x) = (x+1)*(x+2)*...*(x+nq-1). Initially, p(x) = 1. */ rq1fac = rqfac; rqfac = rqfac / (double)nq_; nqm1 = nq_ - 1; fnqm1 = (double)nqm1; nqp1 = nq_ + 1; /* Form coefficients of p(x)*(x+nq-1). */ pc[nq_] = 0.; for (i = nq_; i >= 2; i--) pc[i] = pc[i - 1] + fnqm1 * pc[i]; pc[1] = fnqm1 * pc[1]; /* Compute integral, -1 to 0, of p(x) and x*p(x). */ pint = pc[1]; xpin = pc[1] / 2.; tsign = 1.; for (i = 2; i <= nq_; i++) { tsign = -tsign; pint += tsign * pc[i] / (double)i; xpin += tsign * pc[i] / (double)(i + 1); } /* Store coefficients in elco and tesco. */ elco[nq_][1] = pint * rq1fac; elco[nq_][2] = 1.; for (i = 2; i <= nq_; i++) elco[nq_][i + 1] = rq1fac * pc[i] / (double)i; agamq = rqfac * xpin; ragq = 1. / agamq; tesco[nq_][2] = ragq; if (nq_ < 12) tesco[nqp1][1] = ragq * rqfac / (double)nqp1; tesco[nqm1][3] = ragq; } /* end for */ return; } /* end if ( meth == 1 ) */ /* meth = 2. */ pc[1] = 1.; rq1fac = 1.; /* The pc array will contain the coefficients of the polynomial p(x) = (x+1)*(x+2)*...*(x+nq). Initially, p(x) = 1. */ for (nq_ = 1; nq_ <= 5; nq_++) { fnq = (double)nq_; nqp1 = nq_ + 1; /* Form coefficients of p(x)*(x+nq). */ pc[nqp1] = 0.; for (i = nq_ + 1; i >= 2; i--) pc[i] = pc[i - 1] + fnq * pc[i]; pc[1] *= fnq; /* Store coefficients in elco and tesco. */ for (i = 1; i <= nqp1; i++) elco[nq_][i] = pc[i] / pc[2]; elco[nq_][2] = 1.; tesco[nq_][1] = rq1fac; tesco[nq_][2] = ((double)nqp1) / elco[nq_][1]; tesco[nq_][3] = ((double)(nq_ + 2)) / elco[nq_][1]; rq1fac /= fnq; } } /* end cfode */ void QTAIMLSODAIntegrator::scaleh(double* rh, double* pdh) { double r; int j, i; /* If h is being changed, the h ratio rh is checked against rmax, hmin, and hmxi, and the yh array is rescaled. ialth is set to l = nq + 1 to prevent a change of h for that many steps, unless forced by a convergence or error test failure. */ *rh = min(*rh, rmax); *rh = *rh / max(1., fabs(h) * hmxi * *rh); /* If meth = 1, also restrict the new step size by the stability region. If this reduces h, set irflag to 1 so that if there are roundoff problems later, we can assume that is the cause of the trouble. */ if (meth == 1) { irflag = 0; *pdh = max(fabs(h) * pdlast, 0.000001); if ((*rh * *pdh * 1.00001) >= sm1[nq]) { *rh = sm1[nq] / *pdh; irflag = 1; } } r = 1.; for (j = 2; j <= l; j++) { r *= *rh; yp1 = yh[j]; for (i = 1; i <= n; i++) yp1[i] *= r; } h *= *rh; rc *= *rh; ialth = l; } /* end scaleh */ void QTAIMLSODAIntegrator::prja(int neq, double* y) { int i, ier, j; double fac, hl0, r, r0, yj; /* prja is called by stoda to compute and process the matrix P = I - h * el[1] * J, where J is an approximation to the Jacobian. Here J is computed by finite differencing. J, scaled by -h * el[1], is stored in wm. Then the norm of J ( the matrix norm consistent with the weighted max-norm on vectors given by vmnorm ) is computed, and J is overwritten by P. P is then subjected to LU decomposition in preparation for later solution of linear systems with p as coefficient matrix. This is done by dgefa if miter = 2, and by dgbfa if miter = 5. */ nje++; ierpj = 0; jcur = 1; hl0 = h * el0; /* If miter = 2, make n calls to f to approximate J. */ if (miter != 2) { qDebug("prja -- miter != 2"); return; } if (miter == 2) { fac = vmnorm(n, savf, ewt); r0 = 1000. * fabs(h) * ETA * ((double)n) * fac; if (r0 == 0.) r0 = 1.; for (j = 1; j <= n; j++) { yj = y[j]; r = max(sqrteta * fabs(yj), r0 / ewt[j]); y[j] += r; fac = -hl0 / r; f(neq, tn, y, acor); for (i = 1; i <= n; i++) wm[i][j] = (acor[i] - savf[i]) * fac; y[j] = yj; } nfe += n; /* Compute norm of Jacobian. */ pdnorm = fnorm(n, wm, ewt) / fabs(hl0); /* Add identity matrix. */ for (i = 1; i <= n; i++) wm[i][i] += 1.; /* Do LU decomposition on P. */ dgefa(wm, n, ipvt, &ier); if (ier != 0) ierpj = 1; return; } } /* end prja */ double QTAIMLSODAIntegrator::vmnorm(int n_, double* v, double* w) /* This function routine computes the weighted max-norm of the vector of length n contained in the array v, with weights contained in the array w of length n. vmnorm = max( i = 1, ..., n ) fabs( v[i] ) * w[i]. */ { int i; double vm; vm = 0.; for (i = 1; i <= n_; i++) vm = max(vm, fabs(v[i]) * w[i]); return vm; } /* end vmnorm */ double QTAIMLSODAIntegrator::fnorm(int n_, double** a, double* w) /* This subroutine computes the norm of a full n by n matrix, stored in the array a, that is consistent with the weighted max-norm on vectors, with weights stored in the array w. fnorm = max(i=1,...,n) ( w[i] * sum(j=1,...,n) fabs( a[i][j] ) / w[j] ) */ { int i, j; double an, sum, *ap1; an = 0.; for (i = 1; i <= n_; i++) { sum = 0.; ap1 = a[i]; for (j = 1; j <= n_; j++) sum += fabs(ap1[j]) / w[j]; an = max(an, sum * w[i]); } return an; } /* end fnorm */ // double QTAIMLSODAIntegrator::bnorm() // { // } /* end bnorm */ void QTAIMLSODAIntegrator::correction(int neq, double* y, int* corflag, double pnorm, double* del, double* delp, double* told, int* ncf, double* rh, int* m) /* *corflag = 0 : corrector converged, 1 : step size to be reduced, redo prediction, 2 : corrector cannot converge, failure flag. */ { int i; double rm, rate, dcon; /* Up to maxcor corrector iterations are taken. A convergence test is made on the r.m.s. norm of each correction, weighted by the error weight vector ewt. The sum of the corrections is accumulated in the vector acor[i]. The yh array is not altered in the corrector loop. */ *m = 0; *corflag = 0; rate = 0.; *del = 0.; yp1 = yh[1]; for (i = 1; i <= n; i++) y[i] = yp1[i]; f(neq, tn, y, savf); nfe++; /* If indicated, the matrix P = I - h * el[1] * J is reevaluated and preprocessed before starting the corrector iteration. ipup is set to 0 as an indicator that this has been done. */ while (true) { if (*m == 0) { if (ipup > 0) { prja(neq, y); ipup = 0; rc = 1.; nslp = nst; crate = 0.7; if (ierpj != 0) { corfailure(told, rh, ncf, corflag); return; } } for (i = 1; i <= n; i++) acor[i] = 0.; } /* end if ( *m == 0 ) */ if (miter == 0) { /* In case of functional iteration, update y directly from the result of the last function evaluation. */ yp1 = yh[2]; for (i = 1; i <= n; i++) { savf[i] = h * savf[i] - yp1[i]; y[i] = savf[i] - acor[i]; } *del = vmnorm(n, y, ewt); yp1 = yh[1]; for (i = 1; i <= n; i++) { y[i] = yp1[i] + el[1] * savf[i]; acor[i] = savf[i]; } } /* end functional iteration */ /* In the case of the chord method, compute the corrector error, and solve the linear system with that as right-hand side and P as coefficient matrix. */ else { yp1 = yh[2]; for (i = 1; i <= n; i++) y[i] = h * savf[i] - (yp1[i] + acor[i]); solsy(y); *del = vmnorm(n, y, ewt); yp1 = yh[1]; for (i = 1; i <= n; i++) { acor[i] += y[i]; y[i] = yp1[i] + el[1] * acor[i]; } } /* end chord method */ /* Test for convergence. If *m > 0, an estimate of the convergence rate constant is stored in crate, and this is used in the test. We first check for a change of iterates that is the size of roundoff error. If this occurs, the iteration has converged, and a new rate estimate is not formed. In all other cases, force at least two iterations to estimate a local Lipschitz constant estimate for Adams method. On convergence, form pdest = local maximum Lipschitz constant estimate. pdlast is the most recent nonzero estimate. */ if (*del <= 100. * pnorm * ETA) break; if (*m != 0 || meth != 1) { if (*m != 0) { rm = 1024.0; if (*del <= (1024. * *delp)) rm = *del / *delp; rate = max(rate, rm); crate = max(0.2 * crate, rm); } dcon = *del * min(1., 1.5 * crate) / (tesco[nq][2] * conit); if (dcon <= 1.) { pdest = max(pdest, rate / fabs(h * el[1])); if (pdest != 0.) pdlast = pdest; break; } } /* The corrector iteration failed to converge. If miter != 0 and the Jacobian is out of date, prja is called for the next try. Otherwise the yh array is retracted to its values before prediction, and h is reduced, if possible. If h cannot be reduced or mxncf failures have occurred, exit with corflag = 2. */ (*m)++; if (*m == maxcor || (*m >= 2 && *del > 2. * *delp)) { if (miter == 0 || jcur == 1) { corfailure(told, rh, ncf, corflag); return; } ipup = miter; /* Restart corrector if Jacobian is recomputed. */ *m = 0; rate = 0.; *del = 0.; yp1 = yh[1]; for (i = 1; i <= n; i++) y[i] = yp1[i]; f(neq, tn, y, savf); nfe++; } /* Iterate corrector. */ else { *delp = *del; f(neq, tn, y, savf); nfe++; } } /* end while */ } /* end correction */ void QTAIMLSODAIntegrator::corfailure(double* told, double* rh, int* ncf, int* corflag) { int j, i1, i; (*ncf)++; rmax = 2.; tn = *told; for (j = nq; j >= 1; j--) for (i1 = j; i1 <= nq; i1++) { yp1 = yh[i1]; yp2 = yh[i1 + 1]; for (i = 1; i <= n; i++) yp1[i] -= yp2[i]; } if (fabs(h) <= hmin * 1.00001 || *ncf == mxncf) { *corflag = 2; return; } *corflag = 1; *rh = 0.25; ipup = miter; } /* end corfailure */ void QTAIMLSODAIntegrator::solsy(double* y) /* This routine manages the solution of the linear system arising from a chord iteration. It is called if miter != 0. If miter is 2, it calls dgesl to accomplish this. If miter is 5, it calls dgbsl. y = the right-hand side vector on input, and the solution vector on output. */ { iersl = 0; if (miter != 2) { qDebug("solsy -- miter != 2"); return; } if (miter == 2) dgesl(wm, n, ipvt, y, 0); } /* end solsy */ void QTAIMLSODAIntegrator::methodswitch(double dsm, double pnorm, double* pdh, double* rh) { int lm1, lm1p1, lm2, lm2p1, nqm1, nqm2; double rh1, rh2, rh1it, exm2, dm2, exm1, dm1, alpha, exsm; /* We are current using an Adams method. Consider switching to bdf. If the current order is greater than 5, assume the problem is not stiff, and skip this section. If the Lipschitz constant and error estimate are not polluted by roundoff, perform the usual test. Otherwise, switch to the bdf methods if the last step was restricted to insure stability ( irflag = 1 ), and stay with Adams method if not. When switching to bdf with polluted error estimates, in the absence of other information, double the step size. When the estimates are ok, we make the usual test by computing the step size we could have (ideally) used on this step, with the current (Adams) method, and also that for the bdf. If nq > mxords, we consider changing to order mxords on switching. Compare the two step sizes to decide whether to switch. The step size advantage must be at least ratio = 5 to switch. */ if (meth == 1) { if (nq > 5) return; if (dsm <= (100. * pnorm * ETA) || pdest == 0.) { if (irflag == 0) return; rh2 = 2.; nqm2 = min(nq, mxords); } else { exsm = 1. / (double)l; rh1 = 1. / (1.2 * pow(dsm, exsm) + 0.0000012); rh1it = 2. * rh1; *pdh = pdlast * fabs(h); if ((*pdh * rh1) > 0.00001) rh1it = sm1[nq] / *pdh; rh1 = min(rh1, rh1it); if (nq > mxords) { nqm2 = mxords; lm2 = mxords + 1; exm2 = 1. / (double)lm2; lm2p1 = lm2 + 1; dm2 = vmnorm(n, yh[lm2p1], ewt) / cm2[mxords]; rh2 = 1. / (1.2 * pow(dm2, exm2) + 0.0000012); } else { dm2 = dsm * (cm1[nq] / cm2[nq]); rh2 = 1. / (1.2 * pow(dm2, exsm) + 0.0000012); nqm2 = nq; } if (rh2 < ratio * rh1) return; } /* The method switch test passed. Reset relevant quantities for bdf. */ *rh = rh2; icount = 20; meth = 2; miter = jtyp; pdlast = 0.; nq = nqm2; l = nq + 1; return; } /* end if ( meth == 1 ) */ /* We are currently using a bdf method, considering switching to Adams. Compute the step size we could have (ideally) used on this step, with the current (bdf) method, and also that for the Adams. If nq > mxordn, we consider changing to order mxordn on switching. Compare the two step sizes to decide whether to switch. The step size advantage must be at least 5/ratio = 1 to switch. If the step size for Adams would be so small as to cause roundoff pollution, we stay with bdf. */ exsm = 1. / (double)l; if (mxordn < nq) { nqm1 = mxordn; lm1 = mxordn + 1; exm1 = 1. / (double)lm1; lm1p1 = lm1 + 1; dm1 = vmnorm(n, yh[lm1p1], ewt) / cm1[mxordn]; rh1 = 1. / (1.2 * pow(dm1, exm1) + 0.0000012); } else { dm1 = dsm * (cm2[nq] / cm1[nq]); rh1 = 1. / (1.2 * pow(dm1, exsm) + 0.0000012); nqm1 = nq; exm1 = exsm; } rh1it = 2. * rh1; *pdh = pdnorm * fabs(h); if ((*pdh * rh1) > 0.00001) rh1it = sm1[nqm1] / *pdh; rh1 = min(rh1, rh1it); rh2 = 1. / (1.2 * pow(dsm, exsm) + 0.0000012); if ((rh1 * ratio) < (5. * rh2)) return; alpha = max(0.001, rh1); dm1 *= pow(alpha, exm1); if (dm1 <= 1000. * ETA * pnorm) return; /* The switch test passed. Reset relevant quantities for Adams. */ *rh = rh1; icount = 20; meth = 1; miter = 0; pdlast = 0.; nq = nqm1; l = nq + 1; } /* end methodswitch */ /* This routine returns from stoda to lsoda. Hence freevectors() is not executed. */ void QTAIMLSODAIntegrator::endstoda() { double r; int i; r = 1. / tesco[nqu][2]; for (i = 1; i <= n; i++) acor[i] *= r; hold = h; jstart = 1; } /* end endstoda */ void QTAIMLSODAIntegrator::orderswitch(double* rhup, double dsm, double* pdh, double* rh, int* orderflag) /* Regardless of the success or failure of the step, factors rhdn, rhsm, and rhup are computed, by which h could be multiplied at order nq - 1, order nq, or order nq + 1, respectively. In the case of a failure, rhup = 0. to avoid an order increase. The largest of these is determined and the new order chosen accordingly. If the order is to be increased, we compute one additional scaled derivative. orderflag = 0 : no change in h or nq, 1 : change in h but not nq, 2 : change in both h and nq. */ { int newq, i; double exsm, rhdn, rhsm, ddn, exdn, r; *orderflag = 0; exsm = 1. / (double)l; rhsm = 1. / (1.2 * pow(dsm, exsm) + 0.0000012); rhdn = 0.; if (nq != 1) { ddn = vmnorm(n, yh[l], ewt) / tesco[nq][1]; exdn = 1. / (double)nq; rhdn = 1. / (1.3 * pow(ddn, exdn) + 0.0000013); } /* If meth = 1, limit rh according to the stability region also. */ if (meth == 1) { *pdh = max(fabs(h) * pdlast, 0.000001); if (l < lmax) *rhup = min(*rhup, sm1[l] / *pdh); rhsm = min(rhsm, sm1[nq] / *pdh); if (nq > 1) rhdn = min(rhdn, sm1[nq - 1] / *pdh); pdest = 0.; } if (rhsm >= *rhup) { if (rhsm >= rhdn) { newq = nq; *rh = rhsm; } else { newq = nq - 1; *rh = rhdn; if (kflag < 0 && *rh > 1.) *rh = 1.; } } else { if (*rhup <= rhdn) { newq = nq - 1; *rh = rhdn; if (kflag < 0 && *rh > 1.) *rh = 1.; } else { *rh = *rhup; if (*rh >= 1.1) { r = el[l] / (double)l; nq = l; l = nq + 1; yp1 = yh[l]; for (i = 1; i <= n; i++) yp1[i] = acor[i] * r; *orderflag = 2; return; } else { ialth = 3; return; } } } /* If meth = 1 and h is restricted by stability, bypass 10 percent test. */ if (meth == 1) { if ((*rh * *pdh * 1.00001) < sm1[newq]) if (kflag == 0 && *rh < 1.1) { ialth = 3; return; } } else { if (kflag == 0 && *rh < 1.1) { ialth = 3; return; } } if (kflag <= -2) *rh = min(*rh, 0.2); /* If there is a change of order, reset nq, l, and the coefficients. In any case h is reset according to rh and the yh array is rescaled. Then exit or redo the step. */ if (newq == nq) { *orderflag = 1; return; } nq = newq; l = nq + 1; *orderflag = 2; } /* end orderswitch */ void QTAIMLSODAIntegrator::resetcoeff() /* The el vector and related constants are reset whenever the order nq is changed, or at the start of the problem. */ { int i; double* ep1; ep1 = elco[nq]; for (i = 1; i <= l; i++) el[i] = ep1[i]; rc = rc * el[1] / el0; el0 = el[1]; conit = 0.5 / (double)(nq + 2); } /* end resetcoeff */ void QTAIMLSODAIntegrator::freevectors() { int i; for (i = 1; i <= m_lenyh; ++i) { free(yh[i]); } free(yh); for (i = 1; i <= m_nyh; ++i) { free(wm[i]); } free(wm); free(ewt); free(savf); free(acor); free(ipvt); } /* end freevectors */ } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimlsodaintegrator.h000066400000000000000000000113711506155467400257170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef QTAIMLSODAINTEGRATOR_H #define QTAIMLSODAINTEGRATOR_H #include "qtaimwavefunctionevaluator.h" #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { class QTAIMLSODAIntegrator { public: enum { SteepestAscentPathInElectronDensity = 0, CMBPMinusThreeGradientInElectronDensity = 1, CMBPMinusOneGradientInElectronDensity = 2, CMBPPlusOneGradientInElectronDensity = 3, CMBPPlusThreeGradientInElectronDensity = 4, CMBPMinusThreeGradientInElectronDensityLaplacian = 5, CMBPMinusOneGradientInElectronDensityLaplacian = 6, CMBPPlusOneGradientInElectronDensityLaplacian = 7, CMBPPlusThreeGradientInElectronDensityLaplacian = 8 }; explicit QTAIMLSODAIntegrator(QTAIMWavefunctionEvaluator& eval, const qint64 mode); QVector3D integrate(QVector3D x0y0z0); qint64 status() const { return m_status; } const QList path() const { return m_path; } void setBetaSpheres(QList> betaSpheres) { m_betaSpheres = betaSpheres; } qint64 associatedSphere() const { return m_associatedSphere; } private: QTAIMWavefunctionEvaluator* m_eval; qint64 m_mode; qint64 m_status; QList m_path; QList> m_betaSpheres; qint64 m_associatedSphere; // LSODA integrator void f(int neq, double t, double* y, double* ydot); void daxpy(int n, double da, double* dx, int incx, double* dy, int incy); double ddot(int n, double* dx, int incx, double* dy, int incy); void dgefa(double** a, int n, int* ipvt, int* info); void dgesl(double** a, int n, int* ipvt, double* b, int job); void dscal(int n, double da, double* dx, int incx); int idamax(int n, double* dx, int incx); void terminate(int* istate); void terminate2(double* y, double* t); void successreturn(double* y, double* t, int itask, int ihit, double tcrit, int* istate); void lsoda(int neq, double* y, double* t, double tout, int itol, double* rtol, double* atol, int itask, int* istate, int iopt, int jt, int iwork1, int iwork2, int iwork5, int iwork6, int iwork7, int iwork8, int iwork9, double rwork1, double rwork5, double rwork6, double rwork7); void stoda(int neq, double* y); void ewset(int itol, double* rtol, double* atol, double* ycur); void intdy(double t, int k, double* dky, int* iflag); void cfode(int meth); void scaleh(double* rh, double* pdh); void prja(int neq, double* y); double vmnorm(int n, double* v, double* w); double fnorm(int n, double** a, double* w); // double bnorm(); void correction(int neq, double* y, int* corflag, double pnorm, double* del, double* delp, double* told, int* ncf, double* rh, int* m); void corfailure(double* told, double* rh, int* ncf, int* corflag); void solsy(double* y); void methodswitch(double dsm, double pnorm, double* pdh, double* rh); void endstoda(); void orderswitch(double* rhup, double dsm, double* pdh, double* rh, int* orderflag); void resetcoeff(); void freevectors(); /* newly added static variables */ int ml, mu, imxer; int mord[3]; double sqrteta, *yp1, *yp2; double sm1[13]; /* static variables for lsoda() */ double ccmax, el0, h, hmin, hmxi, hu, rc, tn; int illin, init, mxstep, mxhnil, nhnil, ntrep, nslast, nyh, ierpj, iersl, jcur, jstart, kflag, l, meth, miter, maxord, maxcor, msbp, mxncf, n, nq, nst, nfe, nje, nqu; double tsw, pdnorm; int ixpr, jtyp, mused, mxordn, mxords; /* no static variable for prja(), solsy() */ /* static variables for stoda() */ double conit, crate, el[14], elco[13][14], hold, rmax, tesco[13][4]; int ialth, ipup, lmax, meo, nslp; double pdest, pdlast, ratio, cm1[13], cm2[6]; int icount, irflag; /* static variable for block data */ int mesflg; /* static variables for various vectors and the Jacobian. */ double **yh, **wm, *ewt, *savf, *acor; int* ipvt; int m_lenyh; int m_nyh; }; } // namespace Avogadro::QtPlugins #endif // QTAIMLSODAINTEGRATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimmathutilities.cpp000066400000000000000000000173671506155467400257510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "qtaimmathutilities.h" #include #include #include namespace Avogadro::QtPlugins::QTAIMMathUtilities { Matrix eigenvaluesOfASymmetricThreeByThreeMatrix( const Matrix& A) { SelfAdjointEigenSolver> eigensolver(A); return eigensolver.eigenvalues(); } Matrix eigenvectorsOfASymmetricThreeByThreeMatrix( const Matrix& A) { SelfAdjointEigenSolver> eigensolver(A); return eigensolver.eigenvectors(); } Matrix eigenvaluesOfASymmetricFourByFourMatrix( const Matrix& A) { SelfAdjointEigenSolver> eigensolver(A); return eigensolver.eigenvalues(); } Matrix eigenvectorsOfASymmetricFourByFourMatrix( const Matrix& A) { SelfAdjointEigenSolver> eigensolver(A); return eigensolver.eigenvectors(); } qint64 signOfARealNumber(qreal x) { if (x > 0.) return 1; else if (x == 0.) return 0; else return -1; } qint64 signatureOfASymmetricThreeByThreeMatrix(const Matrix& A) { SelfAdjointEigenSolver> eigensolver(A); Matrix eigenvalues = eigensolver.eigenvalues(); return signOfARealNumber(eigenvalues(0)) + signOfARealNumber(eigenvalues(1)) + signOfARealNumber(eigenvalues(2)); } qreal ellipticityOfASymmetricThreeByThreeMatrix(const Matrix& A) { SelfAdjointEigenSolver> eigensolver(A); Matrix eigenvalues = eigensolver.eigenvalues(); return (eigenvalues(0) / eigenvalues(1)) - 1.0; } qreal distance(const Matrix& a, const Matrix& b) { return std::hypot(a(0) - b(0), a(1) - b(1), a(2) - b(2)); } Matrix sphericalToCartesian(const Matrix& rtp, const Matrix& x0y0z0) { qreal r = rtp(0); qreal theta = rtp(1); qreal phi = rtp(2); qreal x0 = x0y0z0(0); qreal y0 = x0y0z0(1); qreal z0 = x0y0z0(2); qreal costheta = cos(theta); qreal cosphi = cos(phi); qreal sintheta = sin(theta); qreal sinphi = sin(phi); Matrix xyz(r * cosphi * sintheta + x0, r * sintheta * sinphi + y0, r * costheta + z0); return xyz; } Matrix sphericalToCartesian(const Matrix& rtp) { Matrix x0y0z0(0., 0., 0.); return sphericalToCartesian(rtp, x0y0z0); } Matrix cartesianToSpherical(const Matrix& xyz, const Matrix& x0y0z0) { qreal x = xyz(0); qreal y = xyz(1); qreal z = xyz(2); qreal x0 = x0y0z0(0); qreal y0 = x0y0z0(1); qreal z0 = x0y0z0(2); qreal xshift = x - x0; qreal yshift = y - y0; qreal zshift = z - z0; qreal length = std::hypot(xshift, yshift, zshift); Matrix rtp; if (length == 0.) rtp << x0, y0, z0; else if (xshift == 0. && yshift == 0.) rtp << length, acos(zshift / length), 0.; else rtp << length, acos(zshift / length), atan2(xshift, yshift); return rtp; } Matrix cartesianToSpherical(const Matrix& xyz) { Matrix x0y0z0(0., 0., 0.); return cartesianToSpherical(xyz, x0y0z0); } // Cerjan-Miller-Baker-Popelier Methods // // Based on: // Popelier, P.L.A. Comput. Phys. Comm. 1996, 93, 212. Matrix minusThreeSignatureLocatorGradient( const Matrix& g, const Matrix& H) { Matrix value; Matrix b = eigenvaluesOfASymmetricThreeByThreeMatrix(H); Matrix U = eigenvectorsOfASymmetricThreeByThreeMatrix(H); Matrix F = U.transpose() * g; Matrix A; A << b(0), 0., 0., F(0), 0., b(1), 0., F(1), 0., 0., b(2), F(2), F(0), F(1), F(2), 0.; Matrix eval = eigenvaluesOfASymmetricFourByFourMatrix(A); Matrix lambda; lambda << eval(3), eval(3), eval(3); Matrix denom; denom = b - lambda; for (qint64 i = 0; i < 3; ++i) if (denom(i) < SMALL) denom(i) = denom(i) + SMALL; Matrix h; h << 0., 0., 0.; for (qint64 j = 0; j < 3; ++j) for (qint64 i = 0; i < 3; ++i) h(j) = h(j) + (-F(i) * U(j, i)) / denom(i); value = h; return value; } Matrix minusOneSignatureLocatorGradient( const Matrix& g, const Matrix& H) { Matrix value; Matrix b = eigenvaluesOfASymmetricThreeByThreeMatrix(H); Matrix U = eigenvectorsOfASymmetricThreeByThreeMatrix(H); Matrix F = U.transpose() * g; Matrix A; A << b(0), 0., F(0), 0., b(1), F(1), F(0), F(1), 0.; Matrix eval = eigenvaluesOfASymmetricThreeByThreeMatrix(A); Matrix lambda; lambda << eval(2), eval(2), (0.5) * (b(2) - std::hypot(b(2), 2 * F(2))); Matrix denom; denom = b - lambda; for (qint64 i = 0; i < 3; ++i) if (denom(i) < SMALL) denom(i) = denom(i) + SMALL; Matrix h; h << 0., 0., 0.; for (qint64 j = 0; j < 3; ++j) for (qint64 i = 0; i < 3; ++i) h(j) = h(j) + (-F(i) * U(j, i)) / denom(i); value = h; return value; } Matrix plusOneSignatureLocatorGradient( const Matrix& g, const Matrix& H) { Matrix value; Matrix b = eigenvaluesOfASymmetricThreeByThreeMatrix(H); Matrix U = eigenvectorsOfASymmetricThreeByThreeMatrix(H); Matrix F = U * g; Matrix A; A << b(1), 0., F(1), 0., b(2), F(2), F(1), F(2), 0.; Matrix eval = eigenvaluesOfASymmetricThreeByThreeMatrix(A); Matrix lambda; lambda << eval(2), eval(2), (0.5) * (b(0) + std::hypot(b(0), 2.0 * F(0))); Matrix denom; denom = b - lambda; for (qint64 i = 0; i < 3; ++i) if (denom(i) < SMALL) denom(i) = denom(i) + SMALL; Matrix h; h << 0., 0., 0.; for (qint64 j = 0; j < 3; ++j) for (qint64 i = 0; i < 3; ++i) h(j) = h(j) + (-F(i) * U(i, j)) / denom(i); value = h; return value; } Matrix plusThreeSignatureLocatorGradient( const Matrix& g, const Matrix& H) { Matrix value; Matrix b = eigenvaluesOfASymmetricThreeByThreeMatrix(H); Matrix U = eigenvectorsOfASymmetricThreeByThreeMatrix(H); Matrix F = U * g; Matrix A; A << b(0), 0., 0., F(0), 0., b(1), 0., F(1), 0., 0., b(2), F(2), F(0), F(1), F(2), 0.; Matrix eval = eigenvaluesOfASymmetricFourByFourMatrix(A); Matrix lambda; lambda << eval(0), eval(0), eval(0); Matrix denom; denom = b - lambda; for (qint64 i = 0; i < 3; ++i) if (denom(i) < SMALL) denom(i) = denom(i) + SMALL; Matrix h; h << 0., 0., 0.; for (qint64 j = 0; j < 3; ++j) for (qint64 i = 0; i < 3; ++i) h(j) = h(j) + (-F(i) * U(i, j)) / denom(i); value = h; return value; } } // namespace Avogadro::QtPlugins::QTAIMMathUtilities avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimmathutilities.h000066400000000000000000000050611506155467400254020ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef QTAIMMATHUTILITIES_H #define QTAIMMATHUTILITIES_H #include #include using namespace Eigen; namespace Avogadro::QtPlugins::QTAIMMathUtilities { Matrix eigenvaluesOfASymmetricThreeByThreeMatrix( const Matrix& A); Matrix eigenvectorsOfASymmetricThreeByThreeMatrix( const Matrix& A); Matrix eigenvaluesOfASymmetricFourByFourMatrix( const Matrix& A); Matrix eigenvectorsOfASymmetricFourByFourMatrix( const Matrix& A); qint64 signOfARealNumber(qreal x); qint64 signatureOfASymmetricThreeByThreeMatrix(const Matrix& A); qreal ellipticityOfASymmetricThreeByThreeMatrix(const Matrix& A); qreal distance(const Matrix& a, const Matrix& b); Matrix sphericalToCartesian(const Matrix& rtp, const Matrix& x0y0z0); Matrix sphericalToCartesian(const Matrix& rtp); Matrix cartesianToSpherical(const Matrix& xyz, const Matrix& x0y0z0); Matrix cartesianToSpherical(const Matrix& xyz); // Cerjan-Miller-Baker-Popelier Methods // A small number to prevent divide by zero in CMBP routines #define SMALL 1.e-10 Matrix minusThreeSignatureLocatorGradient( const Matrix& g, const Matrix& H); Matrix minusOneSignatureLocatorGradient( const Matrix& g, const Matrix& H); Matrix plusOneSignatureLocatorGradient( const Matrix& g, const Matrix& H); Matrix plusThreeSignatureLocatorGradient( const Matrix& g, const Matrix& H); } // namespace Avogadro::QtPlugins::QTAIMMathUtilities #endif // QTAIMMATHUTILITIES_H avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimodeintegrator.cpp000066400000000000000000000763511506155467400257300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ /* Based on codes written by Herman Watts, Lawrence Shampine, and John Burkardt. */ #include "qtaimodeintegrator.h" #include "qtaimmathutilities.h" namespace Avogadro::QtPlugins { QTAIMODEIntegrator::QTAIMODEIntegrator(QTAIMWavefunctionEvaluator& eval, const qint64 mode) : m_eval(&eval), m_mode(mode), m_associatedSphere(0) { m_betaSpheres.empty(); } QVector3D QTAIMODEIntegrator::integrate(QVector3D x0y0z0) { qreal x0 = x0y0z0.x(); qreal y0 = x0y0z0.y(); qreal z0 = x0y0z0.z(); const qint64 NEQN = 3; abserr_save = -1.0; flag_save = -1000; h = -1.0; init = -1000; kflag = -1000; kop = -1; nfe = -1; relerr_save = -1.0; remin = 1.0e-12; qreal abserr; qint64 flag; qint64 i_step; qint64 n_step; qreal relerr; qreal t; qreal t_out; qreal t_start; qreal t_stop; qreal y[3]; qreal yp[3]; switch (m_mode) { case SteepestAscentPathInElectronDensity: abserr = 1.e-5; // sqrt ( r8_epsilon ( ) ); relerr = 1.e10; // sqrt ( r8_epsilon ( ) ); t_start = 0.0; t_stop = 10.0; n_step = t_stop * 20; break; case CMBPMinusThreeGradientInElectronDensity: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 1000; break; case CMBPMinusOneGradientInElectronDensity: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 100; break; case CMBPPlusOneGradientInElectronDensity: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 200; break; case CMBPPlusThreeGradientInElectronDensity: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 200; break; case CMBPMinusThreeGradientInElectronDensityLaplacian: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 200; break; case CMBPMinusOneGradientInElectronDensityLaplacian: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 200; break; case CMBPPlusOneGradientInElectronDensityLaplacian: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 200; break; case CMBPPlusThreeGradientInElectronDensityLaplacian: abserr = sqrt(r8_epsilon()); relerr = sqrt(r8_epsilon()); t_start = 0.0; t_stop = 1.0; n_step = t_stop * 200; break; default: qDebug() << "Catastrophic: No ODE parameters for this property."; break; } y[0] = x0; y[1] = y0; y[2] = z0; m_path.clear(); m_path.append(QVector3D(y[0], y[1], y[2])); flag = 1; for (i_step = 1; i_step <= n_step; i_step++) { t = ((qreal)(n_step - i_step + 1) * t_start + (qreal)(i_step - 1) * t_stop) / (qreal)(n_step); t_out = ((qreal)(n_step - i_step) * t_start + (qreal)(i_step)*t_stop) / (qreal)(n_step); flag = QTAIMODEIntegrator::r8_rkf45(NEQN, y, yp, &t, t_out, &relerr, abserr, flag); m_status = flag; m_path.append(QVector3D(y[0], y[1], y[2])); if (flag == 7) { flag = 2; m_status = flag; } if (flag != 2) { m_status = flag; return QVector3D(y[0], y[1], y[2]); } if (m_betaSpheres.length() > 0) { for (qint64 n = 0; n < m_betaSpheres.length(); ++n) { Matrix a(y[0], y[1], y[2]); Matrix b(m_betaSpheres.at(n).first.x(), m_betaSpheres.at(n).first.y(), m_betaSpheres.at(n).first.z()); qreal distance = QTAIMMathUtilities::distance(a, b); if (distance < m_betaSpheres.at(n).second) { m_status = 0; m_associatedSphere = n; return QVector3D(m_betaSpheres.at(n).first.x(), m_betaSpheres.at(n).first.y(), m_betaSpheres.at(n).first.z()); } } } // beta spheres } // ODE step return QVector3D(y[0], y[1], y[2]); } void QTAIMODEIntegrator::r8_f(qreal t, qreal y[], qreal yp[]) { t = t; // suppress warning Matrix gradient; Matrix gH; Matrix g; Matrix H; Matrix xyz; xyz << y[0], y[1], y[2]; if (m_mode == SteepestAscentPathInElectronDensity) { g = m_eval->gradientOfElectronDensity(xyz); } else { if (m_mode == 1 || m_mode == 2 || m_mode == 3 || m_mode == 4) { gH = m_eval->gradientAndHessianOfElectronDensity(xyz); } else { gH = m_eval->gradientAndHessianOfElectronDensityLaplacian(xyz); } g(0) = gH(0, 0); g(1) = gH(1, 0); g(2) = gH(2, 0); H(0, 0) = gH(0, 1); H(1, 0) = gH(1, 1); H(2, 0) = gH(2, 1); H(0, 1) = gH(0, 2); H(1, 1) = gH(1, 2); H(2, 1) = gH(2, 2); H(0, 2) = gH(0, 3); H(1, 2) = gH(1, 3); H(2, 2) = gH(2, 3); } switch (m_mode) { case SteepestAscentPathInElectronDensity: gradient = g; break; case CMBPMinusThreeGradientInElectronDensity: gradient = QTAIMMathUtilities::minusThreeSignatureLocatorGradient(g, H); break; case CMBPMinusOneGradientInElectronDensity: gradient = QTAIMMathUtilities::minusOneSignatureLocatorGradient(g, H); break; case CMBPPlusOneGradientInElectronDensity: gradient = QTAIMMathUtilities::plusOneSignatureLocatorGradient(g, H); break; case CMBPPlusThreeGradientInElectronDensity: gradient = QTAIMMathUtilities::plusThreeSignatureLocatorGradient(g, H); break; case CMBPMinusThreeGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::minusThreeSignatureLocatorGradient(g, H); break; case CMBPMinusOneGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::minusOneSignatureLocatorGradient(g, H); break; case CMBPPlusOneGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::plusOneSignatureLocatorGradient(g, H); break; case CMBPPlusThreeGradientInElectronDensityLaplacian: gradient = QTAIMMathUtilities::plusThreeSignatureLocatorGradient(g, H); break; default: qDebug() << "Catastrophic: No ODE parameters for this property."; break; } qreal normGradient = std::hypot(gradient(0), gradient(1), gradient(2)); yp[0] = gradient(0) / normGradient; yp[1] = gradient(1) / normGradient; yp[2] = gradient(2) / normGradient; } //****************************************************************************80 qreal QTAIMODEIntegrator::r8_abs(qreal x) //****************************************************************************80 // // Purpose: // // R8_ABS returns the absolute value of an R8. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 02 April 2005 // // Author: // // John Burkardt // // Parameters: // // Input, qreal X, the quantity whose absolute value is desired. // // Output, qreal R8_ABS, the absolute value of X. // { if (0.0 <= x) { return x; } else { return (-x); } } //****************************************************************************80 qreal QTAIMODEIntegrator::r8_epsilon() //****************************************************************************80 // // Purpose: // // R8_EPSILON returns the R8 round off unit. // // Discussion: // // R8_EPSILON is a number R which is a power of 2 with the property that, // to the precision of the computer's arithmetic, // 1 < 1 + R // but // 1 = ( 1 + R / 2 ) // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 06 May 2003 // // Author: // // John Burkardt // // Parameters: // // Output, qreal R8_EPSILON, the R8 round-off unit. // { qreal r = 1.0; while (1.0 < (qreal)(1.0 + r)) { r = r / 2.0; } return (2.0 * r); } //****************************************************************************80 void QTAIMODEIntegrator::r8_fehl(qint64 neqn, qreal y[], qreal t, qreal h_, qreal yp[], qreal f1[], qreal f2[], qreal f3[], qreal f4[], qreal f5[], qreal s[]) //****************************************************************************80 // // Purpose: // // R8_FEHL takes one Fehlberg fourth-fifth order step. // // Discussion: // // This version of the routine uses qreal real arithmetic. // // This routine integrates a system of NEQN first order ordinary differential // equations of the form // dY(i)/dT = F(T,Y(1:NEQN)) // where the initial values Y and the initial derivatives // YP are specified at the starting point T. // // The routine advances the solution over the fixed step H and returns // the fifth order (sixth order accurate locally) solution // approximation at T+H in array S. // // The formulas have been grouped to control loss of significance. // The routine should be called with an H not smaller than 13 units of // roundoff in T so that the various independent arguments can be // distinguished. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 27 March 2004 // // Author: // // Original FORTRAN77 version by Herman Watts, Lawrence Shampine. // C++ version by John Burkardt. // // Reference: // // Erwin Fehlberg, // Low-order Classical Runge-Kutta Formulas with Stepsize Control, // NASA Technical Report R-315, 1969. // // Lawrence Shampine, Herman Watts, S Davenport, // Solving Non-stiff Ordinary Differential Equations - The State of the Art, // SIAM Review, // Volume 18, pages 376-411, 1976. // // Parameters: // // Input, external F, a user-supplied subroutine to evaluate the // derivatives Y'(T), of the form: // // void f ( qreal t, qreal y[], qreal yp[] ) // // Input, qint64 NEQN, the number of equations to be integrated. // // Input, qreal Y[NEQN], the current value of the dependent variable. // // Input, qreal T, the current value of the independent variable. // // Input, qreal H, the step size to take. // // Input, qreal YP[NEQN], the current value of the derivative of the // dependent variable. // // Output, qreal F1[NEQN], F2[NEQN], F3[NEQN], F4[NEQN], F5[NEQN], derivative // values needed for the computation. // // Output, qreal S[NEQN], the estimate of the solution at T+H. // { qreal ch = h_ / 4.0; for (qint64 i = 0; i < neqn; i++) { f5[i] = y[i] + ch * yp[i]; } QTAIMODEIntegrator::r8_f(t + ch, f5, f1); ch = 3.0 * h_ / 32.0; for (qint64 i = 0; i < neqn; i++) { f5[i] = y[i] + ch * (yp[i] + 3.0 * f1[i]); } QTAIMODEIntegrator::r8_f(t + 3.0 * h_ / 8.0, f5, f2); ch = h_ / 2197.0; for (qint64 i = 0; i < neqn; i++) { f5[i] = y[i] + ch * (1932.0 * yp[i] + (7296.0 * f2[i] - 7200.0 * f1[i])); } QTAIMODEIntegrator::r8_f(t + 12.0 * h_ / 13.0, f5, f3); ch = h_ / 4104.0; for (qint64 i = 0; i < neqn; i++) { f5[i] = y[i] + ch * ((8341.0 * yp[i] - 845.0 * f3[i]) + (29440.0 * f2[i] - 32832.0 * f1[i])); } QTAIMODEIntegrator::r8_f(t + h_, f5, f4); ch = h_ / 20520.0; for (qint64 i = 0; i < neqn; i++) { f1[i] = y[i] + ch * ((-6080.0 * yp[i] + (9295.0 * f3[i] - 5643.0 * f4[i])) + (41040.0 * f1[i] - 28352.0 * f2[i])); } QTAIMODEIntegrator::r8_f(t + h_ / 2.0, f1, f5); // // Ready to compute the approximate solution at T+H. // ch = h_ / 7618050.0; for (qint64 i = 0; i < neqn; i++) { s[i] = y[i] + ch * ((902880.0 * yp[i] + (3855735.0 * f3[i] - 1371249.0 * f4[i])) + (3953664.0 * f2[i] + 277020.0 * f5[i])); } } //****************************************************************************80 qreal QTAIMODEIntegrator::r8_max(qreal x, qreal y) //****************************************************************************80 // // Purpose: // // R8_MAX returns the maximum of two R8's. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 10 January 2002 // // Author: // // John Burkardt // // Parameters: // // Input, qreal X, Y, the quantities to compare. // // Output, qreal R8_MAX, the maximum of X and Y. // { if (y < x) { return x; } else { return y; } } //****************************************************************************80 qreal QTAIMODEIntegrator::r8_min(qreal x, qreal y) //****************************************************************************80 // // Purpose: // // R8_MIN returns the minimum of two R8's. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 09 May 2003 // // Author: // // John Burkardt // // Parameters: // // Input, qreal X, Y, the quantities to compare. // // Output, qreal R8_MIN, the minimum of X and Y. // { if (y < x) { return y; } else { return x; } } //****************************************************************************80 qint64 QTAIMODEIntegrator::r8_rkf45(qint64 neqn, qreal y[], qreal yp[], qreal* t, qreal tout, qreal* relerr, qreal abserr, qint64 flag) //****************************************************************************80 // // Purpose: // // R8_RKF45 carries out the Runge-Kutta-Fehlberg method. // // Discussion: // // This version of the routine uses qreal real arithmetic. // // This routine is primarily designed to solve non-stiff and mildly stiff // differential equations when derivative evaluations are inexpensive. // It should generally not be used when the user is demanding // high accuracy. // // This routine integrates a system of NEQN first-order ordinary differential // equations of the form: // // dY(i)/dT = F(T,Y(1),Y(2),...,Y(NEQN)) // // where the Y(1:NEQN) are given at T. // // Typically the subroutine is used to integrate from T to TOUT but it // can be used as a one-step integrator to advance the solution a // single step in the direction of TOUT. On return, the parameters in // the call list are set for continuing the integration. The user has // only to call again (and perhaps define a new value for TOUT). // // Before the first call, the user must // // * supply the subroutine F(T,Y,YP) to evaluate the right hand side; // and declare F in an EXTERNAL statement; // // * initialize the parameters: // NEQN, Y(1:NEQN), T, TOUT, RELERR, ABSERR, FLAG. // In particular, T should initially be the starting point for integration, // Y should be the value of the initial conditions, and FLAG should // normally be +1. // // Normally, the user only sets the value of FLAG before the first call, and // thereafter, the program manages the value. On the first call, FLAG should // normally be +1 (or -1 for single step mode.) On normal return, FLAG will // have been reset by the program to the value of 2 (or -2 in single // step mode), and the user can continue to call the routine with that // value of FLAG. // // (When the input magnitude of FLAG is 1, this indicates to the program // that it is necessary to do some initialization work. An input magnitude // of 2 lets the program know that initialization can be skipped, // and that useful information was computed earlier.) // // The routine returns with all the information needed to continue // the integration. If the integration reached TOUT, the user need only // define a new TOUT and call again. In the one-step integrator // mode, returning with FLAG = -2, the user must keep in mind that // each step taken is in the direction of the current TOUT. Upon // reaching TOUT, indicated by the output value of FLAG switching to 2, // the user must define a new TOUT and reset FLAG to -2 to continue // in the one-step integrator mode. // // In some cases, an error or difficulty occurs during a call. In that case, // the output value of FLAG is used to indicate that there is a problem // that the user must address. These values include: // // * 3, integration was not completed because the input value of RELERR, the // relative error tolerance, was too small. RELERR has been increased // appropriately for continuing. If the user accepts the output value of // RELERR, then simply reset FLAG to 2 and continue. // // * 4, integration was not completed because more than MAXNFE derivative // evaluations were needed. This is approximately (MAXNFE/6) steps. // The user may continue by simply calling again. The function counter // will be reset to 0, and another MAXNFE function evaluations are allowed. // // * 5, integration was not completed because the solution vanished, // making a pure relative error test impossible. The user must use // a non-zero ABSERR to continue. Using the one-step integration mode // for one step is a good way to proceed. // // * 6, integration was not completed because the requested accuracy // could not be achieved, even using the smallest allowable stepsize. // The user must increase the error tolerances ABSERR or RELERR before // continuing. It is also necessary to reset FLAG to 2 (or -2 when // the one-step integration mode is being used). The occurrence of // FLAG = 6 indicates a trouble spot. The solution is changing // rapidly, or a singularity may be present. It often is inadvisable // to continue. // // * 7, it is likely that this routine is inefficient for solving // this problem. Too much output is restricting the natural stepsize // choice. The user should use the one-step integration mode with // the stepsize determined by the code. If the user insists upon // continuing the integration, reset FLAG to 2 before calling // again. Otherwise, execution will be terminated. // // * 8, invalid input parameters, indicates one of the following: // NEQN <= 0; // T = TOUT and |FLAG| /= 1; // RELERR < 0 or ABSERR < 0; // FLAG == 0 or FLAG < -2 or 8 < FLAG. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 27 March 2004 // // Author: // // Original FORTRAN77 version by Herman Watts, Lawrence Shampine. // C++ version by John Burkardt. // // Reference: // // Erwin Fehlberg, // Low-order Classical Runge-Kutta Formulas with Stepsize Control, // NASA Technical Report R-315, 1969. // // Lawrence Shampine, Herman Watts, S Davenport, // Solving Non-stiff Ordinary Differential Equations - The State of the Art, // SIAM Review, // Volume 18, pages 376-411, 1976. // // Parameters: // // Input, external F, a user-supplied subroutine to evaluate the // derivatives Y'(T), of the form: // // void f ( qreal t, qreal y[], qreal yp[] ) // // Input, qint64 NEQN, the number of equations to be integrated. // // Input/output, qreal Y[NEQN], the current solution vector at T. // // Input/output, qreal YP[NEQN], the derivative of the current solution // vector at T. The user should not set or alter this information! // // Input/output, qreal *T, the current value of the independent variable. // // Input, qreal TOUT, the output point at which solution is desired. // TOUT = T is allowed on the first call only, in which case the routine // returns with FLAG = 2 if continuation is possible. // // Input, qreal *RELERR, ABSERR, the relative and absolute error tolerances // for the local error test. At each step the code requires: // abs ( local error ) <= RELERR * abs ( Y ) + ABSERR // for each component of the local error and the solution vector Y. // RELERR cannot be "too small". If the routine believes RELERR has been // set too small, it will reset RELERR to an acceptable value and return // immediately for user action. // // Input, qint64 FLAG, indicator for status of integration. On the first // call, // set FLAG to +1 for normal use, or to -1 for single step mode. On // subsequent continuation steps, FLAG should be +2, or -2 for single // step mode. // // Output, qint64 RKF45_D, indicator for status of integration. A value of 2 // or -2 indicates normal progress, while any other value indicates a // problem that should be addressed. // { // ECB: originally 3000 #define MAXNFE 1000 qreal ae; qreal dt; qreal ee; qreal eeoet; qreal eps; qreal esttol; qreal et; qreal* f1; qreal* f2; qreal* f3; qreal* f4; qreal* f5; bool hfaild; qreal hmin; qint64 i; qint64 k; qint64 mflag; bool output; qreal relerr_min; qreal s; qreal scale; qreal tol; qreal toln; qreal ypk; // // Check the input parameters. // eps = r8_epsilon(); if (neqn < 1) { return 8; } if ((*relerr) < 0.0) { return 8; } if (abserr < 0.0) { return 8; } if (flag == 0 || 8 < flag || flag < -2) { return 8; } mflag = (qint64)abs((long)flag); // // Is this a continuation call? // if (mflag != 1) { if (*t == tout && kflag != 3) { return 8; } // // FLAG = -2 or +2: // if (mflag == 2) { if (kflag == 3) { flag = flag_save; mflag = (qint64)abs((long)flag); } else if (init == 0) { flag = flag_save; } else if (kflag == 4) { nfe = 0; } else if (kflag == 5 && abserr == 0.0) { // error } else if (kflag == 6 && (*relerr) <= relerr_save && abserr <= abserr_save) { // error } } // // FLAG = 3, 4, 5, 6, 7 or 8. // else { if (flag == 3) { flag = flag_save; if (kflag == 3) { mflag = (qint64)abs((long)flag); } } else if (flag == 4) { nfe = 0; flag = flag_save; if (kflag == 3) { mflag = (qint64)abs((long)flag); } } else if (flag == 5 && 0.0 < abserr) { flag = flag_save; if (kflag == 3) { mflag = (qint64)abs((long)flag); } } // // Integration cannot be continued because the user did not respond to // the instructions pertaining to FLAG = 5, 6, 7 or 8. // else { // error } } } // // Save the input value of FLAG. // Set the continuation flag KFLAG for subsequent input checking. // flag_save = flag; kflag = 0; // // Save RELERR and ABSERR for checking input on subsequent calls. // relerr_save = (*relerr); abserr_save = abserr; // // Restrict the relative error tolerance to be at least // // 2*EPS+REMIN // // to avoid limiting precision difficulties arising from impossible // accuracy requests. // relerr_min = 2.0 * r8_epsilon() + remin; // // Is the relative error tolerance too small? // if ((*relerr) < relerr_min) { (*relerr) = relerr_min; kflag = 3; return 3; } dt = tout - *t; // // Initialization: // // Set the initialization completion indicator, INIT; // set the indicator for too many output points, KOP; // evaluate the initial derivatives // set the counter for function evaluations, NFE; // estimate the starting stepsize. // f1 = new qreal[neqn]; f2 = new qreal[neqn]; f3 = new qreal[neqn]; f4 = new qreal[neqn]; f5 = new qreal[neqn]; if (mflag == 1) { init = 0; kop = 0; QTAIMODEIntegrator::r8_f(*t, y, yp); nfe = 1; if (*t == tout) { return 2; } } if (init == 0) { init = 1; h = r8_abs(dt); toln = 0.0; for (k = 0; k < neqn; k++) { tol = (*relerr) * r8_abs(y[k]) + abserr; if (0.0 < tol) { toln = tol; ypk = r8_abs(yp[k]); if (tol < ypk * pow(h, 5)) { h = pow((tol / ypk), 0.2); } } } if (toln <= 0.0) { h = 0.0; } h = r8_max(h, 26.0 * eps * r8_max(r8_abs(*t), r8_abs(dt))); if (flag < 0) { flag_save = -2; } else { flag_save = 2; } } // // Set stepsize for integration in the direction from T to TOUT. // h = r8_sign(dt) * r8_abs(h); // // Test to see if too may output points are being requested. // if (2.0 * r8_abs(dt) <= r8_abs(h)) { kop = kop + 1; } // // Unnecessary frequency of output. // if (kop == 100) { kop = 0; delete[] f1; delete[] f2; delete[] f3; delete[] f4; delete[] f5; return 7; } // // If we are too close to the output point, then simply extrapolate and // return. // if (r8_abs(dt) <= 26.0 * eps * r8_abs(*t)) { *t = tout; for (i = 0; i < neqn; i++) { y[i] = y[i] + dt * yp[i]; } QTAIMODEIntegrator::r8_f(*t, y, yp); nfe = nfe + 1; delete[] f1; delete[] f2; delete[] f3; delete[] f4; delete[] f5; return 2; } // // Initialize the output point indicator. // output = false; // // To avoid premature underflow in the error tolerance function, // scale the error tolerances. // scale = 2.0 / (*relerr); ae = scale * abserr; // // Step by step integration. // for (;;) { hfaild = false; // // Set the smallest allowable stepsize. // hmin = 26.0 * eps * r8_abs(*t); // // Adjust the stepsize if necessary to hit the output point. // // Look ahead two steps to avoid drastic changes in the stepsize and // thus lessen the impact of output points on the code. // dt = tout - *t; if (2.0 * r8_abs(h) <= r8_abs(dt)) { } else // // Will the next successful step complete the integration to the output // point? // { if (r8_abs(dt) <= r8_abs(h)) { output = true; h = dt; } else { h = 0.5 * dt; } } // // Here begins the core integrator for taking a single step. // // The tolerances have been scaled to avoid premature underflow in // computing the error tolerance function ET. // To avoid problems with zero crossings, relative error is measured // using the average of the magnitudes of the solution at the // beginning and end of a step. // The error estimate formula has been grouped to control loss of // significance. // // To distinguish the various arguments, H is not permitted // to become smaller than 26 units of roundoff in T. // Practical limits on the change in the stepsize are enforced to // smooth the stepsize selection process and to avoid excessive // chattering on problems having discontinuities. // To prevent unnecessary failures, the code uses 9/10 the stepsize // it estimates will succeed. // // After a step failure, the stepsize is not allowed to increase for // the next attempted step. This makes the code more efficient on // problems having discontinuities and more effective in general // since local extrapolation is being used and extra caution seems // warranted. // // Test the number of derivative function evaluations. // If okay, try to advance the integration from T to T+H. // for (;;) { // // Have we done too much work? // if (MAXNFE < nfe) { kflag = 4; delete[] f1; delete[] f2; delete[] f3; delete[] f4; delete[] f5; return 4; } // // Advance an approximate solution over one step of length H. // r8_fehl(neqn, y, *t, h, yp, f1, f2, f3, f4, f5, f1); nfe = nfe + 5; // // Compute and test allowable tolerances versus local error estimates // and remove scaling of tolerances. The relative error is // measured with respect to the average of the magnitudes of the // solution at the beginning and end of the step. // eeoet = 0.0; for (k = 0; k < neqn; k++) { et = r8_abs(y[k]) + r8_abs(f1[k]) + ae; if (et <= 0.0) { delete[] f1; delete[] f2; delete[] f3; delete[] f4; delete[] f5; return 5; } ee = r8_abs((-2090.0 * yp[k] + (21970.0 * f3[k] - 15048.0 * f4[k])) + (22528.0 * f2[k] - 27360.0 * f5[k])); eeoet = r8_max(eeoet, ee / et); } esttol = r8_abs(h) * eeoet * scale / 752400.0; if (esttol <= 1.0) { break; } // // Unsuccessful step. Reduce the stepsize, try again. // The decrease is limited to a factor of 1/10. // hfaild = true; output = false; if (esttol < 59049.0) { s = 0.9 / pow(esttol, 0.2); } else { s = 0.1; } h = s * h; if (r8_abs(h) < hmin) { kflag = 6; delete[] f1; delete[] f2; delete[] f3; delete[] f4; delete[] f5; return 6; } } // // We exited the loop because we took a successful step. // Store the solution for T+H, and evaluate the derivative there. // *t = *t + h; for (i = 0; i < neqn; i++) { y[i] = f1[i]; } QTAIMODEIntegrator::r8_f(*t, y, yp); nfe = nfe + 1; // // Choose the next stepsize. The increase is limited to a factor of 5. // If the step failed, the next stepsize is not allowed to increase. // if (0.0001889568 < esttol) { s = 0.9 / pow(esttol, 0.2); } else { s = 5.0; } if (hfaild) { s = r8_min(s, 1.0); } h = r8_sign(h) * r8_max(s * r8_abs(h), hmin); // // End of core integrator // // Should we take another step? // if (output) { *t = tout; delete[] f1; delete[] f2; delete[] f3; delete[] f4; delete[] f5; return 2; } if (flag <= 0) { delete[] f1; delete[] f2; delete[] f3; delete[] f4; delete[] f5; return (-2); } } #undef MAXNFE } //****************************************************************************80 qreal QTAIMODEIntegrator::r8_sign(qreal x) //****************************************************************************80 // // Purpose: // // R8_SIGN returns the sign of an R8. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 27 March 2004 // // Author: // // John Burkardt // // Parameters: // // Input, qreal X, the number whose sign is desired. // // Output, qreal R8_SIGN, the sign of X. // { if (x < 0.0) { return (-1.0); } else { return (+1.0); } } //****************************************************************************80 } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimodeintegrator.h000066400000000000000000000052511506155467400253640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef QTAIMODEINTEGRATOR_H #define QTAIMODEINTEGRATOR_H #include "qtaimwavefunctionevaluator.h" #include #include #include #include #include namespace Avogadro::QtPlugins { class QTAIMODEIntegrator { public: enum { SteepestAscentPathInElectronDensity = 0, CMBPMinusThreeGradientInElectronDensity = 1, CMBPMinusOneGradientInElectronDensity = 2, CMBPPlusOneGradientInElectronDensity = 3, CMBPPlusThreeGradientInElectronDensity = 4, CMBPMinusThreeGradientInElectronDensityLaplacian = 5, CMBPMinusOneGradientInElectronDensityLaplacian = 6, CMBPPlusOneGradientInElectronDensityLaplacian = 7, CMBPPlusThreeGradientInElectronDensityLaplacian = 8 }; explicit QTAIMODEIntegrator(QTAIMWavefunctionEvaluator& eval, const qint64 mode); QVector3D integrate(QVector3D x0y0z0); qint64 status() const { return m_status; } const QList path() const { return m_path; } void setBetaSpheres(QList> betaSpheres) { m_betaSpheres = betaSpheres; } qint64 associatedSphere() const { return m_associatedSphere; } private: QTAIMWavefunctionEvaluator* m_eval; qint64 m_mode; qint64 m_status; QList m_path; QList> m_betaSpheres; qint64 m_associatedSphere; // ODE integrator qreal r8_abs(qreal x); qreal r8_epsilon(); void r8_fehl(qint64 neqn, qreal y[], qreal t, qreal h, qreal yp[], qreal f1[], qreal f2[], qreal f3[], qreal f4[], qreal f5[], qreal s[]); qreal r8_max(qreal x, qreal y); qreal r8_min(qreal x, qreal y); qint64 r8_rkf45(qint64 neqn, qreal y[], qreal yp[], qreal* t, qreal tout, qreal* relerr, qreal abserr, qint64 flag); qreal r8_sign(qreal x); void r8_f(qreal t, qreal y[], qreal yp[]); qreal abserr_save; qint64 flag_save; qreal h; qint64 init; qint64 kflag; qint64 kop; qint64 nfe; qreal relerr_save; qreal remin; }; } // namespace Avogadro::QtPlugins #endif // QTAIMODEINTEGRATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimsettingswidget.ui000066400000000000000000000114101506155467400257420ustar00rootroot00000000000000 QTAIMSettingsWidget 0 0 307 285 Critical Point Radius: 5 65 10 25 Qt::Horizontal QSlider::TicksBothSides Bond Path Radius: 1 6 1 2 Qt::Horizontal QSlider::TicksBothSides 2 0 20 1 2 20 20 Qt::Horizontal false QSlider::TicksBothSides 4 0 0 0 0 0 Covalent Constant Size Qt::Horizontal Qt::Horizontal Opacity: Qt::Vertical 20 40 avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimwavefunction.cpp000066400000000000000000000463401506155467400255650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "qtaimwavefunction.h" #include #include #include #include #include namespace Avogadro::QtPlugins { QTAIMWavefunction::QTAIMWavefunction() : m_initializationSuccessful(false) {} bool QTAIMWavefunction::initializeWithWFNFile(const QString& fileName) { m_initializationSuccessful = false; QFile file(fileName); bool fileExists = file.exists(); if (!(fileExists)) { m_initializationSuccessful = false; m_fileDoesNotExist = true; return m_initializationSuccessful; } else { m_fileDoesNotExist = false; } bool success = file.open(QIODevice::ReadOnly | QIODevice::Text); if (!(success)) { m_initializationSuccessful = false; m_ioError = true; return m_initializationSuccessful; } else { m_ioError = false; } m_fileName = fileName; QTextStream in(&file); QString fileContents = in.readAll(); file.close(); QStringList fileContentsByLine(fileContents.split("\n")); // Title/Comment m_comment = fileContentsByLine.first(); fileContentsByLine.removeFirst(); m_numberOfMolecularOrbitals = fileContentsByLine.first().mid(8, 15).toLongLong(); m_numberOfGaussianPrimitives = fileContentsByLine.first().mid(36, 8).toLongLong(); ; m_numberOfNuclei = fileContentsByLine.first().mid(54, 10).toLongLong(); ; fileContentsByLine.removeFirst(); // Maximum Number of Nuclei Due to Fixed Format if (m_numberOfNuclei > 999) { m_initializationSuccessful = false; m_tooManyNuclei = true; return m_initializationSuccessful; } else { m_tooManyNuclei = false; } m_xNuclearCoordinates.resize(m_numberOfNuclei); m_yNuclearCoordinates.resize(m_numberOfNuclei); m_zNuclearCoordinates.resize(m_numberOfNuclei); m_nuclearCharges.resize(m_numberOfNuclei); for (qint64 i = 0; i < m_numberOfNuclei; ++i) { m_xNuclearCoordinates[i] = fileContentsByLine.first().mid(24, 13).toDouble(); m_yNuclearCoordinates[i] = fileContentsByLine.first().mid(36, 12).toDouble(); m_zNuclearCoordinates[i] = fileContentsByLine.first().mid(48, 12).toDouble(); m_nuclearCharges[i] = fileContentsByLine.first().mid(70, 3).toLongLong(); fileContentsByLine.removeFirst(); } QList centerAssignmentsList; while (fileContentsByLine.first().startsWith("CENTRE ASSIGNMENTS")) { QString line(fileContentsByLine.first().mid(20, -1)); qint64 counter = 0; while (counter < line.length()) { centerAssignmentsList.append(line.mid(counter, 3).toLongLong()); counter = counter + 3; } fileContentsByLine.removeFirst(); } m_xGaussianPrimitiveCenterCoordinates.resize(m_numberOfGaussianPrimitives); m_yGaussianPrimitiveCenterCoordinates.resize(m_numberOfGaussianPrimitives); m_zGaussianPrimitiveCenterCoordinates.resize(m_numberOfGaussianPrimitives); for (qint64 i = 0; i < m_numberOfGaussianPrimitives; ++i) { m_xGaussianPrimitiveCenterCoordinates[i] = m_xNuclearCoordinates[centerAssignmentsList.at(i) - 1]; m_yGaussianPrimitiveCenterCoordinates[i] = m_yNuclearCoordinates[centerAssignmentsList.at(i) - 1]; m_zGaussianPrimitiveCenterCoordinates[i] = m_zNuclearCoordinates[centerAssignmentsList.at(i) - 1]; } QList typeAssignmentsList; while (fileContentsByLine.first().startsWith("TYPE ASSIGNMENTS")) { QString line(fileContentsByLine.first().mid(20, -1)); QStringList splitLine(line.split(" ", Qt::SkipEmptyParts)); for (qint64 i = 0; i < splitLine.length(); ++i) { typeAssignmentsList.append(splitLine.at(i).toLongLong()); } fileContentsByLine.removeFirst(); } m_xGaussianPrimitiveAngularMomenta.resize(m_numberOfGaussianPrimitives); m_yGaussianPrimitiveAngularMomenta.resize(m_numberOfGaussianPrimitives); m_zGaussianPrimitiveAngularMomenta.resize(m_numberOfGaussianPrimitives); for (qint64 i = 0; i < m_numberOfGaussianPrimitives; ++i) { switch (typeAssignmentsList.at(i)) { case 1: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 2: m_xGaussianPrimitiveAngularMomenta[i] = 1; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 3: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 1; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 4: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 1; break; case 5: m_xGaussianPrimitiveAngularMomenta[i] = 2; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 6: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 2; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 7: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 2; break; case 8: m_xGaussianPrimitiveAngularMomenta[i] = 1; m_yGaussianPrimitiveAngularMomenta[i] = 1; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 9: m_xGaussianPrimitiveAngularMomenta[i] = 1; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 1; break; case 10: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 1; m_zGaussianPrimitiveAngularMomenta[i] = 1; break; case 11: m_xGaussianPrimitiveAngularMomenta[i] = 3; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 12: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 3; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 13: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 3; break; case 14: m_xGaussianPrimitiveAngularMomenta[i] = 2; m_yGaussianPrimitiveAngularMomenta[i] = 1; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 15: m_xGaussianPrimitiveAngularMomenta[i] = 2; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 1; break; case 16: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 2; m_zGaussianPrimitiveAngularMomenta[i] = 1; break; case 17: m_xGaussianPrimitiveAngularMomenta[i] = 1; m_yGaussianPrimitiveAngularMomenta[i] = 2; m_zGaussianPrimitiveAngularMomenta[i] = 0; break; case 18: m_xGaussianPrimitiveAngularMomenta[i] = 1; m_yGaussianPrimitiveAngularMomenta[i] = 0; m_zGaussianPrimitiveAngularMomenta[i] = 2; break; case 19: m_xGaussianPrimitiveAngularMomenta[i] = 0; m_yGaussianPrimitiveAngularMomenta[i] = 1; m_zGaussianPrimitiveAngularMomenta[i] = 2; break; case 20: m_xGaussianPrimitiveAngularMomenta[i] = 1; m_yGaussianPrimitiveAngularMomenta[i] = 1; m_zGaussianPrimitiveAngularMomenta[i] = 1; break; default: m_initializationSuccessful = false; m_maximumAngularMomentumExceeded = true; return m_initializationSuccessful; } } m_maximumAngularMomentumExceeded = false; QList exponentsList; while (fileContentsByLine.first().startsWith("EXPONENTS")) { QString line(fileContentsByLine.first().mid(9, -1)); QStringList splitLine(line.split(" ", Qt::SkipEmptyParts)); for (qint64 i = 0; i < splitLine.length(); ++i) { QString str(splitLine.at(i)); QString replacedString(str.replace("d", "e", Qt::CaseSensitive) .replace("D", "e", Qt::CaseSensitive) .replace("E", "e", Qt::CaseSensitive)); exponentsList.append(replacedString.toDouble()); } fileContentsByLine.removeFirst(); } m_gaussianPrimitiveExponentCoefficients.resize(m_numberOfGaussianPrimitives); for (qint64 i = 0; i < m_numberOfGaussianPrimitives; ++i) { m_gaussianPrimitiveExponentCoefficients[i] = exponentsList.at(i); } m_totalEnergy = fileContentsByLine.last().mid(17, 20).toDouble(); m_virialRatio = fileContentsByLine.last().mid(55, -1).toDouble(); fileContentsByLine.removeLast(); fileContentsByLine.removeLast(); if (fileContentsByLine.last().trimmed().contains("END DATA", Qt::CaseSensitive)) fileContentsByLine.removeLast(); QStringList moHeaderStringList; QStringList moCoefficientsStringList; QList moCoefficientsList; for (qint64 i = 0; i < fileContentsByLine.length(); ++i) { if (fileContentsByLine.at(i).trimmed().startsWith("MO")) { moHeaderStringList.append(fileContentsByLine.at(i)); } else { moCoefficientsStringList.append(fileContentsByLine.at(i)); } } QList molecularOrbitalOccupationNumbersList; QList molecularOrbitalEigenvaluesList; for (qint64 i = 0; i < moHeaderStringList.length(); ++i) { molecularOrbitalOccupationNumbersList.append( moHeaderStringList.at(i).mid(34, 13).toDouble()); molecularOrbitalEigenvaluesList.append( moHeaderStringList.at(i).mid(62, -1).toDouble()); } m_molecularOrbitalOccupationNumbers.resize(m_numberOfMolecularOrbitals); for (qint64 i = 0; i < m_numberOfMolecularOrbitals; ++i) m_molecularOrbitalOccupationNumbers[i] = molecularOrbitalOccupationNumbersList.at(i); m_molecularOrbitalEigenvalues.resize(m_numberOfMolecularOrbitals); for (qint64 i = 0; i < m_numberOfMolecularOrbitals; ++i) m_molecularOrbitalEigenvalues[i] = molecularOrbitalEigenvaluesList.at(i); moCoefficientsStringList = moCoefficientsStringList.join(" ").split(" ", Qt::SkipEmptyParts); for (qint64 i = 0; i < moCoefficientsStringList.length(); ++i) { QString str(moCoefficientsStringList.at(i)); QString replacedString(str.replace("d", "e", Qt::CaseSensitive) .replace("D", "e", Qt::CaseSensitive) .replace("E", "e", Qt::CaseSensitive)); moCoefficientsList.append(replacedString.toDouble()); } m_molecularOrbitalCoefficients.resize(m_numberOfMolecularOrbitals * m_numberOfGaussianPrimitives); for (qint64 i = 0; i < (m_numberOfMolecularOrbitals * m_numberOfGaussianPrimitives); ++i) m_molecularOrbitalCoefficients[i] = moCoefficientsList.at(i); m_initializationSuccessful = true; return m_initializationSuccessful; } bool QTAIMWavefunction::initializeWithMoleculeProperties(QtGui::Molecule*& mol) { if (mol->property("QTAIMNumberOfMolecularOrbitals").isValid()) { QVariant numberOfMolecularOrbitalsVariant = mol->property("QTAIMNumberOfMolecularOrbitals"); m_numberOfMolecularOrbitals = numberOfMolecularOrbitalsVariant.toLongLong(); QVariant numberOfGaussianPrimitivesVariant = mol->property("QTAIMNumberOfGaussianPrimitives"); m_numberOfGaussianPrimitives = numberOfGaussianPrimitivesVariant.toLongLong(); QVariant numberOfNucleiVariant = mol->property("QTAIMNumberOfNuclei"); m_numberOfNuclei = numberOfNucleiVariant.toLongLong(); QVariant xNuclearCoordinatesVariant = mol->property("QTAIMXNuclearCoordinates"); QVariant yNuclearCoordinatesVariant = mol->property("QTAIMYNuclearCoordinates"); QVariant zNuclearCoordinatesVariant = mol->property("QTAIMZNuclearCoordinates"); QVariant nuclearChargesVariant = mol->property("QTAIMNuclearCharges"); QVariantList xNuclearCoordinatesVariantList = xNuclearCoordinatesVariant.toList(); QVariantList yNuclearCoordinatesVariantList = yNuclearCoordinatesVariant.toList(); QVariantList zNuclearCoordinatesVariantList = zNuclearCoordinatesVariant.toList(); QVariantList nuclearChargesVariantList = nuclearChargesVariant.toList(); QList xNuclearCoordinatesList; QList yNuclearCoordinatesList; QList zNuclearCoordinatesList; QList nuclearChargesList_; for (qint64 i = 0; i < m_numberOfNuclei; ++i) { xNuclearCoordinatesList.append( xNuclearCoordinatesVariantList.at(i).toReal()); yNuclearCoordinatesList.append( yNuclearCoordinatesVariantList.at(i).toReal()); zNuclearCoordinatesList.append( zNuclearCoordinatesVariantList.at(i).toReal()); nuclearChargesList_.append(nuclearChargesVariantList.at(i).toLongLong()); } m_xNuclearCoordinates = xNuclearCoordinatesList.toVector(); m_yNuclearCoordinates = yNuclearCoordinatesList.toVector(); m_zNuclearCoordinates = zNuclearCoordinatesList.toVector(); m_nuclearCharges = nuclearChargesList_.toVector(); QVariant xGaussianPrimitiveCenterCoordinatesVariant = mol->property("QTAIMXGaussianPrimitiveCenterCoordinates"); QVariant yGaussianPrimitiveCenterCoordinatesVariant = mol->property("QTAIMYGaussianPrimitiveCenterCoordinates"); QVariant zGaussianPrimitiveCenterCoordinatesVariant = mol->property("QTAIMZGaussianPrimitiveCenterCoordinates"); QVariant xGaussianPrimitiveAngularMomentaVariant = mol->property("QTAIMXGaussianPrimitiveAngularMomenta"); QVariant yGaussianPrimitiveAngularMomentaVariant = mol->property("QTAIMYGaussianPrimitiveAngularMomenta"); QVariant zGaussianPrimitiveAngularMomentaVariant = mol->property("QTAIMZGaussianPrimitiveAngularMomenta"); QVariant gaussianPrimitiveExponentCoefficientsVariant = mol->property("QTAIMGaussianPrimitiveExponentCoefficients"); QVariantList xGaussianPrimitiveCenterCoordinatesVariantList = xGaussianPrimitiveCenterCoordinatesVariant.toList(); QVariantList yGaussianPrimitiveCenterCoordinatesVariantList = yGaussianPrimitiveCenterCoordinatesVariant.toList(); QVariantList zGaussianPrimitiveCenterCoordinatesVariantList = zGaussianPrimitiveCenterCoordinatesVariant.toList(); QVariantList xGaussianPrimitiveAngularMomentaVariantList = xGaussianPrimitiveAngularMomentaVariant.toList(); QVariantList yGaussianPrimitiveAngularMomentaVariantList = yGaussianPrimitiveAngularMomentaVariant.toList(); QVariantList zGaussianPrimitiveAngularMomentaVariantList = zGaussianPrimitiveAngularMomentaVariant.toList(); QVariantList gaussianPrimitiveExponentCoefficientsVariantList = gaussianPrimitiveExponentCoefficientsVariant.toList(); QList xGaussianPrimitiveCenterCoordinatesList; QList yGaussianPrimitiveCenterCoordinatesList; QList zGaussianPrimitiveCenterCoordinatesList; QList xGaussianPrimitiveAngularMomentaList; QList yGaussianPrimitiveAngularMomentaList; QList zGaussianPrimitiveAngularMomentaList; QList gaussianPrimitiveExponentCoefficientsList; for (qint64 p = 0; p < m_numberOfGaussianPrimitives; ++p) { xGaussianPrimitiveCenterCoordinatesList.append( xGaussianPrimitiveCenterCoordinatesVariantList.at(p).toReal()); yGaussianPrimitiveCenterCoordinatesList.append( yGaussianPrimitiveCenterCoordinatesVariantList.at(p).toReal()); zGaussianPrimitiveCenterCoordinatesList.append( zGaussianPrimitiveCenterCoordinatesVariantList.at(p).toReal()); xGaussianPrimitiveAngularMomentaList.append( xGaussianPrimitiveAngularMomentaVariantList.at(p).toLongLong()); yGaussianPrimitiveAngularMomentaList.append( yGaussianPrimitiveAngularMomentaVariantList.at(p).toLongLong()); zGaussianPrimitiveAngularMomentaList.append( zGaussianPrimitiveAngularMomentaVariantList.at(p).toLongLong()); gaussianPrimitiveExponentCoefficientsList.append( gaussianPrimitiveExponentCoefficientsVariantList.at(p).toReal()); } m_xGaussianPrimitiveCenterCoordinates = xGaussianPrimitiveCenterCoordinatesList.toVector(); m_yGaussianPrimitiveCenterCoordinates = yGaussianPrimitiveCenterCoordinatesList.toVector(); m_zGaussianPrimitiveCenterCoordinates = zGaussianPrimitiveCenterCoordinatesList.toVector(); m_xGaussianPrimitiveAngularMomenta = xGaussianPrimitiveAngularMomentaList.toVector(); m_yGaussianPrimitiveAngularMomenta = yGaussianPrimitiveAngularMomentaList.toVector(); m_zGaussianPrimitiveAngularMomenta = zGaussianPrimitiveAngularMomentaList.toVector(); m_gaussianPrimitiveExponentCoefficients = gaussianPrimitiveExponentCoefficientsList.toVector(); QVariant molecularOrbitalOccupationNumbersVariant = mol->property("QTAIMMolecularOrbitalOccupationNumbers"); QVariant molecularOrbitalEigenvaluesVariant = mol->property("QTAIMMolecularOrbitalEigenvalues"); QVariant molecularOrbitalCoefficientsVariant = mol->property("QTAIMMolecularOrbitalCoefficients"); QVariantList molecularOrbitalOccupationNumbersVariantList = molecularOrbitalOccupationNumbersVariant.toList(); QVariantList molecularOrbitalEigenvaluesVariantList = molecularOrbitalEigenvaluesVariant.toList(); QVariantList molecularOrbitalCoefficientsVariantList = molecularOrbitalCoefficientsVariant.toList(); QList molecularOrbitalOccupationNumbersList; QList molecularOrbitalEigenvaluesList; QList molecularOrbitalCoefficientsList; for (qint64 m = 0; m < m_numberOfMolecularOrbitals; ++m) { molecularOrbitalOccupationNumbersList.append( molecularOrbitalOccupationNumbersVariantList.at(m).toReal()); molecularOrbitalEigenvaluesList.append( molecularOrbitalEigenvaluesVariantList.at(m).toReal()); } for (qint64 i = 0; i < (m_numberOfMolecularOrbitals * m_numberOfGaussianPrimitives); ++i) { molecularOrbitalCoefficientsList.append( molecularOrbitalCoefficientsVariantList.at(i).toReal()); } m_molecularOrbitalOccupationNumbers = molecularOrbitalOccupationNumbersList.toVector(); m_molecularOrbitalEigenvalues = molecularOrbitalEigenvaluesList.toVector(); m_molecularOrbitalCoefficients = molecularOrbitalCoefficientsList.toVector(); QVariant totalEnergyVariant = mol->property("QTAIMTotalEnergy"); QVariant virialRatioVariant = mol->property("QTAIMVirialRatio"); m_totalEnergy = totalEnergyVariant.toReal(); m_virialRatio = virialRatioVariant.toReal(); } return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimwavefunction.h000066400000000000000000000176531506155467400252370ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef QTAIMWAVEFUNCTION_H #define QTAIMWAVEFUNCTION_H #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { class QTAIMWavefunction { public: explicit QTAIMWavefunction(); void saveToBinaryFile(const QString& fileName) { QFile file(fileName); file.open(QIODevice::WriteOnly); QDataStream out(&file); out << m_fileName; out << m_comment; out << m_numberOfMolecularOrbitals; out << m_numberOfGaussianPrimitives; out << m_numberOfNuclei; out << m_xNuclearCoordinates; out << m_yNuclearCoordinates; out << m_zNuclearCoordinates; out << m_nuclearCharges; out << m_xGaussianPrimitiveCenterCoordinates; out << m_yGaussianPrimitiveCenterCoordinates; out << m_zGaussianPrimitiveCenterCoordinates; out << m_xGaussianPrimitiveAngularMomenta; out << m_yGaussianPrimitiveAngularMomenta; out << m_zGaussianPrimitiveAngularMomenta; out << m_gaussianPrimitiveExponentCoefficients; out << m_molecularOrbitalOccupationNumbers; out << m_molecularOrbitalEigenvalues; out << m_molecularOrbitalCoefficients; out << m_totalEnergy; out << m_virialRatio; } void loadFromBinaryFile(const QString& fileName) { QFile file(fileName); file.open(QIODevice::ReadOnly); QDataStream in(&file); in >> m_fileName; in >> m_comment; in >> m_numberOfMolecularOrbitals; in >> m_numberOfGaussianPrimitives; in >> m_numberOfNuclei; in >> m_xNuclearCoordinates; in >> m_yNuclearCoordinates; in >> m_zNuclearCoordinates; in >> m_nuclearCharges; in >> m_xGaussianPrimitiveCenterCoordinates; in >> m_yGaussianPrimitiveCenterCoordinates; in >> m_zGaussianPrimitiveCenterCoordinates; in >> m_xGaussianPrimitiveAngularMomenta; in >> m_yGaussianPrimitiveAngularMomenta; in >> m_zGaussianPrimitiveAngularMomenta; in >> m_gaussianPrimitiveExponentCoefficients; in >> m_molecularOrbitalOccupationNumbers; in >> m_molecularOrbitalEigenvalues; in >> m_molecularOrbitalCoefficients; in >> m_totalEnergy; in >> m_virialRatio; } bool initializeWithWFNFile(const QString& fileName); // bool initializeWithMoleculeProperties( Molecule &mol ); bool initializeWithMoleculeProperties(QtGui::Molecule*& mol); // TODO initialize with Avogadro general wavefunction qint64 numberOfMolecularOrbitals() const { return m_numberOfMolecularOrbitals; } qint64 numberOfGaussianPrimitives() const { return m_numberOfGaussianPrimitives; } qint64 numberOfNuclei() const { return m_numberOfNuclei; } const qreal* xNuclearCoordinates() const { return m_xNuclearCoordinates.constData(); } const qreal* yNuclearCoordinates() const { return m_yNuclearCoordinates.constData(); } const qreal* zNuclearCoordinates() const { return m_zNuclearCoordinates.constData(); } qreal xNuclearCoordinate(qint64 i) const { return m_xNuclearCoordinates.at(i); } qreal yNuclearCoordinate(qint64 i) const { return m_yNuclearCoordinates.at(i); } qreal zNuclearCoordinate(qint64 i) const { return m_zNuclearCoordinates.at(i); } const qint64* nuclearCharges() const { return m_nuclearCharges.constData(); } qint64 nuclearCharge(qint64 i) const { return m_nuclearCharges.at(i); } const QList nuclearChargesList() const { return m_nuclearCharges.toList(); } const qreal* xGaussianPrimitiveCenterCoordinates() const { return m_xGaussianPrimitiveCenterCoordinates.constData(); } const qreal* yGaussianPrimitiveCenterCoordinates() const { return m_yGaussianPrimitiveCenterCoordinates.constData(); } const qreal* zGaussianPrimitiveCenterCoordinates() const { return m_zGaussianPrimitiveCenterCoordinates.constData(); } qreal xGaussianPrimitiveCenterCoordinate(qint64 i) const { return m_xGaussianPrimitiveCenterCoordinates.at(i); } qreal yGaussianPrimitiveCenterCoordinate(qint64 i) const { return m_yGaussianPrimitiveCenterCoordinates.at(i); } qreal zGaussianPrimitiveCenterCoordinate(qint64 i) const { return m_zGaussianPrimitiveCenterCoordinates.at(i); } const qint64* xGaussianPrimitiveAngularMomenta() const { return m_xGaussianPrimitiveAngularMomenta.constData(); } const qint64* yGaussianPrimitiveAngularMomenta() const { return m_yGaussianPrimitiveAngularMomenta.constData(); } const qint64* zGaussianPrimitiveAngularMomenta() const { return m_zGaussianPrimitiveAngularMomenta.constData(); } qint64 xGaussianPrimitiveAngularMomentum(qint64 i) const { return m_xGaussianPrimitiveAngularMomenta.at(i); } qint64 yGaussianPrimitiveAngularMomentum(qint64 i) const { return m_yGaussianPrimitiveAngularMomenta.at(i); } qint64 zGaussianPrimitiveAngularMomentum(qint64 i) const { return m_zGaussianPrimitiveAngularMomenta.at(i); } const qreal* gaussianPrimitiveExponentCoefficients() const { return m_gaussianPrimitiveExponentCoefficients.constData(); } qreal gaussianPrimitiveExponentCoefficient(qint64 i) const { return m_gaussianPrimitiveExponentCoefficients.at(i); } const qreal* molecularOrbitalOccupationNumbers() const { return m_molecularOrbitalOccupationNumbers.constData(); } qreal molecularOrbitalOccupationNumber(qint64 i) const { return m_molecularOrbitalOccupationNumbers.at(i); } const qreal* molecularOrbitalEigenvalues() const { return m_molecularOrbitalEigenvalues.constData(); } qreal molecularOrbitalEigenvalue(qint64 i) const { return m_molecularOrbitalEigenvalues.at(i); } const qreal* molecularOrbitalCoefficients() const { return m_molecularOrbitalCoefficients.constData(); } qreal molecularOrbitalCoefficient(qint64 i) const { return m_molecularOrbitalCoefficients.at(i); } qreal molecularOrbitalCoefficient(qint64 mo, qint64 prim) const { return m_molecularOrbitalCoefficients.at(mo * m_numberOfGaussianPrimitives + prim); } qreal totalEnergy() const { return m_totalEnergy; } qreal virialRatio() const { return m_virialRatio; } private: bool m_initializationSuccessful; bool m_fileDoesNotExist; bool m_ioError; bool m_tooManyNuclei; bool m_maximumAngularMomentumExceeded; QString m_fileName; QString m_comment; qint64 m_numberOfMolecularOrbitals; qint64 m_numberOfGaussianPrimitives; qint64 m_numberOfNuclei; QVector m_xNuclearCoordinates; QVector m_yNuclearCoordinates; QVector m_zNuclearCoordinates; QVector m_nuclearCharges; QVector m_xGaussianPrimitiveCenterCoordinates; QVector m_yGaussianPrimitiveCenterCoordinates; QVector m_zGaussianPrimitiveCenterCoordinates; QVector m_xGaussianPrimitiveAngularMomenta; QVector m_yGaussianPrimitiveAngularMomenta; QVector m_zGaussianPrimitiveAngularMomenta; QVector m_gaussianPrimitiveExponentCoefficients; QVector m_molecularOrbitalOccupationNumbers; QVector m_molecularOrbitalEigenvalues; QVector m_molecularOrbitalCoefficients; qreal m_totalEnergy; qreal m_virialRatio; }; } // namespace Avogadro::QtPlugins #endif // QTAIMWAVEFUNCTION_H avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimwavefunctionevaluator.cpp000066400000000000000000002124561506155467400275130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "qtaimwavefunctionevaluator.h" #include namespace Avogadro::QtPlugins { QTAIMWavefunctionEvaluator::QTAIMWavefunctionEvaluator(QTAIMWavefunction& wfn) : m_nmo(wfn.numberOfMolecularOrbitals()), m_nprim(wfn.numberOfGaussianPrimitives()), m_nnuc(wfn.numberOfNuclei()), m_nucxcoord( Map>(wfn.xNuclearCoordinates(), m_nnuc)), m_nucycoord( Map>(wfn.yNuclearCoordinates(), m_nnuc)), m_nuczcoord( Map>(wfn.zNuclearCoordinates(), m_nnuc)), m_nucz(Map>(wfn.nuclearCharges(), m_nnuc)), m_X0(Map>( wfn.xGaussianPrimitiveCenterCoordinates(), m_nprim, 1)), m_Y0(Map>( wfn.yGaussianPrimitiveCenterCoordinates(), m_nprim, 1)), m_Z0(Map>( wfn.zGaussianPrimitiveCenterCoordinates(), m_nprim, 1)), m_xamom(Map>( wfn.xGaussianPrimitiveAngularMomenta(), m_nprim, 1)), m_yamom(Map>( wfn.yGaussianPrimitiveAngularMomenta(), m_nprim, 1)), m_zamom(Map>( wfn.zGaussianPrimitiveAngularMomenta(), m_nprim, 1)), m_alpha(Map>( wfn.gaussianPrimitiveExponentCoefficients(), m_nprim, 1)), m_occno(Map>( wfn.molecularOrbitalOccupationNumbers(), m_nmo, 1)), m_orbe(Map>( wfn.molecularOrbitalEigenvalues(), m_nmo, 1)), m_coef(Map>( wfn.molecularOrbitalCoefficients(), m_nmo, m_nprim)), m_totalEnergy(wfn.totalEnergy()), m_virialRatio(wfn.virialRatio()), m_cutoff(log(1.e-15)) { m_cdg000.resize(m_nmo); m_cdg100.resize(m_nmo); m_cdg010.resize(m_nmo); m_cdg001.resize(m_nmo); m_cdg200.resize(m_nmo); m_cdg110.resize(m_nmo); m_cdg101.resize(m_nmo); m_cdg020.resize(m_nmo); m_cdg011.resize(m_nmo); m_cdg002.resize(m_nmo); m_cdg300.resize(m_nmo); m_cdg120.resize(m_nmo); m_cdg102.resize(m_nmo); m_cdg210.resize(m_nmo); m_cdg030.resize(m_nmo); m_cdg012.resize(m_nmo); m_cdg201.resize(m_nmo); m_cdg021.resize(m_nmo); m_cdg003.resize(m_nmo); m_cdg111.resize(m_nmo); m_cdg400.resize(m_nmo); m_cdg220.resize(m_nmo); m_cdg202.resize(m_nmo); m_cdg310.resize(m_nmo); m_cdg130.resize(m_nmo); m_cdg112.resize(m_nmo); m_cdg301.resize(m_nmo); m_cdg121.resize(m_nmo); m_cdg103.resize(m_nmo); m_cdg040.resize(m_nmo); m_cdg022.resize(m_nmo); m_cdg211.resize(m_nmo); m_cdg031.resize(m_nmo); m_cdg013.resize(m_nmo); m_cdg004.resize(m_nmo); } qreal QTAIMWavefunctionEvaluator::molecularOrbital( const qint64 mo, const Matrix xyz) { qreal value = 0.0; for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qreal ax0 = ipow(xx0, m_xamom(p)); qreal ay0 = ipow(yy0, m_yamom(p)); qreal az0 = ipow(zz0, m_zamom(p)); qreal b0 = exp(b0arg); qreal dg000 = ax0 * ay0 * az0 * b0; value += m_coef(mo, p) * dg000; } } return value; } qreal QTAIMWavefunctionEvaluator::electronDensity(const Matrix xyz) { qreal value; m_cdg000.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qreal ax0 = ipow(xx0, m_xamom(p)); qreal ay0 = ipow(yy0, m_yamom(p)); qreal az0 = ipow(zz0, m_zamom(p)); qreal b0 = exp(b0arg); qreal dg000 = ax0 * ay0 * az0 * b0; for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; } } } value = 0.0; for (qint64 m = 0; m < m_nmo; ++m) { value += m_occno(m) * ipow(m_cdg000(m), 2); } return value; } Matrix QTAIMWavefunctionEvaluator::gradientOfElectronDensity( Matrix xyz) { Matrix value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; } } } value.setZero(); for (qint64 m = 0; m < m_nmo; ++m) { value(0) += m_occno(m) * m_cdg100(m) * m_cdg000(m); value(1) += m_occno(m) * m_cdg010(m) * m_cdg000(m); value(2) += m_occno(m) * m_cdg001(m) * m_cdg000(m); } return value; } Matrix QTAIMWavefunctionEvaluator::hessianOfElectronDensity( const Matrix xyz) { Matrix value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); m_cdg110.setZero(); m_cdg101.setZero(); m_cdg011.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg110 = az0 * b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1); qreal dg101 = ay0 * b0 * (ax1 + ax0 * bx1) * (az1 + az0 * bz1); qreal dg011 = ax0 * b0 * (ay1 + ay0 * by1) * (az1 + az0 * bz1); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; m_cdg110(m) += m_coef(m, p) * dg110; m_cdg101(m) += m_coef(m, p) * dg101; m_cdg011(m) += m_coef(m, p) * dg011; } } } value.setZero(); for (qint64 m = 0; m < m_nmo; ++m) { value(0, 0) += 2 * m_occno(m) * (ipow(m_cdg100(m), 2) + m_cdg000(m) * m_cdg200(m)); value(1, 1) += 2 * m_occno(m) * (ipow(m_cdg010(m), 2) + m_cdg000(m) * m_cdg020(m)); value(2, 2) += 2 * m_occno(m) * (ipow(m_cdg001(m), 2) + m_cdg000(m) * m_cdg002(m)); value(0, 1) += 2 * m_occno(m) * (m_cdg100(m) * m_cdg010(m) + m_cdg000(m) * m_cdg110(m)); value(0, 2) += 2 * m_occno(m) * (m_cdg100(m) * m_cdg001(m) + m_cdg000(m) * m_cdg101(m)); value(1, 2) += 2 * m_occno(m) * (m_cdg010(m) * m_cdg001(m) + m_cdg000(m) * m_cdg011(m)); } value(1, 0) = value(0, 1); value(2, 0) = value(0, 2); value(2, 1) = value(1, 2); return value; } Matrix QTAIMWavefunctionEvaluator::gradientAndHessianOfElectronDensity( const Matrix xyz) { Matrix gValue; Matrix hValue; Matrix value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); m_cdg110.setZero(); m_cdg101.setZero(); m_cdg011.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg110 = az0 * b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1); qreal dg101 = ay0 * b0 * (ax1 + ax0 * bx1) * (az1 + az0 * bz1); qreal dg011 = ax0 * b0 * (ay1 + ay0 * by1) * (az1 + az0 * bz1); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; m_cdg110(m) += m_coef(m, p) * dg110; m_cdg101(m) += m_coef(m, p) * dg101; m_cdg011(m) += m_coef(m, p) * dg011; } } } gValue.setZero(); for (qint64 m = 0; m < m_nmo; ++m) { gValue(0) += m_occno(m) * m_cdg100(m) * m_cdg000(m); gValue(1) += m_occno(m) * m_cdg010(m) * m_cdg000(m); gValue(2) += m_occno(m) * m_cdg001(m) * m_cdg000(m); } hValue.setZero(); for (qint64 m = 0; m < m_nmo; ++m) { hValue(0, 0) += 2 * m_occno(m) * (ipow(m_cdg100(m), 2) + m_cdg000(m) * m_cdg200(m)); hValue(1, 1) += 2 * m_occno(m) * (ipow(m_cdg010(m), 2) + m_cdg000(m) * m_cdg020(m)); hValue(2, 2) += 2 * m_occno(m) * (ipow(m_cdg001(m), 2) + m_cdg000(m) * m_cdg002(m)); hValue(0, 1) += 2 * m_occno(m) * (m_cdg100(m) * m_cdg010(m) + m_cdg000(m) * m_cdg110(m)); hValue(0, 2) += 2 * m_occno(m) * (m_cdg100(m) * m_cdg001(m) + m_cdg000(m) * m_cdg101(m)); hValue(1, 2) += 2 * m_occno(m) * (m_cdg010(m) * m_cdg001(m) + m_cdg000(m) * m_cdg011(m)); } hValue(1, 0) = hValue(0, 1); hValue(2, 0) = hValue(0, 2); hValue(2, 1) = hValue(1, 2); value(0, 0) = gValue(0); value(1, 0) = gValue(1); value(2, 0) = gValue(2); value(0, 1) = hValue(0, 0); value(1, 1) = hValue(1, 0); value(2, 1) = hValue(2, 0); value(0, 2) = hValue(0, 1); value(1, 2) = hValue(1, 1); value(2, 2) = hValue(2, 1); value(0, 3) = hValue(0, 2); value(1, 3) = hValue(1, 2); value(2, 3) = hValue(2, 2); return value; } qreal QTAIMWavefunctionEvaluator::laplacianOfElectronDensity( const Matrix xyz) { qreal value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; } } } value = 0.0; for (qint64 m = 0; m < m_nmo; ++m) { value += 2 * m_occno(m) * (ipow(m_cdg100(m), 2) + m_cdg000(m) * m_cdg200(m)) + 2 * m_occno(m) * (ipow(m_cdg010(m), 2) + m_cdg000(m) * m_cdg020(m)) + 2 * m_occno(m) * (ipow(m_cdg001(m), 2) + m_cdg000(m) * m_cdg002(m)); } return value; } Matrix QTAIMWavefunctionEvaluator::gradientOfElectronDensityLaplacian( const Matrix xyz) { Matrix value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); m_cdg110.setZero(); m_cdg101.setZero(); m_cdg011.setZero(); m_cdg300.setZero(); m_cdg120.setZero(); m_cdg102.setZero(); m_cdg210.setZero(); m_cdg030.setZero(); m_cdg012.setZero(); m_cdg201.setZero(); m_cdg021.setZero(); m_cdg003.setZero(); // m_cdg111.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qint64 aax3 = m_xamom(p) * (m_xamom(p) - 1) * (m_xamom(p) - 2); qint64 aay3 = m_yamom(p) * (m_yamom(p) - 1) * (m_yamom(p) - 2); qint64 aaz3 = m_zamom(p) * (m_zamom(p) - 1) * (m_zamom(p) - 2); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal ax3; qreal ay3; qreal az3; if (m_xamom(p) < 3) { ax3 = zero; } else if (m_xamom(p) == 3) { ax3 = one; } else { ax3 = aax3 * ipow(xx0, m_xamom(p) - 3); } if (m_yamom(p) < 3) { ay3 = zero; } else if (m_yamom(p) == 3) { ay3 = one; } else { ay3 = aay3 * ipow(yy0, m_yamom(p) - 3); } if (m_zamom(p) < 3) { az3 = zero; } else if (m_zamom(p) == 3) { az3 = one; } else { az3 = aaz3 * ipow(zz0, m_zamom(p) - 3); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal bx3 = (12 * ipow(m_alpha(p), 2) * xx0) - (8 * ipow(m_alpha(p), 3) * ipow(xx0, 3)); qreal by3 = (12 * ipow(m_alpha(p), 2) * yy0) - (8 * ipow(m_alpha(p), 3) * ipow(yy0, 3)); qreal bz3 = (12 * ipow(m_alpha(p), 2) * zz0) - (8 * ipow(m_alpha(p), 3) * ipow(zz0, 3)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg110 = az0 * b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1); qreal dg101 = ay0 * b0 * (ax1 + ax0 * bx1) * (az1 + az0 * bz1); qreal dg011 = ax0 * b0 * (ay1 + ay0 * by1) * (az1 + az0 * bz1); qreal dg300 = ay0 * az0 * b0 * (ax3 + 3 * ax2 * bx1 + 3 * ax1 * bx2 + ax0 * bx3); qreal dg030 = ax0 * az0 * b0 * (ay3 + 3 * ay2 * by1 + 3 * ay1 * by2 + ay0 * by3); qreal dg003 = ax0 * ay0 * b0 * (az3 + 3 * az2 * bz1 + 3 * az1 * bz2 + az0 * bz3); qreal dg210 = az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (ay1 + ay0 * by1); qreal dg201 = ay0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (az1 + az0 * bz1); qreal dg120 = az0 * b0 * (ax1 + ax0 * bx1) * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg021 = ax0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2) * (az1 + az0 * bz1); qreal dg102 = ay0 * b0 * (ax1 + ax0 * bx1) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg012 = ax0 * b0 * (ay1 + ay0 * by1) * (az2 + 2 * az1 * bz1 + az0 * bz2); // qreal dg111 = b0*(ax1+ax0*bx1)*(ay1+ay0*by1)*(az1+az0*bz1); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; m_cdg110(m) += m_coef(m, p) * dg110; m_cdg101(m) += m_coef(m, p) * dg101; m_cdg011(m) += m_coef(m, p) * dg011; m_cdg300(m) += m_coef(m, p) * dg300; m_cdg030(m) += m_coef(m, p) * dg030; m_cdg003(m) += m_coef(m, p) * dg003; m_cdg210(m) += m_coef(m, p) * dg210; m_cdg201(m) += m_coef(m, p) * dg201; m_cdg120(m) += m_coef(m, p) * dg120; m_cdg021(m) += m_coef(m, p) * dg021; m_cdg102(m) += m_coef(m, p) * dg102; m_cdg012(m) += m_coef(m, p) * dg012; // m_cdg111(m) += m_coef(m,p) * dg111; } } } qreal deriv300 = zero; qreal deriv030 = zero; qreal deriv003 = zero; qreal deriv210 = zero; qreal deriv201 = zero; qreal deriv120 = zero; qreal deriv021 = zero; qreal deriv102 = zero; qreal deriv012 = zero; // qreal deriv111=zero; for (qint64 m = 0; m < m_nmo; ++m) { deriv300 += (m_occno(m) * (6 * m_cdg100(m) * m_cdg200(m) + 2 * m_cdg000(m) * m_cdg300(m))); deriv030 += (m_occno(m) * (6 * m_cdg010(m) * m_cdg020(m) + 2 * m_cdg000(m) * m_cdg030(m))); deriv003 += (m_occno(m) * (6 * m_cdg001(m) * m_cdg002(m) + 2 * m_cdg000(m) * m_cdg003(m))); deriv210 += (m_occno(m) * (2 * (2 * m_cdg100(m) * m_cdg110(m) + m_cdg010(m) * m_cdg200(m) + m_cdg000(m) * m_cdg210(m)))); deriv201 += (m_occno(m) * (2 * (2 * m_cdg100(m) * m_cdg101(m) + m_cdg001(m) * m_cdg200(m) + m_cdg000(m) * m_cdg201(m)))); deriv120 += (m_occno(m) * (2 * (m_cdg020(m) * m_cdg100(m) + 2 * m_cdg010(m) * m_cdg110(m) + m_cdg000(m) * m_cdg120(m)))); deriv021 += (m_occno(m) * (2 * (2 * m_cdg010(m) * m_cdg011(m) + m_cdg001(m) * m_cdg020(m) + m_cdg000(m) * m_cdg021(m)))); deriv102 += (m_occno(m) * (2 * (m_cdg002(m) * m_cdg100(m) + 2 * m_cdg001(m) * m_cdg101(m) + m_cdg000(m) * m_cdg102(m)))); deriv012 += (m_occno(m) * (2 * (m_cdg002(m) * m_cdg010(m) + 2 * m_cdg001(m) * m_cdg011(m) + m_cdg000(m) * m_cdg012(m)))); // deriv111+=(m_occno(m)*( // 2*(m_cdg011(m)*m_cdg100(m)+m_cdg010(m)*m_cdg101(m)+m_cdg001(m)*m_cdg110(m)+m_cdg000(m)*m_cdg111(m)) // )); } value(0) = deriv300 + deriv120 + deriv102; value(1) = deriv210 + deriv030 + deriv012; value(2) = deriv201 + deriv021 + deriv003; return value; } Matrix QTAIMWavefunctionEvaluator::hessianOfElectronDensityLaplacian( const Matrix xyz) { Matrix value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); m_cdg110.setZero(); m_cdg101.setZero(); m_cdg011.setZero(); m_cdg300.setZero(); m_cdg120.setZero(); m_cdg102.setZero(); m_cdg210.setZero(); m_cdg030.setZero(); m_cdg012.setZero(); m_cdg201.setZero(); m_cdg021.setZero(); m_cdg003.setZero(); m_cdg111.setZero(); m_cdg400.setZero(); m_cdg040.setZero(); m_cdg004.setZero(); m_cdg310.setZero(); m_cdg301.setZero(); m_cdg130.setZero(); m_cdg031.setZero(); m_cdg103.setZero(); m_cdg013.setZero(); m_cdg220.setZero(); m_cdg202.setZero(); m_cdg022.setZero(); m_cdg211.setZero(); m_cdg121.setZero(); m_cdg112.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qint64 aax3 = m_xamom(p) * (m_xamom(p) - 1) * (m_xamom(p) - 2); qint64 aay3 = m_yamom(p) * (m_yamom(p) - 1) * (m_yamom(p) - 2); qint64 aaz3 = m_zamom(p) * (m_zamom(p) - 1) * (m_zamom(p) - 2); qint64 aax4 = m_xamom(p) * (m_xamom(p) - 1) * (m_xamom(p) - 2) * (m_xamom(p) - 3); qint64 aay4 = m_yamom(p) * (m_yamom(p) - 1) * (m_yamom(p) - 2) * (m_xamom(p) - 3); qint64 aaz4 = m_zamom(p) * (m_zamom(p) - 1) * (m_zamom(p) - 2) * (m_xamom(p) - 3); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal ax3; qreal ay3; qreal az3; if (m_xamom(p) < 3) { ax3 = zero; } else if (m_xamom(p) == 3) { ax3 = one; } else { ax3 = aax3 * ipow(xx0, m_xamom(p) - 3); } if (m_yamom(p) < 3) { ay3 = zero; } else if (m_yamom(p) == 3) { ay3 = one; } else { ay3 = aay3 * ipow(yy0, m_yamom(p) - 3); } if (m_zamom(p) < 3) { az3 = zero; } else if (m_zamom(p) == 3) { az3 = one; } else { az3 = aaz3 * ipow(zz0, m_zamom(p) - 3); } qreal ax4; qreal ay4; qreal az4; if (m_xamom(p) < 4) { ax4 = zero; } else if (m_xamom(p) == 4) { ax4 = one; } else { ax4 = aax4 * ipow(xx0, m_xamom(p) - 4); } if (m_yamom(p) < 4) { ay4 = zero; } else if (m_yamom(p) == 4) { ay4 = one; } else { ay4 = aay4 * ipow(yy0, m_yamom(p) - 4); } if (m_zamom(p) < 4) { az4 = zero; } else if (m_zamom(p) == 4) { az4 = one; } else { az4 = aaz4 * ipow(zz0, m_zamom(p) - 4); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal bx3 = (12 * ipow(m_alpha(p), 2) * xx0) - (8 * ipow(m_alpha(p), 3) * ipow(xx0, 3)); qreal by3 = (12 * ipow(m_alpha(p), 2) * yy0) - (8 * ipow(m_alpha(p), 3) * ipow(yy0, 3)); qreal bz3 = (12 * ipow(m_alpha(p), 2) * zz0) - (8 * ipow(m_alpha(p), 3) * ipow(zz0, 3)); qreal bx4 = (12 * ipow(m_alpha(p), 2)) - (48 * ipow(m_alpha(p), 3) * ipow(xx0, 2)) + (16 * ipow(m_alpha(p), 4) * ipow(xx0, 4)); qreal by4 = (12 * ipow(m_alpha(p), 2)) - (48 * ipow(m_alpha(p), 3) * ipow(yy0, 2)) + (16 * ipow(m_alpha(p), 4) * ipow(yy0, 4)); qreal bz4 = (12 * ipow(m_alpha(p), 2)) - (48 * ipow(m_alpha(p), 3) * ipow(zz0, 2)) + (16 * ipow(m_alpha(p), 4) * ipow(zz0, 4)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg110 = az0 * b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1); qreal dg101 = ay0 * b0 * (ax1 + ax0 * bx1) * (az1 + az0 * bz1); qreal dg011 = ax0 * b0 * (ay1 + ay0 * by1) * (az1 + az0 * bz1); qreal dg300 = ay0 * az0 * b0 * (ax3 + 3 * ax2 * bx1 + 3 * ax1 * bx2 + ax0 * bx3); qreal dg030 = ax0 * az0 * b0 * (ay3 + 3 * ay2 * by1 + 3 * ay1 * by2 + ay0 * by3); qreal dg003 = ax0 * ay0 * b0 * (az3 + 3 * az2 * bz1 + 3 * az1 * bz2 + az0 * bz3); qreal dg210 = az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (ay1 + ay0 * by1); qreal dg201 = ay0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (az1 + az0 * bz1); qreal dg120 = az0 * b0 * (ax1 + ax0 * bx1) * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg021 = ax0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2) * (az1 + az0 * bz1); qreal dg102 = ay0 * b0 * (ax1 + ax0 * bx1) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg012 = ax0 * b0 * (ay1 + ay0 * by1) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg111 = b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1) * (az1 + az0 * bz1); qreal dg400 = ay0 * az0 * b0 * (ax4 + 4 * ax3 * bx1 + 6 * ax2 * bx2 + 4 * ax1 * bx3 + ax0 * bx4); qreal dg040 = ax0 * az0 * b0 * (ay4 + 4 * ay3 * by1 + 6 * ay2 * by2 + 4 * ay1 * by3 + ay0 * by4); qreal dg004 = ax0 * ay0 * b0 * (az4 + 4 * az3 * bz1 + 6 * az2 * bz2 + 4 * az1 * bz3 + az0 * bz4); qreal dg310 = az0 * b0 * (ax3 + 3 * ax2 * bx1 + 3 * ax1 * bx2 + ax0 * bx3) * (ay1 + ay0 * by1); qreal dg301 = ay0 * b0 * (ax3 + 3 * ax2 * bx1 + 3 * ax1 * bx2 + ax0 * bx3) * (az1 + az0 * bz1); qreal dg130 = az0 * b0 * (ax1 + ax0 * bx1) * (ay3 + 3 * ay2 * by1 + 3 * ay1 * by2 + ay0 * by3); qreal dg031 = ax0 * b0 * (ay3 + 3 * ay2 * by1 + 3 * ay1 * by2 + ay0 * by3) * (az1 + az0 * bz1); qreal dg103 = ay0 * b0 * (ax1 + ax0 * bx1) * (az3 + 3 * az2 * bz1 + 3 * az1 * bz2 + az0 * bz3); qreal dg013 = ax0 * b0 * (ay1 + ay0 * by1) * (az3 + 3 * az2 * bz1 + 3 * az1 * bz2 + az0 * bz3); qreal dg220 = az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg202 = ay0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg022 = ax0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg211 = b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (ay1 + ay0 * by1) * (az1 + az0 * bz1); qreal dg121 = b0 * (ax1 + ax0 * bx1) * (ay2 + 2 * ay1 * by1 + ay0 * by2) * (az1 + az0 * bz1); qreal dg112 = b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1) * (az2 + 2 * az1 * bz1 + az0 * bz2); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; m_cdg110(m) += m_coef(m, p) * dg110; m_cdg101(m) += m_coef(m, p) * dg101; m_cdg011(m) += m_coef(m, p) * dg011; m_cdg300(m) += m_coef(m, p) * dg300; m_cdg030(m) += m_coef(m, p) * dg030; m_cdg003(m) += m_coef(m, p) * dg003; m_cdg210(m) += m_coef(m, p) * dg210; m_cdg201(m) += m_coef(m, p) * dg201; m_cdg120(m) += m_coef(m, p) * dg120; m_cdg021(m) += m_coef(m, p) * dg021; m_cdg102(m) += m_coef(m, p) * dg102; m_cdg012(m) += m_coef(m, p) * dg012; m_cdg111(m) += m_coef(m, p) * dg111; m_cdg400(m) += m_coef(m, p) * dg400; m_cdg040(m) += m_coef(m, p) * dg040; m_cdg004(m) += m_coef(m, p) * dg004; m_cdg310(m) += m_coef(m, p) * dg310; m_cdg301(m) += m_coef(m, p) * dg301; m_cdg130(m) += m_coef(m, p) * dg130; m_cdg031(m) += m_coef(m, p) * dg031; m_cdg103(m) += m_coef(m, p) * dg103; m_cdg013(m) += m_coef(m, p) * dg013; m_cdg220(m) += m_coef(m, p) * dg220; m_cdg202(m) += m_coef(m, p) * dg202; m_cdg022(m) += m_coef(m, p) * dg022; m_cdg211(m) += m_coef(m, p) * dg211; m_cdg121(m) += m_coef(m, p) * dg121; m_cdg112(m) += m_coef(m, p) * dg112; } } } qreal deriv400 = zero; qreal deriv040 = zero; qreal deriv004 = zero; qreal deriv310 = zero; qreal deriv301 = zero; qreal deriv130 = zero; qreal deriv031 = zero; qreal deriv103 = zero; qreal deriv013 = zero; qreal deriv220 = zero; qreal deriv202 = zero; qreal deriv022 = zero; qreal deriv211 = zero; qreal deriv121 = zero; qreal deriv112 = zero; for (qint64 m = 0; m < m_nmo; ++m) { deriv400 += (m_occno(m) * (6 * ipow(m_cdg200(m), 2) + 8 * m_cdg100(m) * m_cdg300(m) + 2 * m_cdg000(m) * m_cdg400(m))); deriv040 += (m_occno(m) * (6 * ipow(m_cdg020(m), 2) + 8 * m_cdg010(m) * m_cdg030(m) + 2 * m_cdg000(m) * m_cdg040(m))); deriv004 += (m_occno(m) * (6 * ipow(m_cdg002(m), 2) + 8 * m_cdg001(m) * m_cdg003(m) + 2 * m_cdg000(m) * m_cdg004(m))); deriv310 += (m_occno(m) * (2 * (3 * m_cdg110(m) * m_cdg200(m) + 3 * m_cdg100(m) * m_cdg210(m) + m_cdg010(m) * m_cdg300(m) + m_cdg000(m) * m_cdg310(m)))); deriv301 += (m_occno(m) * (2 * (3 * m_cdg101(m) * m_cdg200(m) + 3 * m_cdg100(m) * m_cdg201(m) + m_cdg001(m) * m_cdg300(m) + m_cdg000(m) * m_cdg301(m)))); deriv130 += (m_occno(m) * (2 * (m_cdg030(m) * m_cdg100(m) + 3 * m_cdg020(m) * m_cdg110(m) + 3 * m_cdg010(m) * m_cdg120(m) + m_cdg000(m) * m_cdg130(m)))); deriv031 += (m_occno(m) * (2 * (3 * m_cdg011(m) * m_cdg020(m) + 3 * m_cdg010(m) * m_cdg021(m) + m_cdg001(m) * m_cdg030(m) + m_cdg000(m) * m_cdg031(m)))); deriv103 += (m_occno(m) * (2 * (m_cdg003(m) * m_cdg100(m) + 3 * m_cdg002(m) * m_cdg101(m) + 3 * m_cdg001(m) * m_cdg102(m) + m_cdg000(m) * m_cdg103(m)))); deriv013 += (m_occno(m) * (2 * (m_cdg003(m) * m_cdg010(m) + 3 * m_cdg002(m) * m_cdg011(m) + 3 * m_cdg001(m) * m_cdg012(m) + m_cdg000(m) * m_cdg013(m)))); deriv220 += (m_occno(m) * (2 * (2 * ipow(m_cdg110(m), 2) + 2 * m_cdg100(m) * m_cdg120(m) + m_cdg020(m) * m_cdg200(m) + 2 * m_cdg010(m) * m_cdg210(m) + m_cdg000(m) * m_cdg220(m)))); deriv202 += (m_occno(m) * (2 * (2 * ipow(m_cdg101(m), 2) + 2 * m_cdg100(m) * m_cdg102(m) + m_cdg002(m) * m_cdg200(m) + 2 * m_cdg001(m) * m_cdg201(m) + m_cdg000(m) * m_cdg202(m)))); deriv022 += (m_occno(m) * (2 * (2 * ipow(m_cdg011(m), 2) + 2 * m_cdg010(m) * m_cdg012(m) + m_cdg002(m) * m_cdg020(m) + 2 * m_cdg001(m) * m_cdg021(m) + m_cdg000(m) * m_cdg022(m)))); deriv211 += (m_occno(m) * (2 * (2 * m_cdg101(m) * m_cdg110(m) + 2 * m_cdg100(m) * m_cdg111(m) + m_cdg011(m) * m_cdg200(m) + m_cdg010(m) * m_cdg201(m) + m_cdg001(m) * m_cdg210(m) + m_cdg000(m) * m_cdg211(m)))); deriv121 += (m_occno(m) * (2 * (m_cdg021(m) * m_cdg100(m) + m_cdg020(m) * m_cdg101(m) + 2 * m_cdg011(m) * m_cdg110(m) + 2 * m_cdg010(m) * m_cdg111(m) + m_cdg001(m) * m_cdg120(m) + m_cdg000(m) * m_cdg121(m)))); deriv112 += (m_occno(m) * (2 * (m_cdg012(m) * m_cdg100(m) + 2 * m_cdg011(m) * m_cdg101(m) + m_cdg010(m) * m_cdg102(m) + m_cdg002(m) * m_cdg110(m) + 2 * m_cdg001(m) * m_cdg111(m) + m_cdg000(m) * m_cdg112(m)))); } value(0, 0) = deriv400 + deriv220 + deriv202; value(1, 1) = deriv220 + deriv040 + deriv022; value(2, 2) = deriv202 + deriv022 + deriv004; value(0, 1) = deriv310 + deriv130 + deriv112; value(0, 2) = deriv301 + deriv121 + deriv103; value(1, 2) = deriv211 + deriv031 + deriv013; value(1, 0) = value(0, 1); value(2, 0) = value(0, 2); value(2, 1) = value(1, 2); return value; } Matrix QTAIMWavefunctionEvaluator::gradientAndHessianOfElectronDensityLaplacian( const Matrix xyz) { Matrix gValue; Matrix hValue; Matrix value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); m_cdg110.setZero(); m_cdg101.setZero(); m_cdg011.setZero(); m_cdg300.setZero(); m_cdg120.setZero(); m_cdg102.setZero(); m_cdg210.setZero(); m_cdg030.setZero(); m_cdg012.setZero(); m_cdg201.setZero(); m_cdg021.setZero(); m_cdg003.setZero(); m_cdg111.setZero(); m_cdg400.setZero(); m_cdg040.setZero(); m_cdg004.setZero(); m_cdg310.setZero(); m_cdg301.setZero(); m_cdg130.setZero(); m_cdg031.setZero(); m_cdg103.setZero(); m_cdg013.setZero(); m_cdg220.setZero(); m_cdg202.setZero(); m_cdg022.setZero(); m_cdg211.setZero(); m_cdg121.setZero(); m_cdg112.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qint64 aax3 = m_xamom(p) * (m_xamom(p) - 1) * (m_xamom(p) - 2); qint64 aay3 = m_yamom(p) * (m_yamom(p) - 1) * (m_yamom(p) - 2); qint64 aaz3 = m_zamom(p) * (m_zamom(p) - 1) * (m_zamom(p) - 2); qint64 aax4 = m_xamom(p) * (m_xamom(p) - 1) * (m_xamom(p) - 2) * (m_xamom(p) - 3); qint64 aay4 = m_yamom(p) * (m_yamom(p) - 1) * (m_yamom(p) - 2) * (m_xamom(p) - 3); qint64 aaz4 = m_zamom(p) * (m_zamom(p) - 1) * (m_zamom(p) - 2) * (m_xamom(p) - 3); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal ax3; qreal ay3; qreal az3; if (m_xamom(p) < 3) { ax3 = zero; } else if (m_xamom(p) == 3) { ax3 = one; } else { ax3 = aax3 * ipow(xx0, m_xamom(p) - 3); } if (m_yamom(p) < 3) { ay3 = zero; } else if (m_yamom(p) == 3) { ay3 = one; } else { ay3 = aay3 * ipow(yy0, m_yamom(p) - 3); } if (m_zamom(p) < 3) { az3 = zero; } else if (m_zamom(p) == 3) { az3 = one; } else { az3 = aaz3 * ipow(zz0, m_zamom(p) - 3); } qreal ax4; qreal ay4; qreal az4; if (m_xamom(p) < 4) { ax4 = zero; } else if (m_xamom(p) == 4) { ax4 = one; } else { ax4 = aax4 * ipow(xx0, m_xamom(p) - 4); } if (m_yamom(p) < 4) { ay4 = zero; } else if (m_yamom(p) == 4) { ay4 = one; } else { ay4 = aay4 * ipow(yy0, m_yamom(p) - 4); } if (m_zamom(p) < 4) { az4 = zero; } else if (m_zamom(p) == 4) { az4 = one; } else { az4 = aaz4 * ipow(zz0, m_zamom(p) - 4); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal bx3 = (12 * ipow(m_alpha(p), 2) * xx0) - (8 * ipow(m_alpha(p), 3) * ipow(xx0, 3)); qreal by3 = (12 * ipow(m_alpha(p), 2) * yy0) - (8 * ipow(m_alpha(p), 3) * ipow(yy0, 3)); qreal bz3 = (12 * ipow(m_alpha(p), 2) * zz0) - (8 * ipow(m_alpha(p), 3) * ipow(zz0, 3)); qreal bx4 = (12 * ipow(m_alpha(p), 2)) - (48 * ipow(m_alpha(p), 3) * ipow(xx0, 2)) + (16 * ipow(m_alpha(p), 4) * ipow(xx0, 4)); qreal by4 = (12 * ipow(m_alpha(p), 2)) - (48 * ipow(m_alpha(p), 3) * ipow(yy0, 2)) + (16 * ipow(m_alpha(p), 4) * ipow(yy0, 4)); qreal bz4 = (12 * ipow(m_alpha(p), 2)) - (48 * ipow(m_alpha(p), 3) * ipow(zz0, 2)) + (16 * ipow(m_alpha(p), 4) * ipow(zz0, 4)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg110 = az0 * b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1); qreal dg101 = ay0 * b0 * (ax1 + ax0 * bx1) * (az1 + az0 * bz1); qreal dg011 = ax0 * b0 * (ay1 + ay0 * by1) * (az1 + az0 * bz1); qreal dg300 = ay0 * az0 * b0 * (ax3 + 3 * ax2 * bx1 + 3 * ax1 * bx2 + ax0 * bx3); qreal dg030 = ax0 * az0 * b0 * (ay3 + 3 * ay2 * by1 + 3 * ay1 * by2 + ay0 * by3); qreal dg003 = ax0 * ay0 * b0 * (az3 + 3 * az2 * bz1 + 3 * az1 * bz2 + az0 * bz3); qreal dg210 = az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (ay1 + ay0 * by1); qreal dg201 = ay0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (az1 + az0 * bz1); qreal dg120 = az0 * b0 * (ax1 + ax0 * bx1) * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg021 = ax0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2) * (az1 + az0 * bz1); qreal dg102 = ay0 * b0 * (ax1 + ax0 * bx1) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg012 = ax0 * b0 * (ay1 + ay0 * by1) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg111 = b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1) * (az1 + az0 * bz1); qreal dg400 = ay0 * az0 * b0 * (ax4 + 4 * ax3 * bx1 + 6 * ax2 * bx2 + 4 * ax1 * bx3 + ax0 * bx4); qreal dg040 = ax0 * az0 * b0 * (ay4 + 4 * ay3 * by1 + 6 * ay2 * by2 + 4 * ay1 * by3 + ay0 * by4); qreal dg004 = ax0 * ay0 * b0 * (az4 + 4 * az3 * bz1 + 6 * az2 * bz2 + 4 * az1 * bz3 + az0 * bz4); qreal dg310 = az0 * b0 * (ax3 + 3 * ax2 * bx1 + 3 * ax1 * bx2 + ax0 * bx3) * (ay1 + ay0 * by1); qreal dg301 = ay0 * b0 * (ax3 + 3 * ax2 * bx1 + 3 * ax1 * bx2 + ax0 * bx3) * (az1 + az0 * bz1); qreal dg130 = az0 * b0 * (ax1 + ax0 * bx1) * (ay3 + 3 * ay2 * by1 + 3 * ay1 * by2 + ay0 * by3); qreal dg031 = ax0 * b0 * (ay3 + 3 * ay2 * by1 + 3 * ay1 * by2 + ay0 * by3) * (az1 + az0 * bz1); qreal dg103 = ay0 * b0 * (ax1 + ax0 * bx1) * (az3 + 3 * az2 * bz1 + 3 * az1 * bz2 + az0 * bz3); qreal dg013 = ax0 * b0 * (ay1 + ay0 * by1) * (az3 + 3 * az2 * bz1 + 3 * az1 * bz2 + az0 * bz3); qreal dg220 = az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg202 = ay0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg022 = ax0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2) * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg211 = b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2) * (ay1 + ay0 * by1) * (az1 + az0 * bz1); qreal dg121 = b0 * (ax1 + ax0 * bx1) * (ay2 + 2 * ay1 * by1 + ay0 * by2) * (az1 + az0 * bz1); qreal dg112 = b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1) * (az2 + 2 * az1 * bz1 + az0 * bz2); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; m_cdg110(m) += m_coef(m, p) * dg110; m_cdg101(m) += m_coef(m, p) * dg101; m_cdg011(m) += m_coef(m, p) * dg011; m_cdg300(m) += m_coef(m, p) * dg300; m_cdg030(m) += m_coef(m, p) * dg030; m_cdg003(m) += m_coef(m, p) * dg003; m_cdg210(m) += m_coef(m, p) * dg210; m_cdg201(m) += m_coef(m, p) * dg201; m_cdg120(m) += m_coef(m, p) * dg120; m_cdg021(m) += m_coef(m, p) * dg021; m_cdg102(m) += m_coef(m, p) * dg102; m_cdg012(m) += m_coef(m, p) * dg012; m_cdg111(m) += m_coef(m, p) * dg111; m_cdg400(m) += m_coef(m, p) * dg400; m_cdg040(m) += m_coef(m, p) * dg040; m_cdg004(m) += m_coef(m, p) * dg004; m_cdg310(m) += m_coef(m, p) * dg310; m_cdg301(m) += m_coef(m, p) * dg301; m_cdg130(m) += m_coef(m, p) * dg130; m_cdg031(m) += m_coef(m, p) * dg031; m_cdg103(m) += m_coef(m, p) * dg103; m_cdg013(m) += m_coef(m, p) * dg013; m_cdg220(m) += m_coef(m, p) * dg220; m_cdg202(m) += m_coef(m, p) * dg202; m_cdg022(m) += m_coef(m, p) * dg022; m_cdg211(m) += m_coef(m, p) * dg211; m_cdg121(m) += m_coef(m, p) * dg121; m_cdg112(m) += m_coef(m, p) * dg112; } } } qreal deriv300 = zero; qreal deriv030 = zero; qreal deriv003 = zero; qreal deriv210 = zero; qreal deriv201 = zero; qreal deriv120 = zero; qreal deriv021 = zero; qreal deriv102 = zero; qreal deriv012 = zero; qreal deriv400 = zero; qreal deriv040 = zero; qreal deriv004 = zero; qreal deriv310 = zero; qreal deriv301 = zero; qreal deriv130 = zero; qreal deriv031 = zero; qreal deriv103 = zero; qreal deriv013 = zero; qreal deriv220 = zero; qreal deriv202 = zero; qreal deriv022 = zero; qreal deriv211 = zero; qreal deriv121 = zero; qreal deriv112 = zero; for (qint64 m = 0; m < m_nmo; ++m) { deriv300 += (m_occno(m) * (6 * m_cdg100(m) * m_cdg200(m) + 2 * m_cdg000(m) * m_cdg300(m))); deriv030 += (m_occno(m) * (6 * m_cdg010(m) * m_cdg020(m) + 2 * m_cdg000(m) * m_cdg030(m))); deriv003 += (m_occno(m) * (6 * m_cdg001(m) * m_cdg002(m) + 2 * m_cdg000(m) * m_cdg003(m))); deriv210 += (m_occno(m) * (2 * (2 * m_cdg100(m) * m_cdg110(m) + m_cdg010(m) * m_cdg200(m) + m_cdg000(m) * m_cdg210(m)))); deriv201 += (m_occno(m) * (2 * (2 * m_cdg100(m) * m_cdg101(m) + m_cdg001(m) * m_cdg200(m) + m_cdg000(m) * m_cdg201(m)))); deriv120 += (m_occno(m) * (2 * (m_cdg020(m) * m_cdg100(m) + 2 * m_cdg010(m) * m_cdg110(m) + m_cdg000(m) * m_cdg120(m)))); deriv021 += (m_occno(m) * (2 * (2 * m_cdg010(m) * m_cdg011(m) + m_cdg001(m) * m_cdg020(m) + m_cdg000(m) * m_cdg021(m)))); deriv102 += (m_occno(m) * (2 * (m_cdg002(m) * m_cdg100(m) + 2 * m_cdg001(m) * m_cdg101(m) + m_cdg000(m) * m_cdg102(m)))); deriv012 += (m_occno(m) * (2 * (m_cdg002(m) * m_cdg010(m) + 2 * m_cdg001(m) * m_cdg011(m) + m_cdg000(m) * m_cdg012(m)))); // deriv111+=(m_occno(m)*( // 2*(m_cdg011(m)*m_cdg100(m)+m_cdg010(m)*m_cdg101(m)+m_cdg001(m)*m_cdg110(m)+m_cdg000(m)*m_cdg111(m)) // )); deriv400 += (m_occno(m) * (6 * ipow(m_cdg200(m), 2) + 8 * m_cdg100(m) * m_cdg300(m) + 2 * m_cdg000(m) * m_cdg400(m))); deriv040 += (m_occno(m) * (6 * ipow(m_cdg020(m), 2) + 8 * m_cdg010(m) * m_cdg030(m) + 2 * m_cdg000(m) * m_cdg040(m))); deriv004 += (m_occno(m) * (6 * ipow(m_cdg002(m), 2) + 8 * m_cdg001(m) * m_cdg003(m) + 2 * m_cdg000(m) * m_cdg004(m))); deriv310 += (m_occno(m) * (2 * (3 * m_cdg110(m) * m_cdg200(m) + 3 * m_cdg100(m) * m_cdg210(m) + m_cdg010(m) * m_cdg300(m) + m_cdg000(m) * m_cdg310(m)))); deriv301 += (m_occno(m) * (2 * (3 * m_cdg101(m) * m_cdg200(m) + 3 * m_cdg100(m) * m_cdg201(m) + m_cdg001(m) * m_cdg300(m) + m_cdg000(m) * m_cdg301(m)))); deriv130 += (m_occno(m) * (2 * (m_cdg030(m) * m_cdg100(m) + 3 * m_cdg020(m) * m_cdg110(m) + 3 * m_cdg010(m) * m_cdg120(m) + m_cdg000(m) * m_cdg130(m)))); deriv031 += (m_occno(m) * (2 * (3 * m_cdg011(m) * m_cdg020(m) + 3 * m_cdg010(m) * m_cdg021(m) + m_cdg001(m) * m_cdg030(m) + m_cdg000(m) * m_cdg031(m)))); deriv103 += (m_occno(m) * (2 * (m_cdg003(m) * m_cdg100(m) + 3 * m_cdg002(m) * m_cdg101(m) + 3 * m_cdg001(m) * m_cdg102(m) + m_cdg000(m) * m_cdg103(m)))); deriv013 += (m_occno(m) * (2 * (m_cdg003(m) * m_cdg010(m) + 3 * m_cdg002(m) * m_cdg011(m) + 3 * m_cdg001(m) * m_cdg012(m) + m_cdg000(m) * m_cdg013(m)))); deriv220 += (m_occno(m) * (2 * (2 * ipow(m_cdg110(m), 2) + 2 * m_cdg100(m) * m_cdg120(m) + m_cdg020(m) * m_cdg200(m) + 2 * m_cdg010(m) * m_cdg210(m) + m_cdg000(m) * m_cdg220(m)))); deriv202 += (m_occno(m) * (2 * (2 * ipow(m_cdg101(m), 2) + 2 * m_cdg100(m) * m_cdg102(m) + m_cdg002(m) * m_cdg200(m) + 2 * m_cdg001(m) * m_cdg201(m) + m_cdg000(m) * m_cdg202(m)))); deriv022 += (m_occno(m) * (2 * (2 * ipow(m_cdg011(m), 2) + 2 * m_cdg010(m) * m_cdg012(m) + m_cdg002(m) * m_cdg020(m) + 2 * m_cdg001(m) * m_cdg021(m) + m_cdg000(m) * m_cdg022(m)))); deriv211 += (m_occno(m) * (2 * (2 * m_cdg101(m) * m_cdg110(m) + 2 * m_cdg100(m) * m_cdg111(m) + m_cdg011(m) * m_cdg200(m) + m_cdg010(m) * m_cdg201(m) + m_cdg001(m) * m_cdg210(m) + m_cdg000(m) * m_cdg211(m)))); deriv121 += (m_occno(m) * (2 * (m_cdg021(m) * m_cdg100(m) + m_cdg020(m) * m_cdg101(m) + 2 * m_cdg011(m) * m_cdg110(m) + 2 * m_cdg010(m) * m_cdg111(m) + m_cdg001(m) * m_cdg120(m) + m_cdg000(m) * m_cdg121(m)))); deriv112 += (m_occno(m) * (2 * (m_cdg012(m) * m_cdg100(m) + 2 * m_cdg011(m) * m_cdg101(m) + m_cdg010(m) * m_cdg102(m) + m_cdg002(m) * m_cdg110(m) + 2 * m_cdg001(m) * m_cdg111(m) + m_cdg000(m) * m_cdg112(m)))); } gValue(0) = deriv300 + deriv120 + deriv102; gValue(1) = deriv210 + deriv030 + deriv012; gValue(2) = deriv201 + deriv021 + deriv003; hValue(0, 0) = deriv400 + deriv220 + deriv202; hValue(1, 1) = deriv220 + deriv040 + deriv022; hValue(2, 2) = deriv202 + deriv022 + deriv004; hValue(0, 1) = deriv310 + deriv130 + deriv112; hValue(0, 2) = deriv301 + deriv121 + deriv103; hValue(1, 2) = deriv211 + deriv031 + deriv013; hValue(1, 0) = hValue(0, 1); hValue(2, 0) = hValue(0, 2); hValue(2, 1) = hValue(1, 2); value(0, 0) = gValue(0); value(1, 0) = gValue(1); value(2, 0) = gValue(2); value(0, 1) = hValue(0, 0); value(1, 1) = hValue(1, 0); value(2, 1) = hValue(2, 0); value(0, 2) = hValue(0, 1); value(1, 2) = hValue(1, 1); value(2, 2) = hValue(2, 1); value(0, 3) = hValue(0, 2); value(1, 3) = hValue(1, 2); value(2, 3) = hValue(2, 2); return value; } qreal QTAIMWavefunctionEvaluator::kineticEnergyDensityG(Matrix xyz) { qreal value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; } } } value = zero; for (qint64 m = 0; m < m_nmo; ++m) { value += (0.5) * (m_occno(m) * (ipow(m_cdg100(m), 2) + ipow(m_cdg010(m), 2) + ipow(m_cdg001(m), 2))); } return value; } qreal QTAIMWavefunctionEvaluator::kineticEnergyDensityK( const Matrix xyz) { qreal value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; } } } value = 0.0; for (qint64 m = 0; m < m_nmo; ++m) { value += (0.25) * (m_occno(m) * (2 * m_cdg000(m) * (m_cdg200(m) + m_cdg020(m) + m_cdg002(m)))); } return value; } Matrix QTAIMWavefunctionEvaluator::quantumStressTensor( const Matrix xyz) { Matrix value; const qreal zero = 0.0; const qreal one = 1.0; m_cdg000.setZero(); m_cdg100.setZero(); m_cdg010.setZero(); m_cdg001.setZero(); m_cdg200.setZero(); m_cdg020.setZero(); m_cdg002.setZero(); m_cdg110.setZero(); m_cdg101.setZero(); m_cdg011.setZero(); for (qint64 p = 0; p < m_nprim; ++p) { qreal xx0 = xyz(0) - m_X0(p); qreal yy0 = xyz(1) - m_Y0(p); qreal zz0 = xyz(2) - m_Z0(p); qreal b0arg = -m_alpha(p) * (xx0 * xx0 + yy0 * yy0 + zz0 * zz0); if (b0arg > m_cutoff) { qint64 aax0 = 1; qint64 aay0 = 1; qint64 aaz0 = 1; qint64 aax1 = m_xamom(p); qint64 aay1 = m_yamom(p); qint64 aaz1 = m_zamom(p); qint64 aax2 = m_xamom(p) * (m_xamom(p) - 1); qint64 aay2 = m_yamom(p) * (m_yamom(p) - 1); qint64 aaz2 = m_zamom(p) * (m_zamom(p) - 1); qreal ax0 = aax0 * ipow(xx0, m_xamom(p)); qreal ay0 = aay0 * ipow(yy0, m_yamom(p)); qreal az0 = aaz0 * ipow(zz0, m_zamom(p)); qreal ax1; qreal ay1; qreal az1; if (m_xamom(p) < 1) { ax1 = zero; } else if (m_xamom(p) == 1) { ax1 = one; } else { ax1 = aax1 * ipow(xx0, m_xamom(p) - 1); } if (m_yamom(p) < 1) { ay1 = zero; } else if (m_yamom(p) == 1) { ay1 = one; } else { ay1 = aay1 * ipow(yy0, m_yamom(p) - 1); } if (m_zamom(p) < 1) { az1 = zero; } else if (m_zamom(p) == 1) { az1 = one; } else { az1 = aaz1 * ipow(zz0, m_zamom(p) - 1); } qreal ax2; qreal ay2; qreal az2; if (m_xamom(p) < 2) { ax2 = zero; } else if (m_xamom(p) == 2) { ax2 = one; } else { ax2 = aax2 * ipow(xx0, m_xamom(p) - 2); } if (m_yamom(p) < 2) { ay2 = zero; } else if (m_yamom(p) == 2) { ay2 = one; } else { ay2 = aay2 * ipow(yy0, m_yamom(p) - 2); } if (m_zamom(p) < 2) { az2 = zero; } else if (m_zamom(p) == 2) { az2 = one; } else { az2 = aaz2 * ipow(zz0, m_zamom(p) - 2); } qreal b0 = exp(b0arg); qreal bx1 = -2 * m_alpha(p) * xx0; qreal by1 = -2 * m_alpha(p) * yy0; qreal bz1 = -2 * m_alpha(p) * zz0; qreal bx2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(xx0, 2)); qreal by2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(yy0, 2)); qreal bz2 = -2 * m_alpha(p) + 4 * (ipow(m_alpha(p), 2) * ipow(zz0, 2)); qreal dg000 = ax0 * ay0 * az0 * b0; qreal dg100 = ay0 * az0 * b0 * (ax1 + ax0 * bx1); qreal dg010 = ax0 * az0 * b0 * (ay1 + ay0 * by1); qreal dg001 = ax0 * ay0 * b0 * (az1 + az0 * bz1); qreal dg200 = ay0 * az0 * b0 * (ax2 + 2 * ax1 * bx1 + ax0 * bx2); qreal dg020 = ax0 * az0 * b0 * (ay2 + 2 * ay1 * by1 + ay0 * by2); qreal dg002 = ax0 * ay0 * b0 * (az2 + 2 * az1 * bz1 + az0 * bz2); qreal dg110 = az0 * b0 * (ax1 + ax0 * bx1) * (ay1 + ay0 * by1); qreal dg101 = ay0 * b0 * (ax1 + ax0 * bx1) * (az1 + az0 * bz1); qreal dg011 = ax0 * b0 * (ay1 + ay0 * by1) * (az1 + az0 * bz1); for (qint64 m = 0; m < m_nmo; ++m) { m_cdg000(m) += m_coef(m, p) * dg000; m_cdg100(m) += m_coef(m, p) * dg100; m_cdg010(m) += m_coef(m, p) * dg010; m_cdg001(m) += m_coef(m, p) * dg001; m_cdg200(m) += m_coef(m, p) * dg200; m_cdg020(m) += m_coef(m, p) * dg020; m_cdg002(m) += m_coef(m, p) * dg002; m_cdg110(m) += m_coef(m, p) * dg110; m_cdg101(m) += m_coef(m, p) * dg101; m_cdg011(m) += m_coef(m, p) * dg011; } } } value.setZero(); for (qint64 m = 0; m < m_nmo; ++m) { value(0, 0) += (m_occno(m) * (2 * m_cdg000(m) * m_cdg200(m) - 2 * ipow(m_cdg100(m), 2))); value(0, 1) += (m_occno(m) * (2 * m_cdg000(m) * m_cdg110(m) - 2 * m_cdg100(m) * m_cdg010(m))); value(0, 2) += (m_occno(m) * (2 * m_cdg000(m) * m_cdg101(m) - 2 * m_cdg100(m) * m_cdg001(m))); value(1, 1) += (m_occno(m) * (2 * m_cdg000(m) * m_cdg020(m) - 2 * ipow(m_cdg010(m), 2))); value(1, 2) += (m_occno(m) * (2 * m_cdg000(m) * m_cdg011(m) - 2 * m_cdg010(m) * m_cdg001(m))); value(2, 2) += (m_occno(m) * (2 * m_cdg000(m) * m_cdg002(m) - 2 * ipow(m_cdg001(m), 2))); } value(1, 0) = value(0, 1); value(2, 0) = value(0, 2); value(2, 1) = value(1, 2); return 0.25 * value; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/qtaimwavefunctionevaluator.h000066400000000000000000000102531506155467400271470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright (C) 2010 Eric C. Brown This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef QTAIMWAVEFUNCTIONEVALUATOR_H #define QTAIMWAVEFUNCTIONEVALUATOR_H #include "qtaimwavefunction.h" #include using namespace Eigen; namespace Avogadro::QtPlugins { class QTAIMWavefunctionEvaluator { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW explicit QTAIMWavefunctionEvaluator(QTAIMWavefunction& wfn); qreal molecularOrbital(const qint64 mo, const Matrix xyz); qreal electronDensity(const Matrix xyz); Matrix gradientOfElectronDensity(const Matrix xyz); Matrix hessianOfElectronDensity(const Matrix xyz); Matrix gradientAndHessianOfElectronDensity( const Matrix xyz); qreal laplacianOfElectronDensity(const Matrix xyz); qreal electronDensityLaplacian(const Matrix xyz) { return laplacianOfElectronDensity(xyz); } Matrix gradientOfElectronDensityLaplacian( const Matrix xyz); Matrix hessianOfElectronDensityLaplacian( const Matrix xyz); Matrix gradientAndHessianOfElectronDensityLaplacian( const Matrix xyz); qreal kineticEnergyDensityG(const Matrix xyz); qreal kineticEnergyDensityK(const Matrix xyz); Matrix quantumStressTensor(const Matrix xyz); private: qint64 m_nmo; qint64 m_nprim; qint64 m_nnuc; // qint64 m_noccmo; // number of (significantly) occupied molecular // orbitals Matrix m_nucxcoord; Matrix m_nucycoord; Matrix m_nuczcoord; Matrix m_nucz; Matrix m_X0; Matrix m_Y0; Matrix m_Z0; Matrix m_xamom; Matrix m_yamom; Matrix m_zamom; Matrix m_alpha; Matrix m_occno; Matrix m_orbe; Matrix m_coef; qreal m_totalEnergy; qreal m_virialRatio; qreal m_cutoff; Matrix m_cdg000; Matrix m_cdg100; Matrix m_cdg010; Matrix m_cdg001; Matrix m_cdg200; Matrix m_cdg110; Matrix m_cdg101; Matrix m_cdg020; Matrix m_cdg011; Matrix m_cdg002; Matrix m_cdg300; Matrix m_cdg120; Matrix m_cdg102; Matrix m_cdg210; Matrix m_cdg030; Matrix m_cdg012; Matrix m_cdg201; Matrix m_cdg021; Matrix m_cdg003; Matrix m_cdg111; Matrix m_cdg400; Matrix m_cdg220; Matrix m_cdg202; Matrix m_cdg310; Matrix m_cdg130; Matrix m_cdg112; Matrix m_cdg301; Matrix m_cdg121; Matrix m_cdg103; Matrix m_cdg040; Matrix m_cdg022; Matrix m_cdg211; Matrix m_cdg031; Matrix m_cdg013; Matrix m_cdg004; static qreal ipow(qreal a, qint64 n) { return (qreal)pow(a, (int)n); } }; } // namespace Avogadro::QtPlugins #endif // QTAIMWAVEFUNCTIONEVALUATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/test/000077500000000000000000000000001506155467400222655ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/test/c4h4.wfn000066400000000000000000001023651506155467400235520ustar00rootroot00000000000000Tetrahedrane 6-31G* at 6-31G* geometry 1/11/91 JRC GAUSSIAN 14 MOL ORBITALS 128 PRIMITIVES 8 NUCLEI C 1 (CENTRE 1) 0.97775600 0.97775600 0.97775600 CHARGE = 6.0 C 2 (CENTRE 2) -0.97775600 -0.97775600 0.97775600 CHARGE = 6.0 C 3 (CENTRE 3) 0.97775600 -0.97775600 -0.97775600 CHARGE = 6.0 C 4 (CENTRE 4) -0.97775600 0.97775600 -0.97775600 CHARGE = 6.0 H 5 (CENTRE 5) 2.13806500 2.13806500 2.13806500 CHARGE = 1.0 H 6 (CENTRE 6) -2.13806500 -2.13806500 2.13806500 CHARGE = 1.0 H 7 (CENTRE 7) 2.13806500 -2.13806500 -2.13806500 CHARGE = 1.0 H 8 (CENTRE 8) -2.13806500 2.13806500 -2.13806500 CHARGE = 1.0 CENTRE ASSIGNMENTS 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 CENTRE ASSIGNMENTS 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 CENTRE ASSIGNMENTS 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 CENTRE ASSIGNMENTS 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 CENTRE ASSIGNMENTS 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 CENTRE ASSIGNMENTS 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 6 6 6 6 CENTRE ASSIGNMENTS 7 7 7 7 8 8 8 8 TYPE ASSIGNMENTS 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3 4 4 4 1 2 TYPE ASSIGNMENTS 3 4 5 6 7 8 9 10 1 1 1 1 1 1 1 1 1 2 2 2 TYPE ASSIGNMENTS 3 3 3 4 4 4 1 2 3 4 5 6 7 8 9 10 1 1 1 1 TYPE ASSIGNMENTS 1 1 1 1 1 2 2 2 3 3 3 4 4 4 1 2 3 4 5 6 TYPE ASSIGNMENTS 7 8 9 10 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3 4 TYPE ASSIGNMENTS 4 4 1 2 3 4 5 6 7 8 9 10 1 1 1 1 1 1 1 1 TYPE ASSIGNMENTS 1 1 1 1 1 1 1 1 EXPONENTS 0.3047525E+04 0.4573695E+03 0.1039487E+03 0.2921016E+02 0.9286663E+01 EXPONENTS 0.3163927E+01 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.7868272E+01 EXPONENTS 0.1881289E+01 0.5442493E+00 0.7868272E+01 0.1881289E+01 0.5442493E+00 EXPONENTS 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.1687145E+00 0.1687145E+00 EXPONENTS 0.1687145E+00 0.1687145E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 EXPONENTS 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.3047525E+04 0.4573695E+03 EXPONENTS 0.1039487E+03 0.2921016E+02 0.9286663E+01 0.3163927E+01 0.7868272E+01 EXPONENTS 0.1881289E+01 0.5442493E+00 0.7868272E+01 0.1881289E+01 0.5442493E+00 EXPONENTS 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.7868272E+01 0.1881289E+01 EXPONENTS 0.5442493E+00 0.1687145E+00 0.1687145E+00 0.1687145E+00 0.1687145E+00 EXPONENTS 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 EXPONENTS 0.8000000E+00 0.3047525E+04 0.4573695E+03 0.1039487E+03 0.2921016E+02 EXPONENTS 0.9286663E+01 0.3163927E+01 0.7868272E+01 0.1881289E+01 0.5442493E+00 EXPONENTS 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.7868272E+01 0.1881289E+01 EXPONENTS 0.5442493E+00 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.1687145E+00 EXPONENTS 0.1687145E+00 0.1687145E+00 0.1687145E+00 0.8000000E+00 0.8000000E+00 EXPONENTS 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.3047525E+04 EXPONENTS 0.4573695E+03 0.1039487E+03 0.2921016E+02 0.9286663E+01 0.3163927E+01 EXPONENTS 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.7868272E+01 0.1881289E+01 EXPONENTS 0.5442493E+00 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.7868272E+01 EXPONENTS 0.1881289E+01 0.5442493E+00 0.1687145E+00 0.1687145E+00 0.1687145E+00 EXPONENTS 0.1687145E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 EXPONENTS 0.8000000E+00 0.8000000E+00 0.1873114E+02 0.2825394E+01 0.6401217E+00 EXPONENTS 0.1612778E+00 0.1873114E+02 0.2825394E+01 0.6401217E+00 0.1612778E+00 EXPONENTS 0.1873114E+02 0.2825394E+01 0.6401217E+00 0.1612778E+00 0.1873114E+02 EXPONENTS 0.2825394E+01 0.6401217E+00 0.1612778E+00 MO 1 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -11.2380343 0.26693201E+00 0.49243743E+00 0.79494670E+00 0.10347844E+01 0.88298341E+00 0.30487295E+00 -0.58011440E-02 -0.26737419E-02 0.74974515E-02 -0.61745883E-03 -0.47342827E-03 -0.23627389E-03 -0.61745883E-03 -0.47342827E-03 -0.23627389E-03 -0.61745883E-03 -0.47342827E-03 -0.23627389E-03 -0.24675943E-03 0.17853802E-03 0.17853802E-03 0.17853802E-03 -0.13142320E-02 -0.13142320E-02 -0.13142320E-02 0.30335721E-05 0.30335722E-05 0.30335721E-05 0.26693201E+00 0.49243743E+00 0.79494670E+00 0.10347844E+01 0.88298341E+00 0.30487295E+00 -0.58011440E-02 -0.26737419E-02 0.74974515E-02 0.61745883E-03 0.47342827E-03 0.23627389E-03 0.61745883E-03 0.47342827E-03 0.23627389E-03 -0.61745883E-03 -0.47342827E-03 -0.23627389E-03 -0.24675943E-03 -0.17853802E-03 -0.17853802E-03 0.17853802E-03 -0.13142320E-02 -0.13142320E-02 -0.13142320E-02 0.30335721E-05 -0.30335721E-05 -0.30335722E-05 0.26693201E+00 0.49243743E+00 0.79494670E+00 0.10347844E+01 0.88298341E+00 0.30487295E+00 -0.58011440E-02 -0.26737419E-02 0.74974515E-02 -0.61745883E-03 -0.47342827E-03 -0.23627389E-03 0.61745883E-03 0.47342827E-03 0.23627389E-03 0.61745883E-03 0.47342827E-03 0.23627389E-03 -0.24675943E-03 0.17853802E-03 -0.17853802E-03 -0.17853802E-03 -0.13142320E-02 -0.13142320E-02 -0.13142320E-02 -0.30335722E-05 -0.30335722E-05 0.30335722E-05 0.26693201E+00 0.49243743E+00 0.79494670E+00 0.10347844E+01 0.88298341E+00 0.30487295E+00 -0.58011440E-02 -0.26737419E-02 0.74974515E-02 0.61745883E-03 0.47342827E-03 0.23627389E-03 -0.61745883E-03 -0.47342827E-03 -0.23627389E-03 0.61745883E-03 0.47342827E-03 0.23627389E-03 -0.24675943E-03 -0.17853802E-03 0.17853802E-03 -0.17853802E-03 -0.13142320E-02 -0.13142320E-02 -0.13142320E-02 -0.30335721E-05 0.30335721E-05 -0.30335721E-05 -0.89875950E-04 -0.15244662E-03 -0.17355509E-03 -0.12370279E-03 -0.89875950E-04 -0.15244662E-03 -0.17355509E-03 -0.12370279E-03 -0.89875950E-04 -0.15244662E-03 -0.17355509E-03 -0.12370279E-03 -0.89875950E-04 -0.15244662E-03 -0.17355509E-03 -0.12370279E-03 MO 2 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -11.2364689 -0.13360531E+00 -0.24647571E+00 -0.39788822E+00 -0.51793224E+00 -0.44195252E+00 -0.15259558E+00 0.27798091E-02 0.12812115E-02 -0.35926507E-02 0.40226024E-03 0.30842764E-03 0.15392701E-03 0.39414475E-03 0.30220519E-03 0.15082157E-03 0.39124026E-03 0.29997822E-03 0.14971015E-03 0.87725150E-03 0.46622737E-04 -0.19861053E-03 -0.28637795E-03 0.65105547E-03 0.24361944E-03 0.97800673E-04 -0.47048277E-04 -0.23342264E-03 -0.75417612E-03 -0.24930352E+00 -0.45991631E+00 -0.74244752E+00 -0.96644607E+00 -0.82467018E+00 -0.28473880E+00 0.51870406E-02 0.23907023E-02 -0.67037788E-02 -0.73635732E-03 -0.56459209E-03 -0.28177102E-03 -0.74447281E-03 -0.57081453E-03 -0.28487646E-03 0.73528041E-03 0.56376638E-03 0.28135893E-03 0.16369251E-02 0.34355622E-03 0.98322959E-04 -0.37609819E-03 0.49951990E-03 0.90695593E-03 0.44545394E-03 -0.42388701E-03 0.10137435E-02 0.49298997E-03 0.44833820E+00 0.82709643E+00 0.13351901E+01 0.17380208E+01 0.14830563E+01 0.51206369E+00 -0.93281815E-02 -0.42993503E-02 0.12055827E-01 -0.13282068E-02 -0.10183847E-02 -0.50824535E-03 0.13300344E-02 0.10197860E-02 0.50894468E-03 0.13271299E-02 0.10175590E-02 0.50783326E-03 -0.29437854E-02 0.49790122E-03 -0.44267576E-03 -0.53044319E-03 -0.10975850E-02 -0.11893378E-02 -0.10435190E-02 -0.10721596E-02 -0.12585340E-02 0.11412626E-02 -0.65429372E-01 -0.12070441E+00 -0.19485435E+00 -0.25364247E+00 -0.21643358E+00 -0.74729313E-01 0.13613318E-02 0.62743659E-03 -0.17593977E-02 -0.18958922E-03 -0.14536499E-03 -0.72547319E-04 0.19141680E-03 0.14676626E-03 0.73246649E-04 -0.20060920E-03 -0.15381441E-03 -0.76764171E-04 0.42960878E-03 0.20096773E-03 -0.14574228E-03 -0.13203295E-03 -0.52990380E-04 0.38762430E-04 0.50026442E-03 0.60122434E-03 -0.11367898E-04 -0.10590350E-03 0.33934163E-04 0.57558764E-04 0.65528618E-04 -0.16786044E-04 0.63320134E-04 0.10740293E-03 0.12227444E-03 -0.31322256E-04 -0.11387258E-03 -0.19314945E-03 -0.21989382E-03 0.56328784E-04 0.16618284E-04 0.28187755E-04 0.32090762E-04 -0.82204838E-05 MO 3 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -11.2364689 0.41906123E+00 0.77308614E+00 0.12480007E+01 0.16245261E+01 0.13862111E+01 0.47862538E+00 -0.87190411E-02 -0.40185981E-02 0.11268569E-01 -0.12393200E-02 -0.95023195E-03 -0.47423234E-03 -0.12443521E-02 -0.95409025E-03 -0.47615790E-03 -0.12414499E-02 -0.95186503E-03 -0.47504736E-03 -0.27515530E-02 0.53046067E-03 0.37840097E-03 0.46609931E-03 -0.91779785E-03 -0.11704332E-02 -0.10247293E-02 0.10652259E-02 0.12514535E-02 0.92855433E-03 -0.12338996E+00 -0.22763039E+00 -0.36746603E+00 -0.47833158E+00 -0.40816121E+00 -0.14092826E+00 0.25672673E-02 0.11832511E-02 -0.33179597E-02 -0.36011201E-03 -0.27611105E-03 -0.13779877E-03 -0.36514412E-03 -0.27996935E-03 -0.13972433E-03 0.37158279E-03 0.28490611E-03 0.14218812E-03 0.81017761E-03 0.30117711E-03 0.14911741E-03 0.45445851E-04 0.29356401E-04 0.28199180E-03 0.60524356E-03 -0.70158307E-03 0.28842746E-03 -0.34471718E-04 0.11448112E-01 0.21119531E-01 0.34093471E-01 0.44379569E-01 0.37869167E-01 0.13075313E-01 -0.23819087E-03 -0.10978195E-03 0.30784006E-03 -0.27241759E-04 -0.20887253E-04 -0.10424203E-04 0.35810339E-04 0.27457097E-04 0.13703016E-04 0.38712536E-04 0.29682317E-04 0.14813557E-04 -0.75168219E-04 0.21436981E-03 0.44554818E-04 0.13225316E-03 0.30700967E-03 -0.12317350E-03 -0.26887749E-03 -0.33697909E-03 -0.15075143E-03 -0.39907570E-03 -0.30711937E+00 -0.56657528E+00 -0.91462817E+00 -0.11905741E+01 -0.10159191E+01 -0.35077243E+00 0.63899647E-02 0.29451289E-02 -0.82584489E-02 -0.90644972E-03 -0.69500816E-03 -0.34685778E-03 0.91501831E-03 0.70157800E-03 0.35013659E-03 -0.90857963E-03 -0.69664123E-03 -0.34767280E-03 0.20165436E-02 0.44365337E-03 -0.18472874E-03 0.37929200E-03 0.58143178E-03 0.10116150E-02 0.68836319E-03 0.70062187E-03 -0.11137775E-02 0.56395035E-03 -0.10643658E-03 -0.18053658E-03 -0.20553452E-03 0.52650453E-04 0.31339586E-04 0.53157870E-04 0.60518356E-04 -0.15502597E-04 -0.29076845E-05 -0.49319833E-05 -0.56148887E-05 0.14383298E-05 0.78004676E-04 0.13231069E-03 0.15063105E-03 -0.38586185E-04 MO 4 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -11.2364689 -0.14319701E+00 -0.26417052E+00 -0.42645312E+00 -0.55511525E+00 -0.47368085E+00 -0.16355061E+00 0.29793751E-02 0.13731913E-02 -0.38505716E-02 0.42533783E-03 0.32612207E-03 0.16275777E-03 0.41818344E-03 0.32063654E-03 0.16002010E-03 0.42938655E-03 0.32922637E-03 0.16430703E-03 0.94023051E-03 -0.12532999E-03 -0.34152068E-03 -0.29858984E-05 0.40654850E-03 0.47364448E-04 0.60981372E-03 -0.69586773E-03 0.23011784E-04 -0.43606972E-03 0.36958185E+00 0.68180636E+00 0.11006469E+01 0.14327152E+01 0.12225385E+01 0.42211315E+00 -0.76895670E-02 -0.35441144E-02 0.99380668E-02 0.10985157E-02 0.84227219E-03 0.42035285E-03 0.10913613E-02 0.83678666E-03 0.41761519E-03 -0.10954125E-02 -0.83989282E-03 -0.41916538E-03 -0.24266718E-02 -0.30088417E-03 -0.51707486E-03 0.39465762E-03 -0.10867962E-02 -0.72761210E-03 -0.93099883E-03 0.97429593E-03 -0.71434261E-03 -0.11734241E-02 0.11326904E+00 0.20895925E+00 0.33732502E+00 0.43909695E+00 0.37468223E+00 0.12936877E+00 -0.23566899E-02 -0.10861962E-02 0.30458076E-02 -0.33728951E-03 -0.25861220E-03 -0.12906561E-03 0.32918963E-03 0.25240173E-03 0.12596615E-03 0.34039275E-03 0.26099157E-03 0.13025308E-03 -0.74372367E-03 0.73551176E-04 -0.31831250E-03 0.20222281E-04 -0.36408798E-03 0.42563974E-04 -0.51988530E-03 -0.59838982E-03 0.12048969E-03 0.39926164E-03 -0.33965388E+00 -0.62659509E+00 -0.10115188E+01 -0.13166969E+01 -0.11235398E+01 -0.38793130E+00 0.70668818E-02 0.32571194E-02 -0.91333027E-02 -0.10104674E-02 -0.77476232E-03 -0.38666070E-03 0.10023675E-02 0.76855185E-03 0.38356124E-03 -0.10064187E-02 -0.77165802E-03 -0.38511143E-03 0.22301649E-02 0.24910535E-03 -0.49386668E-03 0.37144944E-03 0.10443356E-02 0.63768368E-03 0.84107041E-03 0.87681802E-03 -0.61686470E-03 0.11366160E-02 0.36370340E-04 0.61690981E-04 0.70233001E-04 -0.17991135E-04 -0.93869405E-04 -0.15922028E-03 -0.18126666E-03 0.46433912E-04 -0.28768991E-04 -0.48797653E-04 -0.55554403E-04 0.14231014E-04 0.86268055E-04 0.14632695E-03 0.16658806E-03 -0.42673790E-04 MO 5 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -1.2965995 -0.57728863E-01 -0.10649848E+00 -0.17192157E+00 -0.22379080E+00 -0.19096109E+00 -0.65934276E-01 -0.82048544E-01 -0.37816099E-01 0.10604029E+00 -0.10615075E+00 -0.81389661E-01 -0.40619145E-01 -0.10615075E+00 -0.81389661E-01 -0.40619145E-01 -0.10615075E+00 -0.81389661E-01 -0.40619145E-01 0.19143947E-01 -0.39103300E-03 -0.39103300E-03 -0.39103300E-03 0.25806563E-02 0.25806563E-02 0.25806563E-02 0.20153688E-01 0.20153688E-01 0.20153688E-01 -0.57728863E-01 -0.10649848E+00 -0.17192157E+00 -0.22379080E+00 -0.19096109E+00 -0.65934276E-01 -0.82048544E-01 -0.37816099E-01 0.10604029E+00 0.10615075E+00 0.81389661E-01 0.40619145E-01 0.10615075E+00 0.81389661E-01 0.40619145E-01 -0.10615075E+00 -0.81389661E-01 -0.40619145E-01 0.19143947E-01 0.39103300E-03 0.39103300E-03 -0.39103300E-03 0.25806563E-02 0.25806563E-02 0.25806563E-02 0.20153688E-01 -0.20153688E-01 -0.20153688E-01 -0.57728863E-01 -0.10649848E+00 -0.17192157E+00 -0.22379080E+00 -0.19096109E+00 -0.65934276E-01 -0.82048544E-01 -0.37816099E-01 0.10604029E+00 -0.10615075E+00 -0.81389661E-01 -0.40619145E-01 0.10615075E+00 0.81389661E-01 0.40619145E-01 0.10615075E+00 0.81389661E-01 0.40619145E-01 0.19143947E-01 -0.39103300E-03 0.39103300E-03 0.39103300E-03 0.25806563E-02 0.25806563E-02 0.25806563E-02 -0.20153688E-01 -0.20153688E-01 0.20153688E-01 -0.57728863E-01 -0.10649848E+00 -0.17192157E+00 -0.22379080E+00 -0.19096109E+00 -0.65934276E-01 -0.82048544E-01 -0.37816099E-01 0.10604029E+00 0.10615075E+00 0.81389661E-01 0.40619145E-01 -0.10615075E+00 -0.81389661E-01 -0.40619145E-01 0.10615075E+00 0.81389661E-01 0.40619145E-01 0.19143947E-01 0.39103300E-03 -0.39103300E-03 0.39103300E-03 0.25806563E-02 0.25806563E-02 0.25806563E-02 -0.20153688E-01 0.20153688E-01 -0.20153688E-01 0.44775954E-02 0.75948493E-02 0.86464675E-02 -0.11533665E-02 0.44775954E-02 0.75948493E-02 0.86464675E-02 -0.11533665E-02 0.44775954E-02 0.75948493E-02 0.86464675E-02 -0.11533665E-02 0.44775954E-02 0.75948493E-02 0.86464675E-02 -0.11533665E-02 MO 6 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.8053912 -0.96389409E-02 -0.17781964E-01 -0.28705603E-01 -0.37366166E-01 -0.31884617E-01 -0.11008992E-01 -0.13914564E-01 -0.64132098E-02 0.17983310E-01 -0.11652462E+00 -0.89343685E-01 -0.44588761E-01 -0.18258901E-01 -0.13999767E-01 -0.69868648E-02 0.16412115E+00 0.12583769E+00 0.62801825E-01 0.76330712E-02 -0.37176846E-02 -0.66911543E-03 0.49889942E-02 0.12085810E-01 0.31759623E-02 -0.13360614E-01 0.24911443E-01 -0.13498814E-02 -0.15499389E-01 -0.69432276E-01 -0.12808899E+00 -0.20677535E+00 -0.26916006E+00 -0.22967477E+00 -0.79301179E-01 -0.10023091E+00 -0.46196336E-01 0.12953935E+00 -0.84750158E-01 -0.64981046E-01 -0.32430096E-01 0.13515558E-01 0.10362872E-01 0.51717996E-02 0.14009343E+00 0.10741476E+00 0.53607492E-01 0.54983376E-01 -0.18898018E-02 0.11587674E-02 0.36067566E-02 0.32676048E-02 0.12177453E-01 -0.17504234E-02 0.29387282E-01 -0.72687689E-02 -0.21418276E-01 0.62661333E-01 0.11559792E+00 0.18661089E+00 0.24291193E+00 0.20727719E+00 0.71567833E-01 0.90456527E-01 0.41691331E-01 -0.11690684E+00 -0.87471035E-01 -0.67067242E-01 -0.33471254E-01 -0.39565741E-01 -0.30336500E-01 -0.15140040E-01 0.14281431E+00 0.10950095E+00 0.54648650E-01 -0.49621470E-01 -0.20463252E-02 -0.18948297E-02 0.37632800E-02 -0.19528773E-02 -0.13471425E-01 0.30651509E-02 0.28880442E-01 0.26191175E-02 -0.20911436E-01 0.16409884E-01 0.30273032E-01 0.48870060E-01 0.63614298E-01 0.54282196E-01 0.18742338E-01 0.23688949E-01 0.10918215E-01 -0.30615815E-01 -0.11924549E+00 -0.91429881E-01 -0.45629919E-01 -0.77912824E-02 -0.59738611E-02 -0.29813754E-02 0.16140027E+00 0.12375149E+00 0.61760667E-01 -0.12994977E-01 -0.38742080E-02 -0.66946879E-04 0.48324709E-02 -0.13400538E-01 -0.18819898E-02 0.12045886E-01 0.25418283E-01 -0.32997700E-02 -0.14992549E-01 0.48009308E-02 0.81432872E-02 0.92708447E-02 0.18997559E-02 0.34582591E-01 0.58658619E-01 0.66780765E-01 0.13684530E-01 -0.31210142E-01 -0.52938309E-01 -0.60268393E-01 -0.12350033E-01 -0.81733791E-02 -0.13863598E-01 -0.15783216E-01 -0.32342531E-02 MO 7 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.8053912 -0.74474309E-01 -0.13739056E+00 -0.22179096E+00 -0.28870593E+00 -0.24635329E+00 -0.85059872E-01 -0.10750948E+00 -0.49551022E-01 0.13894624E+00 0.13361497E+00 0.10244749E+00 0.51128476E-01 0.99095615E-02 0.75980232E-02 0.37919461E-02 0.83149717E-01 0.63753929E-01 0.31817679E-01 0.58976159E-01 0.33520723E-02 -0.48573120E-03 0.17864517E-02 -0.36770411E-03 0.10848786E-01 0.42080297E-02 0.21856993E-01 0.11310974E-01 0.29123602E-01 0.21251560E-01 0.39204978E-01 0.63288991E-01 0.82383461E-01 0.70297957E-01 0.24272195E-01 0.30678286E-01 0.14139594E-01 -0.39648898E-01 0.15500233E+00 0.11884595E+00 0.59312459E-01 0.31296915E-01 0.23996489E-01 0.11975930E-01 0.12161678E+00 0.93248035E-01 0.46537305E-01 -0.16829097E-01 0.45824182E-02 0.74461472E-03 0.39993388E-02 0.10702071E-01 -0.51441884E-03 -0.14379252E-01 0.14691419E-01 0.73269772E-02 0.25139605E-01 -0.25012937E-02 -0.46143984E-02 -0.74490698E-02 -0.96964755E-02 -0.82740201E-02 -0.28568203E-02 -0.36108127E-02 -0.16642203E-02 0.46666474E-02 0.16253705E+00 0.12462310E+00 0.62195660E-01 0.40841906E-01 0.31314983E-01 0.15628371E-01 0.11408206E+00 0.87470888E-01 0.43654104E-01 0.19807729E-02 0.50158666E-02 0.12937075E-02 0.35658904E-02 -0.14342847E-01 0.40977199E-02 0.10738476E-01 0.16094973E-01 0.55489539E-02 0.23736051E-01 0.55724043E-01 0.10279998E+00 0.16595104E+00 0.21601895E+00 0.18432936E+00 0.63644498E-01 0.80442008E-01 0.37075648E-01 -0.10396399E+00 0.14114969E+00 0.10822463E+00 0.54011676E-01 0.62229259E-01 0.47713449E-01 0.23812355E-01 0.90684438E-01 0.69531075E-01 0.34700880E-01 -0.44127835E-01 0.37855206E-02 0.25240534E-02 0.22199001E-02 0.40084805E-02 -0.14432087E-01 -0.56725327E-03 0.20453439E-01 0.15649572E-02 0.27720048E-01 0.37093910E-01 0.62918292E-01 0.71630251E-01 0.14678273E-01 -0.10584904E-01 -0.17954001E-01 -0.20439996E-01 -0.41885076E-02 0.12458358E-02 0.21131733E-02 0.24057731E-02 0.49298440E-03 -0.27754841E-01 -0.47077464E-01 -0.53596028E-01 -0.10982750E-01 MO 8 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.8053912 -0.34536587E-01 -0.63713259E-01 -0.10285295E+00 -0.13388399E+00 -0.11424345E+00 -0.39445517E-01 -0.49856260E-01 -0.22978705E-01 0.64434686E-01 -0.54903460E-01 -0.42096491E-01 -0.21009099E-01 0.18442817E+00 0.14140782E+00 0.70572415E-01 -0.24407188E-01 -0.18713884E-01 -0.93395396E-02 0.27349501E-01 -0.20711301E-02 0.53538302E-02 -0.11250220E-02 0.10425820E-01 -0.11274611E-01 0.76606938E-02 0.10691756E-02 0.31139861E-01 -0.33220531E-02 0.39492253E-01 0.72855495E-01 0.11761136E+00 0.15309504E+00 0.13063628E+00 0.45105567E-01 0.57010151E-01 0.26275926E-01 -0.73680441E-01 -0.56894875E-01 -0.43623381E-01 -0.21771125E-01 0.18243676E+00 0.13988093E+00 0.69810389E-01 0.53410106E-02 0.40951482E-02 0.20437659E-02 -0.31273890E-01 -0.21856899E-02 0.52392704E-02 0.58629661E-03 -0.11388072E-01 0.10312360E-01 -0.67136329E-02 -0.44722640E-02 0.31510818E-01 -0.29510959E-02 0.53846158E-01 0.99335647E-01 0.16035853E+00 0.20873916E+00 0.17811751E+00 0.61499695E-01 0.77731134E-01 0.35826208E-01 -0.10046043E+00 -0.19387201E-01 -0.14864876E-01 -0.74186149E-02 0.17666870E+00 0.13545835E+00 0.67603210E-01 -0.32166663E-01 -0.24663357E-01 -0.12308744E-01 -0.42640739E-01 -0.27993513E-04 0.49074524E-02 -0.15713997E-02 -0.67356325E-02 0.75252335E-02 -0.11410071E-01 0.25145963E-02 0.32585282E-01 -0.99379563E-02 -0.58801824E-01 -0.10847788E+00 -0.17511694E+00 -0.22795022E+00 -0.19451034E+00 -0.67159745E-01 -0.84885025E-01 -0.39123430E-01 0.10970618E+00 -0.17395786E-01 -0.13337985E-01 -0.66565894E-02 0.17467728E+00 0.13393146E+00 0.66841185E-01 0.13100486E-01 0.10044622E-01 0.50129702E-02 0.46565127E-01 0.86566212E-04 0.47928927E-02 0.10326744E-02 0.76978840E-02 -0.65629820E-02 0.10463011E-01 -0.59176848E-02 0.32956239E-01 -0.10308913E-01 0.17201865E-01 0.29177619E-01 0.33217688E-01 0.68068770E-02 -0.19670166E-01 -0.33364325E-01 -0.37984105E-01 -0.77835980E-02 -0.26819510E-01 -0.45490966E-01 -0.51789856E-01 -0.10612635E-01 0.29287812E-01 0.49677672E-01 0.56556273E-01 0.11589356E-01 MO 9 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.7096543 -0.16783389E-01 -0.30962076E-01 -0.49982391E-01 -0.65062221E-01 -0.55517711E-01 -0.19168932E-01 -0.23164280E-01 -0.10676395E-01 0.29937727E-01 0.17449554E+00 0.13379211E+00 0.66771640E-01 0.17449554E+00 0.13379211E+00 0.66771640E-01 0.17449554E+00 0.13379211E+00 0.66771640E-01 0.19011742E-01 0.65481182E-02 0.65481182E-02 0.65481182E-02 0.30838012E-02 0.30838012E-02 0.30838012E-02 -0.23251198E-02 -0.23251198E-02 -0.23251198E-02 -0.16783389E-01 -0.30962076E-01 -0.49982391E-01 -0.65062221E-01 -0.55517711E-01 -0.19168932E-01 -0.23164280E-01 -0.10676395E-01 0.29937727E-01 -0.17449554E+00 -0.13379211E+00 -0.66771640E-01 -0.17449554E+00 -0.13379211E+00 -0.66771640E-01 0.17449554E+00 0.13379211E+00 0.66771640E-01 0.19011742E-01 -0.65481182E-02 -0.65481182E-02 0.65481182E-02 0.30838012E-02 0.30838012E-02 0.30838012E-02 -0.23251198E-02 0.23251198E-02 0.23251198E-02 -0.16783389E-01 -0.30962076E-01 -0.49982391E-01 -0.65062221E-01 -0.55517711E-01 -0.19168932E-01 -0.23164280E-01 -0.10676395E-01 0.29937727E-01 0.17449554E+00 0.13379211E+00 0.66771640E-01 -0.17449554E+00 -0.13379211E+00 -0.66771640E-01 -0.17449554E+00 -0.13379211E+00 -0.66771640E-01 0.19011742E-01 0.65481182E-02 -0.65481182E-02 -0.65481182E-02 0.30838012E-02 0.30838012E-02 0.30838012E-02 0.23251198E-02 0.23251198E-02 -0.23251198E-02 -0.16783389E-01 -0.30962076E-01 -0.49982391E-01 -0.65062221E-01 -0.55517711E-01 -0.19168932E-01 -0.23164280E-01 -0.10676395E-01 0.29937727E-01 -0.17449554E+00 -0.13379211E+00 -0.66771640E-01 0.17449554E+00 0.13379211E+00 0.66771640E-01 -0.17449554E+00 -0.13379211E+00 -0.66771640E-01 0.19011742E-01 -0.65481182E-02 0.65481182E-02 -0.65481182E-02 0.30838012E-02 0.30838012E-02 0.30838012E-02 0.23251198E-02 -0.23251198E-02 0.23251198E-02 0.31940020E-01 0.54176319E-01 0.61677824E-01 0.14962780E-01 0.31940020E-01 0.54176319E-01 0.61677824E-01 0.14962780E-01 0.31940020E-01 0.54176319E-01 0.61677824E-01 0.14962780E-01 0.31940020E-01 0.54176319E-01 0.61677824E-01 0.14962780E-01 MO 10 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.5340742 0.20525814E-02 0.37866118E-02 0.61127655E-02 0.79570044E-02 0.67897263E-02 0.23443293E-02 0.31345534E-02 0.14447128E-02 -0.40511255E-02 0.25602753E+00 0.19630567E+00 0.97970288E-01 0.24662185E+00 0.18909399E+00 0.94371155E-01 -0.93560206E-01 -0.71736032E-01 -0.35801308E-01 -0.11031654E-01 0.15958983E-01 0.15481969E-01 -0.17705431E-02 -0.12066960E-01 -0.11144779E-01 0.22208400E-01 -0.39594607E-02 0.22729481E-01 0.23467402E-01 0.20410881E-02 0.37654090E-02 0.60785376E-02 0.79124499E-02 0.67517078E-02 0.23312025E-02 0.31170018E-02 0.14366233E-02 -0.40284416E-02 -0.24618151E+00 -0.18875637E+00 -0.94202657E-01 -0.25558719E+00 -0.19596805E+00 -0.97801791E-01 -0.94970192E-01 -0.72817119E-01 -0.36340846E-01 -0.10969883E-01 -0.15442982E-01 -0.15919996E-01 -0.18587063E-02 -0.11174596E-01 -0.12096777E-01 0.22273652E-01 -0.40890114E-02 -0.23413925E-01 -0.22676004E-01 -0.21025775E-02 -0.38788450E-02 -0.62616585E-02 -0.81508188E-02 -0.69551085E-02 -0.24014318E-02 -0.32109040E-02 -0.14799027E-02 0.41498016E-02 -0.25372500E+00 -0.19454024E+00 -0.97089213E-01 0.25275535E+00 0.19379677E+00 0.96718172E-01 -0.87426704E-01 -0.67033251E-01 -0.33454290E-01 0.11300360E-01 -0.15914659E-01 0.15865483E-01 -0.13870295E-02 0.11523699E-01 0.11428629E-01 -0.21924550E-01 -0.33959091E-02 0.23293032E-01 -0.23369106E-01 -0.19910919E-02 -0.36731758E-02 -0.59296447E-02 -0.77186355E-02 -0.65863257E-02 -0.22741000E-02 -0.30406512E-02 -0.14014334E-02 0.39297655E-02 0.24848404E+00 0.19052180E+00 0.95083733E-01 -0.24945369E+00 -0.19126527E+00 -0.95454774E-01 -0.10110369E+00 -0.77519900E-01 -0.38687863E-01 0.10701177E-01 0.15487306E-01 -0.15536482E-01 -0.22422199E-02 0.11717858E-01 0.11812927E-01 -0.22557503E-01 -0.46525630E-02 -0.22850374E-01 0.22774300E-01 0.25401252E-01 0.43085330E-01 0.49051126E-01 0.15391510E-01 0.25259019E-01 0.42844078E-01 0.48776468E-01 0.15305326E-01 -0.26019968E-01 -0.44134790E-01 -0.50245899E-01 -0.15766412E-01 -0.24640303E-01 -0.41794618E-01 -0.47581695E-01 -0.14930424E-01 MO 11 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.5340742 0.28913561E-02 0.53339874E-02 0.86107096E-02 0.11208585E-01 0.95643062E-02 0.33023251E-02 0.44154694E-02 0.20350858E-02 -0.57065930E-02 0.10575234E+00 0.81084189E-01 0.40466693E-01 0.11524226E+00 0.88360457E-01 0.44098061E-01 0.35526634E+00 0.27239570E+00 0.13594454E+00 -0.15539672E-01 0.95531729E-02 0.10034459E-01 0.22207407E-01 0.79935715E-02 0.70631316E-02 -0.16470051E-01 0.32634712E-01 0.13803652E-01 0.13059122E-01 -0.28979513E-02 -0.53461544E-02 -0.86303508E-02 -0.11234152E-01 -0.95861226E-02 -0.33098578E-02 -0.44255412E-02 -0.20397279E-02 0.57196099E-02 0.10656145E+00 0.81704558E-01 0.40776301E-01 0.11605136E+00 0.88980825E-01 0.44407668E-01 -0.35496261E+00 -0.27216281E+00 -0.13582832E+00 0.15575118E-01 0.96037642E-02 0.10085050E-01 -0.22201560E-01 -0.79561273E-02 -0.70256874E-02 0.16398386E-01 -0.32621745E-01 0.13877993E-01 0.13133463E-01 0.59539660E-04 0.10983905E-03 0.17731428E-03 0.23081051E-03 0.19695102E-03 0.68002455E-04 0.90924652E-04 0.41907089E-04 -0.11751185E-03 -0.24165329E+00 -0.18528441E+00 -0.92469909E-01 -0.24677195E+00 -0.18920908E+00 -0.94428593E-01 -0.67478710E-02 -0.51738395E-02 -0.25821085E-02 -0.31999751E-03 -0.12169294E-01 -0.12601449E-01 -0.42850123E-03 0.24071035E-01 -0.23816661E-01 -0.28347844E-03 -0.62747080E-03 -0.19458531E-01 -0.18860812E-01 -0.52944403E-04 -0.97672085E-04 -0.15767303E-03 -0.20524344E-03 -0.17513460E-03 -0.60469767E-04 -0.80852853E-04 -0.37265006E-04 0.10449497E-03 -0.24246239E+00 -0.18590478E+00 -0.92779516E-01 -0.24596285E+00 -0.18858871E+00 -0.94118985E-01 0.70516099E-02 0.54067272E-02 0.26983358E-02 0.28455113E-03 -0.12219886E-01 -0.12550858E-01 0.43434826E-03 -0.24108479E-01 0.23779216E-01 0.35514290E-03 0.64043758E-03 -0.19384190E-01 -0.18935153E-01 0.35781317E-01 0.60691885E-01 0.69095566E-01 0.21681156E-01 -0.35862935E-01 -0.60830325E-01 -0.69253175E-01 -0.21730611E-01 0.73681946E-03 0.12497853E-02 0.14228363E-02 0.44646477E-03 -0.65520136E-03 -0.11113455E-02 -0.12652276E-02 -0.39700950E-03 MO 12 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.5340742 0.16654145E-04 0.30723645E-04 0.49597490E-04 0.64561195E-04 0.55090184E-04 0.19021317E-04 0.25433003E-04 0.11722048E-04 -0.32869845E-04 0.24147428E+00 0.18514716E+00 0.92401409E-01 -0.24686103E+00 -0.18927738E+00 -0.94462677E-01 0.87059979E-02 0.66752070E-02 0.33313961E-02 -0.89508152E-04 0.12270629E-01 -0.12495553E-01 0.46566292E-03 -0.23569602E-01 0.24309276E-01 -0.74781523E-03 0.71045894E-03 -0.19340021E-01 0.18972266E-01 -0.95629266E-04 -0.17641732E-03 -0.28479226E-03 -0.37071490E-03 -0.31633170E-03 -0.10922173E-03 -0.14603808E-03 -0.67308819E-04 0.18874095E-03 0.25116290E+00 0.19257578E+00 0.96108812E-01 -0.23717240E+00 -0.18184876E+00 -0.90755274E-01 -0.50688672E-02 -0.38864859E-02 -0.19396288E-02 0.51396208E-03 0.12876436E-01 -0.11889746E-01 -0.39564740E-03 0.24017978E-01 -0.23860900E-01 -0.11033307E-03 -0.55518772E-03 -0.18449821E-01 0.19862465E-01 -0.28546331E-02 -0.52662407E-02 -0.85013454E-02 -0.11066226E-01 -0.94428305E-02 -0.32603824E-02 -0.43593888E-02 -0.20092383E-02 0.56341139E-02 -0.11077362E+00 -0.84934183E-01 -0.42388110E-01 0.10130063E+00 0.77670895E-01 0.38763220E-01 0.35686765E+00 0.27362348E+00 0.13655729E+00 0.15342303E-01 -0.97546139E-02 0.92741862E-02 0.22235402E-01 -0.72680448E-02 -0.81968249E-02 0.16860266E-01 0.32699856E-01 0.12649376E-01 -0.13392579E-01 0.29336083E-02 0.54119344E-02 0.87365402E-02 0.11372379E-01 0.97040720E-02 0.33505829E-02 0.44799939E-02 0.20648251E-02 -0.57899850E-02 -0.12046224E+00 -0.92362804E-01 -0.46095513E-01 0.11098925E+00 0.85099516E-01 0.42470623E-01 -0.35323052E+00 -0.27083476E+00 -0.13516553E+00 -0.15766757E-01 -0.10360421E-01 0.98799933E-02 -0.22165387E-01 0.68196684E-02 0.77484485E-02 -0.16002118E-01 -0.32544585E-01 0.13539576E-01 -0.14282778E-01 0.20609956E-03 0.34958387E-03 0.39798888E-03 0.12488296E-03 -0.11834381E-02 -0.20073351E-02 -0.22852800E-02 -0.71708670E-03 -0.35326860E-01 -0.59921041E-01 -0.68217988E-01 -0.21405784E-01 0.36304199E-01 0.61578793E-01 0.70105279E-01 0.21997988E-01 MO 13 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.3317291 -0.36495206E-14 -0.67326530E-14 -0.10868589E-13 -0.14147674E-13 -0.12072236E-13 -0.41682530E-14 -0.18851650E-14 -0.86887083E-15 0.24364045E-14 -0.63288358E-01 -0.48525499E-01 -0.24217625E-01 -0.24830844E+00 -0.19038716E+00 -0.95016539E-01 0.31159680E+00 0.23891266E+00 0.11923416E+00 0.65142744E-13 -0.59376216E-02 -0.23295936E-01 0.29233558E-01 -0.74388283E-03 -0.29185840E-02 0.36624668E-02 0.38365817E-01 -0.30573344E-01 -0.77924727E-02 -0.20221178E-14 -0.37304124E-14 -0.60220424E-14 -0.78389098E-14 -0.66889560E-14 -0.23095357E-14 -0.88316400E-14 -0.40704948E-14 0.11414092E-13 0.63288358E-01 0.48525499E-01 0.24217625E-01 0.24830844E+00 0.19038716E+00 0.95016539E-01 0.31159680E+00 0.23891266E+00 0.11923416E+00 0.34430177E-13 0.59376216E-02 0.23295936E-01 0.29233558E-01 -0.74388283E-03 -0.29185840E-02 0.36624668E-02 0.38365817E-01 0.30573344E-01 0.77924727E-02 0.57685046E-16 0.10641765E-15 0.17179108E-15 0.22362094E-15 0.19081615E-15 0.65884232E-16 -0.14807045E-13 -0.68245535E-14 0.19136760E-13 -0.63288358E-01 -0.48525499E-01 -0.24217625E-01 0.24830844E+00 0.19038716E+00 0.95016539E-01 -0.31159680E+00 -0.23891266E+00 -0.11923416E+00 0.55760697E-14 -0.59376216E-02 0.23295936E-01 -0.29233558E-01 -0.74388283E-03 -0.29185840E-02 0.36624668E-02 -0.38365817E-01 0.30573344E-01 -0.77924727E-02 -0.15149400E-14 -0.27947685E-14 -0.45116230E-14 -0.58727926E-14 -0.50112645E-14 -0.17302692E-14 -0.70233960E-14 -0.32370768E-14 0.90771012E-14 0.63288358E-01 0.48525499E-01 0.24217625E-01 -0.24830844E+00 -0.19038716E+00 -0.95016539E-01 -0.31159680E+00 -0.23891266E+00 -0.11923416E+00 0.36183546E-13 0.59376216E-02 -0.23295936E-01 -0.29233558E-01 -0.74388283E-03 -0.29185840E-02 0.36624668E-02 -0.38365817E-01 -0.30573344E-01 0.77924727E-02 -0.55690635E-15 -0.94461857E-15 -0.10754149E-14 -0.25677310E-13 -0.97427039E-15 -0.16525470E-14 -0.18813663E-14 -0.37625618E-13 -0.24248538E-14 -0.41130109E-14 -0.46825176E-14 -0.46528245E-13 -0.12681288E-14 -0.21509864E-14 -0.24488220E-14 -0.35763604E-13 MO 14 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.3317291 0.39348459E-14 0.72590224E-14 0.11718313E-13 0.15253762E-13 0.13016062E-13 0.44941335E-14 0.47891249E-13 0.22073033E-13 -0.61895088E-13 0.32326144E+00 0.24785637E+00 0.12369770E+00 -0.21644005E+00 -0.16595250E+00 -0.82821928E-01 -0.10682140E+00 -0.81903871E-01 -0.40875772E-01 -0.34844584E-13 0.30327918E-01 -0.20306090E-01 -0.10021828E-01 0.37995714E-02 -0.25440071E-02 -0.12555643E-02 -0.13152542E-01 -0.26649501E-01 0.39802043E-01 0.38265503E-14 0.70592381E-14 0.11395799E-13 0.14833944E-13 0.12657832E-13 0.43704451E-14 0.48856706E-13 0.22518011E-13 -0.63142854E-13 -0.32326144E+00 -0.24785637E+00 -0.12369770E+00 0.21644005E+00 0.16595250E+00 0.82821928E-01 -0.10682140E+00 -0.81903871E-01 -0.40875772E-01 -0.33404032E-13 -0.30327918E-01 0.20306090E-01 -0.10021828E-01 0.37995714E-02 -0.25440071E-02 -0.12555643E-02 -0.13152542E-01 0.26649501E-01 -0.39802043E-01 0.31654048E-14 0.58395537E-14 0.94268504E-14 0.12270958E-13 0.10470831E-13 0.36153263E-14 0.47551396E-13 0.21916395E-13 -0.61455859E-13 0.32326144E+00 0.24785637E+00 0.12369770E+00 0.21644005E+00 0.16595250E+00 0.82821928E-01 0.10682140E+00 0.81903871E-01 0.40875772E-01 -0.33164406E-13 0.30327918E-01 0.20306090E-01 0.10021828E-01 0.37995714E-02 -0.25440071E-02 -0.12555643E-02 0.13152542E-01 0.26649501E-01 0.39802043E-01 0.37820742E-14 0.69771885E-14 0.11263346E-13 0.14661529E-13 0.12510709E-13 0.43196473E-14 0.44902243E-13 0.20695403E-13 -0.58032070E-13 -0.32326144E+00 -0.24785637E+00 -0.12369770E+00 -0.21644005E+00 -0.16595250E+00 -0.82821928E-01 0.10682140E+00 0.81903871E-01 0.40875772E-01 -0.44495561E-13 -0.30327918E-01 -0.20306090E-01 0.10021828E-01 0.37995714E-02 -0.25440071E-02 -0.12555643E-02 0.13152542E-01 -0.26649501E-01 -0.39802043E-01 -0.19063144E-14 -0.32334701E-14 -0.36811914E-14 0.42081563E-13 -0.12518890E-14 -0.21234406E-14 -0.24174621E-14 0.41194256E-13 -0.79033825E-15 -0.13405633E-14 -0.15261839E-14 0.42572468E-13 -0.11195100E-14 -0.18989009E-14 -0.21618315E-14 0.38501656E-13 END DATA THE RHF ENERGY = -153.597882732801 THE VIRIAL(-V/T)= 2.00054039 avogadrolibs-1.101.0/avogadro/qtplugins/qtaim/test/hco2.wfn000066400000000000000000000571401506155467400236430ustar00rootroot00000000000000Formate anion 6-31++G** GAUSSIAN 12 MOL ORBITALS 104 PRIMITIVES 4 NUCLEI C 1 (CENTRE 1) 0.00000000 0.00000000 0.57942804 CHARGE = 6.0 O 2 (CENTRE 2) 0.00000000 2.11695431 -0.38686460 CHARGE = 8.0 O 3 (CENTRE 3) 0.00000000 -2.11695431 -0.38686460 CHARGE = 8.0 H 4 (CENTRE 4) 0.00000000 0.00000000 2.71326541 CHARGE = 1.0 CENTRE ASSIGNMENTS 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 CENTRE ASSIGNMENTS 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 CENTRE ASSIGNMENTS 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 CENTRE ASSIGNMENTS 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 CENTRE ASSIGNMENTS 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 CENTRE ASSIGNMENTS 4 4 4 4 TYPE ASSIGNMENTS 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3 4 4 4 1 2 TYPE ASSIGNMENTS 3 4 1 2 3 4 5 6 7 8 9 10 1 1 1 1 1 1 1 1 TYPE ASSIGNMENTS 1 2 2 2 3 3 3 4 4 4 1 2 3 4 1 2 3 4 5 6 TYPE ASSIGNMENTS 7 8 9 10 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3 4 TYPE ASSIGNMENTS 4 4 1 2 3 4 1 2 3 4 5 6 7 8 9 10 1 1 1 1 TYPE ASSIGNMENTS 1 2 3 4 EXPONENTS 0.3047525E+04 0.4573695E+03 0.1039487E+03 0.2921016E+02 0.9286663E+01 EXPONENTS 0.3163927E+01 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.7868272E+01 EXPONENTS 0.1881289E+01 0.5442493E+00 0.7868272E+01 0.1881289E+01 0.5442493E+00 EXPONENTS 0.7868272E+01 0.1881289E+01 0.5442493E+00 0.1687145E+00 0.1687145E+00 EXPONENTS 0.1687145E+00 0.1687145E+00 0.4380000E-01 0.4380000E-01 0.4380000E-01 EXPONENTS 0.4380000E-01 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 EXPONENTS 0.8000000E+00 0.8000000E+00 0.5484672E+04 0.8252349E+03 0.1880470E+03 EXPONENTS 0.5296450E+02 0.1689757E+02 0.5799635E+01 0.1553962E+02 0.3599934E+01 EXPONENTS 0.1013762E+01 0.1553962E+02 0.3599934E+01 0.1013762E+01 0.1553962E+02 EXPONENTS 0.3599934E+01 0.1013762E+01 0.1553962E+02 0.3599934E+01 0.1013762E+01 EXPONENTS 0.2700058E+00 0.2700058E+00 0.2700058E+00 0.2700058E+00 0.8450000E-01 EXPONENTS 0.8450000E-01 0.8450000E-01 0.8450000E-01 0.8000000E+00 0.8000000E+00 EXPONENTS 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.5484672E+04 EXPONENTS 0.8252349E+03 0.1880470E+03 0.5296450E+02 0.1689757E+02 0.5799635E+01 EXPONENTS 0.1553962E+02 0.3599934E+01 0.1013762E+01 0.1553962E+02 0.3599934E+01 EXPONENTS 0.1013762E+01 0.1553962E+02 0.3599934E+01 0.1013762E+01 0.1553962E+02 EXPONENTS 0.3599934E+01 0.1013762E+01 0.2700058E+00 0.2700058E+00 0.2700058E+00 EXPONENTS 0.2700058E+00 0.8450000E-01 0.8450000E-01 0.8450000E-01 0.8450000E-01 EXPONENTS 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 0.8000000E+00 EXPONENTS 0.8000000E+00 0.1873114E+02 0.2825394E+01 0.6401217E+00 0.1612778E+00 EXPONENTS 0.3600000E-01 0.1100000E+01 0.1100000E+01 0.1100000E+01 MO 1 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -20.2999736 -0.12188442E-13 -0.22485295E-13 -0.36298238E-13 -0.47249521E-13 -0.40318102E-13 -0.13920872E-13 -0.41960794E-12 -0.19339692E-12 0.54230514E-12 -0.39273770E-15 -0.30112636E-15 -0.15028316E-15 0.29917662E-03 0.22938966E-03 0.11448152E-03 -0.63478413E-13 -0.48671221E-13 -0.24290350E-13 0.22529127E-12 0.13071387E-15 0.10185809E-03 -0.62728986E-13 0.10023463E-12 -0.29802626E-16 0.15830967E-04 -0.98332270E-14 -0.24693743E-12 -0.14752123E-11 -0.25765489E-12 0.18297189E-14 -0.66615675E-15 0.54705851E-03 0.58494882E+00 0.10766181E+01 0.17421713E+01 0.22901329E+01 0.19642337E+01 0.67160487E+00 -0.91594649E-02 -0.40869354E-02 0.12068775E-01 0.13518265E-14 0.10415094E-14 0.45727874E-15 -0.28829800E-02 -0.22211807E-02 -0.97521795E-03 0.12404677E-02 0.95571350E-03 0.41960970E-03 0.87774840E-03 -0.32212280E-15 0.15512643E-03 -0.10153969E-03 -0.16545660E-03 0.61506469E-16 -0.37903054E-05 -0.37038128E-05 -0.33187344E-02 -0.31410279E-02 -0.33038069E-02 0.74824127E-15 -0.24267011E-15 0.10744751E-03 -0.58494882E+00 -0.10766181E+01 -0.17421713E+01 -0.22901329E+01 -0.19642337E+01 -0.67160487E+00 0.91594649E-02 0.40869354E-02 -0.12068775E-01 0.12737899E-14 0.98138644E-15 0.43088151E-15 -0.28829800E-02 -0.22211807E-02 -0.97521795E-03 -0.12404677E-02 -0.95571350E-03 -0.41960970E-03 -0.87774839E-03 -0.13253444E-15 0.15512643E-03 0.10153969E-03 0.16545660E-03 0.23148336E-16 -0.37903054E-05 0.37038128E-05 0.33187344E-02 0.31410279E-02 0.33038069E-02 -0.14848270E-16 -0.68315079E-15 0.10744751E-03 -0.34702469E-13 -0.58861956E-13 -0.67012256E-13 0.22566017E-13 -0.31113147E-14 0.92067613E-15 -0.20220193E-03 0.19169822E-12 MO 2 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -20.2999400 -0.76032131E-05 -0.14026444E-04 -0.22643029E-04 -0.29474496E-04 -0.25150641E-04 -0.86839118E-05 -0.25352583E-03 -0.11684982E-03 0.32765910E-03 -0.46458861E-15 -0.35621708E-15 -0.17777729E-15 -0.49717214E-12 -0.38120007E-12 -0.19024555E-12 -0.36505553E-04 -0.27990143E-04 -0.13969043E-04 0.13379545E-03 0.15410457E-15 -0.16985819E-12 -0.37260932E-04 0.59582199E-04 -0.33013166E-17 -0.26264395E-13 -0.59799736E-05 -0.14837160E-03 -0.88805571E-03 -0.15585303E-03 0.91654207E-15 -0.56688934E-15 -0.90845593E-12 0.58494329E+00 0.10766079E+01 0.17421548E+01 0.22901113E+01 0.19642151E+01 0.67159852E+00 -0.91359783E-02 -0.40764557E-02 0.12037828E-01 -0.69384055E-15 -0.53456674E-15 -0.23470359E-15 -0.28671447E-02 -0.22089804E-02 -0.96986139E-03 0.13496592E-02 0.10398397E-02 0.45654563E-03 0.97103205E-03 0.32365768E-16 0.12453228E-03 -0.14888987E-04 -0.14163251E-03 -0.51015398E-17 -0.58826214E-06 0.49234601E-06 -0.34141266E-02 -0.32557299E-02 -0.31636818E-02 0.85810319E-15 -0.21994607E-15 -0.26326172E-03 0.58494329E+00 0.10766079E+01 0.17421548E+01 0.22901113E+01 0.19642151E+01 0.67159852E+00 -0.91359783E-02 -0.40764557E-02 0.12037828E-01 -0.93713542E-16 -0.72201232E-16 -0.31700230E-16 0.28671447E-02 0.22089804E-02 0.96986139E-03 0.13496592E-02 0.10398397E-02 0.45654563E-03 0.97103205E-03 -0.11050101E-15 -0.12453228E-03 -0.14888987E-04 -0.14163251E-03 -0.29425795E-17 0.58826216E-06 0.49234600E-06 -0.34141266E-02 -0.32557299E-02 -0.31636818E-02 -0.53710110E-15 0.19194431E-15 0.26326172E-03 -0.20738602E-04 -0.35176594E-04 -0.40047309E-04 0.13445219E-04 -0.14915103E-05 -0.62765065E-15 0.33685368E-12 0.11682620E-03 MO 3 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -11.1259858 0.53422217E+00 0.98553558E+00 0.15909600E+01 0.20709572E+01 0.17671515E+01 0.61015496E+00 -0.10842516E-01 -0.49973057E-02 0.14012967E-01 -0.26399094E-15 -0.20241151E-15 -0.10101753E-15 -0.67153739E-15 -0.51489227E-15 -0.25696733E-15 -0.51904345E-03 -0.39796960E-03 -0.19861472E-03 -0.24364266E-02 -0.44618693E-16 -0.11138516E-14 -0.14900667E-04 -0.34405146E-03 0.49907391E-17 -0.72656510E-16 -0.36041018E-04 -0.22405914E-02 -0.17736819E-03 -0.13078581E-02 0.25464249E-16 0.80735526E-16 -0.22785222E-14 -0.21118904E-03 -0.38870057E-03 -0.62899089E-03 -0.82682613E-03 -0.70916397E-03 -0.24247521E-03 -0.32654787E-03 -0.14570502E-03 0.43026889E-03 0.44005858E-15 0.33904142E-15 0.14885744E-15 -0.59465254E-03 -0.45814773E-03 -0.20115153E-03 0.28425497E-03 0.21900313E-03 0.96154171E-04 -0.39057537E-03 0.90780662E-16 0.19698398E-03 -0.14874850E-03 0.51083855E-03 -0.15573357E-16 -0.11842933E-03 0.10543451E-03 0.41231631E-03 0.62289754E-04 0.92118237E-04 -0.21381963E-15 0.15794712E-15 0.36723638E-03 -0.21118904E-03 -0.38870057E-03 -0.62899089E-03 -0.82682613E-03 -0.70916397E-03 -0.24247521E-03 -0.32654787E-03 -0.14570502E-03 0.43026889E-03 -0.56970734E-15 -0.43892879E-15 -0.19271338E-15 0.59465254E-03 0.45814773E-03 0.20115153E-03 0.28425497E-03 0.21900313E-03 0.96154171E-04 -0.39057537E-03 0.88802174E-16 -0.19698398E-03 -0.14874850E-03 0.51083855E-03 -0.16841347E-16 0.11842933E-03 0.10543451E-03 0.41231631E-03 0.62289754E-04 0.92118237E-04 0.85807874E-15 0.14026940E-16 -0.36723638E-03 -0.11890413E-04 -0.20168390E-04 -0.22960999E-04 0.23697920E-03 0.11546874E-03 0.43195428E-15 0.21925556E-14 0.50274602E-03 MO 4 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -1.1740934 -0.73231042E-01 -0.13509697E+00 -0.21808840E+00 -0.28388630E+00 -0.24224069E+00 -0.83639890E-01 -0.10522870E+00 -0.48499814E-01 0.13599854E+00 -0.28727776E-16 -0.22026636E-16 -0.10992835E-16 0.12100552E-13 0.92779359E-14 0.46303402E-14 -0.11901634E+00 -0.91254183E-01 -0.45542233E-01 0.10549147E-01 0.31762539E-16 -0.27780100E-14 0.49219789E-02 0.20754759E-02 0.25998815E-17 -0.53719651E-15 0.17045827E-03 -0.24548666E-01 0.31766034E-01 -0.17348874E-01 0.21249983E-15 0.68948723E-16 0.22101938E-15 -0.11052937E+00 -0.20343305E+00 -0.32919307E+00 -0.43273351E+00 -0.37115301E+00 -0.12690352E+00 -0.18013184E+00 -0.80374477E-01 0.23734691E+00 0.14708424E-14 0.11332048E-14 0.49753794E-15 -0.29446967E+00 -0.22687301E+00 -0.99609470E-01 0.11368823E+00 0.87590655E-01 0.38457016E-01 0.72481314E-01 0.23669280E-15 -0.12000654E-01 0.36696488E-02 -0.63902347E-03 -0.48889586E-16 0.13084694E-03 -0.35384531E-03 -0.10010288E-02 0.15647882E-01 -0.15052629E-02 -0.87594485E-16 -0.29313373E-17 -0.12142530E-01 -0.11052937E+00 -0.20343305E+00 -0.32919307E+00 -0.43273351E+00 -0.37115301E+00 -0.12690352E+00 -0.18013184E+00 -0.80374477E-01 0.23734691E+00 -0.15949994E-14 -0.12288610E-14 -0.53953618E-15 0.29446967E+00 0.22687301E+00 0.99609470E-01 0.11368823E+00 0.87590655E-01 0.38457016E-01 0.72481314E-01 -0.23399000E-15 0.12000654E-01 0.36696488E-02 -0.63902347E-03 0.27944600E-16 -0.13084694E-03 -0.35384531E-03 -0.10010288E-02 0.15647882E-01 -0.15052629E-02 -0.32611019E-16 -0.13048552E-16 0.12142530E-01 0.80895605E-02 0.13721426E-01 0.15621358E-01 -0.20758061E-02 -0.74912400E-03 -0.40079127E-16 0.88502528E-16 -0.12462681E-01 MO 5 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -1.0744921 0.18692576E-14 0.34484153E-14 0.55668115E-14 0.72463345E-14 0.61833103E-14 0.21349485E-14 0.31631417E-14 0.14578891E-14 -0.40880733E-14 0.85863809E-15 0.65834923E-15 0.32856241E-15 0.34495054E+00 0.26448620E+00 0.13199715E+00 0.24759331E-14 0.18983885E-14 0.94742889E-15 0.21014382E-14 0.12892468E-15 -0.41275671E-02 -0.69889242E-15 0.22560192E-15 0.30391526E-16 0.26353774E-03 -0.17884285E-15 0.21133158E-14 0.17535656E-14 0.21051693E-14 -0.33941281E-15 -0.21678398E-15 -0.64772949E-01 -0.12539442E+00 -0.23079267E+00 -0.37346611E+00 -0.49093165E+00 -0.42106922E+00 -0.14397072E+00 -0.20379474E+00 -0.90932815E-01 0.26852582E+00 0.25676757E-14 0.19782557E-14 0.86856081E-15 -0.21694570E+00 -0.16714497E+00 -0.73385643E-01 0.14375607E+00 0.11075630E+00 0.48627981E-01 0.86738391E-01 0.63864588E-16 -0.70857096E-02 0.61101790E-02 0.22488619E-02 -0.43160215E-16 -0.66559843E-03 0.24865111E-03 -0.19514481E-02 0.67320129E-02 0.27753057E-02 -0.88513393E-16 0.19930271E-15 -0.14003368E-01 0.12539442E+00 0.23079267E+00 0.37346611E+00 0.49093165E+00 0.42106922E+00 0.14397072E+00 0.20379474E+00 0.90932815E-01 -0.26852582E+00 -0.28628612E-15 -0.22056802E-15 -0.96841242E-16 -0.21694570E+00 -0.16714497E+00 -0.73385643E-01 -0.14375607E+00 -0.11075630E+00 -0.48627981E-01 -0.86738391E-01 -0.13876243E-15 -0.70857096E-02 -0.61101790E-02 -0.22488619E-02 -0.41652027E-16 -0.66559843E-03 -0.24865111E-03 0.19514481E-02 -0.67320129E-02 -0.27753057E-02 -0.46396439E-15 -0.20076890E-15 -0.14003368E-01 -0.39863194E-15 -0.67615522E-15 -0.76977882E-15 0.31285519E-15 0.25992725E-15 -0.74759164E-16 0.33628781E-02 0.96439545E-15 MO 6 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.5530167 -0.70117172E-01 -0.12935249E+00 -0.20881503E+00 -0.27181512E+00 -0.23194034E+00 -0.80083424E-01 -0.11157011E+00 -0.51422559E-01 0.14419422E+00 0.17896166E-16 0.13721645E-16 0.68480624E-17 -0.21223634E-14 -0.16272936E-14 -0.81213356E-15 0.28800605E+00 0.22082477E+00 0.11020704E+00 0.52815085E-01 -0.21353772E-16 0.14236388E-14 0.10449305E-01 0.81723774E-03 0.55233791E-17 0.16612234E-15 0.41658645E-03 -0.14158017E-01 -0.21644039E-02 0.85164950E-02 0.91567838E-15 0.81779774E-15 0.13475524E-14 0.58244667E-01 0.10720128E+00 0.17347191E+00 0.22803367E+00 0.19558316E+00 0.66873203E-01 0.98204491E-01 0.43818653E-01 -0.12939707E+00 0.44951010E-14 0.34632330E-14 0.15205458E-14 -0.33527287E+00 -0.25830967E+00 -0.11341186E+00 0.48675301E+00 0.37501695E+00 0.16465264E+00 -0.63886549E-01 0.43431938E-15 -0.12722688E-01 0.21301169E-01 -0.47251497E-02 -0.68698575E-16 0.57636688E-04 -0.33885860E-03 -0.31899863E-02 0.19678919E-02 0.35950450E-02 -0.42946813E-15 -0.35150773E-16 -0.20700089E-01 0.58244667E-01 0.10720128E+00 0.17347191E+00 0.22803367E+00 0.19558316E+00 0.66873203E-01 0.98204491E-01 0.43818653E-01 -0.12939707E+00 -0.36200009E-14 -0.27890155E-14 -0.12245280E-14 0.33527287E+00 0.25830967E+00 0.11341186E+00 0.48675301E+00 0.37501695E+00 0.16465264E+00 -0.63886549E-01 -0.22043429E-15 0.12722688E-01 0.21301169E-01 -0.47251497E-02 0.65414131E-16 -0.57636688E-04 -0.33885860E-03 -0.31899863E-02 0.19678919E-02 0.35950450E-02 0.10055396E-14 0.20933174E-15 0.20700089E-01 0.48260411E-01 0.81858791E-01 0.93193340E-01 0.27286475E-01 -0.18822032E-03 -0.24601791E-15 0.94535590E-15 -0.24668746E-01 MO 7 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.4350704 0.29516069E-01 0.54451384E-01 0.87901417E-01 0.11442153E+00 0.97636096E-01 0.33711398E-01 0.54618306E-01 0.25173527E-01 -0.70589198E-01 0.55987897E-15 0.42927968E-15 0.21424065E-15 0.17236844E-13 0.13216118E-13 0.65957693E-14 0.41469345E+00 0.31796063E+00 0.15868464E+00 -0.13947528E-01 0.28029789E-16 -0.83951432E-15 0.11799832E-01 0.25914292E-03 -0.72652828E-17 -0.18414718E-15 0.11905279E-03 0.96763138E-02 -0.26911937E-02 0.62491930E-02 -0.13411045E-14 -0.42749602E-16 0.23406753E-15 -0.42022439E-01 -0.77343721E-01 -0.12515674E+00 -0.16452204E+00 -0.14110960E+00 -0.48247767E-01 -0.70337187E-01 -0.31384316E-01 0.92678305E-01 0.93458021E-14 0.72004367E-14 0.31613795E-14 0.10123545E+01 0.77996454E+00 0.34244644E+00 0.25096101E+00 0.19335193E+00 0.84891910E-01 0.51948210E-01 0.49498841E-15 0.49336295E-01 0.16426237E-01 0.46901968E-02 0.49969754E-16 0.13228224E-02 0.36500115E-03 0.48533355E-02 -0.23809057E-01 0.13331413E-01 -0.82678011E-15 0.75855186E-15 0.14884136E-02 -0.42022439E-01 -0.77343721E-01 -0.12515674E+00 -0.16452204E+00 -0.14110960E+00 -0.48247767E-01 -0.70337187E-01 -0.31384316E-01 0.92678305E-01 -0.60862544E-14 -0.46891309E-14 -0.20587810E-14 -0.10123545E+01 -0.77996454E+00 -0.34244644E+00 0.25096101E+00 0.19335193E+00 0.84891910E-01 0.51948210E-01 -0.30099943E-15 -0.49336295E-01 0.16426237E-01 0.46901968E-02 -0.28745755E-16 -0.13228224E-02 0.36500115E-03 0.48533355E-02 -0.23809057E-01 0.13331413E-01 -0.72580272E-15 -0.98662623E-15 -0.14884136E-02 0.25631277E-01 0.43475498E-01 0.49495318E-01 0.20088197E-01 0.32421027E-03 0.32117158E-15 -0.36079578E-15 -0.77174499E-02 MO 8 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.3925008 -0.10990965E-14 -0.20276184E-14 -0.32732048E-14 -0.42607401E-14 -0.36356972E-14 -0.12553189E-14 -0.12597383E-14 -0.58061223E-15 0.16280973E-14 0.30391715E-15 0.23302440E-15 0.11629551E-15 0.47521351E+00 0.36436358E+00 0.18184296E+00 -0.15674025E-13 -0.12017848E-13 -0.59977485E-14 0.23959725E-14 0.10003107E-15 0.13052360E-01 -0.10191030E-14 0.13275707E-14 -0.84108065E-16 -0.20431390E-03 0.11305413E-15 -0.11047004E-14 -0.41105133E-15 -0.64244187E-15 0.80951759E-15 -0.85955184E-15 -0.34592804E-01 0.45179757E-01 0.83154871E-01 0.13456028E+00 0.17688325E+00 0.15171174E+00 0.51872819E-01 0.76245321E-01 0.34020514E-01 -0.10046303E+00 0.10940547E-13 0.84291014E-14 0.37008295E-14 -0.91268535E+00 -0.70317486E+00 -0.30873164E+00 0.73641683E+00 0.56736947E+00 0.24910576E+00 -0.63894105E-01 0.43466765E-15 -0.40970939E-01 0.36101567E-01 -0.50146391E-02 0.18405106E-15 -0.16027372E-02 0.12749166E-02 -0.75700601E-02 0.10995193E-01 0.26772376E-02 0.71963657E-15 -0.13181106E-15 -0.31118299E-01 -0.45179757E-01 -0.83154871E-01 -0.13456028E+00 -0.17688325E+00 -0.15171174E+00 -0.51872819E-01 -0.76245321E-01 -0.34020514E-01 0.10046303E+00 -0.92429181E-14 -0.71211701E-14 -0.31265772E-14 -0.91268535E+00 -0.70317486E+00 -0.30873164E+00 -0.73641683E+00 -0.56736947E+00 -0.24910576E+00 0.63894105E-01 -0.78890469E-15 -0.40970939E-01 -0.36101567E-01 0.50146391E-02 0.84189932E-16 -0.16027372E-02 -0.12749166E-02 0.75700601E-02 -0.10995193E-01 -0.26772376E-02 -0.10409164E-14 -0.13401409E-14 -0.31118299E-01 -0.93365222E-15 -0.15836509E-14 -0.18029305E-14 -0.46280510E-15 -0.65902358E-15 0.55049012E-15 0.43684877E-02 0.42292198E-15 MO 9 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.3515679 0.42992532E-16 0.79312826E-16 0.12803549E-15 0.16666417E-15 0.14221484E-15 0.49103366E-16 -0.33638851E-15 -0.15504116E-15 0.43475159E-15 0.42325022E+00 0.32452142E+00 0.16195893E+00 -0.21186711E-14 -0.16244626E-14 -0.81072068E-15 0.31852096E-15 0.24422167E-15 0.12188373E-15 -0.44831012E-15 0.22943621E-01 -0.28520655E-14 -0.17311944E-15 -0.25890548E-15 0.10780039E-02 -0.45327461E-16 -0.37838258E-16 -0.16648437E-15 -0.35671005E-15 0.35656373E-15 0.28405848E-14 -0.32004969E-01 0.46027347E-15 -0.46083976E-15 -0.84819117E-15 -0.13725334E-14 -0.18042336E-14 -0.15474807E-14 -0.52910992E-15 0.13739851E-14 0.61306947E-15 -0.18104024E-14 0.10693282E+01 0.82385976E+00 0.36171881E+00 -0.14596552E-13 -0.11245856E-13 -0.49375367E-14 -0.19854389E-13 -0.15296737E-13 -0.67160913E-14 0.45588133E-14 0.63719638E-01 -0.25630748E-14 -0.57762674E-16 -0.34971873E-15 0.11768325E-02 0.32156253E-15 0.39635969E-16 -0.12216953E-14 0.12505853E-15 -0.22736117E-14 -0.47807263E-01 0.23803807E-01 -0.11705483E-14 0.56351519E-15 0.10371688E-14 0.16783348E-14 0.22062181E-14 0.18922605E-14 0.64699599E-15 -0.17334309E-14 -0.77345349E-15 0.22840185E-14 0.10693282E+01 0.82385976E+00 0.36171881E+00 -0.11070953E-13 -0.85295724E-14 -0.37449417E-14 0.14112768E-13 0.10873127E-13 0.47738883E-14 -0.51586321E-14 0.63719638E-01 -0.24936818E-14 -0.11371921E-15 0.55393074E-15 0.11768325E-02 0.40932747E-15 0.18333847E-16 0.14023041E-14 0.11391859E-14 0.25528990E-14 0.47807263E-01 0.23803807E-01 -0.17811519E-14 0.14250971E-15 0.24172344E-15 0.27519359E-15 0.75032357E-16 0.22625231E-15 0.86480103E-02 0.11570854E-14 0.10925372E-15 MO 10 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.2088715 0.16186460E-15 0.29860858E-15 0.48204684E-15 0.62748176E-15 0.53543132E-15 0.18487157E-15 0.94603044E-15 0.43602457E-15 -0.12226584E-14 0.33879951E-14 0.25976997E-14 0.12964342E-14 -0.72908341E-01 -0.55901492E-01 -0.27898762E-01 -0.11993699E-13 -0.91960074E-14 -0.45894522E-14 0.33915210E-15 0.36801763E-16 0.95685588E-02 -0.38307992E-15 0.10074515E-14 0.22947369E-16 0.12130812E-02 0.23928569E-15 -0.35157680E-15 0.36640424E-14 -0.30475341E-14 -0.17735846E-13 0.12387032E-14 0.93730918E-01 0.32475817E-02 0.59772839E-02 0.96723736E-02 0.12714606E-01 0.10905244E-01 0.37286880E-02 0.21740825E-02 0.97007138E-03 -0.28646338E-02 -0.20180964E-12 -0.15548345E-12 -0.68265610E-13 0.89726886E+00 0.69129729E+00 0.30351674E+00 0.11078994E+01 0.85357673E+00 0.37476616E+00 -0.11070026E-01 -0.13948942E-13 0.58903491E-01 0.71329483E-01 -0.36231867E-02 -0.10058512E-14 0.27838881E-02 0.40027887E-02 0.27068274E-02 -0.14876593E-01 0.18711862E-01 0.71278538E-14 -0.34660657E-14 -0.17097552E-01 -0.32475817E-02 -0.59772839E-02 -0.96723736E-02 -0.12714606E-01 -0.10905244E-01 -0.37286880E-02 -0.21740825E-02 -0.97007138E-03 0.28646338E-02 0.23460372E-12 0.18074952E-12 0.79358773E-13 0.89726886E+00 0.69129729E+00 0.30351674E+00 -0.11078994E+01 -0.85357673E+00 -0.37476616E+00 0.11070026E-01 0.16128442E-13 0.58903491E-01 -0.71329483E-01 0.36231867E-02 0.10491727E-14 0.27838881E-02 -0.40027887E-02 -0.27068274E-02 0.14876593E-01 -0.18711862E-01 0.58494747E-14 0.21608028E-14 -0.17097552E-01 -0.26074836E-14 -0.44227857E-14 -0.50351852E-14 -0.35055129E-14 -0.89434247E-15 -0.25195570E-14 0.16181864E-02 0.20874339E-15 MO 11 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.1883276 -0.91916341E-15 -0.16956770E-14 -0.27373485E-14 -0.35632144E-14 -0.30404973E-14 -0.10498106E-14 0.33614825E-14 0.15493042E-14 -0.43444107E-14 -0.33020665E-14 -0.25318152E-14 -0.12635532E-14 -0.14816542E-13 -0.11360385E-13 -0.56696282E-14 -0.57293829E-13 -0.43929274E-13 -0.21923787E-13 0.24193504E-13 -0.34477690E-15 0.24743909E-14 -0.87598959E-14 0.24511915E-13 0.96136067E-18 0.22701680E-15 0.15171890E-14 -0.45754063E-14 0.20657721E-13 -0.17738499E-13 0.10830363E+00 -0.16024470E-15 0.13125499E-13 -0.17260972E-14 -0.31769403E-14 -0.51408891E-14 -0.67578426E-14 -0.57961624E-14 -0.19818064E-14 0.56234982E-15 0.25091939E-15 -0.74096833E-15 0.13558285E+01 0.10445928E+01 0.45863249E+00 0.14837758E-12 0.11431692E-12 0.50191287E-13 0.47994820E-12 0.36977421E-12 0.16235080E-12 0.25551546E-14 0.92788274E-01 0.16808313E-13 0.30752203E-13 -0.19442261E-13 0.64962109E-02 0.30282181E-14 -0.46354044E-16 -0.30222011E-14 -0.10210477E-13 0.22273685E-15 -0.40099335E-01 0.22392489E-01 -0.48999307E-14 -0.21761755E-14 -0.40053247E-14 -0.64813714E-14 -0.85199441E-14 -0.73075065E-14 -0.24985606E-14 -0.13635060E-16 -0.60839372E-17 0.17965948E-16 -0.13558285E+01 -0.10445928E+01 -0.45863249E+00 0.13658309E-12 0.10522991E-12 0.46201597E-13 0.14559424E-12 0.11217251E-12 0.49249775E-13 0.56860577E-14 -0.92788274E-01 0.23147249E-14 0.10382293E-13 -0.16654836E-13 -0.64962109E-02 -0.18573361E-14 -0.11933823E-14 -0.37001322E-14 -0.59396267E-14 -0.34550931E-14 -0.40099335E-01 -0.22392489E-01 0.81285579E-15 -0.12454088E-13 -0.21124490E-13 -0.24049485E-13 -0.14903127E-13 -0.11784076E-13 -0.69210390E-15 -0.35169975E-17 0.29960746E-14 MO 12 MO 0. OCC NO = 2.0000000 ORB. ENERGY = -0.1853055 0.16440141E-02 0.30328851E-02 0.48960169E-02 0.63731592E-02 0.54382283E-02 0.18776895E-02 -0.32414818E-02 -0.14939960E-02 0.41893207E-02 0.10617617E-14 0.81409155E-15 0.40628874E-15 0.43777004E-15 0.33565430E-15 0.16751502E-15 -0.20485919E+00 -0.15707304E+00 -0.78390454E-01 -0.19383023E-01 0.15402398E-15 -0.22614904E-14 -0.36658076E-02 -0.18987697E-02 -0.46312070E-18 -0.13457127E-15 0.61367607E-03 -0.59317254E-02 0.63841864E-01 -0.64032243E-01 -0.27945291E-13 0.71540579E-15 0.50370806E-15 -0.52521498E-02 -0.96667594E-02 -0.15642641E-01 -0.20562690E-01 -0.17636500E-01 -0.60302188E-02 -0.56501612E-02 -0.25210909E-02 0.74448153E-02 -0.34627860E-12 -0.26678900E-12 -0.11713474E-12 0.23389523E-01 0.18020366E-01 0.79119114E-02 0.12252558E+01 0.94399358E+00 0.41446403E+00 0.12234358E-01 -0.24022089E-13 0.10768208E-02 0.82285813E-01 0.39539767E-02 -0.16303915E-14 0.38969476E-03 0.48002253E-02 -0.48315857E-02 -0.56669261E-02 0.34009425E-02 0.11162655E-13 -0.70095075E-14 -0.22149149E-01 -0.52521498E-02 -0.96667594E-02 -0.15642641E-01 -0.20562690E-01 -0.17636500E-01 -0.60302188E-02 -0.56501612E-02 -0.25210909E-02 0.74448153E-02 0.35070484E-12 0.27019917E-12 0.11863199E-12 -0.23389523E-01 -0.18020366E-01 -0.79119114E-02 0.12252558E+01 0.94399358E+00 0.41446403E+00 0.12234358E-01 0.23920700E-13 -0.10768208E-02 0.82285813E-01 0.39539767E-02 0.16340155E-14 -0.38969476E-03 0.48002253E-02 -0.48315857E-02 -0.56669261E-02 0.34009425E-02 0.93558014E-14 0.54268186E-14 0.22149149E-01 -0.46606863E-01 -0.79054060E-01 -0.90000254E-01 -0.61419635E-01 -0.48996275E-02 -0.19529193E-14 0.44206302E-16 0.94257513E-02 END DATA THE RHF ENERGY = -188.209663369086 THE VIRIAL(-V/T)= 2.00251408 avogadrolibs-1.101.0/avogadro/qtplugins/quantuminput/000077500000000000000000000000001506155467400227455ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/quantuminput/CMakeLists.txt000066400000000000000000000022201506155467400255010ustar00rootroot00000000000000# Extension set(quantuminput_srcs quantuminput.cpp ) avogadro_plugin(QuantumInput "Quantum input file generation" ExtensionPlugin quantuminput.h QuantumInput "${quantuminput_srcs}" ) target_link_libraries(QuantumInput PRIVATE Avogadro::IO Avogadro::MoleQueue) set(_prefix "${AvogadroLibs_SOURCE_DIR}/../avogadrogenerators") # Look in parallel directory for the avogadrogenerators repository. if(EXISTS "${_prefix}/generators.cmake") # Bundled generator scripts, include("${_prefix}/generators.cmake") # message("We have ${input_generators}") unset(_generators) foreach(gen ${input_generators}) list(APPEND _generators "${_prefix}/${gen}") endforeach() # debugging info with paths # message("We have ${_generators}") option(INSTALL_TEST_INPUT_GENERATOR "Install a dummy input generator that is used to test generator scripts." OFF) if(INSTALL_TEST_INPUT_GENERATOR) list(APPEND _generators "${_prefix}/${test_generator}") endif() install(PROGRAMS ${_generators} DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/scripts/inputGenerators/") else() message("Avogadro Generators not found at ${_prefix}!") endif() avogadrolibs-1.101.0/avogadro/qtplugins/quantuminput/quantuminput.cpp000066400000000000000000000124151506155467400262260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "quantuminput.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using MoleQueue::InputGenerator; using MoleQueue::InputGeneratorDialog; using MoleQueue::JobObject; QuantumInput::QuantumInput(QObject* parent_) : ExtensionPlugin(parent_), m_molecule(nullptr), m_outputFormat(nullptr) { refreshGenerators(); } QuantumInput::~QuantumInput() { qDeleteAll(m_dialogs.values()); m_dialogs.clear(); } QList QuantumInput::actions() const { return m_actions; } QStringList QuantumInput::menuPath(QAction* action) const { QStringList path; if (action == nullptr) return path; path << tr("&Input"); return path; } void QuantumInput::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; m_molecule = mol; foreach (InputGeneratorDialog* dlg, m_dialogs.values()) dlg->setMolecule(mol); } void QuantumInput::openJobOutput(const JobObject& job) { m_outputFormat = nullptr; m_outputFileName.clear(); QString outputPath(job.value("outputDirectory").toString()); using QtGui::FileFormatDialog; FileFormatDialog::FormatFilePair result = FileFormatDialog::fileToRead( qobject_cast(parent()), tr("Open Output File"), outputPath); if (result.first == nullptr) // User canceled return; m_outputFormat = result.first; m_outputFileName = result.second; emit moleculeReady(1); } bool QuantumInput::readMolecule(QtGui::Molecule& mol) { Io::FileFormat* reader = m_outputFormat->newInstance(); bool success = reader->readFile(m_outputFileName.toStdString(), mol); if (!success) { QMessageBox::information(qobject_cast(parent()), tr("Error"), tr("Error reading output file '%1':\n%2") .arg(m_outputFileName) .arg(QString::fromStdString(reader->error()))); } m_outputFormat = nullptr; m_outputFileName.clear(); return success; } void QuantumInput::refreshGenerators() { updateInputGeneratorScripts(); updateActions(); } void QuantumInput::menuActivated() { auto* theSender = qobject_cast(sender()); if (!theSender) return; QString scriptFileName = theSender->data().toString(); QWidget* theParent = qobject_cast(parent()); InputGeneratorDialog* dlg = m_dialogs.value(scriptFileName, nullptr); if (!dlg) { dlg = new InputGeneratorDialog(scriptFileName, theParent); connect(&dlg->widget(), SIGNAL(openJobOutput(const MoleQueue::JobObject&)), this, SLOT(openJobOutput(const MoleQueue::JobObject&))); m_dialogs.insert(scriptFileName, dlg); } dlg->setMolecule(m_molecule); dlg->show(); dlg->raise(); } void QuantumInput::updateInputGeneratorScripts() { m_inputGeneratorScripts = QtGui::ScriptLoader::scriptList("inputGenerators"); } void QuantumInput::updateActions() { m_actions.clear(); foreach (const QString& programName, m_inputGeneratorScripts.uniqueKeys()) { QStringList scripts = m_inputGeneratorScripts.values(programName); // Include the full path if there are multiple generators with the same // name. QString label = programName; if (!label.endsWith("…") && !label.endsWith("...")) label.append("…"); if (scripts.size() == 1) { addAction(label, scripts.first()); } else { foreach (const QString& filePath, scripts) { addAction(QString("%1 (%2)").arg(label, filePath), filePath); } } } } void QuantumInput::addAction(const QString& label, const QString& scriptFilePath) { auto* action = new QAction(label, this); action->setData(scriptFilePath); action->setEnabled(true); connect(action, SIGNAL(triggered()), SLOT(menuActivated())); m_actions << action; } bool QuantumInput::queryProgramName(const QString& scriptFilePath, QString& displayName) { InputGenerator gen(scriptFilePath); displayName = gen.displayName(); if (gen.hasErrors()) { displayName.clear(); qWarning() << "QuantumInput::queryProgramName: Unable to retrieve program " "name for" << scriptFilePath << ";" << gen.errorList().join("\n\n"); return false; } return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/quantuminput/quantuminput.h000066400000000000000000000043521506155467400256740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_QUANTUMINPUT_H #define AVOGADRO_QTPLUGINS_QUANTUMINPUT_H #include #include #include class QAction; class QDialog; namespace Avogadro { namespace Io { class FileFormat; } namespace MoleQueue { class InputGeneratorDialog; class JobObject; } // namespace MoleQueue namespace QtPlugins { /** * @brief The QuantumInput class implements the extension interface for * simulation input generators. * @author Allison Vacanti */ class QuantumInput : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit QuantumInput(QObject* parent = nullptr); ~QuantumInput() override; QString name() const override { return tr("Quantum input"); } QString description() const override { return tr("Generate input for quantum codes."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; public slots: /** * Scan for new scripts in the input generator directories. */ void refreshGenerators(); /** * Emitted when the user requests that a job's output be loaded in Avogadro. */ void openJobOutput(const MoleQueue::JobObject& job); bool readMolecule(QtGui::Molecule& mol) override; private slots: void menuActivated(); private: void updateInputGeneratorScripts(); void updateActions(); void addAction(const QString& label, const QString& scriptFilePath); bool queryProgramName(const QString& scriptFilePath, QString& displayName); QList m_actions; QtGui::Molecule* m_molecule; // keyed on script file path QMap m_dialogs; // maps program name --> script file path QMultiMap m_inputGeneratorScripts; const Io::FileFormat* m_outputFormat; QString m_outputFileName; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_QUANTUMINPUT_H avogadrolibs-1.101.0/avogadro/qtplugins/resetview/000077500000000000000000000000001506155467400222105ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/resetview/CMakeLists.txt000066400000000000000000000005721506155467400247540ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 REQUIRED COMPONENTS OpenGLWidgets) endif() avogadro_plugin(ResetView "Manipulate the view camera." ExtensionPlugin resetview.h ResetView "resetview.cpp" "" ) target_link_libraries(ResetView PRIVATE Avogadro::QtOpenGL) if(QT_VERSION EQUAL 6) target_link_libraries(ResetView PRIVATE Qt6::OpenGLWidgets) endif() avogadrolibs-1.101.0/avogadro/qtplugins/resetview/resetview.cpp000066400000000000000000000175741506155467400247470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "resetview.h" #include #include #include #include #include #include #include #include #define CAMERA_NEAR_DISTANCE 13.35f // Experimental number namespace Avogadro::QtPlugins { using Avogadro::QtGui::ExtensionPlugin; using Core::Array; using Eigen::Affine3f; using Eigen::EigenSolver; using Eigen::Matrix3d; using Eigen::Matrix3f; using Eigen::Quaternionf; using Eigen::Vector3d; using Rendering::Projection; using std::numeric_limits; const float ResetView::DELTA_TIME = 100.0f / 3.0f; // 33.3 ms ~ 30 fps const int ResetView::TOTAL_FRAMES = 25; // ~1 sec ResetView::ResetView(QObject* parent_) : ExtensionPlugin(parent_), m_centerAction(new QAction(tr("Center"), this)), m_viewToAxesAction(new QAction(tr("Align View to Axes"), this)) { m_centerAction->setProperty("menu priority", 210); m_viewToAxesAction->setProperty("menu priority", 200); connect(m_centerAction, SIGNAL(triggered()), SLOT(centerView())); connect(m_viewToAxesAction, SIGNAL(triggered()), SLOT(alignToAxes())); } ResetView::~ResetView() {} void ResetView::registerCommands() { emit registerCommand("alignView", tr("Align view to axes.")); } bool ResetView::handleCommand(const QString& command, [[maybe_unused]] const QVariantMap& options) { if (m_molecule == nullptr || m_camera == nullptr) return false; // Nothing to do if (command == "alignView") { animationCameraDefault(false); return true; } return false; } QList ResetView::actions() const { QList result; return result << m_centerAction << m_viewToAxesAction; } QStringList ResetView::menuPath(QAction*) const { return QStringList() << tr("&View"); } void ResetView::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void ResetView::setCamera(Rendering::Camera* camera) { m_camera = camera; } void ResetView::setActiveWidget(QWidget* widget) { if (widget != nullptr) { m_glWidget = widget; connect(this, SIGNAL(updateRequested()), m_glWidget, SLOT(requestUpdate())); } } bool ResetView::defaultChecks() { if (m_molecule == nullptr || m_camera == nullptr) return true; // Check for 3D coordinates - it's useless to consider the camera otherwise if (m_molecule->atomPositions3d().size() != m_molecule->atomCount()) return true; // no need to animate when there are no atoms if (m_molecule->atomCount() == 0) { animationCameraDefault(false); return true; } return false; } inline float getZDistance(const Affine3f& projection, float x, Projection perspective) { if (perspective == Projection::Perspective) { float fov = 2.0f * std::atan(1.0f / projection(1, 1)); // float aspect = projection(1,1) / projection(0,0); // tan (fov/2) = (x/2) / z -> z = (x/2) / tan (fov/2) return std::max(CAMERA_NEAR_DISTANCE, (x / 2.0f) / std::tan(fov / 2.0f)); } else { return 3.0f * CAMERA_NEAR_DISTANCE; } } inline void getBB(const Array& mols, Vector3& min, Vector3& max) { if (mols.size() > 0) { min = mols[0]; max = mols[0]; for (size_t i = 1; i < mols.size(); ++i) { min.x() = std::min(mols[i].x(), min.x()); max.x() = std::max(mols[i].x(), max.x()); min.y() = std::min(mols[i].y(), min.y()); max.y() = std::max(mols[i].y(), max.y()); min.z() = std::min(mols[i].z(), min.z()); max.z() = std::max(mols[i].z(), max.z()); } } else { min = max = Vector3(0, 0, 0); } } // namespace QtPlugins void ResetView::animationCameraDefault(bool animate) { Matrix3f linearGoal; linearGoal.row(0) = Vector3f::UnitX(); linearGoal.row(1) = Vector3f::UnitY(); linearGoal.row(2) = Vector3f::UnitZ(); // calculate the translation matrix auto goal = Affine3f(linearGoal); const Array& mols = m_molecule->atomPositions3d(); Vector3 min, max; getBB(mols, min, max); Vector3f mid = (max.cast() + min.cast()) / 2.0f; float d = getZDistance(m_camera->projection(), max.x() - min.x(), m_camera->projectionType()); Vector3f eye = -mid + (Vector3f::UnitZ() * -d); goal.translate(eye); animationCamera(goal, animate); } void ResetView::animationCamera(const Affine3f& goal, bool animate) { if (animate) { Matrix3f rot_aux = goal.rotation(); Vector3f posGoal = goal.translation(); auto rotGoal = Quaternionf(rot_aux); Affine3f start = m_camera->modelView(); rot_aux = start.rotation(); Vector3f posStart = start.translation(); auto rotStart = Quaternionf(rot_aux); for (int frame = 0; frame <= ResetView::TOTAL_FRAMES; ++frame) { Affine3f interpolation; float alpha = frame / float(ResetView::TOTAL_FRAMES); interpolation.fromPositionOrientationScale( ((1.0f - alpha) * posStart) + (alpha * posGoal), rotStart.slerp(alpha, rotGoal), Vector3f(1.0f, 1.0f, 1.0f)); float time = frame * ResetView::DELTA_TIME; QTimer::singleShot(time, this, [this, interpolation]() { m_camera->setModelView(interpolation); emit updateRequested(); }); } } else { m_camera->setModelView(goal); emit updateRequested(); } } // Calculate the oriented bounding box to get the most significand // axis base and the centroid inline void getOBB(const Array& mols, Vector3d& centroid, Vector3d& min, Vector3d& mid, Vector3d& max) { centroid = Vector3::Zero(); for (const auto& mol : mols) centroid += mol; centroid /= (double)mols.size(); Matrix3d covariance = Matrix3::Zero(); for (const auto& mol : mols) { Vector3d adjusted = mol - centroid; covariance += adjusted * adjusted.transpose(); } covariance /= (double)mols.size(); auto solver = EigenSolver(covariance); Eigen::Matrix3d vectors = solver.eigenvectors().real(); max = vectors.row(0); mid = vectors.row(1); min = vectors.row(2); } void ResetView::centerView() { if (defaultChecks()) return; const Array& mols = m_molecule->atomPositions3d(); Vector3d centroid, min, mid, max; getOBB(mols, centroid, min, mid, max); Matrix3f linearGoal; linearGoal.col(0) = (max.normalized()).cast(); // x linearGoal.col(1) = (mid.normalized()).cast(); // y linearGoal.col(2) = (min.normalized()).cast(); // z // calculate the translation matrix auto goal = Affine3f(linearGoal); // eigen return the eigenvectors normalized, but we need a non-normalized // so we project all the points to the axis and get the min/max float infinity = numeric_limits::infinity(); Vector3f minScreen{ infinity, infinity, infinity }; Vector3f maxScreen{ -infinity, -infinity, -infinity }; for (const auto& atom : mols) { Vector3f aux = linearGoal * Vector3f(atom.x(), atom.y(), atom.z()); minScreen.x() = std::min(aux.x(), minScreen.x()); maxScreen.x() = std::max(aux.x(), maxScreen.x()); minScreen.y() = std::min(aux.y(), minScreen.y()); maxScreen.y() = std::max(aux.y(), maxScreen.y()); minScreen.z() = std::min(aux.z(), minScreen.z()); maxScreen.z() = std::max(aux.z(), maxScreen.z()); } Vector3f midScreen = (maxScreen + minScreen) / 2.0f; float d = getZDistance(m_camera->projection(), maxScreen.x() - minScreen.x(), m_camera->projectionType()); midScreen.z() = 0; Vector3f eye = -midScreen + (Vector3f::UnitZ() * -d); goal.pretranslate(eye); animationCamera(goal); } void ResetView::alignToAxes() { if (defaultChecks()) return; animationCameraDefault(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/resetview/resetview.h000066400000000000000000000035031506155467400243770ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_RESETVIEW_H #define AVOGADRO_QTPLUGINS_RESETVIEW_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief The ResetView class is an extension to center the camera in the best * fit panel or the default camera position */ class ResetView : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit ResetView(QObject* parent_ = nullptr); ~ResetView() override; QString name() const override { return tr("Reset view"); } QString description() const override { return tr("Manipulate the view camera."); } QList actions() const override; QStringList menuPath(QAction*) const override; bool handleCommand(const QString& command, const QVariantMap& options) override; void registerCommands() override; public slots: void setMolecule(QtGui::Molecule* mol) override; void setCamera(Rendering::Camera* camera) override; void setActiveWidget(QWidget* widget) override; signals: void updateRequested(); private slots: void centerView(); void alignToAxes(); private: QtGui::Molecule* m_molecule; Rendering::Camera* m_camera; QAction* m_centerAction; QAction* m_viewToAxesAction; QWidget* m_glWidget; static const float DELTA_TIME; static const int TOTAL_FRAMES; bool defaultChecks(); void animationCamera(const Eigen::Affine3f& goal, bool animate = true); void animationCameraDefault(bool animate = true); }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/scriptcharges/000077500000000000000000000000001506155467400230345ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/scriptcharges/CMakeLists.txt000066400000000000000000000005041506155467400255730ustar00rootroot00000000000000set(scriptcharges_srcs scriptchargemodel.cpp scriptcharges.cpp ) avogadro_plugin(ScriptCharges "Scriptable electrostatics models" ExtensionPlugin scriptcharges.h ScriptCharges "${scriptcharges_srcs}" "" ) target_link_libraries(ScriptCharges PRIVATE Avogadro::Calc ) # charge scripts are now download only avogadrolibs-1.101.0/avogadro/qtplugins/scriptcharges/scriptchargemodel.cpp000066400000000000000000000265701506155467400272510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "scriptchargemodel.h" #include #include // formats supported in scripts #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { ScriptChargeModel::ScriptChargeModel(const QString& scriptFileName_) : m_interpreter(new QtGui::PythonScript(scriptFileName_)), m_inputFormat(NotUsed), m_valid(false), m_partialCharges(false), m_electrostatics(false) { m_elements.reset(); readMetaData(); } ScriptChargeModel::~ScriptChargeModel() { delete m_interpreter; } QString ScriptChargeModel::scriptFilePath() const { return m_interpreter->scriptFilePath(); } Calc::ChargeModel* ScriptChargeModel::newInstance() const { return new ScriptChargeModel(m_interpreter->scriptFilePath()); } MatrixX ScriptChargeModel::partialCharges(const Core::Molecule& mol) const { MatrixX charges(mol.atomCount(), 1); // check to see if we already have them in the molecule charges = mol.partialCharges(m_identifier); // if there's a non-zero charge, then we're done for (unsigned int i = 0; i < charges.rows(); ++i) { if (abs(charges(i, 0)) > 0.00001) return charges; } // Create the intermediate format writer std::string intermediate; QScopedPointer format(createFileFormat(m_inputFormat)); if (format.isNull()) { appendError("Cannot create file format."); return charges; } if (!format->writeString(intermediate, mol)) { appendError(format->error(), false); return charges; } // Call the script to convert the file QByteArray result = m_interpreter->execute(QStringList() << "--charges", intermediate.c_str()); if (m_interpreter->hasErrors()) { foreach (const QString& err, m_interpreter->errorList()) { appendError(err.toStdString()); } return charges; } // parse the result - each charge should be on a line QString resultString = QString(result); QStringList lines = resultString.split('\n'); // keep a separate atom counter in case there is other text // (e.g., "normal termination, etc.") unsigned int atom = 0; for (const auto& line : lines) { if (line.isEmpty()) continue; bool ok; double charge = line.toDouble(&ok); if (!ok) { appendError("Invalid charge: " + line.toStdString()); continue; } charges(atom, 0) = charge; ++atom; } return charges; } MatrixX ScriptChargeModel::partialCharges(Core::Molecule& mol) const { // just create a copy of the const version MatrixX charges = partialCharges(static_cast(mol)); // cache them mol.setPartialCharges(m_identifier, charges); return charges; } double ScriptChargeModel::potential(Core::Molecule& mol, const Vector3& point) const { // just create an array of size one and run that Core::Array points; points.push_back(point); Core::Array results = potentials(mol, points); if (results.size() == 1) return results[0]; else return 0.0; } Core::Array ScriptChargeModel::potentials( Core::Molecule& mol, const Core::Array& points) const { // first off, if the script doesn't handle potentials // call the parent class (default method from partial charges) if (!m_electrostatics) return Calc::ChargeModel::potentials(mol, points); // Create the intermediate format writer std::string intermediate; QScopedPointer format(createFileFormat(m_inputFormat)); Core::Array potentials(points.size(), 0.0); if (format.isNull()) { appendError("Cannot create file format."); return potentials; } if (!format->writeString(intermediate, mol)) { appendError(format->error(), false); return potentials; } // now we stuff the file and the points into JSON QJsonObject json; json[m_formatString] = QString::fromStdString(intermediate); QJsonArray pointsArray; for (const auto& i : points) { QJsonArray point; point << i.x() << i.y() << i.z(); pointsArray.append(point); } json["points"] = pointsArray; QJsonDocument doc(json); // Call the script to convert the file QByteArray result = m_interpreter->execute(QStringList() << "--potentials", doc.toJson()); if (m_interpreter->hasErrors()) { foreach (const QString& err, m_interpreter->errorList()) appendError(err.toStdString()); return potentials; } // parse the result - each potential should be on a line QString resultString = QString(result); QStringList lines = resultString.split('\n'); for (const QString& line : lines) { if (line.isEmpty()) continue; bool ok; double potential = line.toDouble(&ok); if (!ok) { appendError("Invalid potential: " + line.toStdString()); continue; } potentials.push_back(potential); } return potentials; } ScriptChargeModel::Format ScriptChargeModel::stringToFormat( const std::string& str) { if (str == "cjson") return Cjson; else if (str == "cml") return Cml; else if (str == "mdl" || str == "mol") return Mdl; else if (str == "sdf") return Sdf; else if (str == "pdb") return Pdb; else if (str == "xyz") return Xyz; return NotUsed; } Io::FileFormat* ScriptChargeModel::createFileFormat( ScriptChargeModel::Format fmt) { switch (fmt) { case Cjson: return new Io::CjsonFormat; case Cml: return new Io::CmlFormat; case Mdl: return new Io::MdlFormat; case Pdb: return new Io::PdbFormat; case Sdf: return new Io::SdfFormat; case Xyz: return new Io::XyzFormat; default: case NotUsed: return nullptr; } } void ScriptChargeModel::resetMetaData() { m_valid = false; m_partialCharges = false; m_electrostatics = false; m_inputFormat = NotUsed; m_identifier.clear(); m_name.clear(); m_description.clear(); m_formatString.clear(); } void ScriptChargeModel::readMetaData() { resetMetaData(); QByteArray output(m_interpreter->execute(QStringList() << "--metadata")); if (m_interpreter->hasErrors()) { qWarning() << "Error retrieving metadata for charge script:" << scriptFilePath() << "\n" << m_interpreter->errorList(); return; } QJsonParseError parseError; QJsonDocument doc(QJsonDocument::fromJson(output, &parseError)); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error parsing metadata for charge script:" << scriptFilePath() << "\n" << parseError.errorString() << "(at offset" << parseError.offset << ")"; return; } if (!doc.isObject()) { qWarning() << "Error parsing metadata for charge script:" << scriptFilePath() << "\nResult is not a JSON object:\n" << output; return; } const QJsonObject metaData(doc.object()); // Read required inputs first. std::string identifierTmp; if (!parseString(metaData, "identifier", identifierTmp)) { qWarning() << "Error parsing metadata for charge script:" << scriptFilePath() << "\n" << "Error parsing required member 'identifier'" << "\n" << output; return; } m_identifier = identifierTmp; std::string nameTmp; if (!parseString(metaData, "name", nameTmp)) { qWarning() << "Error parsing metadata for charge script:" << scriptFilePath() << "\n" << "Error parsing required member 'name'" << "\n" << output; return; } m_name = nameTmp; std::string descriptionTmp; parseString(metaData, "description", descriptionTmp); m_description = descriptionTmp; // optional Format inputFormatTmp = NotUsed; std::string inputFormatStrTmp; if (!parseString(metaData, "inputFormat", inputFormatStrTmp)) { qWarning() << "Error parsing metadata for charge script:" << scriptFilePath() << "\n" << "Member 'inputFormat' required for writable formats." << "\n" << output; return; } m_formatString = inputFormatStrTmp.c_str(); // for the json key // Validate the input format inputFormatTmp = stringToFormat(inputFormatStrTmp); if (inputFormatTmp == NotUsed) { qWarning() << "Error parsing metadata for charge script:" << scriptFilePath() << "\n" << "Member 'inputFormat' not recognized:" << inputFormatStrTmp.c_str() << "\nValid values are cjson, cml, mdl/sdf, pdb, or xyz.\n" << output; return; } m_inputFormat = inputFormatTmp; // check if we handle charges and/or potentials if (!metaData["charges"].isBool()) { return; // not valid } m_partialCharges = metaData["charges"].toBool(); if (!metaData["potential"].isBool()) { return; // not valid } m_electrostatics = metaData["potential"].toBool(); // get the element mask // (if it doesn't exist, the default is no elements anyway) m_valid = parseElements(metaData); } bool ScriptChargeModel::parseString(const QJsonObject& ob, const QString& key, std::string& str) { if (!ob[key].isString()) return false; str = ob[key].toString().toStdString(); return !str.empty(); } void ScriptChargeModel::processElementString(const QString& str) { // parse the QString // first turn any commas into whitespace QString str2(str); str2.replace(',', ' '); // then split on whitespace QStringList strList = str2.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); foreach (QString sstr, strList) { // these should be numbers or ranges (e.g., 1-84) if (sstr.contains('-')) { // range, so split on the dash QStringList strList2 = sstr.split('-'); if (strList2.size() != 2) return; // get the two numbers bool ok; int start = strList2[0].toInt(&ok); if (!ok || start < 1 || start > 119) return; int end = strList2[1].toInt(&ok); if (!ok || end < 1 || end > 119) return; for (int i = start; i <= end; ++i) m_elements.set(i); } bool ok; int i = sstr.toInt(&ok); if (!ok || i < 1 || i > 119) return; m_elements.set(i); } } bool ScriptChargeModel::parseElements(const QJsonObject& ob) { m_elements.reset(); // we could either get a string or an array (of numbers) if (ob["elements"].isString()) { auto str = ob["elements"].toString(); processElementString(str); } else if (ob["elements"].isArray()) { QJsonArray arr = ob["elements"].toArray(); for (auto&& i : arr) { if (i.isString()) { processElementString(i.toString()); } else if (i.isDouble()) { int element = i.toInt(); if (element >= 1 && element <= 119) // check the range m_elements.set(element); } } } return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/scriptcharges/scriptchargemodel.h000066400000000000000000000047271506155467400267160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SCRIPTCHARGEMODEL_H #define AVOGADRO_QTPLUGINS_SCRIPTCHARGEMODEL_H #include #include #include class QJsonObject; namespace Avogadro { namespace Io { class FileFormat; } namespace QtGui { class PythonScript; } namespace QtPlugins { class ScriptChargeModel : public Avogadro::Calc::ChargeModel { public: /** Formats that may be written to the script's input. */ enum Format { NotUsed, Cjson, Cml, Mdl, Pdb, Sdf, Xyz }; ScriptChargeModel(const QString& scriptFileName = ""); ~ScriptChargeModel() override; QString scriptFilePath() const; Format inputFormat() const { return m_inputFormat; } bool isValid() const { return m_valid; } ChargeModel* newInstance() const override; std::string identifier() const override { return m_identifier; } std::string name() const override { return m_name; } Core::Molecule::ElementMask elements() const override { return m_elements; } MatrixX partialCharges(Core::Molecule& mol) const override; MatrixX partialCharges(const Core::Molecule& mol) const override; double potential(Core::Molecule& mol, const Vector3& point) const override; bool supportsCharges() const { return m_partialCharges; } bool supportsElectrostatics() const { return m_electrostatics; } Core::Array potentials( Core::Molecule& mol, const Core::Array& points) const override; private: static Format stringToFormat(const std::string& str); static Io::FileFormat* createFileFormat(Format fmt); void resetMetaData(); void readMetaData(); bool parseString(const QJsonObject& ob, const QString& key, std::string& str); void processElementString(const QString& str); bool parseElements(const QJsonObject& ob); private: QtGui::PythonScript* m_interpreter; Format m_inputFormat; Core::Molecule::ElementMask m_elements; bool m_valid; bool m_partialCharges; bool m_electrostatics; std::string m_identifier; std::string m_name; std::string m_description; QString m_formatString; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SCRIPTCHARGEMODEL_H avogadrolibs-1.101.0/avogadro/qtplugins/scriptcharges/scriptcharges.cpp000066400000000000000000000040351506155467400264030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "scriptcharges.h" #include "scriptchargemodel.h" #include #include #include #include #include namespace Avogadro::QtPlugins { ScriptCharges::ScriptCharges(QObject* p) : ExtensionPlugin(p) { refreshModels(); } ScriptCharges::~ScriptCharges() {} QList ScriptCharges::actions() const { return QList(); } QStringList ScriptCharges::menuPath(QAction*) const { return QStringList(); } void ScriptCharges::setMolecule(QtGui::Molecule*) {} void ScriptCharges::refreshModels() { unregisterModels(); qDeleteAll(m_models); m_models.clear(); QMultiMap scriptPaths = QtGui::ScriptLoader::scriptList("charges"); foreach (const QString& filePath, scriptPaths) { auto* model = new ScriptChargeModel(filePath); if (model->isValid()) m_models.push_back(model); else delete model; } registerModels(); } void ScriptCharges::unregisterModels() { for (QList::const_iterator it = m_models.constBegin(), itEnd = m_models.constEnd(); it != itEnd; ++it) { Calc::ChargeManager::unregisterModel((*it)->identifier()); } } void ScriptCharges::registerModels() { for (QList::const_iterator it = m_models.constBegin(), itEnd = m_models.constEnd(); it != itEnd; ++it) { if (!Calc::ChargeManager::registerModel((*it)->newInstance())) { qDebug() << "Could not register model" << (*it)->identifier().c_str() << "due to name conflict."; } } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/scriptcharges/scriptcharges.h000066400000000000000000000025311506155467400260470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SCRIPTCHARGES_H #define AVOGADRO_QTPLUGINS_SCRIPTCHARGES_H #include #include namespace Avogadro { namespace Calc { class ChargeModel; } namespace QtPlugins { /** * @brief This extension registers ChargeModel electrostatics * implemented as external scripts. */ class ScriptCharges : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit ScriptCharges(QObject* parent = nullptr); ~ScriptCharges() override; QString name() const override { return tr("Script Charge Models"); } QString description() const override { return tr("Load electrostatic models from external scripts."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; private: QList m_models; void refreshModels(); void unregisterModels(); void registerModels(); }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SCRIPTCHARGES_H avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/000077500000000000000000000000001506155467400237335ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/CMakeLists.txt000066400000000000000000000007461506155467400265020ustar00rootroot00000000000000set(scriptfileformats_srcs scriptfileformats.cpp fileformatscript.cpp ) avogadro_plugin(ScriptFileFormats "Scriptable file formats" ExtensionPlugin scriptfileformats.h ScriptFileFormats "${scriptfileformats_srcs}" "" ) target_link_libraries(ScriptFileFormats PRIVATE Avogadro::QuantumIO) # Bundled format scripts: set(format_scripts formatScripts/zyx.py ) install(PROGRAMS ${format_scripts} DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/scripts/formatScripts/") avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/fileformatscript.cpp000066400000000000000000000245411506155467400300220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "fileformatscript.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::string_literals; namespace Avogadro::QtPlugins { FileFormatScript::FileFormatScript(const QString& scriptFileName_) : m_interpreter(new QtGui::PythonScript(scriptFileName_)), m_valid(false), m_bondOnRead(false), m_inputFormat(NotUsed), m_outputFormat(NotUsed) { readMetaData(); } FileFormatScript::~FileFormatScript() { delete m_interpreter; } QString FileFormatScript::scriptFilePath() const { return m_interpreter->scriptFilePath(); } Io::FileFormat* FileFormatScript::newInstance() const { return new FileFormatScript(m_interpreter->scriptFilePath()); } bool FileFormatScript::read(std::istream& in, Core::Molecule& molecule) { // Create intermediate format reader QScopedPointer format(createFileFormat(m_outputFormat)); if (format.isNull()) { appendError("Invalid intermediate format enum value."); return false; } // Copy input into memory /// @todo would be nice to pass the stream to the interpreter directly... // Get length first: in.seekg(0, std::istream::end); std::istream::pos_type size = in.tellg(); // Construct byte array QByteArray buffer; buffer.resize(static_cast(size)); in.seekg(0, std::istream::beg); in.read(buffer.data(), size); // Call the script to convert the file QByteArray result = m_interpreter->execute(QStringList() << "--read", buffer); if (m_interpreter->hasErrors()) { foreach (const QString& err, m_interpreter->errorList()) appendError(err.toStdString()); return false; } if (!format->readString(std::string(result.constData(), result.size()), molecule)) { appendError(format->error(), false); return false; } if (m_bondOnRead) { molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); } return true; } bool FileFormatScript::write(std::ostream& out, const Core::Molecule& molecule) { // Create the intermediate format writer std::string intermediate; QScopedPointer format(createFileFormat(m_inputFormat)); if (format.isNull()) { appendError("Invalid intermediate format enum value."); return false; } if (!format->writeString(intermediate, molecule)) { appendError(format->error(), false); return false; } // Call the script to convert the file QByteArray result = m_interpreter->execute( QStringList() << "--write", QByteArray::fromRawData(intermediate.c_str(), intermediate.size())); if (m_interpreter->hasErrors()) { foreach (const QString& err, m_interpreter->errorList()) appendError(err.toStdString()); return false; } out.write(result.constData(), result.size()); return true; } FileFormatScript::Format FileFormatScript::stringToFormat( const std::string& str) { if (str == "cjson") return Cjson; else if (str == "cml") return Cml; else if (str == "mdl" || str == "mol") return Mdl; else if (str == "pdb") return Pdb; else if (str == "sdf") return Sdf; else if (str == "xyz") return Xyz; return NotUsed; } Io::FileFormat* FileFormatScript::createFileFormat(FileFormatScript::Format fmt) { switch (fmt) { case Cjson: return new Io::CjsonFormat; case Cml: return new Io::CmlFormat; case Mdl: return new Io::MdlFormat; case Pdb: return new Io::PdbFormat; case Sdf: return new Io::SdfFormat; case Xyz: return new Io::XyzFormat; default: case NotUsed: return nullptr; } } void FileFormatScript::resetMetaData() { m_operations = Io::FileFormat::None; m_valid = false; m_bondOnRead = false; m_inputFormat = NotUsed; m_identifier.clear(); m_name.clear(); m_description.clear(); m_specificationUrl.clear(); m_fileExtensions.clear(); m_mimeTypes.clear(); } void FileFormatScript::readMetaData() { resetMetaData(); QByteArray output(m_interpreter->execute(QStringList() << "--metadata")); if (m_interpreter->hasErrors()) { qWarning() << "Error retrieving metadata for file format script:" << scriptFilePath() << "\n" << m_interpreter->errorList(); return; } QJsonParseError parseError; QJsonDocument doc(QJsonDocument::fromJson(output, &parseError)); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << parseError.errorString() << "(at offset" << parseError.offset << ")"; return; } if (!doc.isObject()) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\nResult is not a JSON object:\n" << output; return; } const QJsonObject metaData(doc.object()); // Read required inputs first. std::vector opStringsTmp; if (!parseStringArray(metaData, "operations", opStringsTmp)) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Error parsing required member 'operations'" << "\n" << output; return; } // validate operations: Operations operationsTmp = Io::FileFormat::None; for (auto& it : opStringsTmp) { if (it == "read") operationsTmp |= Io::FileFormat::Read; else if (it == "write") operationsTmp |= Io::FileFormat::Write; else { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Unrecognized operation:" << it.c_str() << "\n" << output; return; } } std::string identifierTmp; if (!parseString(metaData, "identifier", identifierTmp)) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Error parsing required member 'operations'" << "\n" << output; return; } std::string nameTmp; if (!parseString(metaData, "name", nameTmp)) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Error parsing required member 'name'" << "\n" << output; return; } // input format is required if write operations are supported: Format inputFormatTmp = NotUsed; if (operationsTmp & Io::FileFormat::Write) { std::string inputFormatStrTmp; if (!parseString(metaData, "inputFormat", inputFormatStrTmp)) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Member 'inputFormat' required for writable formats." << "\n" << output; return; } // Validate the input format inputFormatTmp = stringToFormat(inputFormatStrTmp); if (inputFormatTmp == NotUsed) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Member 'inputFormat' not recognized:" << inputFormatStrTmp.c_str() << "\nValid values are cjson, cml, mdl/sdf, pdb, or xyz.\n" << output; return; } } // output format is required if read operations are supported: Format outputFormatTmp = NotUsed; if (operationsTmp & Io::FileFormat::Read) { std::string outputFormatStrTmp; if (!parseString(metaData, "outputFormat", outputFormatStrTmp)) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Member 'outputFormat' required for readable formats." << "\n" << output; return; } // Validate the output format outputFormatTmp = stringToFormat(outputFormatStrTmp); if (outputFormatTmp == NotUsed) { qWarning() << "Error parsing metadata for file format script:" << scriptFilePath() << "\n" << "Member 'outputFormat' not recognized:" << outputFormatStrTmp.c_str() << "\nValid values are cjson, cml, mdl/sdf, pdb, or xyz.\n" << output; return; } } // If all required data is present, go ahead and set the member vars: m_operations = operationsTmp | Io::FileFormat::File | Io::FileFormat::Stream | Io::FileFormat::String; m_inputFormat = inputFormatTmp; m_outputFormat = outputFormatTmp; m_identifier = "User Script: "s + identifierTmp; m_name = nameTmp; // check if we should bond on read: if (metaData["bond"].isBool()) { m_bondOnRead = metaData["bond"].toBool(); } // Everything else is optional: parseString(metaData, "description", m_description); parseString(metaData, "specificationUrl", m_specificationUrl); parseStringArray(metaData, "fileExtensions", m_fileExtensions); parseStringArray(metaData, "mimeTypes", m_mimeTypes); m_valid = true; } bool FileFormatScript::parseString(const QJsonObject& ob, const QString& key, std::string& str) { if (!ob[key].isString()) return false; str = ob[key].toString().toStdString(); return !str.empty(); } bool FileFormatScript::parseStringArray(const QJsonObject& ob, const QString& key, std::vector& array) { array.clear(); if (!ob[key].isArray()) return false; foreach (const QJsonValue& val, ob[key].toArray()) { if (!val.isString()) return false; array.push_back(val.toString().toStdString()); if (array.back().empty()) return false; } return !array.empty(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/fileformatscript.h000066400000000000000000000141671506155467400274720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_FILEFORMATSCRIPT_H #define AVOGADRO_QTPLUGINS_FILEFORMATSCRIPT_H #include #include #include class QJsonObject; namespace Avogadro { namespace QtGui { class PythonScript; } namespace QtPlugins { /** * @brief The FileFormatScript class interfaces with external scripts that * implement chemical file reader/writers. * * Script Entry Points * =================== * * The script must handle the following command line arguments: * - `--metadata` Print metadata describing the format and the script's * abilities and exit. * - `--read` Read data from standard input and produce a standard * representation on standard output. * - `--write` Read a standard representation from standard input and write * the formatted result to standard output. * * Identify the Format with `--metadata` * ===================================== * * Running the script with the `--metadata` option should print a JSON object * of the following form: ~~~{.js} { "inputFormat": "cml", "outputFormat": "cml", "operations": ["read", "write"], "identifier": "Unique Name", "name": "User-friendly Name", "description": "Description of format.", "specificationUrl": "http://url.specifying.format/if/any/exist", "fileExtensions": ["ext"], "mimeTypes": ["chemical/x-ext"] } ~~~ * * Details: * - `inputFormat` indicates the format that the script can convert to the * implemented format by the `--write` command. Allowed values are `"cml"`, * `"cjson"`, or `"xyz"`. See the `--write` documentation for more detail. * - `outputFormat` indicates the format that the script can convert to from the * implemented format by the `--read` command. Allowed values are `"cml"`, * `"cjson"`, `"sdf"`, `"pdb"` or `"xyz"`. See the `--read` documentation for more detail. * - `operations` specifies the scripts capabilities. The array should contain * `"read"` if the script implements the `--read` option, and/or `"write"` if * `--write` is available. * - `identifier` is a unique identifier. The value must only be unique amongst * script formats, as it will be prefixed with "User Script: " internally by * Avogadro. * - `name` is a user-friendly name for the format. * - `description` is a description of the format, along with any relevant help * text for users. * - `specificationUrl` is the URL of the format specification if available * (or relevant web page/wiki otherwise). * - `fileExtensions` is an array specifying the file extensions that this * format supports. * - `mimeTypes` is an array specifying the mime types that this format * supports. * - `bond` is a boolean indicating whether the format expects Avogadro * to perceive bonds after reading the file. * * Required members are * - `operations` * - `inputFormat` (if `"write"` is specified in `operations`) * - `outputFormat` (if `"read"` is specified in `operations`) * - `identifier` * - `name` * * Optional members are * - `description` * - `specificationUrl` * - `fileExtensions` * - `mimeTypes` * * Reading a format with `--read` * ============================== * * If `"read"` is specified in the `operations` `--metadata` output along with * a valid `outputFormat`, Avogadro will call the script with `--read` and * write the implemented format to the script's standard input. The script shall * convert the input to `outputFormat` and print it to standard output. * * Writing a format with `--write` * =============================== * * If `"write"` is specified in the `operations` `--metadata` output along with * a valid `inputFormat`, Avogadro will call the script with `--write` and * write the `inputFormat` to the script's standard input. The script shall * convert the input to the implemented format and print it to standard output. */ class FileFormatScript : public Avogadro::Io::FileFormat { public: /** Formats that may be written to the script's input/output formats. */ enum Format { NotUsed, Cjson, Cml, Mdl, Pdb, Sdf, Xyz }; FileFormatScript(const QString& scriptFileName); ~FileFormatScript() override; QString scriptFilePath() const; Format inputFormat() const { return m_inputFormat; } Format outputFormat() const { return m_outputFormat; } bool isValid() const { return m_valid; } FileFormat* newInstance() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; Operations supportedOperations() const override { return m_operations; } std::string identifier() const override { return m_identifier; } std::string name() const override { return m_name; } std::string description() const override { return m_description; } std::string specificationUrl() const override { return m_specificationUrl; } std::vector fileExtensions() const override { return m_fileExtensions; } std::vector mimeTypes() const override { return m_mimeTypes; } private: static Format stringToFormat(const std::string& str); static Io::FileFormat* createFileFormat(Format fmt); void resetMetaData(); void readMetaData(); bool parseString(const QJsonObject& ob, const QString& key, std::string& str); bool parseStringArray(const QJsonObject& ob, const QString& key, std::vector& array); private: QtGui::PythonScript* m_interpreter; bool m_valid; bool m_bondOnRead; Operations m_operations; Format m_inputFormat; Format m_outputFormat; std::string m_identifier; std::string m_name; std::string m_description; std::string m_specificationUrl; std::vector m_fileExtensions; std::vector m_mimeTypes; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_FILEFORMATSCRIPT_H avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/formatScripts/000077500000000000000000000000001506155467400265735ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/formatScripts/zyx.py000066400000000000000000000044301506155467400300000ustar00rootroot00000000000000# This source file is part of the Avogadro project. # This source code is released under the 3-Clause BSD License, (see "LICENSE"). import argparse import json import sys def getMetaData(): metaData = {} metaData['inputFormat'] = 'xyz' metaData['outputFormat'] = 'xyz' metaData['operations'] = ['read', 'write'] metaData['identifier'] = 'ZYX Example Format' metaData['name'] = 'ZYX' metaData['description'] = "Mostly useless file format that reads xyz-style " +\ "files with reversed coordinates. Demonstrates " +\ "the implementation of a user-scripted file format." metaData['fileExtensions'] = ['zyx'] metaData['mimeTypes'] = ['chemical/x-zyx'] return metaData def write(): result = "" # Just copy the first two lines: numAtoms and comment/title result += sys.stdin.readline() result += sys.stdin.readline() for line in sys.stdin: words = line.split() result += '%-3s %9.5f %9.5f %9.5f' %\ (words[0], float(words[3]), float(words[2]), float(words[1])) if len(words) > 4: result += words[4:].join(' ') result += '\n' return result def read(): result = "" # Just copy the first two lines: numAtoms and comment/title result += sys.stdin.readline() result += sys.stdin.readline() for line in sys.stdin: words = line.split() result += '%-3s %9.5f %9.5f %9.5f' %\ (words[0], float(words[3]), float(words[2]), float(words[1])) if len(words) > 4: result += words[4:].join(' ') result += '\n' return result if __name__ == "__main__": parser = argparse.ArgumentParser('Example file format script.') parser.add_argument('--metadata', action='store_true') parser.add_argument('--read', action='store_true') parser.add_argument('--write', action='store_true') parser.add_argument('--display-name', action='store_true') parser.add_argument('--lang', nargs='?', default='en') args = vars(parser.parse_args()) if args['metadata']: print(json.dumps(getMetaData())) elif args['display_name']: print(getMetaData()['name']) elif args['read']: print(read()) elif args['write']: print(write()) avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/scriptfileformats.cpp000066400000000000000000000043331506155467400302020ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "scriptfileformats.h" #include "fileformatscript.h" #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { ScriptFileFormats::ScriptFileFormats(QObject* p) : ExtensionPlugin(p) { refreshFileFormats(); } ScriptFileFormats::~ScriptFileFormats() {} QList ScriptFileFormats::actions() const { return QList(); } QStringList ScriptFileFormats::menuPath(QAction*) const { return QStringList(); } void ScriptFileFormats::setMolecule(QtGui::Molecule*) {} void ScriptFileFormats::refreshFileFormats() { unregisterFileFormats(); qDeleteAll(m_formats); m_formats.clear(); QMultiMap scriptPaths = QtGui::ScriptLoader::scriptList("formatScripts"); foreach (const QString& filePath, scriptPaths) { auto* format = new FileFormatScript(filePath); if (format->isValid()) m_formats.push_back(format); else delete format; } registerFileFormats(); } void ScriptFileFormats::unregisterFileFormats() { for (QList::const_iterator it = m_formats.constBegin(), itEnd = m_formats.constEnd(); it != itEnd; ++it) { Io::FileFormatManager::unregisterFormat((*it)->identifier()); } } void ScriptFileFormats::registerFileFormats() { for (QList::const_iterator it = m_formats.constBegin(), itEnd = m_formats.constEnd(); it != itEnd; ++it) { if (!Io::FileFormatManager::registerFormat((*it)->newInstance())) { qDebug() << "Could not register format" << (*it)->identifier().c_str() << "due to name conflict."; } } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/scriptfileformats/scriptfileformats.h000066400000000000000000000025301506155467400276440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SCRIPTFILEFORMATS_H #define AVOGADRO_QTPLUGINS_SCRIPTFILEFORMATS_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief This extension registers FileFormat reader/writers that are * implemented as external scripts. */ class ScriptFileFormats : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit ScriptFileFormats(QObject* parent = nullptr); ~ScriptFileFormats() override; QString name() const override { return tr("Script File Formats"); } QString description() const override { return tr("Load file reader/writers from external scripts."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; private: QList m_formats; void refreshFileFormats(); void unregisterFileFormats(); void registerFileFormats(); }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_QUANTUMOUTPUT_H avogadrolibs-1.101.0/avogadro/qtplugins/select/000077500000000000000000000000001506155467400214525ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/select/CMakeLists.txt000066400000000000000000000001731506155467400242130ustar00rootroot00000000000000avogadro_plugin(Select "Extension that modifies selections." ExtensionPlugin select.h Select "select.cpp" "" ) avogadrolibs-1.101.0/avogadro/qtplugins/select/select.cpp000066400000000000000000000434661506155467400234520ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "select.h" #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { Select::Select(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_elements(nullptr), m_layerManager("Select") { auto* action = new QAction(tr("Select All"), this); action->setShortcut(QKeySequence("Ctrl+A")); action->setProperty("menu priority", 990); connect(action, SIGNAL(triggered()), SLOT(selectAll())); m_actions.append(action); action = new QAction(tr("Select None"), this); action->setShortcut(QKeySequence("Ctrl+Shift+A")); action->setProperty("menu priority", 980); connect(action, SIGNAL(triggered()), SLOT(selectNone())); m_actions.append(action); action = new QAction(this); action->setSeparator(true); action->setProperty("menu priority", 970); m_actions.append(action); action = new QAction(tr("Invert Selection"), this); action->setProperty("menu priority", 890); connect(action, SIGNAL(triggered()), SLOT(invertSelection())); m_actions.append(action); action = new QAction(tr("Select by Element…"), this); action->setProperty("menu priority", 880); connect(action, SIGNAL(triggered()), SLOT(selectElement())); m_actions.append(action); action = new QAction(tr("Select by Atom Index…"), this); action->setProperty("menu priority", 870); connect(action, SIGNAL(triggered()), SLOT(selectAtomIndex())); m_actions.append(action); action = new QAction(tr("Select by Residue…"), this); action->setProperty("menu priority", 860); connect(action, SIGNAL(triggered()), SLOT(selectResidue())); m_actions.append(action); action = new QAction(tr("Select Backbone Atoms…"), this); action->setProperty("menu priority", 858); connect(action, SIGNAL(triggered()), SLOT(selectBackboneAtoms())); m_actions.append(action); action = new QAction(tr("Select Sidechain Atoms…"), this); action->setProperty("menu priority", 855); connect(action, SIGNAL(triggered()), SLOT(selectSidechainAtoms())); m_actions.append(action); action = new QAction(tr("Select Water"), this); action->setProperty("menu priority", 850); connect(action, SIGNAL(triggered()), SLOT(selectWater())); m_actions.append(action); action = new QAction(this); action->setProperty("menu priority", 840); action->setSeparator(true); m_actions.append(action); action = new QAction(tr("Enlarge Selection"), this); action->setProperty("menu priority", 790); connect(action, SIGNAL(triggered()), SLOT(enlargeSelection())); m_actions.append(action); action = new QAction(tr("Shrink Selection"), this); action->setProperty("menu priority", 780); connect(action, SIGNAL(triggered()), SLOT(shrinkSelection())); m_actions.append(action); action = new QAction(this); action->setProperty("menu priority", 700); action->setSeparator(true); m_actions.append(action); action = new QAction(tr("Create New Layer from Selection"), this); action->setProperty("menu priority", 300); connect(action, SIGNAL(triggered()), SLOT(createLayerFromSelection())); m_actions.append(action); } Select::~Select() { if (m_elements) m_elements->deleteLater(); } QString Select::description() const { return tr("Change selections"); } QList Select::actions() const { return m_actions; } QStringList Select::menuPath(QAction*) const { return QStringList() << tr("&Select"); } void Select::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } bool Select::evalSelect(bool input, Index index) const { return !m_layerManager.atomLocked(index) && input; } void Select::selectAll() { if (m_molecule && m_molecule->atomCount() > 0) { for (Index i = 0; i < m_molecule->atomCount(); ++i) { m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i)); } m_molecule->emitChanged(Molecule::Atoms); } } void Select::selectNone() { if (m_molecule && m_molecule->atomCount() > 0) { for (Index i = 0; i < m_molecule->atomCount(); ++i) m_molecule->undoMolecule()->setAtomSelected(i, false); m_molecule->emitChanged(Molecule::Atoms); } } void Select::selectElement() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; if (m_elements == nullptr) { m_elements = new QtGui::PeriodicTableView(qobject_cast(parent())); connect(m_elements, SIGNAL(elementChanged(int)), this, SLOT(selectElement(int))); } m_elements->show(); } void Select::selectElement(int element) { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; QString undoText = tr("Select Element"); for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (m_molecule->atomicNumber(i) == element) { m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } else m_molecule->undoMolecule()->setAtomSelected(i, false, undoText); } m_molecule->emitChanged(Molecule::Atoms); } bool Select::isWaterOxygen(Index i) { if (m_molecule->atomicNumber(i) != 8) return false; // check to see if it has two bonds auto bonds = m_molecule->bonds(i); if (bonds.size() != 2) return false; // check to see that both bonds are to hydrogens for (auto& bond : bonds) { if (m_molecule->atomicNumber(bond.getOtherAtom(i).index()) != 1) return false; } return true; } void Select::selectWater() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; QString undoText = tr("Select Water"); for (Index i = 0; i < m_molecule->atomCount(); ++i) { auto atomicNumber = m_molecule->atomicNumber(i); if (atomicNumber == 8 && isWaterOxygen(i)) { m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i)); continue; } else if (atomicNumber == 1) { // check if it's attached to a water oxygen auto bonds = m_molecule->bonds(i); bool isWater = false; // check the bonds for a water oxygen for (auto& bond : bonds) { if (isWaterOxygen(bond.getOtherAtom(i).index())) { m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); isWater = true; break; } } if (!isWater) m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(false, i), undoText); continue; } m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(false, i), undoText); } // also select water residues (which may be isolated "O" atoms) for (const auto& residue : m_molecule->residues()) { if (residue.residueName() == "HOH") { for (auto atom : residue.residueAtoms()) { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } } } m_molecule->emitChanged(Molecule::Atoms); } void Select::selectBackboneAtoms() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; // unselect everything selectNone(); QString undoText = tr("Select Backbone"); for (const auto& residue : m_molecule->residues()) { for (auto atom : residue.residueAtoms()) { auto name = residue.getAtomName(atom); if (name == "CA" || name == "C" || name == "N" || name == "O") { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } // also select hydrogens connected to the backbone atoms if (atom.atomicNumber() == 1) { auto bonds = m_molecule->bonds(atom.index()); if (bonds.size() == 1) { auto otherAtom = bonds[0].getOtherAtom(atom.index()); auto otherName = residue.getAtomName(otherAtom); if (otherName == "CA" || otherName == "C" || otherName == "N" || otherName == "O") { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } } } } } m_molecule->emitChanged(Molecule::Atoms); } void Select::selectSidechainAtoms() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; // unselect everything selectNone(); QString undoText = tr("Select Sidechain"); for (const auto& residue : m_molecule->residues()) { for (auto atom : residue.residueAtoms()) { auto name = residue.getAtomName(atom); if (name != "CA" && name != "C" && name != "N" && name != "O") { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } // or is it a hydrogen connected to a backbone atom? // (then we don't want to select it) if (atom.atomicNumber() == 1) { auto bonds = m_molecule->bonds(atom.index()); if (bonds.size() == 1) { auto otherAtom = bonds[0].getOtherAtom(atom.index()); auto otherName = residue.getAtomName(otherAtom); if (otherName == "CA" || otherName == "C" || otherName == "N" || otherName == "O") { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(false, i), undoText); } } } } } m_molecule->emitChanged(Molecule::Atoms); } Vector3 Select::getSelectionCenter() { Vector3 center(0, 0, 0); int count = 0; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (m_molecule->atomSelected(i)) { center += m_molecule->atomPosition3d(i); ++count; } } if (count > 0) center /= count; return center; } void Select::enlargeSelection() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; Vector3 center = getSelectionCenter(); // find the current max distance of the selection Real maxDistance = 0.0; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (m_molecule->atomSelected(i)) { // we'll use the squaredNorm to save a bunch of square roots Vector3 displacement = m_molecule->atomPosition3d(i) - center; Real distance = displacement.squaredNorm(); if (distance > maxDistance) maxDistance = distance; } } maxDistance = sqrt(maxDistance) + 2.5; maxDistance *= maxDistance; // square to compare with .squaredNorm() values QString undoText = tr("Enlarge Selection"); // now select all atoms within the NEW max distance for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (!m_molecule->atomSelected(i)) { Vector3 displacement = m_molecule->atomPosition3d(i) - center; Real distance = displacement.squaredNorm(); if (distance < maxDistance) { m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } } } m_molecule->emitChanged(Molecule::Atoms); } void Select::shrinkSelection() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; Vector3 center = getSelectionCenter(); // find the current max distance of the selection Real maxDistance = 0.0; for (Index i = 0; i < m_molecule->atomCount(); ++i) { if (m_molecule->atomSelected(i)) { // we'll use the squaredNorm to save a bunch of square roots Vector3 displacement = m_molecule->atomPosition3d(i) - center; Real distance = displacement.squaredNorm(); if (distance > maxDistance) maxDistance = distance; } } maxDistance = sqrt(maxDistance) - 2.5; if (maxDistance < 0.0) maxDistance = 0.0; maxDistance *= maxDistance; // square to compare with .squaredNorm() values QString undoText = tr("Shrink Selection"); // now select ONLY atoms within the NEW max distance for (Index i = 0; i < m_molecule->atomCount(); ++i) { Vector3 displacement = m_molecule->atomPosition3d(i) - center; Real distance = displacement.squaredNorm(); if (distance < maxDistance) m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); else m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(false, i), undoText); } m_molecule->emitChanged(Molecule::Atoms); } void Select::selectAtomIndex() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; bool ok; QString text = QInputDialog::getText( qobject_cast(parent()), tr("Select Atoms by Index"), tr("Atoms to Select:"), QLineEdit::Normal, QString(), &ok); if (!ok || text.isEmpty()) return; QString undoText = tr("Select Atom"); auto list = text.simplified().split(','); foreach (const QString item, list) { // check if it's a range if (item.contains('-')) { auto range = item.split('-'); if (range.size() == 2) { bool ok1, ok2; int k = m_molecule->atomCount(); int start = range.first().toInt(&ok1); int last = range.back().toInt(&ok2); if (ok1 && ok2) { if (start < k) for (int i = start; i <= last; ++i) m_molecule->undoMolecule()->setAtomSelected( i, evalSelect(true, i), undoText); } } } else { int i = item.toInt(&ok); int k = m_molecule->atomCount(); if (ok) if (i < k) m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } } m_molecule->emitChanged(Molecule::Atoms); } void Select::selectResidue() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; bool ok; QString text = QInputDialog::getText( qobject_cast(parent()), tr("Select Atoms by Residue"), tr("Residues to Select:"), QLineEdit::Normal, QString(), &ok); if (!ok || text.isEmpty()) return; QString undoText = tr("Select Residue"); auto list = text.simplified().split(','); foreach (const QString item, list) { const QString label = item.simplified(); // get rid of whitespace // check if it's a number - select that residue index int index = label.toInt(&ok); if (ok) { auto residueList = m_molecule->residues(); if (index >= 1 && index < static_cast(residueList.size())) { auto residue = residueList[index]; for (auto& atom : residue.residueAtoms()) { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } } // index makes sense continue; } // okay it's not just a number, so see if it's HIS57, etc. QRegularExpression re("([a-zA-Z]+)([0-9]+)"); QRegularExpressionMatch match = re.match(label); if (match.hasMatch()) { QString name = match.captured(1); index = match.captured(2).toInt(); auto residueList = m_molecule->residues(); if (index >= 1 && index < static_cast(residueList.size())) { auto residue = residueList[index]; if (name == residue.residueName().c_str()) { for (auto atom : residue.residueAtoms()) { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } } // check if name matches specified (e.g. HIS57 is really a HIS) } // index makes sense } else { // standard residue name for (const auto& residue : m_molecule->residues()) { if (label == residue.residueName().c_str()) { // select the atoms of the residue for (auto atom : residue.residueAtoms()) { Index i = atom.index(); m_molecule->undoMolecule()->setAtomSelected(i, evalSelect(true, i), undoText); } } // residue matches label } // for(residues) continue; } // 3-character labels } m_molecule->emitChanged(Molecule::Atoms); } void Select::invertSelection() { if (m_molecule && m_molecule->atomCount() > 0) { for (Index i = 0; i < m_molecule->atomCount(); ++i) m_molecule->undoMolecule()->setAtomSelected( i, evalSelect(!m_molecule->atomSelected(i), i), tr("Invert Selection")); m_molecule->emitChanged(Molecule::Atoms); } } void Select::createLayerFromSelection() { if (m_molecule == nullptr || m_molecule->atomCount() == 0) return; QtGui::RWMolecule* rwmol = m_molecule->undoMolecule(); rwmol->beginMergeMode(tr("Change Layer")); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Layers | Molecule::Modified; auto& layerInfo = Core::LayerManager::getMoleculeInfo(m_molecule)->layer; QtGui::RWLayerManager rwLayerManager; rwLayerManager.addLayer(rwmol); int layer = layerInfo.maxLayer(); for (Index i = 0; i < rwmol->atomCount(); ++i) { auto a = rwmol->atom(i); if (a.selected()) { a.setLayer(layer); } } rwmol->endMergeMode(); rwmol->emitChanged(changes); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/select/select.h000066400000000000000000000033671506155467400231130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SELECT_H #define AVOGADRO_QTPLUGINS_SELECT_H #include #include #include namespace Avogadro { namespace QtGui { class PeriodicTableView; } namespace QtPlugins { /** * @brief The Select class is an extension to modify selections */ class Select : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit Select(QObject* parent_ = nullptr); ~Select() override; QString name() const override { return tr("Select"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; private slots: void selectAll(); void selectNone(); void invertSelection(); void selectElement(); void selectAtomIndex(); void selectElement(int element); void selectResidue(); void selectBackboneAtoms(); void selectSidechainAtoms(); void selectWater(); bool isWaterOxygen(Index i); void enlargeSelection(); void shrinkSelection(); Vector3 getSelectionCenter(); void createLayerFromSelection(); private: QList m_actions; QtGui::Molecule* m_molecule; QtGui::PeriodicTableView* m_elements; QtGui::PluginLayerManager m_layerManager; bool evalSelect(bool input, Index index) const; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SELECT_H avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/000077500000000000000000000000001506155467400230565ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/CMakeLists.txt000066400000000000000000000005251506155467400256200ustar00rootroot00000000000000set(tool_srcs selectiontool.cpp selectiontoolwidget.cpp ) set(tool_uis selectiontoolwidget.ui ) set(tool_rcs selectiontool.qrc) avogadro_plugin(Selection "Selection tool" ToolPlugin selectiontool.h SelectionTool "${tool_srcs}" "${tool_uis}" "${tool_rcs}" ) target_link_libraries(Selection PRIVATE Avogadro::QtOpenGL) avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selection_dark.svg000066400000000000000000000005651506155467400265730ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selection_light.svg000066400000000000000000000007051506155467400267550ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selectiontool.cpp000066400000000000000000000256121506155467400264530ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "selectiontool.h" #include "selectiontoolwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Core::Array; using Avogadro::Core::Atom; using Avogadro::QtGui::Molecule; using Avogadro::QtGui::RWMolecule; using Avogadro::Rendering::GeometryNode; using Avogadro::Rendering::GroupNode; using Avogadro::Rendering::Identifier; using Avogadro::Rendering::MeshGeometry; namespace Avogadro::QtPlugins { SelectionTool::SelectionTool(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(nullptr), m_renderer(nullptr), m_toolWidget(nullptr), m_drawSelectionBox(false), m_doubleClick(false), m_initSelectionBox(false), m_layerManager("Selection Tool") { QString shortcut = tr("Ctrl+5", "control-key 5"); m_activateAction->setText(tr("Selection")); m_activateAction->setToolTip( tr("Selection Tool\t(%1)\n\n" "Left Mouse:\tClick to pick individual atoms, residues, or fragments\n" "\tDrag to select a range of atoms\n" "Right Mouse:\tClick outside the molecule to clear selection\n" "Use Ctrl to toggle the selection and shift to add to the selection.\n" "Double-Click:\tSelect an entire fragment.") .arg(shortcut)); setIcon(); } SelectionTool::~SelectionTool() {} void SelectionTool::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/selection_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/selection_light.svg")); } QWidget* SelectionTool::toolWidget() const { if (m_toolWidget == nullptr) { m_toolWidget = new SelectionToolWidget(qobject_cast(parent())); connect(m_toolWidget, SIGNAL(colorApplied(Vector3ub)), this, SLOT(applyColor(Vector3ub))); connect(m_toolWidget, SIGNAL(changeLayer(int)), this, SLOT(applyLayer(int))); } return m_toolWidget; } QUndoCommand* SelectionTool::mousePressEvent(QMouseEvent* e) { if (e->button() != Qt::LeftButton || !m_renderer) { m_initSelectionBox = false; return nullptr; } m_drawSelectionBox = false; m_initSelectionBox = true; m_start = Vector2(e->pos().x(), e->pos().y()); m_end = m_start; e->accept(); return nullptr; } QUndoCommand* SelectionTool::mouseReleaseEvent(QMouseEvent* e) { // If the click is released on an atom, add it to the list if (e->button() != Qt::LeftButton || !m_renderer || m_doubleClick) { m_doubleClick = false; return nullptr; } // Assess whether the selection box is big enough to use, or a mis-click. m_end = Vector2(e->pos().x(), e->pos().y()); Vector2f start(m_start.x() < m_end.x() ? m_start.x() : m_end.x(), m_start.y() < m_end.y() ? m_start.y() : m_end.y()); Vector2f end(m_start.x() > m_end.x() ? m_start.x() : m_end.x(), m_start.y() > m_end.y() ? m_start.y() : m_end.y()); bool bigEnough = fabs(start.x() - end.x()) > 2 && fabs(start.y() - end.y()) > 2; bool anySelect = false; Index selectedIndex = MaxIndex; if (m_drawSelectionBox && bigEnough) { shouldClean(e); m_initSelectionBox = false; auto hits = m_renderer->hits(start.x(), start.y(), end.x(), end.y()); for (const auto& hit : hits) { if (hit.type == Rendering::AtomType) { anySelect = selectAtom(e, hit.index) || anySelect; selectedIndex = hit.index; } } } else { // Single click m_start = Vector2(e->pos().x(), e->pos().y()); m_end = m_start; Identifier hit = m_renderer->hit(e->pos().x(), e->pos().y()); // Now add the atom on release. if (hit.type == Rendering::AtomType) { // store the result in case it's a toggle bool selected = selectAtom(e, hit.index); shouldClean(e); if (selected) { anySelect = addAtom(hit.index); selectedIndex = hit.index; } else { anySelect = removeAtom(hit.index); selectedIndex = hit.index; } } } if (anySelect && m_toolWidget != nullptr) { m_toolWidget->setDropDown(m_layerManager.getLayerID(selectedIndex), m_layerManager.layerCount()); } m_drawSelectionBox = false; // Disable this code until rectangle selection is ready. emit drawablesChanged(); e->accept(); return nullptr; } QUndoCommand* SelectionTool::mouseDoubleClickEvent(QMouseEvent* e) { if (e->button() == Qt::LeftButton) { m_doubleClick = true; m_initSelectionBox = false; Vector2 select = Vector2(e->pos().x(), e->pos().y()); Identifier hit = m_renderer->hit(select.x(), select.y()); // Reset the atom list if (!hit.isValid()) { clearAtoms(); } else { shouldClean(e); m_drawSelectionBox = false; // resync the select from simple click only on control if (e->modifiers() & Qt::ControlModifier) { toggleAtom(hit.index); } selectLinkedMolecule(e, hit.index); emit drawablesChanged(); e->accept(); } } return nullptr; } // namespace QtPlugins QUndoCommand* SelectionTool::mouseMoveEvent(QMouseEvent* e) { // Disable this code until rectangle selection is ready. if (m_initSelectionBox) { m_drawSelectionBox = true; m_end = Vector2(e->pos().x(), e->pos().y()); emit drawablesChanged(); e->accept(); } return nullptr; } QUndoCommand* SelectionTool::keyPressEvent(QKeyEvent*) { return nullptr; } void SelectionTool::draw(Rendering::GroupNode& node) { if (!m_drawSelectionBox || !m_initSelectionBox) { node.clear(); return; } auto* geo = new GeometryNode; node.addChild(geo); auto* mesh = new MeshGeometry; mesh->setRenderPass(Rendering::Overlay2DPass); Array verts(4); Vector3f start(m_start.x() < m_end.x() ? m_start.x() : m_end.x(), m_start.y() < m_end.y() ? m_start.y() : m_end.y(), 0.0f); Vector3f end(m_start.x() > m_end.x() ? m_start.x() : m_end.x(), m_start.y() > m_end.y() ? m_start.y() : m_end.y(), 0.0f); start.y() = m_renderer->overlayCamera().height() - start.y(); end.y() = m_renderer->overlayCamera().height() - end.y(); verts[0] = Vector3f(start.x(), end.y(), 0.0f); verts[1] = Vector3f(end.x(), end.y(), 0.0f); verts[2] = Vector3f(start.x(), start.y(), 0.0f); verts[3] = Vector3f(end.x(), start.y(), 0.0f); const Vector3f normal = verts[0].cross(verts[1]).normalized(); Array norms(4, normal); Array indices(6); indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 2; indices[4] = 1; indices[5] = 3; mesh->setColor(Vector3ub(200, 200, 0)); mesh->setOpacity(180); mesh->addVertices(verts, norms); mesh->addTriangles(indices); geo->addDrawable(mesh); } void SelectionTool::applyColor(Vector3ub color) { RWMolecule* rwmol = m_molecule->undoMolecule(); rwmol->beginMergeMode(tr("Paint Atoms")); for (Index i = 0; i < rwmol->atomCount(); ++i) { auto a = rwmol->atom(i); if (a.selected()) a.setColor(color); } rwmol->endMergeMode(); rwmol->emitChanged(Molecule::Atoms | Molecule::Modified); } void SelectionTool::applyLayer(int layer) { if (layer <= 0 || m_molecule == nullptr) { return; } RWMolecule* rwmol = m_molecule->undoMolecule(); rwmol->beginMergeMode(tr("Change Layer")); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Modified; // qDebug() << "SelectionTool::applyLayer" << layer << " layerCount " << // m_layerManager.layerCount(); if (layer >= static_cast(m_layerManager.layerCount())) { // add a new layer auto& layerInfo = Core::LayerManager::getMoleculeInfo(m_molecule)->layer; QtGui::RWLayerManager rwLayerManager; rwLayerManager.addLayer(rwmol); layer = layerInfo.maxLayer(); // update the menu too if (m_toolWidget != nullptr) m_toolWidget->setDropDown(layer, m_layerManager.layerCount()); changes |= Molecule::Layers | Molecule::Added; } for (Index i = 0; i < rwmol->atomCount(); ++i) { auto a = rwmol->atom(i); if (a.selected()) { a.setLayer(layer); } } rwmol->endMergeMode(); rwmol->emitChanged(changes); } void SelectionTool::selectLinkedMolecule(QMouseEvent* e, Index atom) { auto connectedAtoms = m_molecule->graph().connectedComponent(atom); for (auto a : connectedAtoms) { selectAtom(e, a); } } void SelectionTool::clearAtoms() { for (Index i = 0; i < m_molecule->atomCount(); ++i) m_molecule->undoMolecule()->setAtomSelected(i, false); } bool SelectionTool::addAtom(const Index& atom) { m_molecule->undoMolecule()->setAtomSelected(atom, true); return true; } bool SelectionTool::removeAtom(const Index& atom) { m_molecule->undoMolecule()->setAtomSelected(atom, false); return true; } bool SelectionTool::toggleAtom(const Index& atom) { Atom a = m_molecule->atom(atom); m_molecule->undoMolecule()->setAtomSelected(atom, !a.selected()); return a.selected(); } bool SelectionTool::shouldClean(QMouseEvent* e) { // accumulate the selection if shift or ctrl are presset if (!(e->modifiers() & Qt::ControlModifier) && !(e->modifiers() & Qt::ShiftModifier)) { clearAtoms(); return true; } return false; } bool SelectionTool::selectAtom(QMouseEvent* e, const Index& index) { if (m_layerManager.atomLocked(index)) { return false; } // control toggles the selection if (e->modifiers() & Qt::ControlModifier) { return toggleAtom(index); } // shift and default selection adds else if (e->modifiers() & Qt::ShiftModifier || m_drawSelectionBox) { return addAtom(index); } // default toggle else { return toggleAtom(index); } } void SelectionTool::setMolecule(QtGui::Molecule* mol) { if (m_molecule != mol) { m_molecule = mol; } size_t currentLayer = 0; size_t maxLayers = 1; if (m_molecule && !m_molecule->isSelectionEmpty()) { // find a selected atom Index selectedIndex = 0; for (Index i = 0; i < m_molecule->atomCount(); ++i) { auto a = m_molecule->atom(i); if (a.selected()) selectedIndex = i; break; } currentLayer = m_layerManager.getLayerID(selectedIndex); maxLayers = m_layerManager.layerCount(); } if (m_toolWidget != nullptr) m_toolWidget->setDropDown(currentLayer, maxLayers); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selectiontool.h000066400000000000000000000050541506155467400261160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SelectionTool_H #define AVOGADRO_QTPLUGINS_SelectionTool_H #include #include #include #include #include #include namespace Avogadro { namespace QtPlugins { class SelectionToolWidget; /** * @brief SelectionTool selects atoms and bonds from the screen. */ class SelectionTool : public QtGui::ToolPlugin { Q_OBJECT public: explicit SelectionTool(QObject* parent_ = nullptr); ~SelectionTool() override; QString name() const override { return tr("Selection tool"); } QString description() const override { return tr("Selection tool"); } unsigned char priority() const override { return 25; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule*) override; void setGLRenderer(Rendering::GLRenderer* renderer) override; QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseDoubleClickEvent(QMouseEvent* e) override; QUndoCommand* mouseMoveEvent(QMouseEvent* e) override; QUndoCommand* keyPressEvent(QKeyEvent* e) override; void draw(Rendering::GroupNode& node) override; private slots: void applyColor(Vector3ub color); void applyLayer(int layer); private: void clearAtoms(); bool selectAtom(QMouseEvent* e, const Index& atom); bool addAtom(const Index& atom); bool removeAtom(const Index& atom); bool toggleAtom(const Index& atom); bool shouldClean(QMouseEvent* e); void selectLinkedMolecule(QMouseEvent* e, Index atom); QAction* m_activateAction; QtGui::Molecule* m_molecule; Rendering::GLRenderer* m_renderer; mutable SelectionToolWidget* m_toolWidget; bool m_drawSelectionBox; bool m_doubleClick; bool m_initSelectionBox; Vector2 m_start; Vector2 m_end; QtGui::PluginLayerManager m_layerManager; }; inline void SelectionTool::setGLRenderer(Rendering::GLRenderer* renderer) { m_renderer = renderer; } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SelectionTool_H avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selectiontool.qrc000066400000000000000000000002171506155467400264500ustar00rootroot00000000000000 selection_light.svg selection_dark.svg avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selectiontoolwidget.cpp000066400000000000000000000040611506155467400276520ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "selectiontoolwidget.h" #include "ui_selectiontoolwidget.h" #include namespace Avogadro::QtPlugins { SelectionToolWidget::SelectionToolWidget(QWidget* parent) : QWidget(parent), m_ui(new Ui::SelectionToolWidget) { m_ui->setupUi(this); setDropDown(0, 1); connect(m_ui->applyColorButton, SIGNAL(clicked()), this, SLOT(userClickedColor())); } SelectionToolWidget::~SelectionToolWidget() { delete m_ui; } void SelectionToolWidget::setDropDown(size_t current, size_t max) { // disconnect the signal so we don't send it accidentally disconnect(m_ui->changeLayerDropDown, nullptr, nullptr, nullptr); m_ui->changeLayerDropDown->clear(); for (size_t i = 0; i < max; ++i) { m_ui->changeLayerDropDown->addItem(QString::number(i + 1)); } m_ui->changeLayerDropDown->addItem(tr("New Layer")); if (current != static_cast(m_ui->changeLayerDropDown->currentIndex())) m_ui->changeLayerDropDown->setCurrentIndex(static_cast(current)); // reconnect the signal connect(m_ui->changeLayerDropDown, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changeLayer(int))); } void SelectionToolWidget::userClickedColor() { QColorDialog dlg(this); QPalette pal = m_ui->applyColorButton->palette(); dlg.setCurrentColor(pal.color(QPalette::Button)); if (dlg.exec()) { QColor new_color = dlg.currentColor(); pal.setColor(QPalette::Button, new_color); m_ui->applyColorButton->setPalette(pal); m_ui->applyColorButton->update(); Vector3ub color; color[0] = static_cast(new_color.red()); color[1] = static_cast(new_color.green()); color[2] = static_cast(new_color.blue()); emit colorApplied(color); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selectiontoolwidget.h000066400000000000000000000020001506155467400273060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SELECTIONTOOLWIDGET_H #define AVOGADRO_QTPLUGINS_SELECTIONTOOLWIDGET_H #include #include namespace Avogadro { namespace QtPlugins { namespace Ui { class SelectionToolWidget; } class SelectionToolWidget : public QWidget { Q_OBJECT public: explicit SelectionToolWidget(QWidget* parent = nullptr); ~SelectionToolWidget() override; void setDropDown(size_t current, size_t max); signals: void colorApplied(Vector3ub color); void changeLayer(int layer); private slots: void userClickedColor(); private: Ui::SelectionToolWidget* m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SELECTIONTOOLWIDGET_H avogadrolibs-1.101.0/avogadro/qtplugins/selectiontool/selectiontoolwidget.ui000066400000000000000000000023741506155467400275120ustar00rootroot00000000000000 Avogadro::QtPlugins::SelectionToolWidget 0 0 400 300 Form Apply Color true Change Layer avogadrolibs-1.101.0/avogadro/qtplugins/spacegroup/000077500000000000000000000000001506155467400223435ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/spacegroup/CMakeLists.txt000066400000000000000000000003371506155467400251060ustar00rootroot00000000000000set(spacegroup_srcs spacegroup.cpp ) set(spacegroup_uis ) avogadro_plugin(SpaceGroup "Space group features for crystals." ExtensionPlugin spacegroup.h SpaceGroup "${spacegroup_srcs}" "${spacegroup_uis}" ) avogadrolibs-1.101.0/avogadro/qtplugins/spacegroup/spacegroup.cpp000066400000000000000000000336411506155467400252260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "spacegroup.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Core::AvoSpglib; using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { SpaceGroup::SpaceGroup(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_actions(QList()), m_molecule(nullptr), m_spgTol(1e-5), m_perceiveSpaceGroupAction(new QAction(this)), m_reduceToPrimitiveAction(new QAction(this)), m_conventionalizeCellAction(new QAction(this)), m_symmetrizeAction(new QAction(this)), m_fillUnitCellAction(new QAction(this)), m_reduceToAsymmetricUnitAction(new QAction(this)), m_setToleranceAction(new QAction(this)) { m_perceiveSpaceGroupAction->setText(tr("Perceive Space Group…")); connect(m_perceiveSpaceGroupAction, SIGNAL(triggered()), SLOT(perceiveSpaceGroup())); m_actions.push_back(m_perceiveSpaceGroupAction); m_perceiveSpaceGroupAction->setProperty("menu priority", 90); m_reduceToPrimitiveAction->setText(tr("Reduce to Primitive")); connect(m_reduceToPrimitiveAction, SIGNAL(triggered()), SLOT(reduceToPrimitive())); m_actions.push_back(m_reduceToPrimitiveAction); m_reduceToPrimitiveAction->setProperty("menu priority", 80); m_conventionalizeCellAction->setText(tr("Conventionalize Cell")); connect(m_conventionalizeCellAction, SIGNAL(triggered()), SLOT(conventionalizeCell())); m_actions.push_back(m_conventionalizeCellAction); m_conventionalizeCellAction->setProperty("menu priority", 70); m_symmetrizeAction->setText(tr("Symmetrize")); connect(m_symmetrizeAction, SIGNAL(triggered()), SLOT(symmetrize())); m_actions.push_back(m_symmetrizeAction); m_symmetrizeAction->setProperty("menu priority", 60); m_fillUnitCellAction->setText(tr("Fill Unit Cell…")); connect(m_fillUnitCellAction, SIGNAL(triggered()), SLOT(fillUnitCell())); m_actions.push_back(m_fillUnitCellAction); // should fall next to the "Wrap Atoms to Unit Cell" action m_fillUnitCellAction->setProperty("menu priority", 185); m_reduceToAsymmetricUnitAction->setText(tr("Reduce to Asymmetric Unit")); connect(m_reduceToAsymmetricUnitAction, SIGNAL(triggered()), SLOT(reduceToAsymmetricUnit())); m_actions.push_back(m_reduceToAsymmetricUnitAction); m_reduceToAsymmetricUnitAction->setProperty("menu priority", 40); m_setToleranceAction->setText(tr("Set Tolerance…")); connect(m_setToleranceAction, SIGNAL(triggered()), SLOT(setTolerance())); m_actions.push_back(m_setToleranceAction); m_setToleranceAction->setProperty("menu priority", 0); updateActions(); } SpaceGroup::~SpaceGroup() { qDeleteAll(m_actions); m_actions.clear(); } QList SpaceGroup::actions() const { return m_actions; } QStringList SpaceGroup::menuPath(QAction* action) const { if (action == m_fillUnitCellAction) return QStringList() << tr("&Crystal"); return QStringList() << tr("&Crystal") << tr("Space Group"); } void SpaceGroup::registerCommands() { emit registerCommand( "fillUnitCell", tr("Fill symmetric atoms based on the crystal space group.")); } bool SpaceGroup::handleCommand(const QString& command, [[maybe_unused]] const QVariantMap& options) { if (m_molecule == nullptr) return false; // No molecule to handle the command. if (command == "fillUnitCell") { fillUnitCell(); return true; } return false; } void SpaceGroup::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); } void SpaceGroup::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); auto changes = static_cast(c); if (changes & Molecule::UnitCell) { if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); } } void SpaceGroup::updateActions() { // Disable everything for nullptr molecules. if (!m_molecule) { foreach (QAction* action, m_actions) action->setEnabled(false); return; } if (m_molecule->unitCell()) { foreach (QAction* action, m_actions) action->setEnabled(true); } else { foreach (QAction* action, m_actions) action->setEnabled(false); } } void SpaceGroup::perceiveSpaceGroup() { // only do this if we don't have a Hall number set if (m_molecule == nullptr) return; if (m_molecule->hallNumber() != 0) { // Ask if the user wants to overwrite the current space group std::string hallSymbol = Core::SpaceGroups::hallSymbol(m_molecule->hallNumber()); QMessageBox::StandardButton reply; reply = QMessageBox::question(nullptr, tr("Perceive Space Group"), tr("The space group is already set to: %1.\n" "Would you like to overwrite it?") .arg(hallSymbol.c_str()), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::No) return; } unsigned short hallNumber = AvoSpglib::getHallNumber(*m_molecule, m_spgTol); unsigned short intNum = Core::SpaceGroups::internationalNumber(hallNumber); std::string hallSymbol = Core::SpaceGroups::hallSymbol(hallNumber); std::string intShort = Core::SpaceGroups::internationalShort(hallNumber); // Success! if (hallNumber != 0) { // Let's make the message std::stringstream ss; ss << "Tolerance: " << m_spgTol << " Å" << "\nSpace Group: " << intNum << "\nHall symbol: " << hallSymbol << "\nInternational symbol: " << intShort; // Now let's make the Message Box QMessageBox retMsgBox; retMsgBox.setText(tr(ss.str().c_str())); retMsgBox.exec(); } // Failure else { // Ask if the user wants to try again with a different tolerance QMessageBox::StandardButton reply; reply = QMessageBox::question(nullptr, tr("Failure"), tr("Space group perception failed.\n" "Would you like to try again with a " "different tolerance?"), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { setTolerance(); perceiveSpaceGroup(); // Recursion! } } } void SpaceGroup::reduceToPrimitive() { // Confirm the tolerance QMessageBox::StandardButton reply; reply = QMessageBox::question(nullptr, tr("Primitive Reduction"), tr("The tolerance is currently set to: %1.\n" "Proceed with this tolerance?") .arg(m_spgTol), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::No) setTolerance(); // Primitive reduction! bool success = m_molecule->undoMolecule()->reduceCellToPrimitive(m_spgTol); if (!success) { // Print an error message. QMessageBox retMsgBox; retMsgBox.setText(tr("Primitive reduction failed.\n" "Please check your crystal and try again " "with a different tolerance.")); retMsgBox.exec(); } } void SpaceGroup::conventionalizeCell() { // Confirm the tolerance QMessageBox::StandardButton reply; reply = QMessageBox::question(nullptr, tr("Conventionalize Cell"), tr("The tolerance is currently set to: %1.\n" "Proceed with this tolerance?") .arg(m_spgTol), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::No) setTolerance(); // Conventionalize the cell! bool success = m_molecule->undoMolecule()->conventionalizeCell(m_spgTol); if (!success) { // Print an error message. QMessageBox retMsgBox; retMsgBox.setText(tr("Conventionalize cell failed.\n" "Please check your crystal and try again " "with a different tolerance.")); retMsgBox.exec(); } } void SpaceGroup::symmetrize() { // Confirm the tolerance QMessageBox::StandardButton reply; reply = QMessageBox::question(nullptr, tr("Symmetrize Cell"), tr("The tolerance is currently set to: %1.\n" "Proceed with this tolerance?") .arg(m_spgTol), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::No) setTolerance(); bool success = m_molecule->undoMolecule()->symmetrizeCell(m_spgTol); if (!success) { // Print an error message. QMessageBox retMsgBox; retMsgBox.setText(tr("Symmetrization failed.\n" "Please check your crystal and try again " "with a different tolerance.")); retMsgBox.exec(); } } void SpaceGroup::fillUnitCell() { unsigned short hallNumber = m_molecule->hallNumber(); // If it's not set, ask the user to select a space group if (hallNumber == 0) hallNumber = selectSpaceGroup(); // If the hall number is zero, the user canceled if (hallNumber == 0) return; m_molecule->undoMolecule()->fillUnitCell(hallNumber, m_spgTol); } void SpaceGroup::reduceToAsymmetricUnit() { // Let's gather some information about the space group first unsigned short hallNumber = AvoSpglib::getHallNumber(*m_molecule, m_spgTol); unsigned short intNum = Core::SpaceGroups::internationalNumber(hallNumber); std::string hallSymbol = Core::SpaceGroups::hallSymbol(hallNumber); std::string intShort = Core::SpaceGroups::internationalShort(hallNumber); // Ask the user if he/she wants to use this space group std::stringstream ss; ss << "With a tolerance of " << m_spgTol << " Å, " << "the space group information was perceived to be the following:" << "\nSpace Group: " << intNum << "\nHall symbol: " << hallSymbol << "\nInternational symbol: " << intShort << "\n\nProceed with this space group?"; QMessageBox::StandardButton reply; reply = QMessageBox::question(nullptr, tr("Reduce to Asymmetric Unit"), tr(ss.str().c_str()), QMessageBox::Yes | QMessageBox::No); // If the user does not want to use the perceived space group, // let the user set it. if (reply == QMessageBox::No) hallNumber = selectSpaceGroup(); // If 0 was set, that means the user cancelled if (hallNumber == 0) return; // Perform the operation! m_molecule->undoMolecule()->reduceCellToAsymmetricUnit(hallNumber, m_spgTol); } void SpaceGroup::setTolerance() { bool ok; double tol = QInputDialog::getDouble(nullptr, tr("Avogadro2"), // title tr("Select tolerance in Å:"), // label m_spgTol, // initial 1e-5, // min 0.5, // max 5, // decimals &ok); if (!ok) return; m_spgTol = tol; } unsigned short SpaceGroup::selectSpaceGroup() { QStandardItemModel spacegroups; QStringList modelHeader; modelHeader << tr("International") << tr("Hall") << tr("Hermann-Mauguin"); spacegroups.setHorizontalHeaderLabels(modelHeader); for (unsigned short i = 1; i <= 530; ++i) { QList row; row << new QStandardItem( QString::number(Core::SpaceGroups::internationalNumber(i))) << new QStandardItem(QString(Core::SpaceGroups::hallSymbol(i))) << new QStandardItem(QString(Core::SpaceGroups::internationalShort(i))); spacegroups.appendRow(row); } QDialog dialog; dialog.setLayout(new QVBoxLayout); dialog.setWindowTitle(tr("Select Space Group")); auto* view = new QTableView; view->setSelectionBehavior(QAbstractItemView::SelectRows); view->setSelectionMode(QAbstractItemView::SingleSelection); view->setCornerButtonEnabled(false); view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); view->verticalHeader()->hide(); view->setModel(&spacegroups); dialog.layout()->addWidget(view); view->selectRow(0); view->resizeColumnsToContents(); view->resizeRowsToContents(); view->setMinimumWidth(view->horizontalHeader()->length() + view->verticalScrollBar()->sizeHint().width()); connect(view, SIGNAL(activated(QModelIndex)), &dialog, SLOT(accept())); auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept())); connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject())); dialog.layout()->addWidget(buttons); if (dialog.exec() != QDialog::Accepted) return 0; // This should be hall number return view->currentIndex().row() + 1; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/spacegroup/spacegroup.h000066400000000000000000000040611506155467400246650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SPACEGROUP_H #define AVOGADRO_QTPLUGINS_SPACEGROUP_H #include namespace Avogadro { namespace QtPlugins { /** * @brief Space group features for crystals. */ class SpaceGroup : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit SpaceGroup(QObject* parent_ = nullptr); ~SpaceGroup() override; QString name() const override { return tr("SpaceGroup"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; bool handleCommand(const QString& command, const QVariantMap& options) override; void registerCommands() override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); private slots: void updateActions(); void perceiveSpaceGroup(); void reduceToPrimitive(); void conventionalizeCell(); void symmetrize(); void fillUnitCell(); void reduceToAsymmetricUnit(); void setTolerance(); private: // Pop up a dialog box and ask the user to select a space group. // Returns the hall number for the selected space group. // Returns 0 if the user canceled. unsigned short selectSpaceGroup(); QList m_actions; QtGui::Molecule* m_molecule; double m_spgTol; QAction* m_perceiveSpaceGroupAction; QAction* m_reduceToPrimitiveAction; QAction* m_conventionalizeCellAction; QAction* m_symmetrizeAction; QAction* m_fillUnitCellAction; QAction* m_reduceToAsymmetricUnitAction; QAction* m_setToleranceAction; }; inline QString SpaceGroup::description() const { return tr("Space group features for crystals."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SPACEGROUP_H avogadrolibs-1.101.0/avogadro/qtplugins/spectra/000077500000000000000000000000001506155467400216345ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/spectra/CMakeLists.txt000066400000000000000000000003471506155467400244000ustar00rootroot00000000000000set(plugin_srcs spectradialog.cpp spectra.cpp ) avogadro_plugin(Spectra "Spectra Plots" ExtensionPlugin spectra.h Spectra "${plugin_srcs}" "spectradialog.ui" ) target_link_libraries(Spectra PRIVATE Avogadro::Vtk) avogadrolibs-1.101.0/avogadro/qtplugins/spectra/spectra.cpp000066400000000000000000000076101506155467400240050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "spectra.h" #include "spectradialog.h" #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { Spectra::Spectra(QObject* p) : ExtensionPlugin(p), m_molecule(nullptr), m_dialog(nullptr) { auto* action = new QAction(this); action->setEnabled(false); action->setText(tr("Plot Spectra…")); action->setProperty("menu priority", -900); connect(action, SIGNAL(triggered()), SLOT(openDialog())); m_actions.push_back(action); } QList Spectra::actions() const { return m_actions; } QStringList Spectra::menuPath(QAction*) const { QStringList path; path << tr("&Analyze"); return path; } void Spectra::setMolecule(QtGui::Molecule* mol) { if (m_molecule != nullptr) m_molecule->disconnect(this); // extract vibrational and other spectra m_molecule = mol; if (m_molecule == nullptr) { return; } bool enableAction = false; // check to see if it has IR or Raman data if (!m_molecule->vibrationFrequencies().empty()) enableAction = true; // check if there are other spectra if (!m_molecule->spectraTypes().empty()) enableAction = true; foreach (auto action, m_actions) action->setEnabled(enableAction); connect(m_molecule, SIGNAL(changed(unsigned int)), SLOT(moleculeChanged(unsigned int))); if (enableAction && m_dialog != nullptr) { gatherSpectra(); } } void Spectra::moleculeChanged(unsigned int changes) { if (m_molecule == nullptr) return; bool enableAction = false; // check to see if it has IR or Raman data if (!m_molecule->vibrationFrequencies().empty()) enableAction = true; // check if there are other spectra if (!m_molecule->spectraTypes().empty()) enableAction = true; foreach (auto action, m_actions) action->setEnabled(enableAction); if (enableAction && m_dialog != nullptr) { gatherSpectra(); } } void Spectra::openDialog() { if (m_molecule == nullptr) return; if (m_dialog == nullptr) { m_dialog = new SpectraDialog(qobject_cast(this->parent())); } gatherSpectra(); // update the elements auto elements = m_molecule->atomicNumbers(); std::vector atomicNumbers(elements.begin(), elements.end()); m_dialog->setElements(atomicNumbers); m_dialog->show(); } void Spectra::gatherSpectra() { if (m_molecule == nullptr || m_dialog == nullptr) return; std::map spectra; // copy any spectra from the molecule for (const auto& type : m_molecule->spectraTypes()) { spectra[type] = m_molecule->spectra(type); } // check to see if it has IR or Raman data if (!m_molecule->vibrationFrequencies().empty()) { const unsigned int n = m_molecule->vibrationFrequencies().size(); MatrixX ir(n, 2); // check max intensity for (unsigned int i = 0; i < n; ++i) { ir(i, 0) = m_molecule->vibrationFrequencies()[i]; ir(i, 1) = m_molecule->vibrationIRIntensities()[i]; } spectra["IR"] = ir; if (m_molecule->vibrationRamanIntensities().size() == m_molecule->vibrationFrequencies().size()) { MatrixX raman(n, 2); for (unsigned int i = 0; i < n; ++i) { raman(i, 0) = m_molecule->vibrationFrequencies()[i]; raman(i, 1) = m_molecule->vibrationRamanIntensities()[i]; } spectra["Raman"] = raman; } } m_dialog->setSpectra(spectra); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/spectra/spectra.h000066400000000000000000000025101506155467400234440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SPECTRA_H #define AVOGADRO_QTPLUGINS_SPECTRA_H #include class QAction; class QDialog; namespace Avogadro { namespace VTK { class ChartDialog; } namespace QtPlugins { class SpectraDialog; /** * @brief The Spectra plugin handles vibrations and spectra. */ class Spectra : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Spectra(QObject* parent = nullptr); ~Spectra() override = default; QString name() const override { return tr("Spectra"); } QString description() const override { return tr("Display spectra plots."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; public slots: void openDialog(); void moleculeChanged(unsigned int changes); private: void gatherSpectra(); QList m_actions; QtGui::Molecule* m_molecule; SpectraDialog* m_dialog; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_Spectra_H avogadrolibs-1.101.0/avogadro/qtplugins/spectra/spectradialog.cpp000066400000000000000000000674241506155467400251760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "spectradialog.h" #include "ui_spectradialog.h" #include #include #include #include #include #include #include using Avogadro::Core::Molecule; namespace Avogadro::QtPlugins { constexpr QColor black(0, 0, 0); constexpr QColor white(1, 1, 1); constexpr QColor red(1, 0, 0); constexpr QColor green(0, 1, 0); constexpr QColor blue(0, 0, 1); float scaleAndBlur(float x, float peak, float intensity, float scale = 1.0, float shift = 0.0, float fwhm = 0.0) { // return the intensity at point x, from a Gaussian centered at peak // with a width of fwhm, scaled by scale and shifted by shift float fwhm_to_sigma = 2.0 * sqrt(2.0 * log(2.0)); float sigma = fwhm / fwhm_to_sigma; // x is the absolute position, but we need to scale the peak position float scaled_peak = (peak - shift) / scale; float delta = x - scaled_peak; float exponent = -(delta * delta) / (2 * sigma * sigma); float gaussian = exp(exponent); return intensity * gaussian; } float closestTo(float x, float peak, float intensity, float scale = 1.0, float shift = 0.0, float xScale = 1.0) { // return peak intensity if x is closer to the peak than another point // scaled by scale and shifted by shift float scaled_peak = (peak - shift) / scale; float delta = x - scaled_peak; // xScale is the reciprocal of the space between points // (i.e., used to generate many points in the loop) float peak_to_peak = 1.0 / xScale; return (fabs(delta) < peak_to_peak / 2.0) ? intensity : 0.0; } std::vector fromMatrix(const MatrixX& matrix) { std::vector result; for (auto i = 0; i < matrix.rows(); ++i) result.push_back(matrix(i, 0)); return result; } SpectraDialog::SpectraDialog(QWidget* parent) : QDialog(parent), m_ui(new Ui::SpectraDialog) { m_ui->setupUi(this); // hide the units for now m_ui->unitsLabel->hide(); m_ui->unitsCombo->hide(); // only for NMR m_ui->elementCombo->hide(); m_ui->dataTable->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch); // Hide advanced options initially m_ui->tab_widget->hide(); m_ui->dataTable->hide(); m_ui->push_exportData->hide(); readSettings(); // connections for options connect(m_ui->push_options, SIGNAL(clicked()), this, SLOT(toggleOptions())); connect(m_ui->push_colorBackground, SIGNAL(clicked()), this, SLOT(changeBackgroundColor())); connect(m_ui->push_colorForeground, SIGNAL(clicked()), this, SLOT(changeForegroundColor())); connect(m_ui->push_colorCalculated, SIGNAL(clicked()), this, SLOT(changeCalculatedSpectraColor())); connect(m_ui->push_colorImported, SIGNAL(clicked()), this, SLOT(changeImportedSpectraColor())); connect(m_ui->fontSizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changeFontSize())); connect(m_ui->lineWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(changeLineWidth())); connectOptions(); } SpectraDialog::~SpectraDialog() { writeSettings(); } void SpectraDialog::connectOptions() { // connect (or reconnect) anything that calls change or update plot connect(m_ui->combo_spectra, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSpectra())); connect(m_ui->xAxisMinimum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); connect(m_ui->xAxisMaximum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); connect(m_ui->yAxisMinimum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); connect(m_ui->yAxisMaximum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); connect(m_ui->peakWidth, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); } void SpectraDialog::disconnectOptions() { // disconnect anything that calls change or update plot disconnect(m_ui->combo_spectra, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSpectra())); disconnect(m_ui->xAxisMinimum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); disconnect(m_ui->xAxisMaximum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); disconnect(m_ui->yAxisMinimum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); disconnect(m_ui->yAxisMaximum, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); disconnect(m_ui->peakWidth, SIGNAL(valueChanged(double)), this, SLOT(updatePlot())); } void SpectraDialog::updateElementCombo() { // update the element combo box disconnect(m_ui->elementCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSpectra())); m_ui->elementCombo->clear(); // go through the elements in atomic number order // make a copy of the vector std::vector elements = m_elements; std::sort(elements.begin(), elements.end()); // add the unique elements, with the element number as the data for (auto& element : elements) { // check to see if it's already in the combo box bool found = false; for (int i = 0; i < m_ui->elementCombo->count(); ++i) { if (m_ui->elementCombo->itemData(i).toInt() == element) { found = true; break; } } if (found) continue; switch (element) { case 1: m_ui->elementCombo->addItem("¹H", element); break; case 3: m_ui->elementCombo->addItem("⁷Li", element); break; case 5: m_ui->elementCombo->addItem("¹¹B", element); break; case 6: m_ui->elementCombo->addItem("¹³C", element); break; case 7: m_ui->elementCombo->addItem("¹⁵N", element); break; case 8: m_ui->elementCombo->addItem("¹⁷O", element); break; case 9: m_ui->elementCombo->addItem("¹⁹F", element); break; case 14: m_ui->elementCombo->addItem("²⁹Si", element); break; case 15: m_ui->elementCombo->addItem("³¹P", element); break; default: m_ui->elementCombo->addItem(QString::number(element), element); break; } } // connect the element combo box connect(m_ui->elementCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSpectra())); changeSpectra(); // default to 1H } void SpectraDialog::changeSpectra() { // based on the current spectra type, update the options // and prep the spectra for plotting QSettings settings; disconnectOptions(); // what type of spectra are we plotting? SpectraType type = static_cast(m_ui->combo_spectra->currentData().toInt()); // only show for NMR m_ui->elementCombo->hide(); // todo: some spectra might want to swtich units m_transitions.clear(); m_intensities.clear(); switch (type) { case SpectraType::Infrared: m_transitions = fromMatrix(m_spectra["IR"].col(0)); m_intensities = fromMatrix(m_spectra["IR"].col(1)); settings.beginGroup("spectra/ir"); m_ui->scaleSpinBox->setValue(settings.value("scale", 1.0).toDouble()); m_ui->offsetSpinBox->setValue(settings.value("offset", 0.0).toDouble()); m_ui->xAxisMinimum->setValue(settings.value("xmin", 4000.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 400.0).toDouble()); m_ui->peakWidth->setValue(settings.value("fwhm", 30.0).toDouble()); settings.endGroup(); break; case SpectraType::Raman: m_transitions = fromMatrix(m_spectra["Raman"].col(0)); m_intensities = fromMatrix(m_spectra["Raman"].col(1)); settings.beginGroup("spectra/raman"); m_ui->scaleSpinBox->setValue(settings.value("scale", 1.0).toDouble()); m_ui->offsetSpinBox->setValue(settings.value("offset", 0.0).toDouble()); m_ui->xAxisMinimum->setValue(settings.value("xmin", 0.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 4000.0).toDouble()); m_ui->peakWidth->setValue(settings.value("fwhm", 30.0).toDouble()); settings.endGroup(); break; case SpectraType::NMR: // settings handled per-element below m_ui->elementCombo->show(); break; case SpectraType::Electronic: m_transitions = fromMatrix(m_spectra["Electronic"].col(0)); m_intensities = fromMatrix(m_spectra["Electronic"].col(1)); settings.beginGroup("spectra/electronic"); m_ui->scaleSpinBox->setValue(settings.value("scale", 1.0).toDouble()); m_ui->offsetSpinBox->setValue(settings.value("offset", 0.0).toDouble()); // in eV m_ui->xAxisMinimum->setValue(settings.value("xmin", 5.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 1.0).toDouble()); m_ui->peakWidth->setValue(settings.value("fwhm", 0.1).toDouble()); settings.endGroup(); break; case SpectraType::CircularDichroism: m_transitions = fromMatrix(m_spectra["Electronic"].col(0)); // check if electronic has a third column if (m_spectra["Electronic"].cols() > 2) m_intensities = fromMatrix(m_spectra["Electronic"].col(2)); else // grab it from the CD data m_intensities = fromMatrix(m_spectra["CircularDichroism"].col(1)); settings.beginGroup("spectra/cd"); m_ui->scaleSpinBox->setValue(settings.value("scale", 1.0).toDouble()); m_ui->offsetSpinBox->setValue(settings.value("offset", 0.0).toDouble()); // default to eV units m_ui->xAxisMinimum->setValue(settings.value("xmin", 5.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 1.0).toDouble()); m_ui->peakWidth->setValue(settings.value("fwhm", 0.1).toDouble()); settings.endGroup(); break; case SpectraType::DensityOfStates: m_transitions = fromMatrix(m_spectra["DensityOfStates"].col(0)); m_intensities = fromMatrix(m_spectra["DensityOfStates"].col(1)); settings.beginGroup("spectra/dos"); m_ui->scaleSpinBox->setValue(settings.value("scale", 1.0).toDouble()); m_ui->offsetSpinBox->setValue(settings.value("offset", 0.0).toDouble()); m_ui->xAxisMinimum->setValue(settings.value("xmin", -50.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 50.0).toDouble()); m_ui->peakWidth->setValue(settings.value("fwhm", 0.1).toDouble()); settings.endGroup(); break; } // a bunch of special work depending on the NMR element if (type == SpectraType::NMR) { // get the element int element = m_ui->elementCombo->currentData().toInt(); settings.beginGroup(QString("spectra/nmr/%1").arg(element)); m_ui->scaleSpinBox->setValue(settings.value("scale", 1.0).toDouble()); m_ui->peakWidth->setValue(settings.value("fwhm", 0.1).toDouble()); // tweak the default axis range // based on https://imserc.northwestern.edu/guide/eNMR/chem/NMRnuclei.html // offsets are approximate from a few calculations // .. to at least provide a starting point switch (element) { case 1: // 1H m_ui->xAxisMinimum->setValue(settings.value("xmin", 12.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 0.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", 31.876).toDouble()); break; case 3: // 7Li m_ui->xAxisMinimum->setValue(settings.value("xmin", -16.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 11.0).toDouble()); // TODO: offset m_ui->offsetSpinBox->setValue(settings.value("offset", 0.0).toDouble()); break; case 5: // 11B m_ui->xAxisMinimum->setValue(settings.value("xmin", 100.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", -120.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", 109.774).toDouble()); break; case 6: // 13C m_ui->xAxisMinimum->setValue(settings.value("xmin", 200.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 0.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", 192.038).toDouble()); break; case 7: // 15N m_ui->xAxisMinimum->setValue(settings.value("xmin", 800.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", 0.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", -106.738).toDouble()); break; case 8: // 17O m_ui->xAxisMinimum->setValue(settings.value("xmin", 1600.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", -50.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", 347.782).toDouble()); break; case 9: // 19F m_ui->xAxisMinimum->setValue(settings.value("xmin", 60.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", -300.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", 206.735).toDouble()); break; case 14: // 29Si m_ui->xAxisMinimum->setValue(settings.value("xmin", 50.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", -200.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", 400.876).toDouble()); break; case 15: // 31P m_ui->xAxisMinimum->setValue(settings.value("xmin", 250.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", -250.0).toDouble()); m_ui->offsetSpinBox->setValue( settings.value("offset", 392.841).toDouble()); break; default: m_ui->xAxisMinimum->setValue(settings.value("xmax", 100.0).toDouble()); m_ui->xAxisMaximum->setValue(settings.value("xmax", -100.0).toDouble()); m_ui->offsetSpinBox->setValue(settings.value("offset", 0.0).toDouble()); break; } settings.endGroup(); // the default NMR data has all the atoms in it, // so we need to loop through m_elements to filter MatrixX nmr = m_spectra["NMR"]; for (int i = 0; i < m_elements.size(); ++i) { if (m_elements[i] == element) { m_transitions.push_back(nmr(i, 0)); } } // fill the intensities with 1.0 m_intensities.resize(m_transitions.size(), 1.0); } // other spectra transitions and intensities are already set // update the data table double maxIntensity = 0.0; m_ui->dataTable->setRowCount(m_transitions.size()); m_ui->dataTable->setColumnCount(2); for (auto i = 0; i < m_transitions.size(); ++i) { // frequency or energy QTableWidgetItem* item = new QTableWidgetItem(QString::number(m_transitions[i], 'f', 4)); m_ui->dataTable->setItem(i, 0, item); // intensities item = new QTableWidgetItem(QString::number(m_intensities[i], 'f', 4)); m_ui->dataTable->setItem(i, 1, item); if (m_intensities[i] > maxIntensity) maxIntensity = m_intensities[i]; } // update the spin boxes m_ui->yAxisMaximum->setValue(maxIntensity); m_ui->yAxisMinimum->setMinimum(0.0); // if CD, set the minimum too if (type == SpectraType::CircularDichroism) { m_ui->yAxisMinimum->setMinimum(-maxIntensity * 2.0); m_ui->yAxisMinimum->setValue(-maxIntensity); } updatePlot(); connectOptions(); } void SpectraDialog::setSpectra(const std::map& spectra) { m_spectra = spectra; // update the combo box disconnect(m_ui->combo_spectra, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSpectra())); m_ui->combo_spectra->clear(); for (auto& spectra : m_spectra) { QString name = QString::fromStdString(spectra.first); if (name == "IR") { name = tr("Infrared"); m_ui->combo_spectra->addItem(name, static_cast(SpectraType::Infrared)); } else if (name == "Raman") { name = tr("Raman"); m_ui->combo_spectra->addItem(name, static_cast(SpectraType::Raman)); } else if (name == "NMR") { name = tr("NMR"); m_ui->combo_spectra->addItem(name, static_cast(SpectraType::NMR)); } else if (name == "Electronic") { name = tr("Electronic"); m_ui->combo_spectra->addItem(name, static_cast(SpectraType::Electronic)); } else if (name == "CircularDichroism") { name = tr("Circular Dichroism"); m_ui->combo_spectra->addItem( name, static_cast(SpectraType::CircularDichroism)); } else if (name == "DensityOfStates") { name = tr("Density of States"); m_ui->combo_spectra->addItem( name, static_cast(SpectraType::DensityOfStates)); } } changeSpectra(); // connect again connect(m_ui->combo_spectra, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSpectra())); } void SpectraDialog::writeSettings() const { QSettings settings; settings.setValue("spectra/currentSpectra", m_ui->combo_spectra->currentIndex()); } void SpectraDialog::readSettings() { QSettings settings; settings.beginGroup("spectra"); // update the dialog with saved settings // font size int fontSize = settings.value("fontSize", 12).toInt(); m_ui->fontSizeCombo->setCurrentText(QString::number(fontSize)); // line width float lineWidth = settings.value("lineWidth", 1.0).toFloat(); m_ui->lineWidthSpinBox->setValue(lineWidth); // TODO: other bits settings.endGroup(); } void SpectraDialog::changeBackgroundColor() { QSettings settings; QColor current = settings.value("backgroundColor", white).value(); QColor color = QColorDialog::getColor(current, this, tr("Select Background Color")); if (color.isValid() && color != current) { settings.setValue("spectra/backgroundColor", color); updatePlot(); } } void SpectraDialog::changeForegroundColor() { QSettings settings; QColor current = settings.value("spectra/foregroundColor", black).value(); QColor color = QColorDialog::getColor(current, this, tr("Select Foreground Color")); if (color.isValid() && color != current) { settings.setValue("spectra/foregroundColor", color); updatePlot(); } } void SpectraDialog::changeCalculatedSpectraColor() { QSettings settings; QColor current = settings.value("spectra/calculatedColor", black).value(); QColor color = QColorDialog::getColor(current, this, tr("Select Calculated Spectra Color")); if (color.isValid() && color != current) { settings.setValue("spectra/calculatedColor", color); updatePlot(); } } void SpectraDialog::changeImportedSpectraColor() { QSettings settings; QColor current = settings.value("spectra/importedColor", red).value(); QColor color = QColorDialog::getColor(current, this, tr("Select Imported Spectra Color")); if (color.isValid() && color != current) { settings.setValue("spectra/importedColor", color); updatePlot(); } } void SpectraDialog::changeFontSize() { int size = m_ui->fontSizeCombo->currentText().toInt(); QSettings settings; settings.setValue("spectra/fontSize", size); updatePlot(); } void SpectraDialog::changeLineWidth() { float width = m_ui->lineWidthSpinBox->value(); QSettings settings; settings.setValue("spectra/lineWidth", width); updatePlot(); } /////////////////////// // Plot Manipulation // /////////////////////// void SpectraDialog::updatePlot() { // disconnect the options while we update the plot disconnectOptions(); // the raw data std::vector transitions, intensities; // for the plot std::vector xData, yData, yStick; // determine the type to plot SpectraType type = static_cast(m_ui->combo_spectra->currentData().toInt()); QSettings settings; QString windowName; QString xTitle; QString yTitle; // TODO: switch units for electronic and CD QString xWave = tr("Wavelength (nm)"); bool transmission = false; // get the raw data from the spectra map switch (type) { case SpectraType::Infrared: windowName = tr("Vibrational Spectra"); xTitle = tr("Wavenumbers (cm⁻¹)"); yTitle = tr("Transmission"); transmission = true; settings.beginGroup("spectra/ir"); settings.setValue("xmin", float(m_ui->xAxisMinimum->value())); settings.setValue("xmax", m_ui->xAxisMaximum->value()); settings.setValue("fwhm", float(m_ui->peakWidth->value())); settings.setValue("scale", m_ui->scaleSpinBox->value()); settings.setValue("offset", m_ui->offsetSpinBox->value()); settings.endGroup(); break; case SpectraType::Raman: windowName = tr("Raman Spectra"); xTitle = tr("Wavenumbers (cm⁻¹)"); yTitle = tr("Intensity"); // save the plot settings settings.beginGroup("spectra/raman"); settings.setValue("xmin", m_ui->xAxisMinimum->value()); settings.setValue("xmax", m_ui->xAxisMaximum->value()); settings.setValue("fwhm", m_ui->peakWidth->value()); settings.setValue("scale", m_ui->scaleSpinBox->value()); settings.setValue("offset", m_ui->offsetSpinBox->value()); settings.endGroup(); break; case SpectraType::NMR: windowName = tr("NMR Spectra"); xTitle = tr("Chemical Shift (ppm)"); yTitle = tr("Intensity"); // save the plot settings on a per element basis settings.beginGroup(QString("spectra/nmr/%1") .arg(m_ui->elementCombo->currentData().toInt())); settings.setValue("xmin", m_ui->xAxisMinimum->value()); settings.setValue("xmax", m_ui->xAxisMaximum->value()); settings.setValue("fwhm", m_ui->peakWidth->value()); settings.setValue("scale", m_ui->scaleSpinBox->value()); settings.setValue("offset", m_ui->offsetSpinBox->value()); settings.endGroup(); break; case SpectraType::Electronic: windowName = tr("Electronic Spectra"); xTitle = tr("Energy (eV)"); yTitle = tr("Intensity"); // save settings settings.beginGroup("spectra/electronic"); settings.setValue("xmin", m_ui->xAxisMinimum->value()); settings.setValue("xmax", m_ui->xAxisMaximum->value()); settings.setValue("fwhm", m_ui->peakWidth->value()); settings.setValue("scale", m_ui->scaleSpinBox->value()); settings.setValue("offset", m_ui->offsetSpinBox->value()); settings.endGroup(); break; case SpectraType::CircularDichroism: windowName = tr("Circular Dichroism Spectra"); xTitle = tr("Energy (eV)"); yTitle = tr("Intensity"); // save settings settings.beginGroup("spectra/cd"); settings.setValue("xmin", m_ui->xAxisMinimum->value()); settings.setValue("xmax", m_ui->xAxisMaximum->value()); settings.setValue("fwhm", m_ui->peakWidth->value()); settings.setValue("scale", m_ui->scaleSpinBox->value()); settings.setValue("offset", m_ui->offsetSpinBox->value()); settings.endGroup(); break; case SpectraType::DensityOfStates: windowName = tr("Density of States"); xTitle = tr("Energy (eV)"); yTitle = tr("Density"); // save settings settings.beginGroup("spectra/dos"); settings.setValue("xmin", m_ui->xAxisMinimum->value()); settings.setValue("xmax", m_ui->xAxisMaximum->value()); settings.setValue("fwhm", m_ui->peakWidth->value()); settings.setValue("scale", m_ui->scaleSpinBox->value()); settings.setValue("offset", m_ui->offsetSpinBox->value()); settings.endGroup(); break; } setWindowTitle(windowName); double maxIntensity = 0.0f; for (auto intensity : m_intensities) { if (intensity > maxIntensity) maxIntensity = intensity; } // if transmission for IR, set the max intensity to 100 if (type == SpectraType::Infrared) maxIntensity = 100.0; if (maxIntensity < 1.0) maxIntensity = 1.0; // now compose the plot data float scale = m_ui->scaleSpinBox->value(); float offset = m_ui->offsetSpinBox->value(); // NMR offsets should be inverted if (type == SpectraType::NMR) { scale = -scale; } float fwhm = m_ui->peakWidth->value(); float xMin = m_ui->xAxisMinimum->value(); float xMax = m_ui->xAxisMaximum->value(); float start = std::min(xMin, xMax); float end = std::max(xMin, xMax); // for some spectra, we need to take small steps, so we scale the x axis float xScale = 1.0; if (type == SpectraType::Electronic || type == SpectraType::CircularDichroism) xScale = 1.0f / 0.05f; else if (type == SpectraType::NMR) xScale = 1.0f / 0.01f; // TODO: process an experimental spectrum via interpolation for (unsigned int x = round(start * xScale); x < round(end * xScale); ++x) { float xValue = static_cast(x) / xScale; xData.push_back(xValue); yData.push_back(0.0f); yStick.push_back(0.0f); // now we add up the intensity from any frequency for (auto index = 0; index < m_transitions.size(); ++index) { float freq = m_transitions[index]; float peak = m_intensities[index]; float intensity = scaleAndBlur(xValue, freq, peak, scale, offset, fwhm); float stick = closestTo(xValue, freq, peak, scale, offset, xScale); yData.back() += intensity; yStick.back() += stick; } // if transmission, we need to invert the intensity if (transmission) { float trans = 1.0f - (yData.back() / (maxIntensity * 1.25)); yData.back() = trans * 100.0; // percent trans = 1.0f - (yStick.back() / maxIntensity); yStick.back() = trans * 100.0; // percent } } auto* chart = chartWidget(); chart->clearPlots(); chart->setXAxisTitle(xTitle.toStdString()); chart->setYAxisTitle(yTitle.toStdString()); unsigned int fontSize = m_ui->fontSizeCombo->currentText().toInt(); chart->setFontSize(fontSize); float lineWidth = m_ui->lineWidthSpinBox->value(); chart->setLineWidth(lineWidth); // get the spectra color QColor spectraColor = settings.value("spectra/calculatedColor", black).value(); VTK::color4ub calculatedColor = { static_cast(spectraColor.red()), static_cast(spectraColor.green()), static_cast(spectraColor.blue()), static_cast(spectraColor.alpha()) }; chart->addPlot(xData, yData, calculatedColor); VTK::color4ub importedColor = { 255, 0, 0, 255 }; chart->addSeries(yStick, importedColor); // axis limits float xAxisMin = m_ui->xAxisMinimum->value(); float xAxisMax = m_ui->xAxisMaximum->value(); float yAxisMin = m_ui->yAxisMinimum->value(); float yAxisMax = m_ui->yAxisMaximum->value(); chart->setXAxisLimits(xAxisMin, xAxisMax); chart->setYAxisLimits(yAxisMin, yAxisMax); // re-enable the options connectOptions(); } VTK::ChartWidget* SpectraDialog::chartWidget() { return m_ui->plot; } void SpectraDialog::toggleOptions() { if (m_ui->tab_widget->isHidden()) { m_ui->tab_widget->show(); // todo: show the data table // m_ui->dataTable->show(); // m_ui->push_exportData->show(); QSize s = size(); s.setWidth(s.width() + m_ui->dataTable->size().width()); s.setHeight(s.height() + m_ui->tab_widget->size().height()); QRect rect = QGuiApplication::primaryScreen()->geometry(); if (s.width() > rect.width() || s.height() > rect.height()) s = rect.size() * 0.9; resize(s); move(rect.width() / 2 - s.width() / 2, rect.height() / 2 - s.height() / 2); } else { QSize s = size(); s.setWidth(s.width() - m_ui->dataTable->size().width()); s.setHeight(s.height() - m_ui->tab_widget->size().height()); resize(s); m_ui->tab_widget->hide(); m_ui->dataTable->hide(); m_ui->push_exportData->hide(); QRect rect = QGuiApplication::primaryScreen()->geometry(); move(rect.width() / 2 - s.width() / 2, rect.height() / 2 - s.height() / 2); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/spectra/spectradialog.h000066400000000000000000000034171506155467400246330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SPECTRADIALOG_H #define AVOGADRO_QTPLUGINS_SPECTRADIALOG_H #include #include #include namespace Ui { class SpectraDialog; } namespace Avogadro { namespace VTK { class ChartWidget; } namespace QtPlugins { enum class SpectraType { Infrared, Raman, NMR, Electronic, CircularDichroism, DensityOfStates }; class SpectraDialog : public QDialog { Q_OBJECT public: explicit SpectraDialog(QWidget* parent = 0); ~SpectraDialog() override; void writeSettings() const; void readSettings(); void setSpectra(const std::map& spectra); void setElements(const std::vector& elements) { m_elements = elements; updateElementCombo(); } VTK::ChartWidget* chartWidget(); void disconnectOptions(); void connectOptions(); private slots: void changeBackgroundColor(); void changeForegroundColor(); void changeCalculatedSpectraColor(); void changeImportedSpectraColor(); void changeFontSize(); void changeLineWidth(); void changeSpectra(); void updateElementCombo(); void updatePlot(); void toggleOptions(); private: std::map m_spectra; std::vector m_elements; // for NMR // current spectra data std::vector m_transitions; std::vector m_intensities; QString m_currentSpectraType; Ui::SpectraDialog* m_ui; }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/spectra/spectradialog.ui000066400000000000000000000474351506155467400250310ustar00rootroot00000000000000 SpectraDialog 0 0 808 480 808 480 Spectra Visualization true 0 0 0 Spectra Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter -4000.000000000000000 4000.000000000000000 Scale Factor: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4000.000000000000000 X-Axis Minimum: X-Axis Maximum: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 3 Offset: Y-Axis Maximum: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4000.000000000000000 Y-Axis Minimum: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4000.000000000000000 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 0.100000000000000 1.000000000000000 Peak Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0.010000000000000 false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Peak Threshold: Units: false &Appearance false Show false 0 0 Imports a tsv of experimental spectra to overlay on the plot. &Import… false 0 0 Set Color… 0 0 Axis: Imported Spectra: true 0 0 Show true 0 0 Set Color… false 0 0 &Export… 0 0 Set Color… 0 0 Background: 0 0 Calculated Spectra: 0 0 Set Color… Font Size: true 10 12 14 16 18 Line Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 1.000000000000000 10.000000000000000 0.500000000000000 0 0 Export Data 0 0 &Close 0 0 &Options… 0 0 160 0 Qt::ScrollBarAlwaysOff QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true true false false true x y 0 0 500 210 800 600 CrossCursor Controls: Double left click: Restore default axis limits Right click + drag: Move plot Middle click + drag: Zoom to region Scroll wheel: Zoom to cursor false 0 0 &Load data… ¹H ¹³C Avogadro::VTK::ChartWidget QWidget
      avogadro/vtk/chartwidget.h
      1
      combo_spectra push_loadSpectra push_options scaleSpinBox offsetSpinBox peakWidth peakThreshold xAxisMinimum xAxisMaximum yAxisMinimum yAxisMaximum push_colorBackground push_colorForeground push_colorCalculated cb_calculate push_export push_colorImported cb_import push_import lineWidthSpinBox fontSizeCombo dataTable push_exportData pushButton tab_widget pushButton clicked() SpectraDialog accept() 525 277 199 149
      avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/000077500000000000000000000000001506155467400220065ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/CMakeLists.txt000066400000000000000000000015731506155467400245540ustar00rootroot00000000000000if(QT_VERSION EQUAL 6) find_package(Qt6 REQUIRED COMPONENTS OpenGL) endif() set(surfaces_srcs surfacedialog.cpp surfaces.cpp ) avogadro_plugin(Surfaces "Surfaces" ExtensionPlugin surfaces.h Surfaces "${surfaces_srcs}" "surfacedialog.ui" ) target_link_libraries(Surfaces PRIVATE Avogadro::Calc Avogadro::QuantumIO Avogadro::QtOpenGL Qt::Concurrent gif-h gwavi tinycolormap) set(orbitals_srcs orbitaltablemodel.cpp orbitalwidget.cpp orbitals.cpp ) avogadro_plugin(Orbitals "Render Molecular Orbitals" ExtensionPlugin orbitals.h Orbitals "${orbitals_srcs}" "orbitalwidget.ui" ) target_link_libraries(Orbitals PRIVATE Avogadro::QtOpenGL Qt::Concurrent) if(QT_VERSION EQUAL 6) target_link_libraries(Surfaces PRIVATE Qt6::OpenGL) target_link_libraries(Orbitals PRIVATE Qt6::OpenGL) endif() avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/orbitals.cpp000066400000000000000000000346721506155467400243450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "orbitals.h" #include "orbitalwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { const double cubePadding = 5.0; const int smoothingPasses = 1; Orbitals::Orbitals(QObject* p) : ExtensionPlugin(p), m_action(new QAction(this)), m_molecule(nullptr), m_dialog(nullptr) { m_action->setEnabled(false); m_action->setText(tr("Molecular Orbitals…")); connect(m_action, SIGNAL(triggered()), SLOT(openDialog())); } Orbitals::~Orbitals() { // cancel the current running calculation if (m_gaussianConcurrent) { qDebug() << "Cancelling current calculation"; auto* watcher = &m_gaussianConcurrent->watcher(); if (watcher != nullptr && watcher->isRunning()) watcher->cancel(); } if (m_dialog) m_dialog->deleteLater(); // molecule and basis are freed elsewhere } QList Orbitals::actions() const { return QList() << m_action; } QStringList Orbitals::menuPath(QAction*) const { QStringList path; path << tr("&Analyze"); return path; } void Orbitals::setMolecule(QtGui::Molecule* mol) { if (mol == nullptr) return; if (m_molecule != nullptr) m_molecule->disconnect(this); m_molecule = mol; // check if it has basis set data bool hasOrbitals = (m_molecule->basisSet() != nullptr); // sanity check if there are actually mo coefficients auto* basis = dynamic_cast(m_molecule->basisSet()); if (basis == nullptr || basis->moMatrix().size() == 0) hasOrbitals = false; if (hasOrbitals) m_action->setEnabled(true); else { m_action->setEnabled(false); if (m_dialog) m_dialog->hide(); } connect(m_molecule, SIGNAL(changed(unsigned int)), SLOT(moleculeChanged(unsigned int))); // Stuff we manage that will not be valid any longer m_queue.clear(); m_currentRunningCalculation = -1; m_currentMeshCalculation = -1; loadBasis(); if (!m_basis || m_basis->electronCount() == 0 || !hasOrbitals) return; // no electrons, no orbitals loadOrbitals(); precalculateOrbitals(); } void Orbitals::loadBasis() { if (m_molecule != nullptr) { m_basis = m_molecule->basisSet(); } } void Orbitals::loadOrbitals() { if (m_basis == nullptr || m_molecule == nullptr) return; auto* basis = dynamic_cast(m_molecule->basisSet()); if (basis == nullptr || basis->moMatrix().size() == 0) return; if (!m_dialog) { m_dialog = new OrbitalWidget(qobject_cast(parent()), Qt::Window); connect(m_dialog, SIGNAL(orbitalSelected(unsigned int)), this, SLOT(renderOrbital(unsigned int))); connect(m_dialog, SIGNAL(renderRequested(unsigned int, double)), this, SLOT(calculateOrbitalFromWidget(unsigned int, double))); connect(m_dialog, SIGNAL(calculateAll()), this, SLOT(precalculateOrbitals())); } m_dialog->fillTable(m_basis); m_dialog->show(); } void Orbitals::moleculeChanged([[maybe_unused]] unsigned int changes) { if (m_molecule == nullptr) return; bool isEnabled = m_action->isEnabled(); bool hasOrbitals = (m_molecule->basisSet() != nullptr); // sanity check if there are actually mo coefficients auto* basis = dynamic_cast(m_molecule->basisSet()); if (basis == nullptr || basis->moMatrix().size() == 0) hasOrbitals = false; if (isEnabled != hasOrbitals) { m_action->setEnabled(hasOrbitals); if (hasOrbitals) openDialog(); } } void Orbitals::openDialog() { if (!m_dialog) { m_dialog = new OrbitalWidget(qobject_cast(parent()), Qt::Window); connect(m_dialog, SIGNAL(orbitalSelected(unsigned int)), this, SLOT(renderOrbital(unsigned int))); connect(m_dialog, SIGNAL(renderRequested(unsigned int, double)), this, SLOT(calculateOrbitalFromWidget(unsigned int, double))); connect(m_dialog, SIGNAL(calculateAll()), this, SLOT(precalculateOrbitals())); } m_dialog->show(); m_dialog->raise(); } void Orbitals::calculateOrbitalFromWidget(unsigned int orbital, double resolution) { m_updateMesh = true; // check if the orbital is already in the queue bool found = false; for (int i = 0; i < m_queue.size(); i++) { if (m_queue[i].orbital == orbital && m_queue[i].resolution == resolution) { // change the priority to the highest m_queue[i].priority = 0; found = true; break; } } if (!found) { addCalculationToQueue(orbital, resolution, m_dialog->isovalue(), 0); } checkQueue(); } void Orbitals::precalculateOrbitals() { if (m_basis == nullptr) return; m_updateMesh = false; // Determine HOMO unsigned int homo = m_basis->homo(); // Initialize prioritizer at HOMO's index int priority = homo; // Loop through all MOs, submitting calculations with increasing // priority until HOMO is reached, submit both HOMO and LUMO at // priority=1, then start increasing for orbitals above LUMO. // E.g, // .... HOMO-2 HOMO-1 HOMO LUMO LUMO+1 LUMO+2 ... << orbitals // .... 3 2 1 1 2 3 ... << priorities // Determine range of precalculated orbitals unsigned int startIndex = (m_dialog->precalcLimit()) ? homo - (m_dialog->precalcRange() / 2) : 0; if (startIndex > homo) { // overflow check startIndex = 0; } unsigned int endIndex = (m_dialog->precalcLimit()) ? homo + (m_dialog->precalcRange() / 2) - 1 : m_basis->molecularOrbitalCount(); if (endIndex >= m_basis->molecularOrbitalCount()) { endIndex = m_basis->molecularOrbitalCount() - 1; } for (unsigned int i = startIndex; i <= endIndex; i++) { #ifndef NDEBUG qDebug() << " precalculate " << i << " priority " << priority; #endif addCalculationToQueue( i, // orbital OrbitalWidget::OrbitalQualityToDouble(m_dialog->defaultQuality()), m_dialog->isovalue(), priority); // Update priority. Stays the same when i = homo. if (i + 1 < homo) priority--; else if (i + 1 > homo) priority++; } checkQueue(); } void Orbitals::addCalculationToQueue(unsigned int orbital, double resolution, double isovalue, unsigned int priority) { // Create new queue entry calcInfo newCalc; newCalc.orbital = orbital; newCalc.resolution = resolution; newCalc.isovalue = isovalue; newCalc.priority = priority; newCalc.state = NotStarted; // Add new calculation m_queue.append(newCalc); // Set progress to show 0% m_dialog->calculationQueued(newCalc.orbital); } void Orbitals::checkQueue() { if (m_runningCube) return; m_runningCube = true; // Create a hash: keys=priority, values=indices QHash hash; CalcState state; for (int i = 0; i < m_queue.size(); i++) { state = m_queue.at(i).state; // If there is already a running cube, return. if (state == Running) { return; } if (state == NotStarted) { hash.insert(m_queue[i].priority, i); } } // Do nothing if all calcs are finished. if (hash.size() == 0) { m_runningCube = false; #ifndef NDEBUG qDebug() << "Finished queue."; #endif return; } QList priorities = hash.keys(); std::sort(priorities.begin(), priorities.end()); startCalculation(hash.value(priorities.first())); } void Orbitals::startCalculation(unsigned int queueIndex) { // This will launch calculateMesh when finished. m_currentRunningCalculation = queueIndex; calcInfo* info = &m_queue[m_currentRunningCalculation]; #ifndef NDEBUG qDebug() << info->orbital << " startCalculation() called"; #endif switch (info->state) { case NotStarted: // Start calculation calculateCube(); break; case Running: // Nothing below should happen... qWarning() << "startCalculation called on a running calc..."; break; case Completed: qWarning() << "startCalculation called on a completed calc..."; break; case Canceled: qWarning() << "startCalculation called on a canceled calc..."; break; } } void Orbitals::calculateCube() { if (m_currentRunningCalculation == -1) return; calcInfo* info = &m_queue[m_currentRunningCalculation]; info->state = Running; // Check if the cube we want already exists for (int i = 0; i < m_queue.size(); i++) { calcInfo* cI = &m_queue[i]; if (cI->state == Completed && cI->orbital == info->orbital && cI->resolution == info->resolution) { info->cube = cI->cube; #ifndef NDEBUG qDebug() << "Reusing cube from calculation " << i << ":\n" << "\tOrbital " << cI->orbital << "\n" << "\tResolution " << cI->resolution; #endif m_currentMeshCalculation = m_currentRunningCalculation; calculatePosMesh(); calculationComplete(); return; } } // Create new cube Core::Cube* cube = m_molecule->addCube(); info->cube = cube; cube->setLimits(*m_molecule, info->resolution, cubePadding); cube->setName("Molecular Orbital " + std::to_string(info->orbital + 1)); cube->setCubeType(Core::Cube::Type::MO); if (!m_gaussianConcurrent) { m_gaussianConcurrent = new QtGui::GaussianSetConcurrent(this); } m_gaussianConcurrent->setMolecule(m_molecule); auto* watcher = &m_gaussianConcurrent->watcher(); connect(watcher, SIGNAL(finished()), this, SLOT(calculateCubeDone())); m_dialog->initializeProgress(info->orbital, watcher->progressMinimum(), watcher->progressMaximum(), 1, 3); connect(watcher, SIGNAL(progressValueChanged(int)), this, SLOT(updateProgress(int))); #ifndef NDEBUG qDebug() << info->orbital << " Cube calculation started."; #endif // TODO: add alpha / beta m_gaussianConcurrent->calculateMolecularOrbital(cube, info->orbital); } void Orbitals::calculateCubeDone() { auto* watcher = &m_gaussianConcurrent->watcher(); watcher->disconnect(this); if (m_updateMesh) { if (m_currentMeshCalculation == -1) { m_currentMeshCalculation = m_currentRunningCalculation; calculatePosMesh(); } else { m_nextMeshCalculation = m_currentRunningCalculation; } } calculationComplete(); } void Orbitals::calculatePosMesh() { if (m_currentMeshCalculation == -1) return; calcInfo* info = &m_queue[m_currentMeshCalculation]; m_molecule->clearMeshes(); auto posMesh = m_molecule->addMesh(); auto cube = info->cube; if (!m_meshGenerator) { m_meshGenerator = new QtGui::MeshGenerator; } connect(m_meshGenerator, SIGNAL(finished()), SLOT(calculatePosMeshDone())); m_meshGenerator->initialize(cube, posMesh, m_isoValue, smoothingPasses); m_meshGenerator->start(); } void Orbitals::calculatePosMeshDone() { disconnect(m_meshGenerator, 0, this, 0); calculateNegMesh(); } void Orbitals::calculateNegMesh() { if (m_currentMeshCalculation == -1) return; calcInfo* info = &m_queue[m_currentMeshCalculation]; auto negMesh = m_molecule->addMesh(); auto cube = info->cube; if (!m_meshGenerator) { // shouldn't happen, but better to be careful m_meshGenerator = new QtGui::MeshGenerator; } connect(m_meshGenerator, SIGNAL(finished()), SLOT(calculateNegMeshDone())); // true indicates that we want to reverse the surface m_meshGenerator->initialize(cube, negMesh, -m_isoValue, smoothingPasses, true); m_meshGenerator->start(); } void Orbitals::calculateNegMeshDone() { disconnect(m_meshGenerator, 0, this, 0); meshComplete(); // ask for a repaint m_molecule->emitChanged(QtGui::Molecule::Added); } void Orbitals::meshComplete() { if (m_currentMeshCalculation == -1) return; if (m_nextMeshCalculation != -1) { // start the next mesh calculation m_currentMeshCalculation = m_nextMeshCalculation; m_nextMeshCalculation = -1; calculatePosMesh(); return; } else m_currentMeshCalculation = -1; } void Orbitals::calculationComplete() { if (m_currentRunningCalculation == -1) return; calcInfo* info = &m_queue[m_currentRunningCalculation]; m_dialog->calculationComplete(info->orbital); info->state = Completed; m_currentRunningCalculation = -1; m_runningCube = false; #ifndef NDEBUG qDebug() << info->orbital << " all calculations complete."; #endif checkQueue(); } void Orbitals::renderOrbital(unsigned int row) { if (row == 0) return; // table rows are indexed from 1 // orbitals are indexed from 0 unsigned int orbital = row - 1; #ifndef NDEBUG qDebug() << "Rendering orbital " << orbital; #endif // Find the most recent calc matching the selected orbital: calcInfo calc; int index = -1; // in the event of ties, pick the best resolution double resolution = OrbitalWidget::OrbitalQualityToDouble(0); for (int i = 0; i < m_queue.size(); i++) { calc = m_queue[i]; if (calc.orbital == orbital && calc.state == Completed) { if (calc.resolution <= resolution) { resolution = calc.resolution; index = i; } } } // calculate the meshes if (index == -1) { // need to calculate the cube first calculateOrbitalFromWidget(orbital, OrbitalWidget::OrbitalQualityToDouble( m_dialog->defaultQuality())); } else { // just need to update the meshes if (m_currentMeshCalculation == -1) { m_currentMeshCalculation = index; calculatePosMesh(); // will eventually call negMesh too } else { // queue it up m_nextMeshCalculation = index; } } // add the orbital to the renderer QStringList displayTypes; displayTypes << tr("Meshes"); requestActiveDisplayTypes(displayTypes); } void Orbitals::updateProgress(int current) { if (m_currentRunningCalculation == -1) return; calcInfo* info = &m_queue[m_currentRunningCalculation]; int orbital = info->orbital; m_dialog->updateProgress(orbital, current); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/orbitals.h000066400000000000000000000102251506155467400237760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_ORBITALS_H #define AVOGADRO_QTPLUGINS_ORBITALS_H #include #include class QAction; class QDialog; class QProgressDialog; namespace Avogadro { namespace QtGui { class MeshGenerator; class GaussianSetConcurrent; class SlaterSetConcurrent; } // namespace QtGui namespace Core { class BasisSet; class Cube; class Mesh; } // namespace Core namespace QtPlugins { /** * @brief The Orbital plugin shows a window with a list of orbitals and * .. renders selected orbitals */ class OrbitalSettingsDialog; class OrbitalWidget; class Orbitals : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Orbitals(QObject* parent = nullptr); ~Orbitals() override; QString name() const override { return tr("Orbital Window"); } QString description() const override { return tr("Display orbital lists."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; enum CalcState { NotStarted = 0, Running, Completed, Canceled }; struct calcInfo { Core::Mesh* posMesh; Core::Mesh* negMesh; Core::Cube* cube; unsigned int orbital; double resolution; double isovalue; unsigned int priority; CalcState state; }; public slots: void moleculeChanged(unsigned int changes); void openDialog(); private slots: void loadBasis(); void loadOrbitals(); /** * Re-render an orbital at a higher resolution * * @param orbital The orbital to render * @param resolution The resolution of the cube */ void calculateOrbitalFromWidget(unsigned int orbital, double resolution); /** * Calculate all molecular orbitals at low priority and moderate * resolution. */ void precalculateOrbitals(); /** * Add an orbital calculation to the queue. Lower priority values * run first. Do not set automatic calculations to priority zero, * this is reserved for user requested calculations and will run * first, displaying a progress dialog. * * @param orbital Orbital number * @param resolution Resolution of grid * @param isoval Isovalue for surface * @param priority Priority. Default = 0 (user requested) */ void addCalculationToQueue(unsigned int orbital, double resolution, double isoval, unsigned int priority = 0); /** * Check that no calculations are currently running and start the * highest priority calculation. */ void checkQueue(); /** * Start or resume the calculation at the indicated index of the * queue. */ void startCalculation(unsigned int queueIndex); void calculateCube(); void calculateCubeDone(); void calculatePosMesh(); void calculatePosMeshDone(); void calculateNegMesh(); void calculateNegMeshDone(); void calculationComplete(); void meshComplete(); /** * Draw the indicated orbital on the GLWidget */ void renderOrbital(unsigned int orbital); /** * Update the progress of the current calculation */ void updateProgress(int current); private: QAction* m_action; QtGui::Molecule* m_molecule = nullptr; Core::BasisSet* m_basis = nullptr; QList m_queue; int m_currentRunningCalculation = -1; bool m_runningCube = false; int m_currentMeshCalculation = -1; int m_nextMeshCalculation = -1; QtGui::GaussianSetConcurrent* m_gaussianConcurrent = nullptr; QtGui::SlaterSetConcurrent* m_slaterConcurrent = nullptr; QFutureWatcher m_displayMeshWatcher; QtGui::MeshGenerator* m_meshGenerator = nullptr; float m_isoValue = 0.01; int m_smoothingPasses = 1; int m_meshesLeft = 0; bool m_updateMesh = false; OrbitalWidget* m_dialog = nullptr; // OrbitalSettingsDialog* m_orbitalSettingsDialog = nullptr; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_ORBITALS_H avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/orbitaltablemodel.cpp000066400000000000000000000207011506155467400261770ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "orbitaltablemodel.h" #include "orbitalwidget.h" #include #include #include namespace Avogadro::QtPlugins { OrbitalTableModel::OrbitalTableModel(QWidget* parent) : QAbstractTableModel(parent) { m_orbitals.clear(); } OrbitalTableModel::~OrbitalTableModel() {} int OrbitalTableModel::columnCount(const QModelIndex&) const { return COUNT; } QVariant OrbitalTableModel::data(const QModelIndex& index, int role) const { if ((role != Qt::DisplayRole && role != Qt::TextAlignmentRole) || !index.isValid()) return QVariant(); // Simple lambda to convert QFlags to variant as in Qt 6 this needs help. auto toVariant = [&](auto flags) { return static_cast(flags); }; if (role == Qt::TextAlignmentRole) { switch (Column(index.column())) { case C_Energy: return toVariant(Qt::AlignRight | Qt::AlignVCenter); // numeric alignment case C_Status: // everything else can be centered case C_Description: case C_Symmetry: default: return toVariant(Qt::AlignHCenter | Qt::AlignVCenter); } } const Orbital* orb = m_orbitals.at(index.row()); QString symbol; // use subscripts int subscriptStart; switch (Column(index.column())) { case C_Description: return orb->description; case C_Energy: return QString("%L1").arg(orb->energy, 0, 'f', 3); case C_Status: { // Check for divide by zero int percent; if (orb->max == orb->min) percent = 0; else { percent = 100 * (orb->current - orb->min) / float(orb->max - orb->min); // Adjust for stages int stages = (orb->totalStages == 0) ? 1 : orb->totalStages; percent /= float(stages); percent += (orb->stage - 1) * (100.0 / float(stages)); // clamp to 100% if (percent > 100) percent = 100; } return QString("%L1%").arg(percent); } case C_Symmetry: symbol = orb->symmetry; if (symbol.length() > 1) { subscriptStart = 1; if (symbol[0] == '?') subscriptStart++; symbol.insert(subscriptStart, QString("")); symbol.append(QString("")); } symbol.replace('\'', QString("'")); symbol.replace('"', QString("\"")); return symbol; default: case COUNT: return QVariant(); } } QVariant OrbitalTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (Column(section)) { case C_Description: return tr("Orbital"); case C_Energy: return tr("Energy (eV)"); case C_Symmetry: return tr("Symmetry"); case C_Status: return tr("Status"); default: case COUNT: return QVariant(); } } else return QString::number(section + 1); } QModelIndex OrbitalTableModel::HOMO() const { for (int i = 0; i < m_orbitals.size(); i++) { if (m_orbitals.at(i)->description == tr("HOMO", "Highest Occupied MO")) return index(i, 0); } return QModelIndex(); } QModelIndex OrbitalTableModel::LUMO() const { for (int i = 0; i < m_orbitals.size(); i++) { if (m_orbitals.at(i)->description == tr("LUMO", "Lowest Unoccupied MO")) return index(i, 0); } return QModelIndex(); } // predicate for sorting below bool orbitalIndexLessThan(const Orbital* o1, const Orbital* o2) { return (o1->index < o2->index); } bool OrbitalTableModel::setOrbitals(const Core::BasisSet* basis) { clearOrbitals(); // assemble the orbital information // TODO: Alpha / Beta orbitals unsigned int homo = basis->homo(); unsigned int lumo = basis->lumo(); unsigned int count = homo - 1; bool leqHOMO = true; // orbital <= homo // energies and symmetries // TODO: handle both alpha and beta (separate columns?) // TODO: move moEnergies to the BasisSet class QList alphaEnergies; auto* gaussianBasis = dynamic_cast(basis); if (gaussianBasis != nullptr) { auto moEnergies = gaussianBasis->moEnergy(); alphaEnergies.reserve(moEnergies.size()); for (double energy : moEnergies) { alphaEnergies.push_back(energy); } } // not sure if any import supports symmetry labels yet const auto labels = basis->symmetryLabels(); QStringList alphaSymmetries; alphaSymmetries.reserve(labels.size()); for (const std::string& label : labels) { alphaSymmetries.push_back(QString::fromStdString(label)); } for (unsigned int i = 0; i < basis->molecularOrbitalCount(); i++) { QString num = ""; if (i + 1 != homo && i + 1 != lumo) { num = (leqHOMO) ? "-" : "+"; num += QString::number(count); } QString desc = QString("%1") // (HOMO|LUMO)(+|-)[0-9]+ .arg((leqHOMO) ? tr("HOMO", "Highest Occupied MO") + num : tr("LUMO", "Lowest Unoccupied MO") + num); Orbital* orb = new Orbital; // Get the energy from the molecule property list, if available if (static_cast(alphaEnergies.size()) > i) orb->energy = alphaEnergies[i].toDouble(); else orb->energy = 0.0; // symmetries (if available) if (static_cast(alphaSymmetries.size()) > i) orb->symmetry = alphaSymmetries[i]; orb->index = i; orb->description = desc; orb->queueEntry = 0; orb->min = 0; orb->max = 0; orb->current = 0; m_orbitals.append(orb); if (i + 1 < homo) count--; else if (i + 1 == homo) leqHOMO = false; else if (i + 1 >= lumo) count++; } // sort the orbital list (not sure if this is necessary) std::sort(m_orbitals.begin(), m_orbitals.end(), orbitalIndexLessThan); // add the rows for all the new orbitals beginInsertRows(QModelIndex(), 0, m_orbitals.size() - 1); endInsertRows(); return true; } bool OrbitalTableModel::clearOrbitals() { if (m_orbitals.size() > 0) { beginRemoveRows(QModelIndex(), 0, m_orbitals.size() - 1); m_orbitals.clear(); endRemoveRows(); } return true; } void OrbitalTableModel::setOrbitalProgressRange(int orbital, int min, int max, int stage, int totalStages) { Orbital* orb = m_orbitals[orbital]; orb->min = min; orb->current = min; orb->max = max; orb->stage = stage; orb->totalStages = totalStages; // Update display QModelIndex status = index(orbital, int(C_Status), QModelIndex()); emit dataChanged(status, status); } void OrbitalTableModel::incrementStage(int orbital, int newmin, int newmax) { Orbital* orb = m_orbitals[orbital]; orb->stage++; orb->min = newmin; orb->current = newmin; orb->max = newmax; // Update display QModelIndex status = index(orbital, C_Status, QModelIndex()); emit dataChanged(status, status); } void OrbitalTableModel::setOrbitalProgressValue(int orbital, int currentValue) { Orbital* orb = m_orbitals[orbital]; orb->current = currentValue; // Update display QModelIndex status = index(orbital, C_Status, QModelIndex()); emit dataChanged(status, status); } void OrbitalTableModel::finishProgress(int orbital) { Orbital* orb = m_orbitals[orbital]; orb->stage = 1; orb->totalStages = 1; orb->min = 0; orb->current = 1; orb->max = 1; // Update display QModelIndex status = index(orbital, C_Status, QModelIndex()); emit dataChanged(status, status); } void OrbitalTableModel::resetProgress(int orbital) { Orbital* orb = m_orbitals[orbital]; orb->stage = 1; orb->totalStages = 1; orb->min = 0; orb->current = 0; orb->max = 0; // Update display QModelIndex status = index(orbital, C_Status, QModelIndex()); emit dataChanged(status, status); } void OrbitalTableModel::setProgressToZero(int orbital) { Orbital* orb = m_orbitals[orbital]; orb->stage = 1; orb->totalStages = 1; orb->min = 0; orb->current = 0; orb->max = 1; // Update display QModelIndex status = index(orbital, C_Status, QModelIndex()); emit dataChanged(status, status); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/orbitaltablemodel.h000066400000000000000000000074061506155467400256530ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_ORBITALTABLEMODEL_H #define AVOGADRO_QTPLUGINS_ORBITALTABLEMODEL_H #include #include #include #include namespace Avogadro::Core { class BasisSet; } namespace Avogadro::QtPlugins { struct calcInfo; struct Orbital { double energy; int index; QString description; // (HOMO|LUMO)[(+|-)N] QString symmetry; // e.g., A1g (with subscripts) calcInfo* queueEntry; // Progress data: int min; int max; int current; int stage; int totalStages; }; // Allow progress bars to be embedded in the table class ProgressBarDelegate : public QStyledItemDelegate { Q_OBJECT public: ProgressBarDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) {}; QSize sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const override { return QSize(60, 30); }; void paint(QPainter* p, const QStyleOptionViewItem& o, const QModelIndex& ind) const override { QStyleOptionProgressBar opt; // Call initFrom() which will set the style based on the parent // GRH: This is critical to get things right on Mac // otherwise the status bars always look disabled opt.initFrom(qobject_cast(this->parent())); opt.rect = o.rect; opt.minimum = 1; // percentage opt.maximum = 100; opt.textVisible = true; int percent = ind.model()->data(ind, Qt::DisplayRole).toInt(); opt.progress = percent; opt.text = QString("%1%").arg(QString::number(percent)); QApplication::style()->drawControl(QStyle::CE_ProgressBar, &opt, p); } }; // Used for sorting: class OrbitalSortingProxyModel : public QSortFilterProxyModel { Q_OBJECT public: OrbitalSortingProxyModel(QObject* parent = 0) : QSortFilterProxyModel(parent), m_HOMOFirst(false) {}; bool isHOMOFirst() { return m_HOMOFirst; }; void HOMOFirst(bool b) { m_HOMOFirst = b; }; protected: // Compare orbital values bool lessThan(const QModelIndex& left, const QModelIndex& right) const override { if (m_HOMOFirst) return left.row() < right.row(); else return left.row() > right.row(); } private: bool m_HOMOFirst; }; class OrbitalTableModel : public QAbstractTableModel { Q_OBJECT public: enum Column { C_Description = 0, C_Energy, C_Symmetry, C_Status, // also occupation (0/1/2) COUNT }; //! Constructor explicit OrbitalTableModel(QWidget* parent = 0); //! Deconstructor ~OrbitalTableModel() override; int rowCount(const QModelIndex&) const override { return m_orbitals.size(); }; int columnCount(const QModelIndex&) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QModelIndex HOMO() const; QModelIndex LUMO() const; bool setOrbitals(const Core::BasisSet* basis); bool clearOrbitals(); // Stages are used for multi-step processes, e.g. cube, posmesh, negmesh, etc void setOrbitalProgressRange(int orbital, int min, int max, int stage, int totalStages); void incrementStage(int orbital, int newmin, int newmax); void setOrbitalProgressValue(int orbital, int currentValue); void finishProgress(int orbital); void resetProgress(int orbital); void setProgressToZero(int orbital); private: QList m_orbitals; }; } // namespace Avogadro::QtPlugins #endif avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/orbitalwidget.cpp000066400000000000000000000155461506155467400253650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "orbitalwidget.h" #include "orbitaltablemodel.h" #include #include #include using Avogadro::QtGui::RichTextDelegate; namespace Avogadro::QtPlugins { OrbitalWidget::OrbitalWidget(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f), m_settings(0), m_quality(OQ_Low), m_isovalue(0.03), m_precalc_limit(true), m_precalc_range(10), m_tableModel(new OrbitalTableModel(this)), m_sortedTableModel(new OrbitalSortingProxyModel(this)) { ui.setupUi(this); setWindowTitle(tr("Molecular Orbitals")); m_sortedTableModel->setSourceModel(m_tableModel); ui.table->setModel(m_sortedTableModel); ui.table->horizontalHeader()->setSectionResizeMode( QHeaderView::ResizeToContents); ui.table->horizontalHeader()->setStretchLastSection(true); // ui.table->setItemDelegateForColumn(OrbitalTableModel::C_Status, // new ProgressBarDelegate(this)); ui.table->setItemDelegateForColumn(OrbitalTableModel::C_Symmetry, new RichTextDelegate(this)); // TODO: Support orbital symmetry labels ui.table->hideColumn(OrbitalTableModel::C_Symmetry); ui.table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); connect( ui.table->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(tableClicked(const QItemSelection&))); connect(ui.push_render, SIGNAL(clicked()), this, SLOT(renderClicked())); // TODO: Implement configure dialog ui.push_configure->setVisible(false); connect(ui.push_configure, SIGNAL(clicked()), this, SLOT(configureClicked())); readSettings(); } OrbitalWidget::~OrbitalWidget() { writeSettings(); } void OrbitalWidget::readSettings() { QSettings settings; settings.beginGroup("orbitals"); m_quality = OrbitalQuality(settings.value("defaultQuality", 1).toInt()); m_isovalue = settings.value("isoValue", 0.03).toDouble(); ui.combo_quality->setCurrentIndex( settings.value("selectedQuality", 1).toInt()); m_sortedTableModel->HOMOFirst(settings.value("HOMOFirst", false).toBool()); m_precalc_limit = settings.value("precalc/limit", true).toBool(); m_precalc_range = settings.value("precalc/range", 10).toInt(); settings.endGroup(); } void OrbitalWidget::writeSettings() { QSettings settings; settings.beginGroup("orbitals"); settings.setValue("defaultQuality", m_quality); settings.setValue("isoValue", m_isovalue); settings.setValue("selectedQuality", ui.combo_quality->currentIndex()); settings.setValue("HOMOFirst", m_sortedTableModel->isHOMOFirst()); settings.setValue("precalc/limit", m_precalc_limit); settings.setValue("precalc/range", m_precalc_range); settings.endGroup(); } void OrbitalWidget::reject() { hide(); } void OrbitalWidget::configureClicked() { /* if (!m_settings) { m_settings = new OrbitalSettingsDialog(this); } m_settings->setDefaultQuality(m_quality); m_settings->setIsoValue(m_isovalue); m_settings->setHOMOFirst(m_sortedTableModel->isHOMOFirst()); m_settings->setLimitPrecalc(m_precalc_limit); m_settings->setPrecalcRange(m_precalc_range); m_settings->show(); */ } void OrbitalWidget::fillTable(Core::BasisSet* basis) { if (basis == nullptr || m_tableModel == nullptr) { return; } // Populate the model m_tableModel->setOrbitals(basis); ui.table->horizontalHeader()->sectionResizeMode( QHeaderView::ResizeToContents); // Sort table m_sortedTableModel->sort(0, Qt::AscendingOrder); // // Find HOMO and scroll to it QModelIndex homo = m_tableModel->HOMO(); homo = m_sortedTableModel->mapFromSource(homo); // qDebug() << "HOMO at: " << homo.row(); ui.table->scrollTo(homo, QAbstractItemView::PositionAtCenter); } void OrbitalWidget::setQuality(OrbitalQuality q) { ui.combo_quality->setCurrentIndex(int(q)); } void OrbitalWidget::selectOrbital(unsigned int orbital) { QModelIndex start = m_tableModel->index(orbital - 1, 0, QModelIndex()); QModelIndex end = m_tableModel->index( orbital - 1, m_tableModel->columnCount(QModelIndex()) - 1, QModelIndex()); QItemSelection selection(start, end); selection = m_sortedTableModel->mapSelectionFromSource(selection); ui.table->selectionModel()->clear(); ui.table->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent); } void OrbitalWidget::tableClicked(const QItemSelection& selected) { QItemSelection mappedSelected = m_sortedTableModel->mapSelectionToSource(selected); QModelIndexList selection = mappedSelected.indexes(); // Only one row can be selected at a time, so just check the row // of the first entry. if (selection.size() == 0) return; int orbital = selection.first().row() + 1; emit orbitalSelected(orbital); } void OrbitalWidget::renderClicked() { double quality = OrbitalQualityToDouble(ui.combo_quality->currentIndex()); QModelIndexList selection = ui.table->selectionModel()->selectedIndexes(); // Only one row can be selected at a time, so just check the row // of the first entry. if (selection.size() == 0) return; QModelIndex first = selection.first(); first = m_sortedTableModel->mapToSource(first); int orbital = first.row(); // renderRequested handles the +1 emit renderRequested(orbital, quality); } double OrbitalWidget::OrbitalQualityToDouble(OrbitalQuality q) { switch (q) { case OQ_VeryLow: return 0.5; case OQ_Low: return 0.35; case OQ_Medium: default: return 0.18; case OQ_High: return 0.10; case OQ_VeryHigh: return 0.05; } } void OrbitalWidget::setDefaults(OrbitalQuality q, double i, bool HOMOFirst) { m_quality = q; m_isovalue = i; m_sortedTableModel->HOMOFirst(HOMOFirst); m_sortedTableModel->sort(0, Qt::AscendingOrder); } void OrbitalWidget::setPrecalcSettings(bool limit, int range) { m_precalc_limit = limit; m_precalc_range = range; } void OrbitalWidget::initializeProgress(int orbital, int min, int max, int stage, int totalStages) { m_tableModel->setOrbitalProgressRange(orbital, min, max, stage, totalStages); } void OrbitalWidget::nextProgressStage(int orbital, int newmin, int newmax) { m_tableModel->incrementStage(orbital, newmin, newmax); } void OrbitalWidget::updateProgress(int orbital, int current) { m_tableModel->setOrbitalProgressValue(orbital, current); } void OrbitalWidget::calculationComplete(int orbital) { m_tableModel->finishProgress(orbital); } void OrbitalWidget::calculationQueued(int orbital) { m_tableModel->setProgressToZero(orbital); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/orbitalwidget.h000066400000000000000000000047431506155467400250270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_ORBITALWIDGET_H #define AVOGADRO_QTPLUGINS_ORBITALWIDGET_H #include #include "orbitaltablemodel.h" #include "ui_orbitalwidget.h" namespace Avogadro::Core { class BasisSet; } namespace Avogadro::QtPlugins { class OrbitalSettingsDialog; class OrbitalTableModel; class OrbitalWidget : public QWidget { Q_OBJECT public: enum OrbitalQuality { OQ_VeryLow = 0, OQ_Low, OQ_Medium, OQ_High, OQ_VeryHigh }; //! Constructor explicit OrbitalWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::Widget); //! Deconstructor ~OrbitalWidget() override; double isovalue() { return m_isovalue; }; OrbitalQuality defaultQuality() { return m_quality; }; bool precalcLimit() { return m_precalc_limit; } int precalcRange() { return m_precalc_range; } static double OrbitalQualityToDouble(OrbitalQuality q); static double OrbitalQualityToDouble(int i) { return OrbitalQualityToDouble(OrbitalQuality(i)); }; public slots: void readSettings(); void writeSettings(); void reject(); void fillTable(Core::BasisSet* basis); void setQuality(OrbitalQuality q); void selectOrbital(unsigned int orbital); void setDefaults(OrbitalWidget::OrbitalQuality quality, double isovalue, bool HOMOFirst); void setPrecalcSettings(bool limit, int range); void initializeProgress(int orbital, int min, int max, int stage, int totalStages); void nextProgressStage(int orbital, int newmin, int newmax); void updateProgress(int orbital, int current); void calculationComplete(int orbital); void calculationQueued(int orbital); signals: void orbitalSelected(unsigned int orbital); void renderRequested(unsigned int orbital, double resolution); void calculateAll(); private slots: void tableClicked(const QItemSelection&); void renderClicked(); void configureClicked(); private: Ui::OrbitalWidget ui; OrbitalSettingsDialog* m_settings; OrbitalQuality m_quality; double m_isovalue; bool m_precalc_limit; int m_precalc_range; OrbitalTableModel* m_tableModel; OrbitalSortingProxyModel* m_sortedTableModel; }; } // namespace Avogadro::QtPlugins #endif avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/orbitalwidget.ui000066400000000000000000000070661506155467400252160ustar00rootroot00000000000000 OrbitalWidget 0 0 366 696 0 0 Form 0 0 Qt::ScrollBarAlwaysOff QAbstractItemView::SingleSelection QAbstractItemView::SelectRows QAbstractItemView::ScrollPerItem true true true 0 0 Quality: 0 Very Low Low Medium High Very High Render Qt::Horizontal 40 20 Configure avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/surfacedialog.cpp000066400000000000000000000233621506155467400253300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "surfacedialog.h" #include "ui_surfacedialog.h" namespace Avogadro::QtPlugins { SurfaceDialog::SurfaceDialog(QWidget* parent_, Qt::WindowFlags f) : QDialog(parent_, f), m_ui(new Ui::SurfaceDialog), m_automaticResolution(true) { m_ui->setupUi(this); setupSteps(1); m_ui->orbitalCombo->setVisible(false); m_ui->spinCombo->setVisible(false); m_ui->recordButton->setVisible(false); // set the data for the default items m_ui->surfaceCombo->addItem(tr("Van der Waals"), Surfaces::Type::VanDerWaals); m_ui->surfaceCombo->addItem(tr("Solvent Accessible"), Surfaces::Type::SolventAccessible); m_ui->surfaceCombo->addItem(tr("Solvent Excluded"), Surfaces::Type::SolventExcluded); connect(m_ui->surfaceCombo, SIGNAL(currentIndexChanged(int)), SLOT(surfaceComboChanged(int))); connect(m_ui->propertyCombo, SIGNAL(currentIndexChanged(int)), SLOT(propertyComboChanged(int))); connect(m_ui->resolutionCombo, SIGNAL(currentIndexChanged(int)), SLOT(resolutionComboChanged(int))); connect(m_ui->smoothingCombo, SIGNAL(currentIndexChanged(int)), SLOT(smoothingComboChanged(int))); connect(m_ui->stepValue, SIGNAL(valueChanged(int)), SIGNAL(stepChanged(int))); connect(m_ui->calculateButton, SIGNAL(clicked()), SLOT(calculateClicked())); connect(m_ui->recordButton, SIGNAL(clicked()), SLOT(record())); } SurfaceDialog::~SurfaceDialog() { delete m_ui; } void SurfaceDialog::surfaceComboChanged(int n) { int type = m_ui->surfaceCombo->itemData(n).toInt(); if (type == Surfaces::Type::MolecularOrbital || type == Surfaces::Type::FromFile) { m_ui->orbitalCombo->setEnabled(true); m_ui->propertyCombo->setEnabled(false); m_ui->propertyCombo->setCurrentIndex(0); // None m_ui->colormapCombo->setEnabled(false); } else { m_ui->orbitalCombo->setEnabled(false); m_ui->propertyCombo->setEnabled(true); } } void SurfaceDialog::propertyComboChanged(int n) { switch (n) { case 0: // None m_ui->colormapCombo->setEnabled(false); m_ui->modelCombo->setEnabled(false); m_ui->modelCombo->clear(); break; case 1: // Electrostatic Potential m_ui->colormapCombo->setEnabled(true); m_ui->modelCombo->setEnabled(true); for (const auto& model : m_chargeModels) m_ui->modelCombo->addItem(model.first.c_str(), model.second.c_str()); } } void SurfaceDialog::resolutionComboChanged(int n) { m_automaticResolution = false; // resolutions are in Angstrom switch (n) { case 0: // Very low resolution m_ui->resolutionDoubleSpinBox->setValue(0.5); m_ui->resolutionDoubleSpinBox->setEnabled(false); break; case 1: // Low resolution m_ui->resolutionDoubleSpinBox->setValue(0.35); m_ui->resolutionDoubleSpinBox->setEnabled(false); break; case 2: // Medium resolution m_ui->resolutionDoubleSpinBox->setValue(0.18); m_ui->resolutionDoubleSpinBox->setEnabled(false); break; case 3: // High resolution m_ui->resolutionDoubleSpinBox->setValue(0.1); m_ui->resolutionDoubleSpinBox->setEnabled(false); break; case 4: // Very high resolution m_ui->resolutionDoubleSpinBox->setValue(0.05); m_ui->resolutionDoubleSpinBox->setEnabled(false); break; case 5: // Automatic resolution m_automaticResolution = true; m_ui->resolutionDoubleSpinBox->setEnabled(false); break; case 6: // Custom resolution m_ui->resolutionDoubleSpinBox->setValue(0.18); m_ui->resolutionDoubleSpinBox->setEnabled(true); break; default: m_ui->resolutionDoubleSpinBox->setValue(0.18); m_ui->resolutionDoubleSpinBox->setEnabled(false); break; } } void SurfaceDialog::smoothingComboChanged(int n) { switch (n) { case 0: // No smoothing m_ui->smoothingPassesSpinBox->setValue(0); m_ui->smoothingPassesSpinBox->setEnabled(false); break; case 2: // Medium smoothing m_ui->smoothingPassesSpinBox->setValue(5); m_ui->smoothingPassesSpinBox->setEnabled(false); break; case 3: // Strong smoothing m_ui->smoothingPassesSpinBox->setValue(9); m_ui->smoothingPassesSpinBox->setEnabled(false); break; case 4: // Custom smoothing m_ui->smoothingPassesSpinBox->setValue(5); m_ui->smoothingPassesSpinBox->setEnabled(true); break; case 1: // Light smoothing default: m_ui->smoothingPassesSpinBox->setValue(1); m_ui->smoothingPassesSpinBox->setEnabled(false); break; } } void SurfaceDialog::setupBasis(int numElectrons, int numMOs, bool beta) { // only if we have electrons if (numMOs < 1) return; m_ui->orbitalCombo->setVisible(true); auto n = m_ui->surfaceCombo->currentIndex(); int type = m_ui->surfaceCombo->itemData(n).toInt(); if (type == Surfaces::Type::MolecularOrbital || type == Surfaces::Type::FromFile) m_ui->orbitalCombo->setEnabled(true); else m_ui->orbitalCombo->setEnabled(false); if (m_ui->surfaceCombo->findData(Surfaces::Type::MolecularOrbital) == -1) m_ui->surfaceCombo->addItem(tr("Molecular Orbital"), Surfaces::Type::MolecularOrbital); if (m_ui->surfaceCombo->findData(Surfaces::Type::ElectronDensity) == -1) m_ui->surfaceCombo->addItem(tr("Electron Density"), Surfaces::Type::ElectronDensity); if (beta) { m_ui->spinCombo->setVisible(true); m_ui->spinCombo->setEnabled(true); if (m_ui->surfaceCombo->findData(Surfaces::Type::SpinDensity) == -1) m_ui->surfaceCombo->addItem(tr("Spin Density"), Surfaces::Type::SpinDensity); } else { auto pos = m_ui->surfaceCombo->findData(Surfaces::Type::SpinDensity); if (pos != -1) m_ui->surfaceCombo->removeItem(pos); } // TODO: get this from the basis information QString text; m_ui->orbitalCombo->clear(); for (int i = 1; i <= numMOs; ++i) { text = tr("MO %L1", "Molecular orbital").arg(i); if (i == numElectrons / 2) text += ' ' + tr("(HOMO)", "Highest occupied molecular orbital"); if (i == numElectrons / 2 + 1) text += ' ' + tr("(LUMO)", "Lowest unoccupied molecular orbital"); m_ui->orbitalCombo->addItem(text); } m_ui->orbitalCombo->setCurrentIndex(numElectrons / 2); } void SurfaceDialog::setupCubes(QStringList cubeNames) { if (cubeNames.size() < 1) return; m_ui->orbitalCombo->setVisible(true); m_ui->orbitalCombo->setEnabled(true); m_ui->surfaceCombo->addItem(tr("From File"), Surfaces::Type::FromFile); for (int i = 0; i < cubeNames.size(); ++i) { m_ui->orbitalCombo->addItem(cubeNames[i]); } m_ui->orbitalCombo->setCurrentIndex(0); } void SurfaceDialog::setupSteps(int stepCount) { if (stepCount < 2) { m_ui->frameLabel->hide(); m_ui->stepValue->setEnabled(false); m_ui->stepValue->hide(); m_ui->recordButton->setEnabled(false); m_ui->recordButton->hide(); m_ui->vcrBack->setEnabled(false); m_ui->vcrBack->hide(); m_ui->vcrPlay->setEnabled(false); m_ui->vcrPlay->hide(); m_ui->vcrForward->setEnabled(false); m_ui->vcrForward->hide(); } else { m_ui->frameLabel->show(); m_ui->stepValue->setEnabled(true); m_ui->stepValue->setRange(1, stepCount); m_ui->stepValue->setSuffix(tr(" of %0").arg(stepCount)); m_ui->stepValue->show(); m_ui->recordButton->setEnabled(true); m_ui->recordButton->show(); /* Disable for now, this would be nice in future. m_ui->vcrBack->setEnabled(true); m_ui->vcrBack->setVisible(true); m_ui->vcrPlay->setEnabled(true); m_ui->vcrPlay->setVisible(true); m_ui->vcrForward->setEnabled(true); m_ui->vcrForward->setVisible(true); */ } } void SurfaceDialog::setupModels( const std::set>& chargeModels) { m_chargeModels = chargeModels; } Surfaces::Type SurfaceDialog::surfaceType() { return static_cast(m_ui->surfaceCombo->currentData().toInt()); } Surfaces::ColorProperty SurfaceDialog::colorProperty() { return static_cast( m_ui->propertyCombo->currentIndex()); } QString SurfaceDialog::colorModel() { return m_ui->modelCombo->currentData().toString(); } QString SurfaceDialog::colormapName() { return m_ui->colormapCombo->currentText(); } int SurfaceDialog::surfaceIndex() { return m_ui->orbitalCombo->currentIndex(); } bool SurfaceDialog::beta() { return m_ui->spinCombo->currentIndex() == 1; } float SurfaceDialog::isosurfaceValue() { return static_cast(m_ui->isosurfaceDoubleSpinBox->value()); } int SurfaceDialog::smoothingPassesValue() { return static_cast(m_ui->smoothingPassesSpinBox->value()); } float SurfaceDialog::resolution() { return static_cast(m_ui->resolutionDoubleSpinBox->value()); } bool SurfaceDialog::automaticResolution() { return m_automaticResolution; } int SurfaceDialog::step() { return m_ui->stepValue->value(); } void SurfaceDialog::setStep(int step) { m_ui->stepValue->setValue(step); } void SurfaceDialog::calculateClicked() { m_ui->calculateButton->setEnabled(false); emit calculateClickedSignal(); } void SurfaceDialog::reenableCalculateButton() { m_ui->calculateButton->setEnabled(true); } void SurfaceDialog::record() { m_ui->calculateButton->setEnabled(false); m_ui->recordButton->setEnabled(false); emit recordClicked(); } void SurfaceDialog::enableRecord() { m_ui->calculateButton->setEnabled(true); m_ui->recordButton->setEnabled(true); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/surfacedialog.h000066400000000000000000000043421506155467400247720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SURFACEDIALOG_H #define AVOGADRO_QTPLUGINS_SURFACEDIALOG_H #include #include #include #include "surfaces.h" // for the enum namespace Ui { class SurfaceDialog; } namespace Avogadro { namespace QtPlugins { /** * @brief The SurfaceDialog presents various properties that have been read in * from a quantum output file and provides interface to initiate calculations. */ class SurfaceDialog : public QDialog { Q_OBJECT public: explicit SurfaceDialog(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~SurfaceDialog() override; void setupBasis(int numElectrons, int numMOs, bool beta); void setupCubes(QStringList cubeNames); void setupSteps(int stepCount); void setupModels( const std::set>& chargeModels); void reenableCalculateButton(); void enableRecord(); Surfaces::Type surfaceType(); Surfaces::ColorProperty colorProperty(); QString colorModel(); QString colormapName(); /** * This holds the value of the molecular orbital at present. */ int surfaceIndex(); /** * Only relevant for electronic structure, was the beta orbital selected? */ bool beta(); float isosurfaceValue(); int smoothingPassesValue(); float resolution(); bool automaticResolution(); int step(); void setStep(int step); public slots: protected slots: void surfaceComboChanged(int n); void propertyComboChanged(int n); void resolutionComboChanged(int n); void smoothingComboChanged(int n); void calculateClicked(); void record(); signals: void stepChanged(int n); void calculateClickedSignal(); void recordClicked(); private: Ui::SurfaceDialog* m_ui; bool m_automaticResolution; std::set> m_chargeModels; }; } // End namespace QtPlugins } // End namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SURFACEDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/surfacedialog.ui000066400000000000000000000367471506155467400251760ustar00rootroot00000000000000 SurfaceDialog Qt::NonModal true 0 0 443 354 Qt::DefaultContextMenu Create Surfaces false Surface: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true false false alpha beta Qt::Horizontal 40 20 Color by: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter None Electrostatic Potential false Qt::Horizontal 40 20 Colormap: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Balance Blue-DarkRed Coolwarm Spectral Turbo Qt::Horizontal 40 20 Resolution: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true Automatic 5 Very Low Low Medium High Very High Automatic Custom false Å 0.010000000000000 2.000000000000000 0.100000000000000 0.180000000000000 Qt::Horizontal 40 20 Isosurface Value: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 4 0.000100000000000 0.999000000000000 0.001000000000000 0.030000000000000 Qt::Horizontal 40 20 Smoothing: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true Light 1 None Light Medium Strong Custom false 0 19 1 1 Qt::Horizontal 40 20 Frame: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false false .. false .. false .. Qt::Horizontal 40 20 true Calculate true false Record Movie… true QDialogButtonBox::Close Qt::Vertical 20 40 buttonBox clicked(QAbstractButton*) SurfaceDialog close() 307 174 257 106 avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/surfaces.cpp000066400000000000000000001025201506155467400243250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "surfaces.h" #include "surfacedialog.h" // Header only, but duplicate symbols if included globally... namespace { #include } #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace tinycolormap; namespace Avogadro::QtPlugins { using Core::Array; using Core::Cube; using Core::GaussianSet; using Core::NeighborPerceiver; using QtGui::GaussianSetConcurrent; using QtGui::Molecule; using QtGui::SlaterSetConcurrent; class Surfaces::PIMPL { public: GifWriter* gifWriter = nullptr; gwavi_t* gwaviWriter = nullptr; }; Surfaces::Surfaces(QObject* p) : ExtensionPlugin(p), d(new PIMPL()) { auto action = new QAction(this); action->setText(tr("Create Surfaces…")); connect(action, SIGNAL(triggered()), SLOT(surfacesActivated())); connect(&m_displayMeshWatcher, SIGNAL(finished()), SLOT(displayMesh())); connect(&m_performEDTStepWatcher, SIGNAL(finished()), SLOT(performEDTStep())); m_actions.push_back(action); // Register quantum file formats Io::FileFormatManager::registerFormat(new QuantumIO::GAMESSUSOutput); Io::FileFormatManager::registerFormat(new QuantumIO::GaussianFchk); Io::FileFormatManager::registerFormat(new QuantumIO::GaussianCube); Io::FileFormatManager::registerFormat(new QuantumIO::GenericJson); Io::FileFormatManager::registerFormat(new QuantumIO::GenericOutput); Io::FileFormatManager::registerFormat(new QuantumIO::MoldenFile); Io::FileFormatManager::registerFormat(new QuantumIO::MopacAux); Io::FileFormatManager::registerFormat(new QuantumIO::NWChemJson); Io::FileFormatManager::registerFormat(new QuantumIO::NWChemLog); Io::FileFormatManager::registerFormat(new QuantumIO::ORCAOutput); Io::FileFormatManager::registerFormat(new QuantumIO::QCSchema); } Surfaces::~Surfaces() { delete d; // delete m_cube; // should be freed by the molecule } void Surfaces::registerCommands() { // register some scripting commands emit registerCommand("renderVDW", tr("Render the van der Waals surface.")); emit registerCommand("renderVanDerWaals", tr("Render the van der Waals molecular surface.")); emit registerCommand("renderSolventAccessible", tr("Render the solvent-accessible molecular surface.")); emit registerCommand("renderSolventExcluded", tr("Render the solvent-excluded molecular surface.")); emit registerCommand("renderOrbital", tr("Render a molecular orbital.")); emit registerCommand("renderMO", tr("Render a molecular orbital.")); emit registerCommand("renderElectronDensity", tr("Render the electron density.")); emit registerCommand("renderSpinDensity", tr("Render the spin density.")); emit registerCommand("renderCube", tr("Render a cube supplied with the file.")); } bool Surfaces::handleCommand(const QString& command, const QVariantMap& options) { if (m_molecule == nullptr) return false; // No molecule to handle the command. // Set up some defaults for the options. int index = -1; int homo = -1; float isoValue = 0.03; float cubeResolution = resolution(); // use default resolution if (options.contains("resolution") && options["resolution"].canConvert()) { bool ok; float res = options["resolution"].toFloat(&ok); if (ok) cubeResolution = res; } if (options.contains("isovalue") && options["isovalue"].canConvert()) { bool ok; float iso = options["isovalue"].toFloat(&ok); if (ok) isoValue = iso; } if (m_basis != nullptr) homo = m_basis->homo(); if (options.contains("orbital")) { // check if options contains "homo" or "lumo" bool ok = false; if (options["orbital"].canConvert()) { // internally, we count orbitals from zero // if the conversion worked, ok = true // and we'll skip the next conditional index = options.value("orbital").toInt(&ok) - 1; } if (!ok && options["orbital"].canConvert()) { // should be something like "homo-1" or "lumo+2" QString name = options["orbital"].toString(); QString expression, modifier; if (name.contains("homo", Qt::CaseInsensitive)) { index = homo; // modified by the expression after the string expression = name.remove("homo", Qt::CaseInsensitive); } else if (name.contains("lumo", Qt::CaseInsensitive)) { index = homo + 1; // again modified by the expression expression = name.remove("lumo", Qt::CaseInsensitive); } // modify HOMO / LUMO based on "+ number" or "- number" if (expression.contains('-')) { modifier = expression.remove('-'); bool ok1; int n = modifier.toInt(&ok1); if (ok1) index = index - n; } else if (expression.contains('+')) { modifier = expression.remove('+'); bool ok2; int n = modifier.toInt(&ok2); if (ok2) index = index + n; } index = index - 1; // start from zero } } bool beta = false; if (options.contains("spin")) { beta = options["spin"].toString().contains("beta"); } if ((command.compare("renderVanDerWaals", Qt::CaseInsensitive) == 0) || (command.compare("renderVDW", Qt::CaseInsensitive) == 0)) { calculateEDT(VanDerWaals, cubeResolution); return true; } else if (command.compare("renderSolventAccessible", Qt::CaseInsensitive) == 0) { calculateEDT(SolventAccessible, cubeResolution); return true; } else if (command.compare("renderSolventExcluded", Qt::CaseInsensitive) == 0) { calculateEDT(SolventExcluded, cubeResolution); return true; } else if ((command.compare("renderOrbital", Qt::CaseInsensitive) == 0) || (command.compare("renderMO", Qt::CaseInsensitive) == 0)) { calculateQM(MolecularOrbital, index, beta, isoValue, cubeResolution); return true; } else if (command.compare("renderElectronDensity", Qt::CaseInsensitive) == 0) { calculateQM(ElectronDensity, index, beta, isoValue, cubeResolution); return true; } else if (command.compare("renderSpinDensity", Qt::CaseInsensitive) == 0) { calculateQM(SpinDensity, index, beta, isoValue, cubeResolution); return true; } return false; } void Surfaces::setMolecule(QtGui::Molecule* mol) { if (m_molecule != nullptr) { m_molecule->disconnect(this); } m_cube = nullptr; m_mesh1 = nullptr; m_mesh2 = nullptr; m_molecule = mol; if (mol->basisSet()) { m_basis = mol->basisSet(); } else if (mol->cubes().size() != 0) { m_cubes = mol->cubes(); // calculate the mesh for the first cube if (m_cubes.size() > 0) { calculateCube(0, m_isoValue); } } if (m_molecule != nullptr) { connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); } } void Surfaces::moleculeChanged(unsigned int changes) { if (changes & Molecule::Added || changes & Molecule::Removed) { m_cubes = m_molecule->cubes(); m_basis = m_molecule->basisSet(); } } QList Surfaces::actions() const { return m_actions; } QStringList Surfaces::menuPath(QAction*) const { QStringList path; path << tr("&Analyze"); return path; } void Surfaces::surfacesActivated() { if (!m_dialog) { m_dialog = new SurfaceDialog(qobject_cast(parent())); connect(m_dialog, SIGNAL(calculateClickedSignal()), SLOT(calculateSurface())); connect(m_dialog, SIGNAL(recordClicked()), SLOT(recordMovie())); connect(m_dialog, SIGNAL(stepChanged(int)), SLOT(stepChanged(int))); } if (m_basis) { // we have quantum data, set up the dialog accordingly auto gaussian = dynamic_cast(m_basis); bool beta = false; if (gaussian) { auto b = gaussian->moMatrix(GaussianSet::Beta); if (b.rows() > 0 && b.cols() > 0) beta = true; } m_dialog->setupBasis(m_basis->electronCount(), m_basis->molecularOrbitalCount(), beta); } // only show cubes from files so we don't duplicate orbitals if (m_cubes.size() > 0) { QStringList cubeNames; for (auto* cube : m_cubes) { if (cube->cubeType() == Core::Cube::Type::FromFile) { // check if there's a name if (cube->name().empty()) { // use a numbered name int number = 1; QString name; name = tr("Cube %1", "3D Volume Data Set").arg(number); cubeNames << name; } else { cubeNames << cube->name().c_str(); } } } m_dialog->setupCubes(cubeNames); } m_dialog->setupSteps(m_molecule->coordinate3dCount()); const auto identifiers = Calc::ChargeManager::instance().identifiersForMolecule(*m_molecule); std::set> chargeModels; for (const auto& identifier : identifiers) chargeModels.emplace( Calc::ChargeManager::instance().nameForModel(identifier), identifier); m_dialog->setupModels(chargeModels); m_dialog->show(); } float Surfaces::resolution(float specified) { if (specified != 0.0) return specified; if (m_dialog != nullptr && !m_dialog->automaticResolution()) return m_dialog->resolution(); float r = 0.02 * powf(m_molecule->atomCount(), 1.0f / 3.0f); float minimum = 0.05; float maximum = 0.5; if (m_dialog != nullptr) { switch (m_dialog->surfaceType()) { case SolventExcluded: minimum = 0.1; break; default:; } } r = std::clamp(r, minimum, maximum); return r; } void Surfaces::calculateSurface() { if (!m_dialog) return; Type type = m_dialog->surfaceType(); if (!m_cube) m_cube = m_molecule->addCube(); // TODO we should add a name, type, etc. switch (type) { case VanDerWaals: case SolventAccessible: case SolventExcluded: calculateEDT(); break; case ElectronDensity: case MolecularOrbital: case ElectrostaticPotential: case SpinDensity: calculateQM(); break; case FromFile: default: calculateCube(); break; } } float inline square(float x) { return x * x; } void Surfaces::calculateEDT(Type type, float defaultResolution) { if (type == Unknown && m_dialog != nullptr) type = m_dialog->surfaceType(); if (!m_cube) m_cube = m_molecule->addCube(); QFuture future = QtConcurrent::run([=]() { double probeRadius = 0.0; switch (type) { case VanDerWaals: m_cube->setCubeType(Core::Cube::Type::VdW); break; case SolventAccessible: m_cube->setCubeType(Core::Cube::Type::SolventAccessible); break; case SolventExcluded: probeRadius = 1.4; m_cube->setCubeType(Core::Cube::Type::SolventExcluded); break; default: break; } // first, make a list of all atom positions and radii Array atomPositions = m_molecule->atomPositions3d(); auto* atoms = new std::vector>(); double max_radius = probeRadius; QtGui::RWLayerManager layerManager; for (size_t i = 0; i < m_molecule->atomCount(); i++) { if (!layerManager.visible(m_molecule->layer(i))) continue; // ignore invisible atoms auto radius = Core::Elements::radiusVDW(m_molecule->atomicNumber(i)) + probeRadius; atoms->emplace_back(atomPositions[i], radius); if (radius > max_radius) max_radius = radius; } double padding = max_radius + probeRadius + 0.2; m_cube->setLimits(*m_molecule, resolution(defaultResolution), padding); m_cube->fill(-1.0); const float res = resolution(defaultResolution); const Vector3 min = m_cube->min(); // then, for each atom, set cubes around it up to a certain radius QFuture innerFuture = QtConcurrent::map(*atoms, [=](std::pair& in) { double startPosX = in.first(0) - in.second; double endPosX = in.first(0) + in.second; int startIndexX = (startPosX - min(0)) / res; int endIndexX = (endPosX - min(0)) / res + 1; for (int indexX = startIndexX; indexX < endIndexX; indexX++) { double posX = indexX * res + min(0); double radiusXsq = square(in.second) - square(posX - in.first(0)); if (radiusXsq < 0.0) continue; double radiusX = sqrt(radiusXsq); double startPosY = in.first(1) - radiusX; double endPosY = in.first(1) + radiusX; int startIndexY = (startPosY - min(1)) / res; int endIndexY = (endPosY - min(1)) / res + 1; for (int indexY = startIndexY; indexY < endIndexY; indexY++) { double posY = indexY * res + min(1); double lengthXYsq = square(radiusX) - square(posY - in.first(1)); if (lengthXYsq < 0.0) continue; double lengthXY = sqrt(lengthXYsq); double startPosZ = in.first(2) - lengthXY; double endPosZ = in.first(2) + lengthXY; int startIndexZ = (startPosZ - min(2)) / res; int endIndexZ = (endPosZ - min(2)) / res + 1; m_cube->fillStripe(indexX, indexY, startIndexZ, endIndexZ - 1, 1.0f); } } }); innerFuture.waitForFinished(); }); // SolventExcluded requires an extra pass if (type == SolventExcluded) { m_performEDTStepWatcher.setFuture(future); } else { m_displayMeshWatcher.setFuture(future); } } void Surfaces::performEDTStep() { QFuture future = QtConcurrent::run([=]() { const double probeRadius = 1.4; const double scaledProbeRadius = probeRadius / resolution(); // make a list of all "outside" cubes in contact with an "inside" cube // these are the only ones that can be "nearest" to an "inside" cube Array relativePositions; // also make a list of all "inside" cubes auto* insideIndices = new std::vector; Vector3i size = m_cube->dimensions(); relativePositions.reserve(size(0) * size(1) * 4); // O(n^2) insideIndices->reserve(size(0) * size(1) * size(2)); // O(n^3) for (int z = 0; z < size(2); z++) { int zp = std::max(z - 1, 0); int zn = std::min(z + 1, size(2) - 1); for (int y = 0; y < size(1); y++) { int yp = std::max(y - 1, 0); int yn = std::min(y + 1, size(1) - 1); for (int x = 0; x < size(0); x++) { if (m_cube->value(x, y, z) > 0.0) { insideIndices->emplace_back(x, y, z); continue; } int xp = std::max(x - 1, 0); int xn = std::min(x + 1, size(0) - 1); if (m_cube->value(xp, y, z) > 0.0 || m_cube->value(xn, y, z) > 0.0 || m_cube->value(x, yp, z) > 0.0 || m_cube->value(x, yn, z) > 0.0 || m_cube->value(x, y, zp) > 0.0 || m_cube->value(x, y, zn) > 0.0) { relativePositions.push_back(Vector3(x, y, z)); } } } } // pass the list to a NeighborPerceiver so it's faster to look up NeighborPerceiver perceiver(relativePositions, scaledProbeRadius); // now, exclude all "inside" cubes too close to any "outside" cube thread_local Array* neighbors = nullptr; QFuture innerFuture = QtConcurrent::map(*insideIndices, [=](Vector3i& in) { Vector3 pos = in.cast(); if (neighbors == nullptr) neighbors = new Array; perceiver.getNeighborsInclusiveInPlace(*neighbors, pos); for (Index neighbor : *neighbors) { const Vector3& npos = relativePositions[neighbor]; float distance = (npos - pos).norm(); if (distance <= scaledProbeRadius) { m_cube->setValue(in(0), in(1), in(2), -1.0f); break; } } }); innerFuture.waitForFinished(); }); m_displayMeshWatcher.setFuture(future); } void Surfaces::calculateQM(Type type, int index, bool beta, float isoValue, float defaultResolution) { if (!m_basis) return; // nothing to do if (m_dialog != nullptr) { beta = m_dialog->beta(); // we're using the GUI } // TODO: check if we already calculated the requested cube // Reset state a little more frequently, minimal cost, avoid bugs. m_molecule->clearCubes(); m_molecule->clearMeshes(); m_cube = nullptr; m_mesh1 = nullptr; m_mesh2 = nullptr; m_molecule->emitChanged(Molecule::Atoms | Molecule::Added); bool connectSlots = false; // set up QtConcurrent calculators for Gaussian or Slater basis sets if (dynamic_cast(m_basis)) { if (!m_gaussianConcurrent) { m_gaussianConcurrent = new GaussianSetConcurrent(this); connectSlots = true; } m_gaussianConcurrent->setMolecule(m_molecule); } else { if (!m_slaterConcurrent) { m_slaterConcurrent = new SlaterSetConcurrent(this); connectSlots = true; } m_slaterConcurrent->setMolecule(m_molecule); } // TODO: Check to see if this cube or surface has already been computed if (!m_progressDialog) { m_progressDialog = new QProgressDialog(qobject_cast(parent())); m_progressDialog->setWindowModality(Qt::NonModal); connectSlots = true; } if (!m_cube) m_cube = m_molecule->addCube(); if (type == Unknown) type = m_dialog->surfaceType(); if (index == -1 && m_dialog != nullptr) index = m_dialog->surfaceIndex(); if (isoValue == 0.0 && m_dialog != nullptr) m_isoValue = m_dialog->isosurfaceValue(); else m_isoValue = isoValue; float cubeResolution = resolution(defaultResolution); float padding = 5.0; // TODO: allow extra padding for some molecules / properties m_cube->setLimits(*m_molecule, cubeResolution, padding); QString progressText; if (type == ElectronDensity) { progressText = tr("Calculating electron density"); m_cube->setName("Electron Density"); m_cube->setCubeType(Core::Cube::Type::ElectronDensity); if (dynamic_cast(m_basis)) { m_gaussianConcurrent->calculateElectronDensity(m_cube); } else { m_slaterConcurrent->calculateElectronDensity(m_cube); } } else if (type == SpinDensity) { progressText = tr("Calculating spin density"); m_cube->setName("Spin Density"); m_cube->setCubeType(Core::Cube::Type::SpinDensity); if (dynamic_cast(m_basis)) { m_gaussianConcurrent->calculateSpinDensity(m_cube); } else { m_slaterConcurrent->calculateSpinDensity(m_cube); } } else if (type == MolecularOrbital) { progressText = tr("Calculating molecular orbital %L1").arg(index); m_cube->setName("Molecular Orbital " + std::to_string(index + 1)); m_cube->setCubeType(Core::Cube::Type::MO); if (dynamic_cast(m_basis)) { m_gaussianConcurrent->calculateMolecularOrbital(m_cube, index, beta); } else { m_slaterConcurrent->calculateMolecularOrbital(m_cube, index); } } // Set up the progress dialog. if (dynamic_cast(m_basis)) { m_progressDialog->setWindowTitle(progressText); m_progressDialog->setRange( m_gaussianConcurrent->watcher().progressMinimum(), m_gaussianConcurrent->watcher().progressMaximum()); m_progressDialog->setValue(m_gaussianConcurrent->watcher().progressValue()); m_progressDialog->show(); if (connectSlots) { connect(&m_gaussianConcurrent->watcher(), SIGNAL(progressValueChanged(int)), m_progressDialog, SLOT(setValue(int))); connect(&m_gaussianConcurrent->watcher(), SIGNAL(progressRangeChanged(int, int)), m_progressDialog, SLOT(setRange(int, int))); connect(m_gaussianConcurrent, SIGNAL(finished()), SLOT(displayMesh())); connect(m_progressDialog, SIGNAL(canceled()), &m_gaussianConcurrent->watcher(), SLOT(cancel())); } } else { // slaters m_progressDialog->setWindowTitle(progressText); m_progressDialog->setRange(m_slaterConcurrent->watcher().progressMinimum(), m_slaterConcurrent->watcher().progressMaximum()); m_progressDialog->setValue(m_slaterConcurrent->watcher().progressValue()); m_progressDialog->show(); connect(&m_slaterConcurrent->watcher(), SIGNAL(progressValueChanged(int)), m_progressDialog, SLOT(setValue(int))); connect(&m_slaterConcurrent->watcher(), SIGNAL(progressRangeChanged(int, int)), m_progressDialog, SLOT(setRange(int, int))); connect(m_progressDialog, SIGNAL(canceled()), &m_slaterConcurrent->watcher(), SLOT(cancel())); connect(m_slaterConcurrent, SIGNAL(finished()), SLOT(displayMesh())); } } void Surfaces::calculateCube(int index, float isoValue) { if (m_cubes.size() == 0) return; if (index == -1 && m_dialog != nullptr) index = m_dialog->surfaceIndex(); if (index < 0 || index >= static_cast(m_cubes.size())) return; // check bounds m_cube = m_cubes[index]; if (m_cube == nullptr) return; if (isoValue == 0.0 && m_dialog != nullptr) m_isoValue = m_dialog->isosurfaceValue(); else m_isoValue = isoValue; displayMesh(); } void Surfaces::stepChanged(int n) { if (!m_molecule || !m_basis) return; qDebug() << "\n\t==== Step changed to" << n << "===="; auto g = dynamic_cast(m_basis); if (g) { g->setActiveSetStep(n - 1); m_molecule->clearCubes(); m_molecule->clearMeshes(); m_cube = nullptr; m_mesh1 = nullptr; m_mesh2 = nullptr; m_molecule->emitChanged(Molecule::Atoms | Molecule::Added); } } void Surfaces::displayMesh() { if (!m_cube) return; if (m_dialog != nullptr) m_smoothingPasses = m_dialog->smoothingPassesValue(); else m_smoothingPasses = 0; if (!m_mesh1) m_mesh1 = m_molecule->addMesh(); if (!m_meshGenerator1) { m_meshGenerator1 = new QtGui::MeshGenerator; connect(m_meshGenerator1, SIGNAL(finished()), SLOT(meshFinished())); } m_meshGenerator1->initialize(m_cube, m_mesh1, m_isoValue, m_smoothingPasses); bool isMO = false; // if it's from a file we should "play it safe" if (m_cube->cubeType() == Cube::Type::MO || m_cube->cubeType() == Cube::Type::FromFile) { isMO = true; } if (isMO) { if (!m_mesh2) m_mesh2 = m_molecule->addMesh(); if (!m_meshGenerator2) { m_meshGenerator2 = new QtGui::MeshGenerator; connect(m_meshGenerator2, SIGNAL(finished()), SLOT(meshFinished())); } m_meshGenerator2->initialize(m_cube, m_mesh2, -m_isoValue, m_smoothingPasses, true); } // Start the mesh generation - this needs an improved mutex with a read lock // to function as expected. Write locks are exclusive, read locks can have // many read locks but no write lock. m_meshGenerator1->start(); if (isMO) m_meshGenerator2->start(); // Track how many meshes are left to show. if (isMO) m_meshesLeft = 2; else m_meshesLeft = 1; } Core::Color3f Surfaces::chargeGradient(double value, double clamp, ColormapType colormap) const { // okay, typically color scales have blue at the bottom, red at the top. // so we need to invert, so blue is positive charge, red is negative charge. // we also need to scale the color to the range of the charge. double scaledValue = value / clamp; // from -1 to 1.0 double scaledValue2 = 1.0 - ((scaledValue + 1.0) / 2.0); // from 0 to 1.0 red to blue auto color = tinycolormap::GetColor(scaledValue2, colormap); Core::Color3f r(float(color.r()), color.g(), color.b()); return r; } ColormapType Surfaces::getColormapFromString(const QString& name) const { // Just do all of them, even though we won't use them all if (name == tr("Parula", "colormap")) return ColormapType::Parula; else if (name == tr("Heat", "colormap")) return ColormapType::Heat; else if (name == tr("Hot", "colormap")) return ColormapType::Hot; else if (name == tr("Gray", "colormap")) return ColormapType::Gray; else if (name == tr("Magma", "colormap")) return ColormapType::Magma; else if (name == tr("Inferno", "colormap")) return ColormapType::Inferno; else if (name == tr("Plasma", "colormap")) return ColormapType::Plasma; else if (name == tr("Viridis", "colormap")) return ColormapType::Viridis; else if (name == tr("Cividis", "colormap")) return ColormapType::Cividis; else if (name == tr("Spectral", "colormap")) return ColormapType::Spectral; else if (name == tr("Coolwarm", "colormap")) return ColormapType::Coolwarm; else if (name == tr("Balance", "colormap")) return ColormapType::Balance; else if (name == tr("Blue-DarkRed", "colormap")) return ColormapType::BlueDkRed; else if (name == tr("Turbo", "colormap")) return ColormapType::Turbo; return ColormapType::Turbo; } void Surfaces::colorMeshByPotential() { const auto model = m_dialog->colorModel().toStdString(); const auto colormap = getColormapFromString(m_dialog->colormapName()); const auto positionsf = m_mesh1->vertices(); if (positionsf.empty()) return; Core::Array positions(positionsf.size()); std::transform(positionsf.begin(), positionsf.end(), positions.begin(), [](const Vector3f& pos) { return pos.cast(); }); const auto potentials = Calc::ChargeManager::instance().potentials(model, *m_molecule, positions); double minPotential = *std::min_element(potentials.begin(), potentials.end()); double maxPotential = *std::max_element(potentials.begin(), potentials.end()); double clamp = std::max(std::abs(minPotential), std::abs(maxPotential)); Core::Array colors(positions.size()); for (size_t i = 0; i < potentials.size(); i++) colors[i] = chargeGradient(potentials[i], clamp, colormap); m_mesh1->setColors(colors); } void Surfaces::colorMesh() { if (m_dialog == nullptr) return; switch (m_dialog->colorProperty()) { case None: break; case ByElectrostaticPotential: colorMeshByPotential(); break; } } void Surfaces::meshFinished() { --m_meshesLeft; if (m_meshesLeft == 0) { colorMesh(); // finished, so request to enable the mesh display type QStringList displayTypes; displayTypes << tr("Meshes"); requestActiveDisplayTypes(displayTypes); if (m_recordingMovie) { // Move to the next frame. qDebug() << "Let's get to the next frame…"; m_molecule->emitChanged(QtGui::Molecule::Added); movieFrame(); } else { if (m_dialog != nullptr) m_dialog->reenableCalculateButton(); m_molecule->emitChanged(QtGui::Molecule::Added); } } } void Surfaces::recordMovie() { QString baseFileName; if (m_molecule) baseFileName = m_molecule->data("fileName").toString().c_str(); QString selectedFilter = tr("Movie AVI (*.avi)"); QString baseName = QFileDialog::getSaveFileName( qobject_cast(parent()), tr("Export Movie"), "", tr("Movie MP4 (*.mp4);;Movie AVI (*.avi);;GIF (*.gif)"), &selectedFilter); if (baseName.isEmpty()) { m_dialog->enableRecord(); return; } QFileInfo fileInfo(baseName); if (!fileInfo.suffix().isEmpty()) baseName = fileInfo.absolutePath() + "/" + fileInfo.baseName(); m_baseFileName = baseName; m_numberLength = static_cast( ceil(log10(static_cast(m_molecule->coordinate3dCount()) + 1))); m_recordingMovie = true; m_currentFrame = 1; m_frameCount = m_molecule->coordinate3dCount(); // Figure out the save type, and work accordingly... if (selectedFilter == tr("GIF (*.gif)")) { d->gwaviWriter = nullptr; d->gifWriter = new GifWriter; GifBegin(d->gifWriter, (baseName + ".gif").toLatin1().data(), 800, 600, 100 / 4); } else if (selectedFilter == tr("Movie AVI (*.avi)")) { d->gifWriter = nullptr; d->gwaviWriter = gwavi_open((baseName + ".avi").toLatin1().data(), 800, 600, "MJPG", 4, nullptr); } else { d->gwaviWriter = nullptr; d->gifWriter = nullptr; } stepChanged(m_currentFrame); m_dialog->setStep(m_currentFrame); calculateSurface(); } void Surfaces::movieFrame() { // Not ideal, need to let things update asynchronously, complete, before we // capture the frame. When appropriate move to the next frame or complete. QCoreApplication::sendPostedEvents(); QCoreApplication::processEvents(); auto glWidget = QtOpenGL::ActiveObjects::instance().activeGLWidget(); if (!glWidget) { QMessageBox::warning(qobject_cast(parent()), tr("Avogadro"), "Couldn't find the active render widget, failing."); m_recordingMovie = false; m_dialog->enableRecord(); return; } glWidget->resize(800 / glWidget->devicePixelRatio(), 600 / glWidget->devicePixelRatio()); QImage exportImage; glWidget->raise(); glWidget->repaint(); if (QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) { exportImage = glWidget->grabFramebuffer(); } else { auto* screen = QGuiApplication::primaryScreen(); auto pixmap = screen->grabWindow(glWidget->winId()); exportImage = pixmap.toImage(); } if (d->gifWriter) { int pixelCount = exportImage.width() * exportImage.height(); auto* imageData = new uint8_t[pixelCount * 4]; int imageIndex = 0; for (int j = 0; j < exportImage.height(); ++j) { for (int k = 0; k < exportImage.width(); ++k) { QColor color = exportImage.pixel(k, j); imageData[imageIndex] = static_cast(color.red()); imageData[imageIndex + 1] = static_cast(color.green()); imageData[imageIndex + 2] = static_cast(color.blue()); imageData[imageIndex + 3] = static_cast(color.alpha()); imageIndex += 4; } } GifWriteFrame(d->gifWriter, imageData, 800, 600, 100 / 4); delete[] imageData; } else if (d->gwaviWriter) { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); exportImage.save(&buffer, "JPG"); if (gwavi_add_frame( d->gwaviWriter, reinterpret_cast(buffer.data().data()), buffer.size()) == -1) { QMessageBox::warning(qobject_cast(parent()), tr("Avogadro"), tr("Error: cannot add frame to video.")); } } else { QString fileName = QString::number(m_currentFrame); while (fileName.length() < m_numberLength) fileName.prepend('0'); fileName.prepend(m_baseFileName); fileName.append(".png"); qDebug() << "Writing to" << fileName; if (!exportImage.save(fileName)) { QMessageBox::warning(qobject_cast(parent()), tr("Avogadro"), tr("Cannot save file %1.").arg(fileName)); return; } } // Increment current frame. ++m_currentFrame; if (m_currentFrame <= m_frameCount) { qDebug() << "Starting next frame…"; stepChanged(m_currentFrame); m_dialog->setStep(m_currentFrame); calculateSurface(); } else { qDebug() << "We are done! Make some movies."; if (d->gifWriter) { GifEnd(d->gifWriter); delete d->gifWriter; d->gifWriter = nullptr; } else if (d->gwaviWriter) { gwavi_close(d->gwaviWriter); d->gwaviWriter = nullptr; } else { QProcess proc; QStringList args; args << "-y" << "-r" << QString::number(10) << "-i" << m_baseFileName + "%0" + QString::number(m_numberLength) + "d.png" << "-c:v" << "libx264" << "-r" << "30" << "-pix_fmt" << "yuv420p" << m_baseFileName + ".mp4"; proc.execute("avconv", args); } /* args.clear(); args << "-dispose" << "Background" << "-delay" << QString::number(100 / 10) << m_baseFileName + "%0" + QString::number(m_numberLength) + "d.png[0-" + QString::number(m_molecule->coordinate3dCount() - 1) + "]" << m_baseFileName + ".gif"; proc.execute("convert", args); */ m_recordingMovie = false; m_dialog->enableRecord(); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/surfaces/surfaces.h000066400000000000000000000074651506155467400240060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SURFACES_H #define AVOGADRO_QTPLUGINS_SURFACES_H #include #include #include #include class QAction; class QDialog; class QProgressDialog; namespace Avogadro { namespace QtGui { class MeshGenerator; class GaussianSetConcurrent; class SlaterSetConcurrent; } // namespace QtGui namespace Core { class BasisSet; class Cube; class Mesh; } // namespace Core namespace QtPlugins { /** * @brief The Surfaces plugin registers quantum file formats, adds several * menu entries to calculate surfaces, including QM ones. */ class SurfaceDialog; class Surfaces : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Surfaces(QObject* parent = nullptr); ~Surfaces() override; enum Type { VanDerWaals, SolventAccessible, SolventExcluded, ElectrostaticPotential, ElectronDensity, MolecularOrbital, SpinDensity, FromFile, Unknown }; enum ColorProperty { None, ByElectrostaticPotential }; QString name() const override { return tr("Surfaces"); } QString description() const override { return tr("Read and render surfaces."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; void registerCommands() override; public slots: bool handleCommand(const QString& command, const QVariantMap& options) override; void moleculeChanged(unsigned int changes); private slots: void surfacesActivated(); void calculateSurface(); void calculateEDT(Type type = Unknown, float defaultResolution = 0.0); void performEDTStep(); // EDT step for SolventExcluded void calculateQM(Type type = Unknown, int index = -1, bool betaSpin = false, float isoValue = 0.0, float defaultResolution = 0.0); void calculateCube(int index = -1, float isoValue = 0.0); void stepChanged(int); void displayMesh(); void meshFinished(); void colorMesh(); void colorMeshByPotential(); void recordMovie(); void movieFrame(); private: float resolution(float specified = 0.0); Core::Color3f chargeGradient(double value, double clamp, tinycolormap::ColormapType colormap) const; tinycolormap::ColormapType getColormapFromString(const QString& name) const; QList m_actions; QProgressDialog* m_progressDialog = nullptr; QtGui::Molecule* m_molecule = nullptr; Core::BasisSet* m_basis = nullptr; QtGui::GaussianSetConcurrent* m_gaussianConcurrent = nullptr; QtGui::SlaterSetConcurrent* m_slaterConcurrent = nullptr; Core::Cube* m_cube = nullptr; std::vector m_cubes; /* One QFutureWatcher per asynchronous slot function, e.g.:*/ /* calculateEDT() -> [performEDTStep()] -> displayMesh() */ QFutureWatcher m_performEDTStepWatcher; QFutureWatcher m_displayMeshWatcher; Core::Mesh* m_mesh1 = nullptr; Core::Mesh* m_mesh2 = nullptr; /* displayMesh() -> meshFinished() */ QtGui::MeshGenerator* m_meshGenerator1 = nullptr; QtGui::MeshGenerator* m_meshGenerator2 = nullptr; float m_isoValue = 0.025; int m_smoothingPasses = 2; int m_meshesLeft = 0; bool m_recordingMovie = false; int m_currentFrame = 0; int m_frameCount = 1; QString m_baseFileName; int m_numberLength = 1; SurfaceDialog* m_dialog = nullptr; class PIMPL; PIMPL* d = nullptr; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SURFACES_H avogadrolibs-1.101.0/avogadro/qtplugins/svg/000077500000000000000000000000001506155467400207725ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/svg/CMakeLists.txt000066400000000000000000000003441506155467400235330ustar00rootroot00000000000000find_package(Qt${QT_VERSION} COMPONENTS Svg REQUIRED) avogadro_plugin(SVG "Project the screen in a SVG image." ExtensionPlugin svg.h SVG "svg.cpp" "" ) target_link_libraries(SVG PRIVATE Avogadro::QtOpenGL Qt::Svg) avogadrolibs-1.101.0/avogadro/qtplugins/svg/svg.cpp000066400000000000000000000207251506155467400223030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "svg.h" #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { const float SVG::DEFAULT_RADIUS = 35.0f; const float SVG::DEFAULT_PEN_WIDTH_MOL = 1.0f; const float SVG::DEFAULT_PEN_WIDTH_BOND = 4.0f; const float SVG::DEFAULT_OFF_SET_PARALEL = 7.0f; const float SVG::DEFAULT_IMAGE_PADDING = 1.0f; const Vector3ub SVG::DEFAULT_BOND_COLOR = Vector3ub(125, 125, 125); SVG::SVG(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_scene(nullptr), m_camera(nullptr), m_action(new QAction(tr("&SVG…"), this)) { connect(m_action, SIGNAL(triggered()), SLOT(render())); } SVG::~SVG() {} QList SVG::actions() const { QList result; return result << m_action; } QStringList SVG::menuPath(QAction*) const { return QStringList() << tr("&File") << tr("&Export"); } void SVG::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void SVG::setScene(Rendering::Scene* scene) { m_scene = scene; } void SVG::setCamera(Rendering::Camera* camera) { m_camera = camera; } bool SVG::defaultChecks() { if ((m_molecule == nullptr) || (m_camera == nullptr)) return true; // Check for 3D coordinates - it's useless to consider the camera otherwise if (m_molecule->atomPositions3d().size() != m_molecule->atomCount()) return true; // no need to create SVG when there are no atoms if (m_molecule->atomCount() == 0) return true; return false; } void SVG::setOptions() { m_radius = DEFAULT_RADIUS; m_penWidthMol = DEFAULT_PEN_WIDTH_MOL; m_penWidthBond = DEFAULT_PEN_WIDTH_BOND; m_offSetParalel = DEFAULT_OFF_SET_PARALEL; m_ImagePadding = DEFAULT_IMAGE_PADDING; m_BondColor = DEFAULT_BOND_COLOR; } // calculate the camera frustrum void SVG::calculateCamera() { Eigen::Matrix4f m = (m_camera->projection() * m_camera->modelView()).matrix(); m_frustrum = std::vector(6); // right m_frustrum[0] = Eigen::Vector4f(m(0, 3) + m(0, 0), m(1, 3) + m(1, 0), m(2, 3) + m(2, 0), m(3, 3) + m(3, 0)); // left m_frustrum[1] = Eigen::Vector4f(m(0, 3) - m(0, 0), m(1, 3) - m(1, 0), m(2, 3) - m(2, 0), m(3, 3) - m(3, 0)); // top m_frustrum[2] = Eigen::Vector4f(m(0, 3) - m(0, 1), m(1, 3) - m(1, 1), m(2, 3) - m(2, 1), m(3, 3) - m(3, 1)); // bottom m_frustrum[3] = Eigen::Vector4f(m(0, 3) + m(0, 1), m(1, 3) + m(1, 1), m(2, 3) + m(2, 1), m(3, 3) + m(3, 1)); // far m_frustrum[4] = Eigen::Vector4f(m(0, 2), m(1, 2), m(2, 2), m(3, 2)); // near m_frustrum[5] = Eigen::Vector4f(m(0, 3) - m(0, 2), m(1, 3) - m(1, 2), m(2, 3) - m(2, 2), m(3, 3) - m(3, 2)); } // conservative frustrum culling bool SVG::frustrumCulling(const SVGAtom& atom) { return true; for (const auto& p : m_frustrum) { Eigen::Vector4f pos_model; pos_model << atom.pos_model, 1.0f; if (p.dot(pos_model) + atom.radius <= 0) return true; } return false; } // transform view position to 2d SVG image Eigen::Vector3f SVG::posToSVGImage(const SVGAtom& atom) { Eigen::Vector3f result = m_camera->project(atom.pos_model); float scale = 1.0f + ((atom.pos_view - m_min).norm() / m_max.norm()); float r_scale = 2.0f - (1.0f / atom.radius); result[1] = ((result[1] - (m_height / 2.0f)) * -1.0f) + (m_height / 2.0f); result[2] = m_radius * r_scale * scale; return result; } // get all model, view, image positions sort them and save id to vector index void SVG::getPositions() { m_atoms.clear(); calculateCamera(); m_max[0] = m_max[1] = m_max[2] = Eigen::Infinity; m_min[0] = m_min[1] = m_min[2] = Eigen::Infinity * -1.0f; const Core::Array atomPositions = m_molecule->atomPositions3d(); for (unsigned int i = 0; i < atomPositions.size(); ++i) { Eigen::Vector3f pos = m_camera->modelView() * atomPositions[i].cast(); pos[1] *= -1.0f; SVGAtom atom(atomPositions[i].cast(), pos, i, m_molecule->bonds(i).size()); m_atoms.push_back(atom); if (frustrumCulling(atom)) { for (unsigned int j = 0; j < 3; ++j) { if (m_min[j] > pos[j]) m_min[j] = pos[j]; if (m_max[j] < pos[j]) m_max[j] = pos[j]; } } } m_min[0] -= m_ImagePadding; m_min[1] -= m_ImagePadding; m_max[0] += m_ImagePadding; m_max[1] += m_ImagePadding; m_max -= m_min; std::sort(m_atoms.begin(), m_atoms.end(), [&](const SVGAtom& a, const SVGAtom& b) { // sort using the distance to the camera return a.pos_view.norm() > b.pos_view.norm(); }); int i = 0; for (auto& m : m_atoms) { m_idToindex[m.id] = i; m.pos_image = posToSVGImage(m); ++i; } } // paint all bondings only once void SVG::paintBonds(QPainter& painter, const SVGAtom& atom, unsigned int i, const NeighborListType& bonds) { painter.setPen(QPen(QColor(m_BondColor[0], m_BondColor[1], m_BondColor[2]), m_penWidthBond)); for (auto bond : bonds) { unsigned int j = bond.atom2().index(); if (m_idToindex[j] == i) { j = bond.atom1().index(); } if (m_idToindex[j] <= i) { continue; } // calculate the inicial and final position, considering the atom middle // and the middle of the pen width auto mol_to = m_atoms[m_idToindex[j]].pos_image; Eigen::Vector2f from(atom.pos_image[0] - (m_penWidthBond / 2.0f), atom.pos_image[1] - (m_penWidthBond / 2.0f)); Eigen::Vector2f to(mol_to[0] - (m_penWidthBond / 2.0f), mol_to[1] - (m_penWidthBond / 2.0f)); float L = std::hypot(from[0] - to[0], from[1] - to[1]); float offsetX = (to[1] - from[1]) / L; float offsetY = (from[0] - to[0]) / L; unsigned int order = int(bond.order()); // for each bound offset it following the orthogonal direction for (unsigned int o = 0; o < order; ++o) { // if there is only one bond, don't displace float x = 0, y = 0; if (order > 1) { x = (float(o) - (order / 2.0f)) * m_offSetParalel * offsetX; y = (float(o) - (order / 2.0f)) * m_offSetParalel * offsetY; } QLineF line(from[0] + x, from[1] + y, to[0] + x, to[1] + y); painter.drawLine(line); } } } void SVG::paintCore(QPainter& painter, const SVGAtom& atom) { Vector3ub atom_color = m_molecule->color(atom.id); QColor color(atom_color[0], atom_color[1], atom_color[2]); painter.setPen(QPen(QColor(0, 0, 0), m_penWidthMol)); painter.setBrush(color); painter.drawEllipse(atom.pos_image[0] - atom.pos_image[2] / 2.0f, atom.pos_image[1] - atom.pos_image[2] / 2.0f, atom.pos_image[2], atom.pos_image[2]); } void SVG::paintSVG(QPainter& painter) { const Vector4ub background = m_scene->backgroundColor(); QColor backgroundColor(background[0], background[1], background[2], background[3]); painter.fillRect(QRect(0, 0, m_width, m_height), backgroundColor); getPositions(); for (unsigned int i = 0; i < m_atoms.size(); ++i) { auto atom = m_atoms[i]; const NeighborListType bonds = m_molecule->bonds(atom.id); if (frustrumCulling(atom)) { paintBonds(painter, atom, i, bonds); paintCore(painter, atom); } } } void SVG::render() { setOptions(); if (defaultChecks()) return; QString filename = QFileDialog::getSaveFileName( qobject_cast(parent()), tr("Save File"), QDir::homePath(), tr("SVG (*.svg)")); if (!filename.endsWith(".svg", Qt::CaseInsensitive)) filename.append(".svg"); QFile file(filename); if (!file.open(QIODevice::WriteOnly)) return; m_width = m_camera->width(); m_height = m_camera->height(); QSvgGenerator generator; generator.setFileName(filename); generator.setSize(QSize(m_width, m_height)); generator.setViewBox(QRect(0, 0, m_width, m_height)); QPainter painter; painter.begin(&generator); paintSVG(painter); painter.end(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/svg/svg.h000066400000000000000000000051361506155467400217470ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SVG_H #define AVOGADRO_QTPLUGINS_SVG_H #include #include #include #include #include #include #include namespace Avogadro { namespace QtPlugins { typedef Avogadro::Core::Array NeighborListType; class SVG : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit SVG(QObject* parent_ = nullptr); ~SVG() override; QString name() const override { return tr("SVG"); } QString description() const override { return tr("Render the scene in a SVG file."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void setScene(Rendering::Scene* scene) override; void setCamera(Rendering::Camera* camera) override; private slots: void render(); private: struct SVGAtom { Eigen::Vector3f pos_model, pos_view, pos_image; unsigned int radius; unsigned int id; SVGAtom(Vector3f m, Vector3f v, unsigned int i, unsigned int bonds) : pos_model(m), pos_view(v), id(i) { radius = (bonds <= 0 ? 1 : bonds); } }; QtGui::Molecule* m_molecule; Rendering::Scene* m_scene; Rendering::Camera* m_camera; QAction* m_action; int m_width, m_height; Eigen::Vector3f m_min, m_max; std::vector m_atoms; std::vector m_frustrum; std::map m_idToindex; static const float DEFAULT_RADIUS, DEFAULT_PEN_WIDTH_MOL, DEFAULT_PEN_WIDTH_BOND, DEFAULT_OFF_SET_PARALEL, DEFAULT_IMAGE_PADDING; static const Vector3ub DEFAULT_BOND_COLOR; float m_radius, m_penWidthMol, m_penWidthBond, m_offSetParalel, m_ImagePadding; Vector3ub m_BondColor; void paintSVG(QPainter& painter); void paintCore(QPainter& painter, const SVGAtom& atom); void paintBonds(QPainter& painter, const SVGAtom& atom, unsigned int i, const NeighborListType& bonds); bool defaultChecks(); void setOptions(); void getPositions(); void calculateCamera(); bool frustrumCulling(const SVGAtom& atom); Eigen::Vector3f posToSVGImage(const SVGAtom& atom); }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SVG_H avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/000077500000000000000000000000001506155467400220645ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/CMakeLists.txt000066400000000000000000000012771506155467400246330ustar00rootroot00000000000000find_package(libmsym NO_MODULE) if(NOT DEFINED LIBMSYM_INCLUDE_DIRS) find_package(libmsym REQUIRED) endif() include_directories(${LIBMSYM_INCLUDE_DIRS}) set(symmetry_srcs symmetry.cpp symmetrywidget.cpp operationstablemodel.cpp symmetryutil.cpp ) set(symmetry_uis symmetrywidget.ui ) avogadro_plugin(Symmetry "Provide symmetry functionality." ExtensionPlugin symmetry.h Symmetry "${symmetry_srcs}" "${symmetry_uis}" ) avogadro_plugin(SymmetryScene "Render symmetry elements." ScenePlugin symmetryscene.h SymmetryScene symmetryscene.cpp) target_link_libraries(Symmetry PRIVATE ${LIBMSYM_LIBRARIES}) target_link_libraries(SymmetryScene PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/operationstablemodel.cpp000066400000000000000000000044641506155467400270140ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2015 Marcus Johansson This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "operationstablemodel.h" #include "symmetryutil.h" using namespace Avogadro::QtPlugins::SymmetryUtil; namespace Avogadro::QtPlugins { OperationsTableModel::OperationsTableModel(QObject* parent) : QAbstractTableModel(parent) { m_operations = nullptr; m_operations_size = 0; } OperationsTableModel::~OperationsTableModel() {} void OperationsTableModel::setOperations( int operations_size, const msym::msym_symmetry_operation_t* operations) { beginResetModel(); m_operations_size = operations_size; m_operations = operations; endResetModel(); } /* Qt */ QVariant OperationsTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); QString name; if (orientation == Qt::Horizontal) { switch (Column(section)) { case ColumnType: return tr("Type"); case ColumnClass: return tr("Class"); case ColumnVector: return tr("Element"); default: return QVariant(); } } else { return QString::number(section + 1); } } QVariant OperationsTableModel::data(const QModelIndex& index, int role) const { if (role != Qt::DisplayRole || !index.isValid()) return QVariant(); const msym::msym_symmetry_operation_t* operation = &m_operations[index.row()]; switch (Column(index.column())) { case ColumnType: return operationSymbol(operation); case ColumnClass: return operation->cla; case ColumnVector: return QString("NA"); default: return QVariant(); } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/operationstablemodel.h000066400000000000000000000037461506155467400264630ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2015 Marcus Johansson This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_OPERATIONSTABLEMODEL_H #define AVOGADRO_QTPLUGINS_OPERATIONSTABLEMODEL_H namespace msym { extern "C" { #include } } // namespace msym #include #include #include #define OPERATIONSTABLEMODEL_COLUMN_COUNT 3 namespace Avogadro { namespace QtPlugins { class OperationsTableModel : public QAbstractTableModel { Q_OBJECT public: enum Column { ColumnType = 0, ColumnClass = 1, ColumnVector = 2 }; explicit OperationsTableModel(QObject* parent = nullptr); ~OperationsTableModel() override; int rowCount(const QModelIndex&) const override { return m_operations_size; }; int columnCount(const QModelIndex&) const override { return OPERATIONSTABLEMODEL_COLUMN_COUNT; }; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; void setOperations(int operations_size, const msym::msym_symmetry_operation_t* operations); void clearOperations(); private: const msym::msym_symmetry_operation_t* m_operations; int m_operations_size; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_OPERATIONSTABLEMODEL_H avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetry.cpp000066400000000000000000000263111506155467400244640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "symmetry.h" #include "symmetrywidget.h" #include "symmetryutil.h" // #include // #include #include #include #include #include #include // using Avogadro::Core::CrystalTools; // using Avogadro::Core::UnitCell; using Avogadro::QtGui::Molecule; using namespace msym; using namespace Avogadro::QtPlugins::SymmetryUtil; namespace Avogadro::QtPlugins { Symmetry::Symmetry(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), m_symmetryWidget(nullptr), m_viewSymmetryAction(new QAction(this)) { m_ctx = msymCreateContext(); m_viewSymmetryAction->setText(tr("Symmetry…")); m_viewSymmetryAction->setProperty("menu priority", -50); connect(m_viewSymmetryAction, SIGNAL(triggered()), SLOT(viewSymmetry())); m_actions.push_back(m_viewSymmetryAction); /* connect(m_symmetryWidget, SIGNAL(clicked()), this, SLOT(detectSymmetry())); connect(m_ui->symmetrizeButton, SIGNAL(clicked()), this, SLOT(symmetrize())); connect(m_ui->toleranceCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(toleranceChanged(int)));*/ /* m_standardOrientationAction->setText(tr("Rotate to Standard &Orientation")); connect(m_standardOrientationAction, SIGNAL(triggered()), SLOT(standardOrientation())); m_actions.push_back(m_standardOrientationAction); m_standardOrientationAction->setProperty("menu priority", -250);*/ updateActions(); } Symmetry::~Symmetry() { if (m_symmetryWidget) m_symmetryWidget->deleteLater(); qDeleteAll(m_actions); m_actions.clear(); if (m_ctx != nullptr) { msymReleaseContext(m_ctx); } } QList Symmetry::actions() const { return m_actions; } QStringList Symmetry::menuPath(QAction*) const { return QStringList() << tr("&Analyze") << tr("&Properties"); } void Symmetry::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_symmetryWidget) m_symmetryWidget->setMolecule(m_molecule); if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); m_dirty = true; } void Symmetry::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); auto changes = static_cast(c); if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); /* if (changes & Molecule::UnitCell) { if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); }*/ if ((changes & Molecule::Atoms) && (changes & Molecule::Modified || changes & Molecule::Added || changes & Molecule::Removed)) { m_dirty = true; } } void Symmetry::updateActions() { // Disable everything for NULL molecules. if (!m_molecule) { foreach (QAction* action, m_actions) action->setEnabled(false); return; } else { foreach (QAction* action, m_actions) action->setEnabled(true); } /* if (m_molecule->unitCell()) { foreach (QAction *action, m_actions) action->setEnabled(true); m_toggleUnitCellAction->setText(tr("Remove &Unit Cell")); } else { foreach (QAction *action, m_actions) action->setEnabled(false); m_toggleUnitCellAction->setEnabled(true); m_toggleUnitCellAction->setText(tr("Add &Unit Cell")); }*/ } void Symmetry::viewSymmetry() { if (!m_symmetryWidget) { m_symmetryWidget = new SymmetryWidget(qobject_cast(parent())); m_symmetryWidget->setMolecule(m_molecule); connect(m_symmetryWidget, SIGNAL(detectSymmetry()), SLOT(detectSymmetry())); connect(m_symmetryWidget, SIGNAL(symmetrizeMolecule()), SLOT(symmetrizeMolecule())); } if (m_dirty) { detectSymmetry(); } m_symmetryWidget->show(); } void Symmetry::detectSymmetry() { unsigned int length = m_molecule->atomCount(); if (m_molecule == nullptr || m_molecule->atomPositions3d().size() != length || length < 2) return; // if one atom = Kh if (length == 1) { m_symmetryWidget->setPointGroupSymbol(QString("Kh")); return; } // interface with libmsym msym_error_t ret = MSYM_SUCCESS; msym_element_t* elements = nullptr; char point_group[6]; double cm[3], radius = 0.0; /* Do not free these variables */ const msym_symmetry_operation_t* msops = nullptr; const msym_subgroup_t* msg = nullptr; const msym_equivalence_set_t* mes = nullptr; int mesl = 0, msgl = 0, msopsl = 0; // initialize the c-style array of atom names and coordinates msym_element_t* a; a = (msym_element_t*)malloc(length * sizeof(msym_element_t)); memset(a, 0, length * sizeof(msym_element_t)); for (Index i = 0; i < length; ++i) { Vector3 ipos = m_molecule->atomPositions3d()[i]; // this is yucky, but msym uses type for id :-( a[i].id = reinterpret_cast(i); a[i].n = m_molecule->atomicNumbers()[i]; if (a[i].n < 1 || a[i].n > 118) a[i].n = 1; // pretend to be an H atom for libmsym a[i].v[0] = ipos[0]; a[i].v[1] = ipos[1]; a[i].v[2] = ipos[2]; } elements = a; if (m_ctx != nullptr) { msymReleaseContext(m_ctx); m_ctx = msymCreateContext(); } // Set the thresholds // switch (m_dock->toleranceCombo->currentIndex()) { msym_thresholds_t* thresholds = m_symmetryWidget->getThresholds(); msymSetThresholds(m_ctx, thresholds); // At any point, we'll set the text to NULL which will use C1 instead if (MSYM_SUCCESS != (ret = msymSetElements(m_ctx, length, elements))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } if (MSYM_SUCCESS != (ret = msymFindSymmetry(m_ctx))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } /* Get the point group name */ if (MSYM_SUCCESS != (ret = msymGetPointGroupName(m_ctx, sizeof(char[6]), point_group))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } if (MSYM_SUCCESS != (ret = msymGetSymmetryOperations(m_ctx, &msopsl, &msops))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } if (MSYM_SUCCESS != (ret = msymGetEquivalenceSets(m_ctx, &mesl, &mes))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } if (MSYM_SUCCESS != (ret = msymGetCenterOfMass(m_ctx, cm))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } if (MSYM_SUCCESS != (ret = msymGetRadius(m_ctx, &radius))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } if (point_group[1] != '0') { if (MSYM_SUCCESS != (ret = msymGetSubgroups(m_ctx, &msgl, &msg))) { free(elements); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(nullptr)); m_symmetryWidget->setEquivalenceSets(0, nullptr); m_symmetryWidget->setSymmetryOperations(0, nullptr); m_symmetryWidget->setSubgroups(0, nullptr); qDebug() << "Error:" << msymErrorString(ret) << " " << msymGetErrorDetails(); return; } } else { m_symmetryWidget->setSubgroups(0, nullptr); } // TODO: Subgroups // if(MSYM_SUCCESS != (ret = msymGetSubgroups(ctx, &msgl, &msg))) goto err; // printf("Found point group [0] %s select subgroup\n",point_group); // for(int i = 0; i < msgl;i++) printf("\t [%d] %s\n",i+1,msg[i].name); m_symmetryWidget->setPointGroupSymbol(pointGroupSymbol(point_group)); m_symmetryWidget->setEquivalenceSets(mesl, mes); m_symmetryWidget->setSymmetryOperations(msopsl, msops); m_symmetryWidget->setSubgroups(msgl, msg); m_symmetryWidget->setCenterOfMass(cm); m_symmetryWidget->setRadius(radius); // m_symmetryWidget->m_ui->pointGroupLabel->setText(pgSymbol(point_group)); qDebug() << "detected symmetry" << point_group; free(elements); m_dirty = false; } void Symmetry::symmetrizeMolecule() { qDebug() << "symmetrize"; unsigned int length = m_molecule->atomCount(); if (m_molecule == nullptr || m_molecule->atomPositions3d().size() != length || length < 2) return; // if one atom = Kh msym_element_t* melements = nullptr; int mlength = 0; double symerr = 0.0; msym_error_t ret = MSYM_SUCCESS; // detectSymmetry(); if (MSYM_SUCCESS != (ret = msymSymmetrizeElements(m_ctx, &symerr))) return; if (MSYM_SUCCESS != (ret = msymGetElements(m_ctx, &mlength, &melements))) return; if (mlength != static_cast(length)) return; for (Index i = 0; i < length; ++i) { m_molecule->atomPositions3d()[i] = Vector3(melements[i].v); } m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Modified); } /* void Symmetry::standardOrientation() { CrystalTools::rotateToStandardOrientation(*m_molecule, CrystalTools::TransformAtoms); m_molecule->emitChanged(Molecule::Modified | Molecule::Atoms | Molecule::UnitCell); }*/ } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetry.h000066400000000000000000000030711506155467400241270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SYMMETRY_H #define AVOGADRO_QTPLUGINS_SYMMETRY_H #include #include "symmetrywidget.h" namespace msym { extern "C" { #include } } // namespace msym namespace Avogadro { namespace QtPlugins { class SymmetryWidget; /** * @brief symmetry functionality. */ class Symmetry : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit Symmetry(QObject* parent_ = nullptr); ~Symmetry() override; QString name() const override { return tr("Symmetry"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); private slots: void updateActions(); void viewSymmetry(); void detectSymmetry(); void symmetrizeMolecule(); private: QList m_actions; QtGui::Molecule* m_molecule; SymmetryWidget* m_symmetryWidget; QAction* m_viewSymmetryAction; msym::msym_context m_ctx; bool m_dirty = true; }; inline QString Symmetry::description() const { return tr("Provide symmetry functionality."); } } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SYMMETRY_H avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetryscene.cpp000066400000000000000000000110211506155467400254720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "symmetryscene.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Avogadro; namespace Avogadro::QtPlugins { using Core::Array; using Rendering::ArcSector; using Rendering::CylinderGeometry; using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::MeshGeometry; using Rendering::SphereGeometry; SymmetryScene::SymmetryScene(QObject* p) : QtGui::ScenePlugin(p), m_enabled(true) { } SymmetryScene::~SymmetryScene() {} void SymmetryScene::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { QVariant origo = molecule.property("SymmetryOrigo"); QVariant radius = molecule.property("SymmetryRadius"); QVariant inversion = molecule.property("SymmetryInversion"); QVariant properRotation = molecule.property("SymmetryProperRotationVariantList"); QVariant improperRotation = molecule.property("SymmetryImproperRotationVariantList"); QVariant reflection = molecule.property("SymmetryReflectionVariantList"); if (!origo.isValid() || !radius.isValid()) { return; } auto qorigo = origo.value(); Vector3f forigo = Vector3f(qorigo.x(), qorigo.y(), qorigo.z()); float fradius = radius.toFloat(); auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; spheres->identifier().molecule = reinterpret_cast(&molecule); spheres->identifier().type = Rendering::AtomType; geometry->addDrawable(spheres); auto* cylinders = new CylinderGeometry; cylinders->identifier().molecule = &molecule; cylinders->identifier().type = Rendering::BondType; geometry->addDrawable(cylinders); if (inversion.isValid()) { Vector3ub color(0, 0, 255); auto qvec = inversion.value(); Vector3f fvec = Vector3f(qvec.x(), qvec.y(), qvec.z()); spheres->addSphere(fvec, color, 0.3f); } if (properRotation.isValid()) { Vector3ub color(255, 0, 0); QVariantList properRotationVariantList = properRotation.toList(); foreach (QVariant qv, properRotationVariantList) { auto qvec = qv.value(); Vector3f fvec = Vector3f(qvec.x(), qvec.y(), qvec.z()); cylinders->addCylinder(forigo, forigo + 1.1 * fradius * fvec, 0.05f, color); } } if (improperRotation.isValid()) { QVariantList improperRotationVariantList = improperRotation.toList(); } if (reflection.isValid()) { Vector3ub color(255, 255, 0); QVariantList reflectionVariantList = reflection.toList(); foreach (QVariant qv, reflectionVariantList) { auto qvec = qv.value(); // normal to the mirror plane Vector3f vecNormal = Vector3f(qvec.x(), qvec.y(), qvec.z()); // get an arbitrary vector in the plane, scaled by fradius Vector3f vecPlane; if (qvec.z() < qvec.x()) vecPlane = Vector3f(-qvec.y(), qvec.x(), 0); else vecPlane = Vector3f(0, -qvec.z(), qvec.y()); vecPlane = vecPlane.normalized() * fradius; auto* sect = new ArcSector; geometry->addDrawable(sect); sect->setColor(Vector3ub(color)); sect->setOpacity(127); // 50% sect->setRenderPass(Rendering::TranslucentPass); sect->setArcSector(forigo, vecPlane, vecNormal, 360.0f, 5.f); // cylinders->addCylinder(forigo - fvec * 0.025f, forigo + fvec * // 0.025f, fradius, color); } } } void SymmetryScene::processEditable(const QtGui::RWMolecule& molecule, Rendering::GroupNode& node) { process(molecule.molecule(), node); } bool SymmetryScene::isEnabled() const { return m_enabled; } bool SymmetryScene::isActiveLayerEnabled() const { return m_enabled; } void SymmetryScene::setEnabled(bool enable) { m_enabled = enable; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetryscene.h000066400000000000000000000025041506155467400251450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SYMMETRYSCENE_H #define AVOGADRO_QTPLUGINS_SYMMETRYSCENE_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief Render the symmetry elements */ class SymmetryScene : public QtGui::ScenePlugin { Q_OBJECT public: explicit SymmetryScene(QObject* parent = nullptr); ~SymmetryScene() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; void processEditable(const QtGui::RWMolecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Symmetry Elements"); } QString description() const override { return tr("Render symmetry elements."); } bool isEnabled() const override; bool isActiveLayerEnabled() const override; void setEnabled(bool enable) override; private: bool m_enabled; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SYMMETRYSCENE_H avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetryutil.cpp000066400000000000000000000054371506155467400253700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2015 Marcus Johansson This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #include "symmetryutil.h" #define _ORIENT_HORIZONTAL \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_ORIENTATION_HORIZONTAL #define _ORIENT_VERTICAL \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_ORIENTATION_VERTICAL #define _ORIENT_DIHEDRAL \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_ORIENTATION_DIHEDRAL namespace Avogadro::QtPlugins::SymmetryUtil { QString pointGroupSymbol(const char* point_group) { QString pointGroup(point_group); if (pointGroup.isEmpty()) pointGroup = "C1"; // default // check if we need an infinity symbol if (pointGroup[1] == '0') pointGroup = pointGroup.replace(1, 1, "\u221e"); // infinity symbol // After first character, point group should subscript everything pointGroup.insert(1, ""); pointGroup.append(""); return pointGroup; } QString operationSymbol(const msym::msym_symmetry_operation_t* operation) { // omit first power QString symbol; if (operation->type == IDENTITY) { symbol = QString("E"); } else if (operation->type == INVERSION) { symbol = QString("i"); } else if (operation->type == PROPER_ROTATION) { symbol = QString("C%1").arg(operation->order); if (operation->power > 1) // add the power for ^2, ^3, etc. symbol.append(QString("%2").arg(operation->power)); } else if (operation->type == IMPROPER_ROTATION) { symbol = QString("S%1").arg(operation->order); if (operation->power > 1) // add the power for ^2, ^3, etc. symbol.append(QString("%2").arg(operation->power)); } else if (operation->type == REFLECTION) { symbol = QString("σ"); if (operation->orientation == _ORIENT_HORIZONTAL) symbol.append("h"); else if (operation->orientation == _ORIENT_DIHEDRAL) symbol.append("d"); else if (operation->orientation == _ORIENT_VERTICAL) symbol.append("v"); } return symbol; } } // namespace Avogadro::QtPlugins::SymmetryUtil avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetryutil.h000066400000000000000000000040571506155467400250320ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2015 Marcus Johansson This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SYMMETRYUTIL_H #define AVOGADRO_QTPLUGINS_SYMMETRYUTIL_H #include namespace msym { extern "C" { #include } } // namespace msym #ifndef IDENTITY #define IDENTITY \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_TYPE_IDENTITY #endif #ifndef REFLECTION #define REFLECTION \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_TYPE_REFLECTION #endif #ifndef INVERSION #define INVERSION \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_TYPE_INVERSION #endif #ifndef PROPER_ROTATION #define PROPER_ROTATION \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_TYPE_PROPER_ROTATION #endif #ifndef IMPROPER_ROTATION #define IMPROPER_ROTATION \ msym::_msym_symmetry_operation::MSYM_SYMMETRY_OPERATION_TYPE_IMPROPER_ROTATION #endif namespace Avogadro { namespace QtPlugins { namespace SymmetryUtil { QString pointGroupSymbol(const char* point_group); QString operationSymbol(const msym::msym_symmetry_operation_t* operation); } // namespace SymmetryUtil } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SYMMETRYUTIL_H avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetrywidget.cpp000066400000000000000000000277501506155467400257000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "symmetrywidget.h" #include "symmetryutil.h" #include "ui_symmetrywidget.h" #include #include #include #include using Avogadro::QtGui::Molecule; using Avogadro::QtGui::RichTextDelegate; using namespace msym; using namespace Avogadro::QtPlugins::SymmetryUtil; namespace Avogadro::QtPlugins { msym_thresholds_t tight_thresholds = { // all defaults /*.zero =*/1.0e-3, /*.geometry =*/1.0e-3, /*.angle =*/1.0e-3, /*.equivalence =*/5.0e-4, /*.eigfact =*/1.0e-3, /*.permutation =*/5.0e-3, /*.orthogonalization =*/0.1 }; msym_thresholds_t medium_thresholds = { /*.zero =*/1.0e-2, /*.geometry =*/1.0e-2, /*.angle =*/1.0e-2, /*.equivalence =*/6.3e-3, /*.eigfact =*/1.0e-3, /*.permutation =*/1.58e-2, /*.orthogonalization =*/0.1 }; msym_thresholds_t loose_thresholds = { /*.zero =*/0.06, /*.geometry =*/0.1, /*.angle =*/0.06, /*.equivalence =*/0.025, /*.eigfact =*/1.0e-3, /*.permutation =*/1.0e-1, /*.orthogonalization =*/0.1 }; msym_thresholds_t sloppy_thresholds = { /*.zero =*/0.1, /*.geometry =*/0.1, /*.angle =*/0.1, /*.equivalence =*/0.075, /*.eigfact =*/1.0e-3, /*.permutation =*/1.0e-1, /*.orthogonalization =*/0.1 }; SymmetryWidget::SymmetryWidget(QWidget* parent_) : QWidget(parent_), m_ui(new Ui::SymmetryWidget), m_equivalenceTreeModel(new QStandardItemModel(this)), m_operationsTableModel(new OperationsTableModel(this)), m_subgroupsTreeModel(new QStandardItemModel(this)), m_molecule(nullptr), m_es(nullptr), m_sops(nullptr), m_sg(nullptr), m_sopsl(0), m_sgl(0), m_radius(0.0) { setWindowFlags(Qt::Dialog); m_ui->setupUi(this); m_ui->equivalenceTree->setModel(m_equivalenceTreeModel); m_ui->operationsTable->setModel(m_operationsTableModel); m_ui->operationsTable->setItemDelegateForColumn( OperationsTableModel::ColumnType, new RichTextDelegate(this)); m_ui->subgroupsTree->setModel(m_subgroupsTreeModel); m_ui->subgroupsTree->setItemDelegateForColumn(0, new RichTextDelegate(this)); connect(m_ui->detectSymmetryButton, SIGNAL(clicked()), SIGNAL(detectSymmetry())); connect(m_ui->symmetrizeMoleculeButton, SIGNAL(clicked()), SIGNAL(symmetrizeMolecule())); connect( m_ui->equivalenceTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(equivalenceSelectionChanged(const QItemSelection&, const QItemSelection&))); connect( m_ui->operationsTable->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(operationsSelectionChanged(const QItemSelection&, const QItemSelection&))); connect( m_ui->subgroupsTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT( subgroupsSelectionChanged(const QItemSelection&, const QItemSelection&))); } SymmetryWidget::~SymmetryWidget() { delete m_ui; } void SymmetryWidget::setMolecule(QtGui::Molecule* molecule) { if (molecule != m_molecule) { if (m_molecule) m_molecule->disconnect(this); m_molecule = molecule; if (m_molecule) { connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); } } } void SymmetryWidget::moleculeChanged([[maybe_unused]] unsigned int changes) { /* if (changes & Molecule::UnitCell) revert();*/ } void SymmetryWidget::operationsSelectionChanged( [[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) { if (!m_molecule) return; if (m_ui->tabWidget->currentWidget() != m_ui->subgroupsTab) { // qDebug() << "subgroupsTab not selected"; m_ui->subgroupsTree->selectionModel()->reset(); } QModelIndexList selection = m_ui->operationsTable->selectionModel()->selectedRows(); // qDebug() << "operations changed"; // qDebug() << "selection " << selection.size(); QVariantList reflectionVariantList; QVariantList properRotationVariantList; QVariantList improperRotationVariantList; m_molecule->setProperty("SymmetryOrigo", QVariant()); m_molecule->setProperty("SymmetryRadius", QVariant()); m_molecule->setProperty("SymmetryInversion", QVariant()); m_molecule->setProperty("SymmetryProperRotationVariantList", QVariant()); m_molecule->setProperty("SymmetryImproperRotationVariantList", QVariant()); m_molecule->setProperty("SymmetryReflectionVariantList", QVariant()); // qDebug() << "cleared elements"; if (m_sopsl > 0 && selection.size() > 0) { m_molecule->setProperty("SymmetryOrigo", m_cm); m_molecule->setProperty("SymmetryRadius", m_radius); } foreach (QModelIndex i, selection) { int row = i.row(); if (!i.isValid() || row >= m_sopsl) continue; float x = m_sops[row].v[0], y = m_sops[row].v[1], z = m_sops[row].v[2]; switch (m_sops[row].type) { case IDENTITY: break; case PROPER_ROTATION: properRotationVariantList.append(QVector3D(x, y, z)); break; case IMPROPER_ROTATION: improperRotationVariantList.append(QVector3D(x, y, z)); break; case REFLECTION: reflectionVariantList.append(QVector3D(x, y, z)); break; case INVERSION: m_molecule->setProperty("SymmetryInversion", m_cm); break; default: break; } } if (properRotationVariantList.size() > 0) m_molecule->setProperty("SymmetryProperRotationVariantList", properRotationVariantList); if (improperRotationVariantList.size() > 0) m_molecule->setProperty("SymmetryImproperRotationVariantList", improperRotationVariantList); if (reflectionVariantList.size() > 0) m_molecule->setProperty("SymmetryReflectionVariantList", reflectionVariantList); /* A little bit ugly, but it'll do for now */ m_molecule->emitChanged(QtGui::Molecule::Atoms); } void SymmetryWidget::subgroupsSelectionChanged( [[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) { // QModelIndexList selection = // m_ui->subgroupsTree->selectionModel()->selectedIndexes(); QModelIndex i = m_ui->subgroupsTree->selectionModel()->selectedIndexes().first(); // qDebug() << "subgroupsSelectionChanged"; if (!i.isValid()) return; // qDebug() << "valid"; int sgi = i.data(Qt::UserRole).value(); // qDebug() << "index " << sgi; if (sgi < 0 || sgi >= m_sgl) return; const msym::msym_subgroup_t* sg = &m_sg[sgi]; // m_ui->operationsTable->selectionModel()->clear(); QItemSelectionModel* selectionModel = m_ui->operationsTable->selectionModel(); // selectionModel->clear(); QItemSelection selection; for (int j = 0; j < sg->order; j++) { int row = static_cast(sg->sops[j] - m_sops); QModelIndex left = m_operationsTableModel->index(row, 0); QModelIndex right = m_operationsTableModel->index( row, m_operationsTableModel->columnCount(left) - 1); // if (!left.isValid() || !right.isValid()) // qDebug() << "invalid index " << j; QItemSelection sel(left, right); selection.merge(sel, QItemSelectionModel::Select); } QModelIndexList tmp = selection.indexes(); // foreach (QModelIndex j, tmp) { // qDebug() << "selecting " << j.row() << " " << j.column(); // } selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); } void SymmetryWidget::equivalenceSelectionChanged( [[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) { QModelIndex i = m_ui->equivalenceTree->selectionModel()->selectedIndexes().first(); // qDebug() << "equivalenceSelectionChanged"; if (!i.isValid()) return; int atomInGroup = i.data(Qt::UserRole).value(); QModelIndex g = i.parent(); if (!g.isValid()) return; int group = g.data(Qt::UserRole).value(); // qDebug() << "valid " << group << atomInGroup; if (group < 0 || group >= m_esl) return; // TODO: okay, now we have to find the atoms and select them if (!m_molecule) return; const msym_equivalence_set_t* smes = &m_es[group]; if (smes == nullptr) return; const msym_element_t* a = smes->elements[atomInGroup]; if (a == nullptr) return; Index length = m_molecule->atomCount(); // unselect all the atoms for (Index iat = 0; iat < length; ++iat) { m_molecule->setAtomSelected(iat, false); } // this is yucky, but libmsym uses for id auto selectedAtom = reinterpret_cast(a->id); m_molecule->setAtomSelected(selectedAtom, true); m_molecule->emitChanged(QtGui::Molecule::Atoms); } void SymmetryWidget::setRadius(double radius) { m_radius = radius; } void SymmetryWidget::setCenterOfMass(double cm[3]) { m_cm = QVector3D(cm[0], cm[1], cm[2]); } void SymmetryWidget::setPointGroupSymbol(QString pg) { m_ui->pointGroupLabel->setText(pg); m_molecule->setData("pointgroup", pg.toStdString()); } void SymmetryWidget::setSymmetryOperations( int sopsl, const msym::msym_symmetry_operation_t* sops) { m_sops = sops; m_sopsl = sopsl; m_operationsTableModel->setOperations(sopsl, sops); m_molecule->setProperty("SymmetryOrigo", QVariant()); m_molecule->setProperty("SymmetryRadius", QVariant()); m_molecule->setProperty("SymmetryInversion", QVariant()); m_molecule->setProperty("SymmetryProperRotationVariantList", QVariant()); m_molecule->setProperty("SymmetryImproperRotationVariantList", QVariant()); m_molecule->setProperty("SymmetryReflectionVariantList", QVariant()); /* need another change event */ m_molecule->emitChanged(QtGui::Molecule::Atoms); } void SymmetryWidget::setEquivalenceSets(int esl, const msym::msym_equivalence_set_t* es) { m_esl = esl; m_es = es; m_equivalenceTreeModel->clear(); for (int i = 0; i < esl; i++) { auto* const parent = new QStandardItem; QString label = tr("Group %1").arg(QString::number(i + 1)); parent->setText(label); parent->setData(i, Qt::UserRole); m_equivalenceTreeModel->appendRow(parent); const msym_equivalence_set_t* smes = &es[i]; for (int j = 0; j < smes->length; j++) { auto* const child = new QStandardItem; label = tr("%1 %2").arg(smes->elements[j]->name).arg(QString::number(j + 1)); child->setText(label); child->setData(j, Qt::UserRole); parent->appendRow(child); } } } void SymmetryWidget::setSubgroups(int sgl, const msym::msym_subgroup_t* sg) { m_sg = sg; m_sgl = sgl; m_subgroupsTreeModel->clear(); for (int i = 0; i < sgl; i++) { if (sg[i].order <= 2) continue; auto* const parent = new QStandardItem; parent->setText(pointGroupSymbol(sg[i].name)); parent->setData(i, Qt::UserRole); m_subgroupsTreeModel->appendRow(parent); for (auto generator : sg[i].generators) { if (generator == nullptr) continue; // qDebug() << "child " << sg[i].generators[j] - m_sg << " " // << sg[i].generators[j] << " " << m_sg; auto* const child = new QStandardItem; child->setText(pointGroupSymbol(generator->name)); child->setData(static_cast(generator - m_sg), Qt::UserRole); parent->appendRow(child); } } } msym_thresholds_t* SymmetryWidget::getThresholds() const { msym_thresholds_t* thresholds = nullptr; switch (m_ui->toleranceCombo->currentIndex()) { case 3: // sloppy thresholds = &sloppy_thresholds; break; case 2: // loose thresholds = &loose_thresholds; break; case 1: // normal thresholds = &medium_thresholds; break; case 0: // tight default: thresholds = &tight_thresholds; } return thresholds; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetrywidget.h000066400000000000000000000051521506155467400253350ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_SYMMETRYWIDGET_H #define AVOGADRO_QTPLUGINS_SYMMETRYWIDGET_H #include #include #include #include #include #include #include #include "operationstablemodel.h" // class QPlainTextEdit; namespace msym { extern "C" { #include } } // namespace msym namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { namespace Ui { class SymmetryWidget; } /** * @brief The SymmetryWidget class provides a widget for handling symmetry * unit cell. */ class SymmetryWidget : public QWidget { Q_OBJECT public: explicit SymmetryWidget(QWidget* parent = nullptr); ~SymmetryWidget() override; void setMolecule(QtGui::Molecule* molecule); signals: void detectSymmetry(); void symmetrizeMolecule(); public slots: void moleculeChanged(unsigned int changes); void setPointGroupSymbol(QString pg); void setEquivalenceSets(int mesl, const msym::msym_equivalence_set_t* es); void setSymmetryOperations(int sopsl, const msym::msym_symmetry_operation_t* sops); void setSubgroups(int sgl, const msym::msym_subgroup_t* sg); void setCenterOfMass(double cm[3]); void setRadius(double radius); msym::msym_thresholds_t* getThresholds() const; private slots: void equivalenceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void operationsSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void subgroupsSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); private: Ui::SymmetryWidget* m_ui; QStandardItemModel* m_equivalenceTreeModel; OperationsTableModel* m_operationsTableModel; QStandardItemModel* m_subgroupsTreeModel; QtGui::Molecule* m_molecule; QVector3D m_cm; const msym::msym_equivalence_set_t* m_es; const msym::msym_symmetry_operation_t* m_sops; const msym::msym_subgroup_t* m_sg; int m_esl, m_sopsl, m_sgl; double m_radius; void addSubgroup(QStandardItem*, msym::msym_subgroup_t*); }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_SYMMETRYWIDGET_H avogadrolibs-1.101.0/avogadro/qtplugins/symmetry/symmetrywidget.ui000066400000000000000000000165241506155467400255300ustar00rootroot00000000000000 Avogadro::QtPlugins::SymmetryWidget 0 0 412 584 Symmetry 18 75 true true C<sub>1 true 0 Molecule Symmetrically equivalent atoms: 0 0 QAbstractItemView::SingleSelection QAbstractItemView::SelectRows false false Lock Symmetry Qt::Horizontal 40 20 Symmetrize Operations Symmetry elements: 0 0 QAbstractItemView::MultiSelection QAbstractItemView::SelectRows true true Subgroups Subgroups: QAbstractItemView::NoEditTriggers QAbstractItemView::SelectRows false Qt::Horizontal 40 20 Select Qt::Horizontal 40 20 Tolerance: 1 Tight Normal Loose Very Loose Detect Symmetry true avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/000077500000000000000000000000001506155467400227045ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/CMakeLists.txt000066400000000000000000000023371506155467400254510ustar00rootroot00000000000000include(ExternalProject) find_package(Qt${QT_VERSION} REQUIRED COMPONENTS Svg) set(template_srcs templatetool.cpp templatetoolwidget.cpp ) set(template_uis templatetoolwidget.ui ) set(template_rcs template.qrc ) avogadro_plugin(TemplateTool "Template tool" ToolPlugin templatetool.h TemplateTool "${template_srcs}" "${template_uis}" "${template_rcs}" ) # Install the fragments set(_fragments "${AvogadroLibs_SOURCEDATA_DIR}/fragments") # Look in parallel directory for the molecule fragment repository if(NOT EXISTS "${_fragments}") # download molecules... ExternalProject_Add(fragments GIT_REPOSITORY https://github.com/openchemistry/fragments GIT_TAG main # or https://github.com/OpenChemistry/molecules/archive/refs/heads/master.zip SOURCE_DIR "${AvogadroLibs_SOURCEDATA_DIR}/fragments" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ) endif() install(DIRECTORY "${AvogadroLibs_SOURCEDATA_DIR}/fragments" DESTINATION "${INSTALL_DATA_DIR}/avogadro2" PATTERN ".git" EXCLUDE PATTERN ".gitignore" EXCLUDE PATTERN "README.md" EXCLUDE PATTERN "LICENSE" EXCLUDE PATTERN "*.smi" EXCLUDE ) target_link_libraries(TemplateTool PRIVATE Avogadro::QtOpenGL Qt::Svg ) avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/000077500000000000000000000000001506155467400243475ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/1-lin.cjson000066400000000000000000000005661506155467400263340ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [ -0.21242, 0.76221, 0.0, 1.78758, 0.76221, 0.0 ] }, "elements": { "number": [ 26, 1 ] } }, "bonds": { "connections": { "index": [ 0, 1 ] }, "order": [ 1 ] } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/1-lin.svg000066400000000000000000000067571506155467400260270ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/2-lin.cjson000066400000000000000000000007661506155467400263370ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, -2.0, 0.0, 0.0 ] }, "elements": { "number": [ 77, 1, 1 ] }, "selected": [ false, false, false ] }, "bonds": { "connections": { "index": [ 0, 2, 0, 1 ] }, "order": [ 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/2-lin.svg000066400000000000000000000070211506155467400260110ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/3-tpl.cjson000066400000000000000000000012031506155467400263400ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ 0.13014, 0.16203, -0.18761, -1.7077, 0.63871, 0.44099, 1.03712, -1.47301, 0.52233, 1.061, 1.3204, -1.52614 ] }, "elements": { "number": [ 77, 1, 1, 1 ] }, "selected": [ false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 1, 0, 3, 0, 2 ] }, "order": [ 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/3-tpl.svg000066400000000000000000000076401506155467400260360ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/4-sqp.cjson000066400000000000000000000015111506155467400263470ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ -0.077, -0.077, 0.077, -0.07699999999999978, -0.07699999999999996, -1.923, 1.923, -0.07699999999999999, 0.07700000000000022, -0.07700000000000022, -0.07700000000000004, 2.077, -2.077, -0.07700000000000001, 0.07699999999999978 ] }, "elements": { "number": [ 77, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 4, 0, 1, 0, 2, 0, 3 ] }, "order": [ 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/4-sqp.svg000066400000000000000000000103411506155467400260330ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/4-tet.cjson000066400000000000000000000013661506155467400263500ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ -0.02551, -0.02115, 0.06899, 1.97449, -0.02117, 0.06896, -0.69221, -1.3061, -1.31101, -0.69216, 1.81647, -0.35381, -0.69217, -0.57378, 1.87181 ] }, "elements": { "number": [ 77, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 1, 0, 4, 0, 2, 0, 3 ] }, "order": [ 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/4-tet.svg000066400000000000000000000077601506155467400260370ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/5-spy.cjson000066400000000000000000000020531506155467400263620ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ -0.35415723030115415, 0.4712815725231753, 0.24930979069666648, -1.5038019120144048, -0.19870064929744152, -1.2438201551044432, 0.3209614119791839, -0.3618507984628015, 1.9375341176319567, 0.24338221789204112, 2.364054971070828, 0.003640261185014565, -2.0867960444671247, 0.30704277539467495, 1.2346952299835472, 0.9039555444319038, -0.8675942231549181, -0.5409812674560335 ] }, "elements": { "number": [ 77, 1, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 1, 0, 2, 0, 3, 0, 4, 0, 5 ] }, "order": [ 1, 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/5-spy.svg000066400000000000000000000104411506155467400260450ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/5-tbp.cjson000066400000000000000000000015251506155467400263370ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ -0.0, -0.03357, -0.19938, -2.0, -0.03342, -0.1998, 2.0, -0.03329, -0.19942, 0.00014, 1.50411, -1.47825, -0.00039, 0.3099, 1.77091, 0.00025, -1.90996, -0.89161 ] }, "elements": { "number": [ 77, 1, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 1, 0, 2, 0, 4, 0, 3, 0, 5 ] }, "order": [ 1, 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/5-tbp.svg000066400000000000000000000100521506155467400260150ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/6-oct.cjson000066400000000000000000000016441506155467400263420ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ -0.077, -0.077, 0.077, -0.077, 1.923, 0.077, 1.923, -0.077, 0.077, -0.077, -2.077, 0.077, -2.077, -0.077, 0.077, -0.077, -0.077, 2.077, -0.077, -0.077, -1.923 ] }, "elements": { "number": [ 77, 1, 1, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 4, 0, 6, 0, 1, 0, 2, 0, 5, 0, 3 ] }, "order": [ 1, 1, 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/6-oct.svg000066400000000000000000000105351506155467400260240ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/6-tpr.cjson000066400000000000000000000017421506155467400263610ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ -0.246829, 0.100072, -0.818583, -1.73205, -0.757422, 0.210409, 1.238392, -0.757422, 0.210409, -0.246829, 1.815057, 0.210409, -1.424432, -0.684997, -2.231706, 0.930774, -0.684997, -2.231706, -0.246829, 1.515345, -2.231736 ] }, "elements": { "number": [ 77, 1, 1, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 1, 0, 4, 0, 6, 0, 3, 0, 2, 0, 5 ] }, "order": [ 1, 1, 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/6-tpr.svg000066400000000000000000000105411506155467400260410ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/7-pbp.cjson000066400000000000000000000017441506155467400263400ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.618, 1.9021, 0.0, -1.618, 1.1755, 0.0, -1.618, -1.1755, 0.0, 0.618, -1.9021, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, -2.0 ] }, "elements": { "number": [ 77, 1, 1, 1, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 3, 0, 4, 0, 5, 0, 2, 0, 1, 0, 6, 0, 7 ] }, "order": [ 1, 1, 1, 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/7-pbp.svg000066400000000000000000000104131506155467400260140ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/8-sqa.cjson000066400000000000000000000022431506155467400263370ustar00rootroot00000000000000{ "atoms": { "coords": { "3d": [ -0.42051, 0.42051, -0.51085, -1.81026, -0.96925, -0.8812, -1.81026, 1.81026, -0.14049, -1.15939, -0.31837, 1.19447, -1.15939, 1.15939, -2.21616, 0.96925, -0.96925, -0.14049, 0.96925, 1.81026, -0.8812, 0.31837, -0.31837, -2.21616, 0.31837, 1.15939, 1.19447 ] }, "elements": { "number": [ 77, 1, 1, 1, 1, 1, 1, 1, 1 ] }, "selected": [ false, false, false, false, false, false, false, false, false ] }, "bonds": { "connections": { "index": [ 0, 2, 0, 3, 0, 7, 0, 8, 0, 5, 0, 1, 0, 4, 0, 6 ] }, "order": [ 1, 1, 1, 1, 1, 1, 1, 1 ] }, "chemical json": 0 }avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/centers/8-sqa.svg000066400000000000000000000113051506155467400260210ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/000077500000000000000000000000001506155467400243255ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-ammine.cjson000066400000000000000000000007361506155467400267750ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.0773, 0.0496, 0.0855, 3.0905, 0.0496, 0.0855, 3.436, -0.43, -0.7761, 3.436, -0.4568, 0.9316, 3.436, 1.0355, 0.1009] }, "elements": { "number": [0, 7, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 1, 4] }, "order": [1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-ammine.svg000066400000000000000000000007761506155467400264640ustar00rootroot00000000000000H3N avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-aqua.cjson000066400000000000000000000006771506155467400264620ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.0536, -0.098, -0.0209, 2.9883, -0.0088, -0.0585, 3.2638, -0.8108, -0.5699, 3.2638, -0.2015, 0.8729] }, "elements": { "number": [0, 8, 1, 1] }, "formalCharges": [0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3] }, "order": [1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-aqua.svg000066400000000000000000000007761506155467400261450ustar00rootroot00000000000000H2O avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-carbonyl.cjson000066400000000000000000000006241506155467400273340ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.089, -0.0913, 0.0374, 3.1277, -0.0913, 0.0374, 4.2558, -0.0913, 0.0374] }, "elements": { "number": [0, 6, 8] }, "formalCharges": [0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2] }, "order": [1, 3] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-carbonyl.svg000066400000000000000000000011061506155467400270130ustar00rootroot00000000000000O avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-cyano.cjson000066400000000000000000000006221506155467400266320ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.2042, 0.0055, 0.0077, 3.2428, 0.0055, 0.0077, 4.3999, 0.0055, 0.0077] }, "elements": { "number": [0, 6, 7] }, "formalCharges": [0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2] }, "order": [1, 3] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-cyano.svg000066400000000000000000000011061506155467400263130ustar00rootroot00000000000000N avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-ethyl.cjson000066400000000000000000000011331506155467400266440ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [0.9683, 0.0534, -0.0549, 3.0576, 0.0387, -0.052, 3.5709, 1.4401, -0.3266, 3.4203, -0.6568, -0.838, 3.4203, -0.3089, 0.9383, 3.2026, 1.7841, -1.3154, 4.6813, 1.4429, -0.3271, 3.2026, 2.1318, 0.4594] }, "elements": { "number": [0, 6, 6, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6, 2, 7] }, "order": [1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-ethyl.svg000066400000000000000000000012271506155467400263330ustar00rootroot00000000000000CH3 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-isopropyl.cjson000066400000000000000000000013251506155467400275620ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [0.9846, 0.0494, -0.2094, 2.8608, -0.7946, 0.1606, 4.1822, -0.8515, -0.6677, 4.1471, 0.5458, -1.3436, 3.094, 0.0024, 0.9155, 2.3122, -1.1393, -0.7477, 4.1627, -1.8603, -0.1764, 5.089, -0.7591, -0.0606, 3.5918, 0.5227, -2.303, 5.2019, 0.867, -1.5346, 3.7175, 1.3558, -0.7258] }, "elements": { "number": [0, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6, 2, 7, 3, 8, 3, 9, 3, 10] }, "order": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-isopropyl.svg000066400000000000000000000014541506155467400272500ustar00rootroot00000000000000CH3H3C avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-methyl.cjson000066400000000000000000000007431506155467400270270ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.1178, 0.0228, -0.0627, 3.2072, 0.0228, -0.0627, 3.577, -0.5427, 0.8172, 3.577, 1.0675, -0.0129, 3.577, -0.4565, -0.9924] }, "elements": { "number": [0, 6, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 1, 4] }, "order": [1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-methyl.svg000066400000000000000000000007761506155467400265200ustar00rootroot00000000000000H3C avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-phosphine.cjson000066400000000000000000000007441506155467400275230ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.0953, -0.2634, 0.5595, 3.2748, 0.2355, -0.273, 4.5601, 0.5307, -0.7655, 3.5615, -1.1408, -0.348, 3.5616, 0.9504, 0.9054] }, "elements": { "number": [0, 15, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 1, 4] }, "order": [1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-phosphine.svg000066400000000000000000000010021506155467400271720ustar00rootroot00000000000000H3P avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-propyl.cjson000066400000000000000000000013221506155467400270440ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.002, 0.0539, 0.0093, 3.0907, 0.0978, 0.0266, 3.6245, -1.2305, -0.4957, 5.15, -1.2501, -0.5035, 3.4379, 0.2704, 1.0677, 3.438, 0.9334, -0.6178, 3.2487, -1.3907, -1.5287, 3.2487, -2.0515, 0.1513, 5.5428, -0.4441, -1.1594, 5.508, -2.2283, -0.8883, 5.5428, -1.107, 0.5258] }, "elements": { "number": [0, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 4, 1, 5, 2, 3, 2, 6, 2, 7, 3, 8, 3, 9, 3, 10] }, "order": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-propyl.svg000066400000000000000000000013621506155467400265330ustar00rootroot00000000000000H3C avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-pyridyl.cjson000066400000000000000000000014071506155467400272170ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.0294, 0.0221, -0.0881, 3.026, 0.0381, -0.0877, 3.7337, -1.2039, -0.0878, 5.0778, -1.2219, -0.0876, 3.6494, 1.1717, -0.0873, 5.1316, 1.214, -0.087, 5.8164, 0.0608, -0.0872, 3.179, -2.1363, -0.088, 5.6164, -2.1642, -0.0877, 3.0838, 2.0971, -0.0872, 5.6528, 2.1658, -0.0867, 6.9017, 0.0662, -0.0869] }, "elements": { "number": [0, 7, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 4, 2, 3, 2, 7, 3, 6, 3, 8, 4, 5, 4, 9, 5, 6, 5, 10, 6, 11] }, "order": [1, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-pyridyl.svg000066400000000000000000000017431506155467400267050ustar00rootroot00000000000000N avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-t-butyl.cjson000066400000000000000000000015141506155467400271220ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.0249, 0.0874, -0.0047, 3.1138, 0.0866, -0.0048, 3.601, 1.2326, -0.8956, 3.6008, 0.2849, 1.4333, 3.6, -1.2582, -0.5518, 3.2186, 2.2011, -0.5096, 3.2189, 1.0973, -1.9294, 4.7116, 1.2674, -0.9224, 3.2192, 1.2481, 1.8329, 4.7114, 0.2902, 1.4771, 3.2178, -0.5334, 2.079, 3.2175, -1.4078, -1.5835, 3.2172, -2.0857, 0.0821, 4.7106, -1.2998, -0.5686] }, "elements": { "number": [0, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 1, 4, 2, 5, 2, 6, 2, 7, 3, 8, 3, 9, 3, 10, 4, 11, 4, 12, 4, 13] }, "order": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-t-butyl.svg000066400000000000000000000020061506155467400266020ustar00rootroot00000000000000H3CCH3H3C avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-thiol.cjson000066400000000000000000000006251506155467400266430ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.1103, 0.0215, 0.2093, 3.4648, -0.0902, -0.0247, 3.6702, 0.5099, 1.2317] }, "elements": { "number": [0, 16, 1] }, "formalCharges": [0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2] }, "order": [1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/1-thiol.svg000066400000000000000000000006771506155467400263350ustar00rootroot00000000000000HS avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/2-acetylacetonate.cjson000066400000000000000000000015651506155467400306760ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [0.9269, 0.0395, 0.0886, 2.4266, -0.0362, 0.0885, 2.9945, -1.2566, 0.0909, 4.8719, -1.6033, 0.0916, 3.141, 1.0994, 0.086, 4.6197, 1.2093, 0.0855, 5.3721, 0.2448, 0.0876, 5.247, 2.5735, 0.0826, 0.4884, -0.9809, 0.0906, 0.5738, 0.575, -0.8182, 0.5742, 0.5783, 0.9936, 2.5871, 2.0354, 0.0842, 4.4834, 3.3797, 0.0807, 5.8809, 2.6882, -0.8219, 5.881, 2.6923, 0.9864] }, "elements": { "number": [6, 6, 8, 0, 6, 6, 8, 6, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 8, 0, 9, 0, 10, 1, 2, 1, 4, 2, 3, 3, 6, 4, 5, 4, 11, 5, 6, 5, 7, 7, 12, 7, 13, 7, 14] }, "order": [1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/2-acetylacetonate.svg000066400000000000000000000023731506155467400303570ustar00rootroot00000000000000H3COOCH3 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/2-bipyridine.cjson000066400000000000000000000022201506155467400276540ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-1.2892, 0.5895, 0.0072, -0.4545, 0.8573, 1.0314, 0.989, 0.5505, 0.8653, 1.4297, 0.0164, -0.2879, 0.4584, -0.2521, -1.3772, -0.7884, 0.03, -1.1968, -2.2016, -0.2648, -2.5582, -3.3561, 0.4967, -1.1095, -2.7509, 0.825, -0.0096, -3.528, 1.3924, 1.1263, -4.8534, 1.5553, 0.9844, -5.5084, 1.1725, -0.2889, -4.7626, 0.6635, -1.2845, -0.8061, 1.2862, 1.9621, 1.6834, 0.7559, 1.6734, 2.4818, -0.2138, -0.4227, 0.7898, -0.6798, -2.317, -3.05, 1.6721, 2.0583, -5.445, 1.9654, 1.797, -6.5785, 1.3024, -0.414, -5.2263, 0.3779, -2.2229] }, "elements": { "number": [6, 6, 6, 6, 6, 7, 0, 7, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 5, 0, 8, 1, 2, 1, 13, 2, 3, 2, 14, 3, 4, 3, 15, 4, 5, 4, 16, 5, 6, 6, 7, 7, 8, 7, 12, 8, 9, 9, 10, 9, 17, 10, 11, 10, 18, 11, 12, 11, 19, 12, 20] }, "order": [2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/2-bipyridine.svg000066400000000000000000000031151506155467400273430ustar00rootroot00000000000000NN avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/2-ethylenediamine.cjson000066400000000000000000000014571506155467400306750ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-0.7423, -1.1756, 0.0569, 0.672, -1.0105, -0.263, 1.1219, 0.3438, 0.2775, 0.2768, 1.3678, -0.3261, -1.588, 0.6211, -0.2649, -0.8457, -1.4042, 1.0721, -1.1769, -1.925, -0.5286, 0.8052, -1.0111, -1.3672, 1.2699, -1.842, 0.1694, 2.1929, 0.5375, 0.0495, 0.9828, 0.3512, 1.3811, 0.5428, 1.4916, -1.3303, 0.3571, 2.2761, 0.1844] }, "elements": { "number": [7, 6, 6, 7, 0, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 4, 0, 5, 0, 6, 1, 2, 1, 7, 1, 8, 2, 3, 2, 9, 2, 10, 3, 4, 3, 11, 3, 12] }, "order": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/2-ethylenediamine.svg000066400000000000000000000020031506155467400303440ustar00rootroot00000000000000NH2NH2 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/3-terpyridine.cjson000066400000000000000000000030001506155467400300520ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-2.435, -0.4449, 0.0045, -3.9703, -1.1042, -0.0403, -4.7944, 0.0949, 0.0093, -5.1806, 0.6699, 0.033, -2.8336, 2.1763, 0.0422, -1.8505, 1.0284, 0.0098, -0.1461, 0.7325, -0.0023, -0.045, -0.7321, 0.026, -1.6098, -1.23, 0.0742, -1.3704, -2.7071, -0.0926, -1.2584, -3.3823, 0.0449, 1.9232, -2.7617, -0.0036, 1.742, -1.0128, 0.0003, 2.4073, 0.7058, -0.0016, 4.125, 1.1695, -0.0046, 4.1835, 2.6925, 0.0222, 4.1867, 3.3395, 0.0357, 1.2765, 3.3191, 0.0199, 1.1155, 1.7621, 0.0023, -3.9733, -1.8817, -0.0119, -4.003, 0.652, -0.1589, -6.7462, -0.1365, -0.0075, -1.9548, 3.0949, 0.0132, -0.001, -2.503, -0.0081, -3.1347, -3.9383, 0.0385, 3.4435, -2.6954, 0.0066, 4.5078, 0.5806, -0.0045, 3.073, 2.7296, -0.0349, 5.8142, 3.6231, 0.0019, 0.0653, 3.5565, -0.0012] }, "elements": { "number": [6, 6, 6, 6, 6, 7, 0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 5, 0, 8, 1, 2, 1, 19, 2, 3, 2, 20, 3, 4, 3, 21, 4, 5, 4, 22, 5, 6, 6, 7, 6, 18, 7, 8, 7, 12, 8, 9, 9, 10, 9, 23, 10, 11, 10, 24, 11, 12, 11, 25, 12, 13, 13, 14, 13, 18, 14, 15, 14, 26, 15, 16, 15, 27, 16, 17, 16, 28, 17, 18, 17, 29] }, "order": [2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1] }, "properties": { "totalCharge": 0 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/3-terpyridine.svg000066400000000000000000000042461506155467400275520ustar00rootroot00000000000000NNN avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/4-phthalocyanine.cjson000066400000000000000000000052261506155467400305370ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [3.9509, 1.9554, 0.1005, 4.3848, 0.5891, 0.0914, 3.1562, -0.2601, 0.0535, 2.4607, 1.9065, 0.0658, 2.0139, 0.647, 0.0391, 1.6828, 3.0706, 0.0636, 0.3745, 3.0681, 0.0455, -0.53, 1.9655, 0.0261, -1.8965, 2.3751, 0.0145, -0.4893, 4.2846, 0.0445, -1.8592, 3.8668, 0.0279, -2.9818, 1.6442, -0.0051, -2.9716, 0.2439, -0.0213, -1.8932, -0.5463, -0.0194, -3.8211, -1.9276, -0.0599, -4.2311, -0.5542, -0.0458, -0.2027, -3.0233, -0.0246, 0.6691, -1.9246, -0.0068, 2.0061, -2.3495, 0.0137, 1.9887, -3.7864, 0.007, 0.6136, -4.2057, -0.0166, 3.1404, -1.5574, 0.0407, -1.5858, -3.0028, -0.0441, -2.3273, -1.9384, -0.0417, 4.8584, 2.9945, 0.1381, 5.7281, 0.2735, 0.1191, -0.1535, 5.6224, 0.0563, -2.8836, 4.7905, 0.0251, 3.0256, -4.7491, 0.0205, 0.2876, -5.5827, -0.0255, -2.536, 6.1613, 0.038, -1.2061, 6.5668, 0.0526, -5.5669, -0.2071, -0.0556, -6.5162, -1.2508, -0.0806, -6.1185, -2.5861, -0.0955, -4.7534, -2.9452, -0.0854, 6.2306, 2.6663, 0.166, 6.6536, 1.3389, 0.1565, 1.3156, -6.4986, -0.0114, 2.6818, -6.0823, 0.0113, 1.1022, -7.566, -0.0173, -0.7594, -5.8801, -0.0428, 4.5259, 4.0321, 0.1462, 6.0629, -0.764, 0.1127, 0.8905, 5.9414, 0.068, -3.9282, 4.4738, 0.0133, 4.0611, -4.4137, 0.0379, -3.333, 6.907, 0.036, -0.9599, 7.63, 0.0615, -5.8767, 0.8375, -0.0445, -7.5769, -1.001, -0.0882, -6.8709, -3.3755, -0.1153, -4.445, -3.9907, -0.0973, 6.9666, 3.4696, 0.1959, 7.7198, 1.1103, 0.1787, 3.4535, -6.8499, 0.0214, 0.06495, 0.0354, 0.00975] }, "elements": { "number": [6, 6, 6, 6, 7, 7, 6, 7, 6, 6, 6, 7, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [34, 51, 35, 52, 34, 35, 33, 34, 33, 50, 14, 35, 32, 33, 14, 15, 14, 23, 15, 32, 32, 49, 12, 15, 22, 23, 16, 22, 29, 41, 13, 23, 20, 29, 29, 38, 16, 20, 16, 17, 12, 13, 11, 12, 38, 40, 19, 20, 38, 39, 37, 54, 17, 18, 8, 11, 18, 19, 19, 28, 28, 39, 39, 55, 27, 45, 18, 21, 7, 8, 8, 10, 36, 53, 28, 46, 10, 27, 27, 30, 6, 7, 9, 10, 30, 47, 30, 31, 2, 4, 3, 4, 2, 21, 6, 9, 9, 26, 5, 6, 26, 31, 31, 48, 1, 2, 26, 44, 3, 5, 0, 3, 0, 1, 1, 25, 0, 24, 25, 43, 25, 37, 24, 42, 24, 36, 36, 37, 13, 56, 17, 56, 4, 56, 7, 56] }, "order": [1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1] } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/4-phthalocyanine.svg000066400000000000000000000076641506155467400302320ustar00rootroot00000000000000NNNNNNNN avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/4-porphin.cjson000066400000000000000000000036201506155467400272040ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [3.90733, 1.93279, -0.00421, 4.33924, 0.64502, -0.00313, 3.15144, -0.22323, 0.00548, 2.43339, 1.90813, 0.00598, 2.00264, 0.64364, 0.01209, 1.60179, 3.07842, 0.00092, 0.24764, 3.08235, 0.0466, -0.61327, 1.95224, 0.19906, -1.96569, 2.38724, 0.04589, -0.62988, 4.2638, -0.00643, -1.92123, 3.85827, -0.00681, -3.07442, 1.60992, -0.00076, -3.08747, 0.17431, 0.00373, -2.01111, -0.61682, 0.01047, -3.92878, -1.95167, -0.00705, -4.31077, -0.64821, -0.00792, -0.22576, -3.08254, 0.00861, 0.62054, -1.97491, 0.02319, 1.94802, -2.40011, 0.00967, 1.93083, -3.81847, -0.00897, 0.599, -4.23656, -0.00963, 3.11758, -1.57495, 0.00788, -1.65702, -3.07429, 0.00555, -2.45786, -1.98484, 0.00287, 4.50103, 2.84416, -0.00958, 5.3626, 0.27616, -0.00812, 2.12678, 4.04027, -0.0542, -0.0003, 0.00104, 0.0612, -0.2489, 5.28343, -0.04003, -2.81679, 4.47689, -0.04075, -4.05508, 2.0988, -0.05631, -4.55752, -2.83933, -0.013, -5.31896, -0.2401, -0.01403, -2.12636, -4.06801, 0.00159, 2.81215, -4.45539, -0.02153, 0.23969, -5.26286, -0.02278, 4.07081, -2.12188, 0.00474] }, "elements": { "number": [6, 6, 6, 6, 7, 6, 6, 7, 6, 6, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 0, 3, 3, 4, 2, 4, 3, 5, 5, 6, 6, 7, 7, 8, 6, 9, 9, 10, 8, 10, 8, 11, 11, 12, 12, 13, 14, 15, 12, 15, 16, 17, 17, 18, 18, 19, 19, 20, 16, 20, 2, 21, 18, 21, 13, 23, 14, 23, 22, 23, 16, 22, 0, 24, 1, 25, 5, 26, 22, 33, 9, 28, 10, 29, 11, 30, 14, 31, 15, 32, 21, 36, 19, 34, 20, 35, 13, 27, 17, 27, 4, 27, 7, 27] }, "order": [2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/4-porphin.svg000066400000000000000000000051571506155467400266760ustar00rootroot00000000000000NNNN avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/4-salen.cjson000066400000000000000000000035231506155467400266310ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-0.76776, 1.3622, -0.17865, -0.88161, 1.57658, -1.91998, 1.00117, 1.89979, -0.08942, -0.62927, 1.11743, 1.65144, -2.45458, 0.87088, -0.2497, 1.40887, 2.18715, 1.26719, -6.76696, 0.89456, 1.06651, 0.50135, 1.0595, -6.18041, 0.74768, 1.11269, 2.09187, 1.84067, 1.91833, -1.06805, -5.01282, 0.78319, -0.67228, -3.37636, 0.9451, 0.72465, -3.01388, 1.06212, 2.071, -4.01843, 1.11436, 3.05407, -5.36695, 1.06083, 2.69219, -5.72307, 0.94237, 1.34854, -4.73014, 0.88001, 0.3686, -1.6082, 1.06863, 2.4892, -3.75905, 1.19281, 4.10299, -6.13469, 1.10348, 3.45397, -1.26669, 1.19436, -4.45736, 0.10936, 1.4996, -2.82361, 1.45182, 1.63928, -2.45445, 2.44999, 1.57194, -3.44281, 2.10855, 1.35656, -4.7806, 0.76845, 1.22003, -5.14386, -0.22797, 1.29624, -4.16847, 0.96595, 3.16138, 1.56899, 3.49439, 1.68931, -3.17995, 2.88217, 1.30352, -5.53574, -1.39957, 1.00292, 3.55537, 0.87009, 1.26239, 3.18798, 1.20563, 0.14106, 1.80458, 2.51233, 2.23085, 1.40586, 2.88114, 2.17484, -0.87815] }, "elements": { "number": [0, 8, 7, 7, 8, 6, 1, 1, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 4, 0, 1, 0, 2, 0, 3, 2, 5, 4, 11, 9, 22, 3, 8, 2, 9, 10, 16, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 11, 16, 12, 17, 13, 18, 14, 19, 6, 15, 20, 26, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 21, 26, 5, 27, 23, 28, 24, 29, 7, 25, 1, 21, 5, 8, 3, 17, 17, 30, 8, 31, 8, 32, 5, 33, 9, 34] }, "order": [1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/4-salen.svg000066400000000000000000000066121506155467400263160ustar00rootroot00000000000000ONNO avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/6-edta.cjson000066400000000000000000000030621506155467400264440ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [0.152, 1.76, -0.736, 1.716, 0.164, 0.844, -0.772, 0.28, 1.572, 0.156, -1.776, 0.42, -1.7, 0.048, -0.624, 0.5, -0.64, -1.664, 0.748, 0.288, -2.596, 1.06, -0.024, -3.744, 0.46, 1.704, -2.204, -0.384, 2.048, -2.804, 1.32, 2.324, -2.46, 1.248, 2.424, 0.02, 2.36, 1.368, 0.248, 1.648, 3.288, -0.516, 0.892, 2.772, 0.992, 2.812, 1.124, -0.716, 3.144, 1.768, 0.892, -1.18, 2.392, -0.508, -2.188, 1.304, -0.748, -3.26, 1.516, -1.32, -1.344, 3.24, -1.172, -1.284, 2.732, 0.524, 1.512, 0.336, 2.324, 0.052, 0.512, 2.604, -0.38, 0.696, 3.74, 1.848, -0.544, 2.876, 2.056, 1.2, 2.712, 1.428, -2.192, 0.62, 2.46, -1.092, 0.564, 1.668, -3.288, 1.116, 2.888, -1.08, -0.436, 3.26, -1.26, 1.288, 0.00867, -0.02733, -0.03133] }, "elements": { "number": [7, 7, 8, 8, 8, 8, 6, 8, 6, 1, 1, 6, 6, 1, 1, 1, 1, 6, 6, 8, 1, 1, 6, 6, 8, 1, 1, 6, 6, 8, 1, 1, 0] }, "formalCharges": [-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 17, 0, 11, 1, 22, 2, 23, 3, 27, 6, 8, 5, 6, 6, 7, 0, 8, 8, 9, 8, 10, 11, 12, 11, 14, 1, 12, 12, 16, 11, 13, 12, 15, 17, 21, 4, 18, 17, 18, 18, 19, 17, 20, 22, 23, 22, 26, 22, 25, 23, 24, 27, 29, 27, 28, 1, 28, 28, 31, 28, 30, 4, 32, 2, 32, 1, 32, 0, 32, 5, 32, 3, 32] }, "order": [1, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": -4, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/6-edta.svg000066400000000000000000000134471506155467400261370ustar00rootroot00000000000000NNO-O-OO-OOO-O avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/amide.cjson000066400000000000000000000007611506155467400264460ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-0.3181, -1.3526, -0.2993, -0.9252, -1.114, 0.5943, -1.5121, -0.0747, 0.7304, -0.9241, -2.119, 1.4907, -1.4461, -2.0056, 2.3452, -0.4231, -2.9755, 1.341] }, "elements": { "number": [0, 6, 8, 7, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 3, 4, 3, 5] }, "order": [1, 2, 1, 1, 1] }, "properties": { "totalCharge": 0 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/amide.svg000066400000000000000000000025661506155467400261360ustar00rootroot00000000000000ONH2 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/boc-tert-butyloxycarbonyl.cjson000066400000000000000000000016621506155467400325360ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.381, -1.2221, -3.3394, 1.2697, -0.6057, -2.7521, 1.4763, 0.6001, -3.0489, 0.8569, -1.0077, -1.4805, 0.673, -0.0032, -0.5141, 1.9184, 0.7968, -0.2233, 0.2593, -0.7637, 0.7539, -0.4526, 0.9337, -0.8372, 1.7367, 1.2768, 0.7697, 2.8152, 0.1878, -0.1067, 1.9764, 1.6146, -0.9548, -0.7107, -1.2395, 0.5014, 0.9992, -1.5758, 0.8889, 0.1708, -0.0934, 1.6226, -1.4053, 0.3601, -0.8096, -0.3277, 1.4182, -1.8078, -0.5161, 1.6707, -0.0105] }, "elements": { "number": [0, 6, 8, 8, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 3, 4, 4, 5, 4, 6, 4, 7, 5, 8, 5, 9, 5, 10, 6, 11, 6, 12, 6, 13, 7, 14, 7, 15, 7, 16] }, "order": [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/boc-tert-butyloxycarbonyl.svg000066400000000000000000000040231506155467400322130ustar00rootroot00000000000000OOH3CCH3CH3 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/carboxylate.cjson000066400000000000000000000007121506155467400277000ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [0.5643, -2.5978, -1.0739, 0.6005, -1.6853, -0.4405, 1.4629, -1.4857, 0.3501, -0.4101, -0.842, -0.6542, -1.0336, -1.1379, -1.3293] }, "elements": { "number": [0, 6, 8, 8, 1] }, "formalCharges": [0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 3, 4] }, "order": [1, 2, 1, 1] }, "properties": { "totalCharge": 0 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/carboxylate.svg000066400000000000000000000024741506155467400273720ustar00rootroot00000000000000OOH avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/cbz-benzyloxycarbonyl.cjson000066400000000000000000000017451506155467400317430ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-3.878, -0.6445, 2.4626, -2.857, -0.814, 2.3373, -2.176, 0.1908, 2.4767, -2.5348, -2.0829, 2.0553, -1.2418, -2.5822, 1.8395, -0.6279, -1.9001, 0.699, -0.8464, -2.403, -0.5733, -0.2898, -1.813, -1.7037, 0.5002, -0.7035, -1.5888, 0.736, -0.1787, -0.3284, 0.1731, -0.7806, 0.7833, -0.5942, -2.4976, 2.7398, -1.3599, -3.6472, 1.5833, -1.4685, -3.2869, -0.7288, -0.4874, -2.24, -2.6793, 0.918, -0.2688, -2.491, 1.3506, 0.6837, -0.243, 0.3829, -0.3503, 1.7309] }, "elements": { "number": [0, 6, 8, 8, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 5, 4, 11, 4, 12, 6, 13, 7, 14, 8, 15, 9, 16, 10, 17] }, "order": [1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/cbz-benzyloxycarbonyl.svg000066400000000000000000000041741506155467400314250ustar00rootroot00000000000000OO avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/ester.cjson000066400000000000000000000011051506155467400265020ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-0.0869, -0.7101, -1.7424, -0.6829, -0.6473, -0.8168, -1.4629, 0.224, -0.5716, -0.3779, -1.6819, -0.0544, -1.0426, -1.7957, 1.2024, -0.2855, -1.988, 1.961, -1.7414, -2.6315, 1.1552, -1.5813, -0.8726, 1.4229] }, "elements": { "number": [0, 6, 8, 8, 6, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 3, 4, 4, 5, 4, 6, 4, 7] }, "order": [1, 2, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/ester.svg000066400000000000000000000030461506155467400261730ustar00rootroot00000000000000OOH3C avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta2-ethylene.cjson000066400000000000000000000010671506155467400300350ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-0.0, 0.0, -0.6679, 0.0, -0.0, 0.6679, 0.9216, -0.1247, -1.2278, -0.9216, 0.1247, -1.2278, 0.9216, -0.1247, 1.2278, -0.9216, 0.1247, 1.2278, 0.2, 2.0, 0.0, 0.0, 0.0, 0.0] }, "elements": { "number": [6, 6, 1, 1, 1, 1, 0, 0] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 2, 0, 3, 1, 4, 1, 5, 6, 7] }, "order": [2, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta2-ethylene.svg000066400000000000000000000021461506155467400275170ustar00rootroot00000000000000H2CCH2 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta3-alyl.cjson000066400000000000000000000012431506155467400271560ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [0.9625, 0.0994, -0.1005, 2.3156, -0.0674, 0.0751, 3.0433, -1.2198, 0.2489, 0.5281, 1.0735, -0.3, 0.2865, -0.7456, -0.1666, 2.9023, 0.8624, 0.088, 2.5732, -2.1964, 0.2787, 4.1114, -1.2, 0.4395, 2.10713, -0.39593, 0.0745, 2.34588, -0.54381, -1.90568] }, "elements": { "number": [6, 6, 6, 1, 1, 1, 1, 1, 0, 0] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 3, 0, 4, 1, 2, 1, 5, 2, 6, 2, 7, 8, 9] }, "order": [2, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta3-alyl.svg000066400000000000000000000025361506155467400266470ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta5-cyclopentyl.cjson000066400000000000000000000014111506155467400305610ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-1.0963, -0.4179, -0.1293, -0.7943, 0.9338, 0.0286, 0.5925, 1.0681, 0.0281, 1.1481, -0.2005, -0.13, 0.1042, -1.1184, -0.2282, -2.0934, -0.8488, -0.17, -1.5152, 1.7406, 0.1346, 1.1453, 1.9983, 0.1338, 2.2094, -0.4321, -0.1714, 0.2083, -2.1929, -0.3581, -0.00916, 0.05302, -0.08616, 0.02372, -0.23835, 1.98561] }, "elements": { "number": [6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 0, 0] }, "formalCharges": [0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 4, 0, 5, 1, 2, 1, 6, 2, 3, 2, 7, 3, 4, 3, 8, 4, 9, 10, 11] }, "order": [2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta5-cyclopentyl.svg000066400000000000000000000015301506155467400302460ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta6-benzene.cjson000066400000000000000000000015271506155467400276530ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.3729, -0.22, 0.0055, 0.5032, -1.2973, -0.0087, -0.8647, -1.0826, -0.0137, -1.3628, 0.2093, -0.0045, -0.493, 1.2865, 0.0097, 0.8748, 1.0718, 0.0147, 2.4403, -0.3876, 0.0094, 0.8918, -2.3054, -0.0159, -1.5434, -1.9232, -0.0248, -2.4302, 0.3768, -0.0084, -0.8817, 2.2946, 0.0168, 1.5535, 1.9124, 0.0258, 0.0051, -0.0054, 0.0005, 0.01017, 0.01815, -1.99989] }, "elements": { "number": [6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 0, 0] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 0, 5, 0, 6, 1, 2, 1, 7, 2, 3, 2, 8, 3, 4, 3, 9, 4, 5, 4, 10, 5, 11, 12, 13] }, "order": [1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 1 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/eta6-benzene.svg000066400000000000000000000017431506155467400273360ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/ethylene.cjson000066400000000000000000000010161506155467400271760ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-1.5534, 0.0749, 0.2664, -1.1883, 0.1873, 1.3083, -0.6202, 1.3071, 1.4603, -1.4535, -0.6764, 1.7673, -0.1841, 1.6185, 2.4293, -0.5234, 2.0418, 0.6403] }, "elements": { "number": [0, 6, 6, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 2, 4, 2, 5] }, "order": [1, 2, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/ethylene.svg000066400000000000000000000024771506155467400266750ustar00rootroot00000000000000H2C avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/ethyne.cjson000066400000000000000000000007021506155467400266560ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-2.6584, -0.0146, -0.0614, -2.3373, -0.3508, 0.7114, -1.9233, -0.7914, 1.7521, -1.5732, -1.1666, 2.6719] }, "elements": { "number": [0, 6, 6, 1] }, "formalCharges": [0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 2, 3] }, "order": [1, 3, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/ethyne.svg000066400000000000000000000023351506155467400263450ustar00rootroot00000000000000CH avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/fmoc-fluorenylmethoxycarbonyl.cjson000066400000000000000000000027131506155467400334770ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-4.5451, 0.0889, 1.9458, -3.5829, -0.0207, 2.1999, -3.3979, -0.2392, 3.4245, -2.6586, 0.0649, 1.2321, -1.2989, -0.128, 1.5198, -0.3828, 0.1387, 0.415, 1.05, -0.1285, 0.5917, 1.9121, -0.1455, 1.6532, 3.2513, -0.4011, 1.5512, 3.7453, -0.6579, 0.2742, 2.9035, -0.6581, -0.8444, 1.5573, -0.3919, -0.666, 0.4739, -0.3995, -1.6481, 0.505, -0.639, -3.015, -0.7212, -0.6003, -3.7051, -1.9195, -0.3402, -3.0467, -1.8454, -0.1169, -1.6888, -0.6953, -0.1359, -0.9617, -1.1198, -1.0568, 2.0847, -1.0528, 0.7079, 2.3272, -0.3384, 1.3779, 0.3857, 1.4779, 0.0489, 2.6399, 3.8925, -0.4157, 2.4319, 4.8121, -0.858, 0.1267, 3.3181, -0.8509, -1.8139, 1.4144, -0.827, -3.5262, -0.7649, -0.76, -4.7748, -2.8163, -0.3373, -3.6182, -2.8065, 0.0828, -1.15] }, "elements": { "number": [0, 6, 8, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 5, 11, 6, 17, 12, 4, 18, 4, 19, 5, 20, 7, 21, 8, 22, 9, 23, 10, 24, 13, 25, 14, 26, 15, 27, 16, 28] }, "order": [1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/fmoc-fluorenylmethoxycarbonyl.svg000066400000000000000000000056111506155467400331620ustar00rootroot00000000000000OO avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/nitrile.cjson000066400000000000000000000006251506155467400270340ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.4585, 2.4514, -0.8813, 1.0397, 1.5528, -0.5113, 0.5905, 0.5853, -0.1158] }, "elements": { "number": [0, 6, 7] }, "formalCharges": [0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2] }, "order": [1, 3] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/nitrile.svg000066400000000000000000000021441506155467400265150ustar00rootroot00000000000000N avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/nitro.cjson000066400000000000000000000006741506155467400265250ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.2861, 0.3494, 3.0027, 1.0088, 0.2164, 1.9831, 1.8224, -0.3255, 1.2899, -0.0752, 0.6285, 1.6816] }, "elements": { "number": [0, 7, 8, 8] }, "formalCharges": [0, 1, 0, -1] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3] }, "order": [1, 2, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/nitro.svg000066400000000000000000000025641506155467400262100ustar00rootroot00000000000000N+OO- avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/phenyl.cjson000066400000000000000000000014041506155467400266610ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-1.5285, 0.2007, -0.8381, -0.6479, 0.2177, -0.212, -0.6433, -0.4586, 0.9961, 0.4857, -0.436, 1.7975, 1.6098, 0.2626, 1.3911, 1.605, 0.9389, 0.1829, 0.4761, 0.9164, -0.6186, -1.5201, -1.0039, 1.314, 0.4889, -0.9639, 2.7402, 2.4906, 0.2801, 2.0166, 2.482, 1.4842, -0.1347, 0.4731, 1.4443, -1.5613] }, "elements": { "number": [0, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 1, 2, 7, 3, 8, 4, 9, 5, 10, 6, 11] }, "order": [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/phenyl.svg000066400000000000000000000030431506155467400263450ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/phosphate.cjson000066400000000000000000000011461506155467400273600ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [-1.0432, -3.0021, -1.018, -0.6229, -2.4983, -0.3244, 0.0053, -1.1643, -0.9673, 0.1276, -1.2157, -2.4175, 1.3655, -0.9921, -0.1479, -0.8777, 0.0094, -0.3854, 2.0864, -1.3719, -0.6453, -0.8116, 0.0459, 0.5684] }, "elements": { "number": [0, 8, 15, 8, 8, 8, 1, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 2, 3, 2, 4, 2, 5, 4, 6, 5, 7] }, "order": [1, 1, 2, 1, 1, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/phosphate.svg000066400000000000000000000031221506155467400270370ustar00rootroot00000000000000OPOOHOH avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/sulfonate.cjson000066400000000000000000000010161506155467400273610ustar00rootroot00000000000000{ "chemicalJson": 1, "atoms": { "coords": { "3d": [1.2748, 0.5448, -1.1488, 1.7352, -0.2603, -0.1183, 1.9773, -1.5452, -0.6583, 2.7442, 0.4603, 0.5831, 0.4144, -0.2758, 0.7807, 0.5393, 0.2946, 1.5409] }, "elements": { "number": [0, 16, 8, 8, 8, 1] }, "formalCharges": [0, 0, 0, 0, 0, 0] }, "bonds": { "connections": { "index": [0, 1, 1, 2, 1, 3, 1, 4, 4, 5] }, "order": [1, 2, 2, 1, 1] }, "properties": { "totalCharge": 0, "totalSpinMultiplicity": 2 } } avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/ligands/sulfonate.svg000066400000000000000000000025531506155467400270530ustar00rootroot00000000000000SOOOH avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/template.qrc000066400000000000000000000067451506155467400252420ustar00rootroot00000000000000 centers/1-lin.svg centers/2-lin.svg centers/3-tpl.svg centers/4-sqp.svg centers/4-tet.svg centers/5-spy.svg centers/5-tbp.svg centers/6-oct.svg centers/6-tpr.svg centers/7-pbp.svg centers/8-sqa.svg ligands/1-ammine.svg ligands/1-aqua.svg ligands/1-carbonyl.svg ligands/1-cyano.svg ligands/1-ethyl.svg ligands/1-isopropyl.svg ligands/1-methyl.svg ligands/1-phosphine.svg ligands/1-propyl.svg ligands/1-pyridyl.svg ligands/1-t-butyl.svg ligands/1-thiol.svg ligands/2-acetylacetonate.svg ligands/2-bipyridine.svg ligands/2-ethylenediamine.svg ligands/3-terpyridine.svg ligands/4-phthalocyanine.svg ligands/4-porphin.svg ligands/4-salen.svg ligands/6-edta.svg ligands/amide.svg ligands/carboxylate.svg ligands/ester.svg ligands/ethylene.svg ligands/ethyne.svg ligands/nitro.svg ligands/phenyl.svg ligands/phosphate.svg ligands/sulfonate.svg ligands/eta2-ethylene.svg ligands/eta5-cyclopentyl.svg ligands/eta6-benzene.svg template_light.svg template_dark.svg centers/1-lin.cjson centers/2-lin.cjson centers/3-tpl.cjson centers/4-sqp.cjson centers/4-tet.cjson centers/5-spy.cjson centers/5-tbp.cjson centers/6-oct.cjson centers/6-tpr.cjson centers/7-pbp.cjson centers/8-sqa.cjson ligands/1-ammine.cjson ligands/1-aqua.cjson ligands/1-carbonyl.cjson ligands/1-cyano.cjson ligands/1-phosphine.cjson ligands/1-thiol.cjson ligands/1-methyl.cjson ligands/1-ethyl.cjson ligands/1-propyl.cjson ligands/1-isopropyl.cjson ligands/1-t-butyl.cjson ligands/1-pyridyl.cjson ligands/2-acetylacetonate.cjson ligands/2-bipyridine.cjson ligands/2-ethylenediamine.cjson ligands/3-terpyridine.cjson ligands/4-phthalocyanine.cjson ligands/4-porphin.cjson ligands/4-salen.cjson ligands/6-edta.cjson ligands/amide.cjson ligands/carboxylate.cjson ligands/ester.cjson ligands/ethylene.cjson ligands/ethyne.cjson ligands/nitro.cjson ligands/phenyl.cjson ligands/phosphate.cjson ligands/sulfonate.cjson ligands/eta2-ethylene.cjson ligands/eta5-cyclopentyl.cjson ligands/eta6-benzene.cjson avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/template_dark.svg000066400000000000000000000023761506155467400262510ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/template_light.svg000066400000000000000000000024061506155467400264310ustar00rootroot00000000000000 avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/templatetool.cpp000066400000000000000000000627211506155467400261310ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "templatetool.h" #include "templatetoolwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { const unsigned char INVALID_ATOMIC_NUMBER = std::numeric_limits::max(); } namespace Avogadro { namespace QtPlugins { using QtGui::Molecule; using QtGui::RWAtom; using QtGui::RWBond; using QtGui::RWMolecule; using QtOpenGL::GLWidget; using Avogadro::Core::Elements; using Avogadro::Io::CjsonFormat; using Avogadro::Rendering::GeometryNode; using Avogadro::Rendering::GroupNode; using Avogadro::Rendering::Identifier; using Avogadro::Rendering::TextLabel2D; using Avogadro::Rendering::TextLabel3D; using Avogadro::Rendering::TextProperties; TemplateTool::TemplateTool(QObject* parent_) : QtGui::ToolPlugin(parent_), m_activateAction(new QAction(this)), m_molecule(NULL), m_glWidget(NULL), m_renderer(NULL), m_toolWidget(new TemplateToolWidget(qobject_cast(parent_))), m_pressedButtons(Qt::NoButton), m_clickedAtomicNumber(INVALID_ATOMIC_NUMBER), m_bondAdded(false), m_fixValenceLater(false) { QString shortcut = tr("Ctrl+3", "control-key 3"); m_activateAction->setText(tr("Template")); m_activateAction->setToolTip( tr("Template Tool\t(%1)\n\n" "Insert fragments, including metal centers.\n" "Select an element and coordination geometry, " "then click to insert a fragment.\n\n" "Select a ligand or functional group and click " "on a hydrogen atom to attach it.") .arg(shortcut)); setIcon(); reset(); } TemplateTool::~TemplateTool() {} void TemplateTool::setIcon(bool darkTheme) { if (darkTheme) m_activateAction->setIcon(QIcon(":/icons/template_dark.svg")); else m_activateAction->setIcon(QIcon(":/icons/template_light.svg")); } QWidget* TemplateTool::toolWidget() const { return m_toolWidget; } QUndoCommand* TemplateTool::mousePressEvent(QMouseEvent* e) { clearKeyPressBuffer(); if (!m_renderer) return NULL; updatePressedButtons(e, false); m_clickPosition = e->pos(); if (m_molecule) { m_molecule->setInteractive(true); } if (m_pressedButtons & Qt::LeftButton) { m_clickedObject = m_renderer->hit(e->pos().x(), e->pos().y()); switch (m_clickedObject.type) { case Rendering::InvalidType: emptyLeftClick(e); return NULL; case Rendering::AtomType: atomLeftClick(e); return NULL; default: break; } } else if (m_pressedButtons & Qt::RightButton) { m_clickedObject = m_renderer->hit(e->pos().x(), e->pos().y()); switch (m_clickedObject.type) { case Rendering::AtomType: atomRightClick(e); return NULL; default: break; } } return NULL; } QUndoCommand* TemplateTool::mouseReleaseEvent(QMouseEvent* e) { if (!m_renderer) return NULL; updatePressedButtons(e, true); if (m_molecule) { m_molecule->setInteractive(false); } if (m_clickedObject.type == Rendering::InvalidType) return NULL; switch (e->button()) { case Qt::LeftButton: case Qt::RightButton: reset(); e->accept(); break; default: break; } return NULL; } QUndoCommand* TemplateTool::mouseMoveEvent(QMouseEvent* e) { if (!m_renderer) return NULL; if (m_pressedButtons & Qt::LeftButton) if (m_clickedObject.type == Rendering::AtomType) atomLeftDrag(e); return NULL; } QUndoCommand* TemplateTool::keyPressEvent(QKeyEvent* e) { if (e->text().isEmpty()) return NULL; e->accept(); // check which tab is currently active int currentTab = m_toolWidget->currentTab(); // if it's arrow keys, change tabs if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right || e->key() == Qt::Key_BracketLeft || e->key() == Qt::Key_BracketRight) { // cycle through tabs // the widget will handle any wrap-around if (e->key() == Qt::Key_Left || e->key() == Qt::Key_BracketLeft) { currentTab--; } else { currentTab++; } m_toolWidget->setCurrentTab(currentTab); return NULL; } // Set a timer to clear the buffer on first keypress: if (m_keyPressBuffer.isEmpty()) QTimer::singleShot(2000, this, SLOT(clearKeyPressBuffer())); m_keyPressBuffer.append(m_keyPressBuffer.isEmpty() ? e->text().toUpper() : e->text().toLower()); if (m_keyPressBuffer.size() >= 3) { clearKeyPressBuffer(); return NULL; } if (currentTab == 0) { // if it's + or -, change the formal charge if (e->key() == Qt::Key_Plus || e->key() == Qt::Key_Minus) { int formalCharge = m_toolWidget->formalCharge(); if (e->key() == Qt::Key_Plus) formalCharge++; else formalCharge--; m_toolWidget->setFormalCharge(formalCharge); clearKeyPressBuffer(); return NULL; } // metal center -- interpret as an element int atomicNum = Core::Elements::atomicNumberFromSymbol(m_keyPressBuffer.toStdString()); if (atomicNum != Avogadro::InvalidElement) m_toolWidget->setAtomicNumber(static_cast(atomicNum)); else { // if it's a number, try a coordination number bool ok = false; int coordinationNumber = m_keyPressBuffer.toInt(&ok); if (ok) { unsigned char geometry = 0; switch (coordinationNumber) { // 1, 2, 3, 4, 4, 5, 5, 6, 6, 7, 8 are valid case 1: break; case 2: geometry = 1; break; case 3: geometry = 2; break; case 4: geometry = 3; break; case 44: geometry = 4; break; case 5: geometry = 5; break; case 55: geometry = 6; break; case 6: geometry = 7; break; case 66: geometry = 8; break; case 7: geometry = 9; break; case 8: geometry = 10; break; default: // do nothing, invalid coordination number clearKeyPressBuffer(); break; } m_toolWidget->setCoordination(geometry); } } } else if (currentTab == 1) { // ligand // e.g. bpy = bipyridine // e.g. edta, tpy, etc. } else if (currentTab == 2) { // functional group // e.g. c8 = octyl group // p = phenyl group } return NULL; } void TemplateTool::draw(Rendering::GroupNode&) {} void TemplateTool::updatePressedButtons(QMouseEvent* e, bool release) { /// @todo Use modifier keys on mac if (release) m_pressedButtons &= e->buttons(); else m_pressedButtons |= e->buttons(); } void TemplateTool::reset() { if (m_fixValenceLater) { Index a1 = m_newObject.index; Index a2 = m_bondedAtom.index; Index a3 = m_clickedObject.index; // order them if (a1 > a2) std::swap(a1, a2); if (a1 > a3) std::swap(a1, a3); if (a2 > a3) std::swap(a2, a3); // This preserves the order so they are adjusted in order. Core::Array atomIds; atomIds.push_back(a3); atomIds.push_back(a2); atomIds.push_back(a1); // This function checks to make sure the ids are valid, so no need // to check out here. m_molecule->adjustHydrogens(atomIds); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Added; changes |= Molecule::Bonds | Molecule::Added | Molecule::Removed; m_molecule->emitChanged(changes); m_fixValenceLater = false; } m_clickedObject = Identifier(); m_newObject = Identifier(); m_bondedAtom = Identifier(); m_clickPosition = QPoint(); m_pressedButtons = Qt::NoButton; m_clickedAtomicNumber = INVALID_ATOMIC_NUMBER; m_bondAdded = false; emit drawablesChanged(); } void TemplateTool::emptyLeftClick(QMouseEvent* e) { // Get the coordinates of the clicked position if (m_renderer == nullptr) return; m_toolWidget->selectedUIDs().clear(); Vector2f windowPos(e->localPos().x(), e->localPos().y()); Vector3f atomPos = m_renderer->camera().unProject(windowPos); // center of inserted template Vector3 center(0.0f, 0.0f, 0.0f); CjsonFormat ff; Molecule templateMolecule; // before we do anything, check if it's a metal or a ligand // in the dialog int currentTab = m_toolWidget->currentTab(); if (currentTab == 0) { // metal center QFile templ(":/templates/centers/" + m_toolWidget->coordinationString() + ".cjson"); if (!templ.open(QFile::ReadOnly | QFile::Text)) return; QTextStream templateStream(&templ); if (!ff.readString(templateStream.readAll().toStdString(), templateMolecule)) return; // Add the atom and hydrogens around it following template [[maybe_unused]] size_t centerIndex = 0; for (size_t i = 0; i < templateMolecule.atomCount(); i++) { if (templateMolecule.atomicNumber(i) != 1) { center = templateMolecule.atomPosition3d(i); centerIndex = i; templateMolecule.setAtomicNumber(i, m_toolWidget->atomicNumber()); templateMolecule.setFormalCharge(i, m_toolWidget->formalCharge()); continue; } } // done with metal center and coordination } else { // ligand // check if it's clipboard first if (m_toolWidget->ligandString() == tr("Clipboard")) { const QMimeData* mimeData(QApplication::clipboard()->mimeData()); if (!mimeData) { return; } // Try to find a reader that can handle the available mime-types. Io::FileFormatManager& mgr = Io::FileFormatManager::instance(); QStringList mimeTypes(mimeData->formats()); Io::FileFormat* pastedFormat = nullptr; QByteArray pastedData; Io::FileFormat::Operations ops(Io::FileFormat::Read | Io::FileFormat::String); foreach (const QString& mimeType, mimeTypes) { if ((pastedFormat = mgr.newFormatFromMimeType(mimeType.toStdString(), ops))) { pastedData = mimeData->data(mimeType); break; } } // No mime-type match, default to cjson. if (!pastedFormat && mimeData->hasText()) { pastedFormat = new Io::CjsonFormat; pastedData = mimeData->text().toLatin1(); } if (pastedFormat == nullptr) return; // we have a format, so try to insert the new bits into the molecule bool success = pastedFormat->readString( std::string(pastedData.constData(), pastedData.size()), templateMolecule); if (!success) return; center = templateMolecule.centerOfGeometry(); // change the dummy atom(s) to hydrogen for (size_t i = 0; i < templateMolecule.atomCount(); i++) { if (templateMolecule.atomicNumber(i) == 0) { templateMolecule.setAtomicNumber(i, 1); templateMolecule.setFormalCharge(i, 0); } } // done with clipboard ligands } else { // a ligand file QString path; if (m_toolWidget->ligandString().endsWith(".cjson")) { // we already have the full path .. from the insert browser path = m_toolWidget->ligandString(); } else { path = ":/templates/ligands/" + m_toolWidget->ligandString() + ".cjson"; } QFile templ(path); if (!templ.open(QFile::ReadOnly | QFile::Text)) return; QTextStream templateStream(&templ); if (!ff.readString(templateStream.readAll().toStdString(), templateMolecule)) return; center = templateMolecule.centerOfGeometry(); // change the dummy atom(s) to hydrogen for (size_t i = 0; i < templateMolecule.atomCount(); i++) { if (templateMolecule.atomicNumber(i) == 0) { templateMolecule.setAtomicNumber(i, 1); templateMolecule.setFormalCharge(i, 0); } } // done with ligand } } // move the template to the clicked position for (size_t i = 0; i < templateMolecule.atomCount(); i++) { Vector3 pos = templateMolecule.atomPosition3d(i) - center + atomPos.cast(); templateMolecule.setAtomPosition3d(i, pos); } size_t firstIndex = m_molecule->atomCount(); m_molecule->appendMolecule(templateMolecule, tr("Insert Template")); Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Bonds | Molecule::Added; // m_fixValenceLater = true; // add hydrogens m_fixValenceLater = false; // Update the clicked object m_clickedObject.type = Rendering::AtomType; m_clickedObject.molecule = m_molecule; m_clickedObject.index = firstIndex; // Emit changed signal m_molecule->emitChanged(changes); e->accept(); } Vector3 rotateLigandCoords(Vector3 in, Vector3 centerVector, Vector3 outVector) { if (centerVector.norm() == 0.0 || outVector.norm() == 0.0) return in; Vector3 axis = centerVector.cross(outVector); if (axis.norm() < 1e-12) { // vectors are parallel, let's pick an arbitrary // perpendicular axis Matrix3 rotx = Eigen::AngleAxisd(M_PI / 2.0, Vector3(1.0, 0.0, 0.0)).toRotationMatrix(); Matrix3 roty = Eigen::AngleAxisd(M_PI / 2.0, Vector3(0.0, 1.0, 0.0)).toRotationMatrix(); axis = centerVector.cross(rotx * outVector); if (axis.norm() < 1e-12) axis = centerVector.cross(roty * outVector); } axis.normalize(); double cosine = centerVector.dot(outVector) / centerVector.norm() / outVector.norm(); double angle = (abs(cosine) < 1.0) ? acos(cosine) : 0.0; Matrix3 rot = Eigen::AngleAxisd(angle, axis).toRotationMatrix(); return rot * in; } Matrix3 applyKabsch(std::vector templatePoints, std::vector moleculePoints) { assert(templatePoints.size() == moleculePoints.size()); MatrixX TP(templatePoints.size(), 3); MatrixX MP(templatePoints.size(), 3); for (size_t i = 0; i < templatePoints.size(); i++) { TP.row(i) = templatePoints[i]; MP.row(i) = moleculePoints[i]; } Matrix3 H = TP.transpose() * MP; Eigen::JacobiSVD svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV); MatrixX U = svd.matrixU(); Matrix3 V = svd.matrixV(); Matrix3 Idd = Matrix3::Identity(); Idd(2, 2) = copysign(1.0, (V * U.transpose()).determinant()); Matrix3 r = V * Idd * U.transpose(); return r; } void TemplateTool::atomLeftClick(QMouseEvent*) { size_t selectedIndex = m_clickedObject.index; // if it's a valid selected atom and a hydrogen or dummy atom if (m_molecule->atom(selectedIndex).isValid() && (m_molecule->atomicNumber(selectedIndex) == 1 || m_molecule->atomicNumber(selectedIndex) == 0)) { m_toolWidget->selectedUIDs().push_back( m_molecule->atomUniqueId(selectedIndex)); if (static_cast(m_toolWidget->selectedUIDs().size()) != m_toolWidget->denticity()) return; // Get the ligand template // - check if we should use the clipboard // - otherwise use the template Molecule templateMolecule; if (m_toolWidget->ligandString() == tr("Clipboard")) { const QMimeData* mimeData(QApplication::clipboard()->mimeData()); if (!mimeData) { return; } // Try to find a reader that can handle the available mime-types. Io::FileFormatManager& mgr = Io::FileFormatManager::instance(); QStringList mimeTypes(mimeData->formats()); Io::FileFormat* pastedFormat = nullptr; QByteArray pastedData; Io::FileFormat::Operations ops(Io::FileFormat::Read | Io::FileFormat::String); foreach (const QString& mimeType, mimeTypes) { if ((pastedFormat = mgr.newFormatFromMimeType(mimeType.toStdString(), ops))) { pastedData = mimeData->data(mimeType); break; } } // No mime-type match, default to cjson. if (!pastedFormat && mimeData->hasText()) { pastedFormat = new Io::CjsonFormat; pastedData = mimeData->text().toLatin1(); } if (pastedFormat == nullptr) return; // we have a format, so try to insert the new bits into the molecule bool success = pastedFormat->readString( std::string(pastedData.constData(), pastedData.size()), templateMolecule); if (!success) return; } else { QString path; if (m_toolWidget->ligandString().endsWith(".cjson")) { // we already have the full path .. from the insert browser path = m_toolWidget->ligandString(); } else { path = ":/templates/ligands/" + m_toolWidget->ligandString() + ".cjson"; } QFile templ(path); if (!templ.open(QFile::ReadOnly | QFile::Text)) return; QTextStream templateStream(&templ); CjsonFormat ff; if (!ff.readString(templateStream.readAll().toStdString(), templateMolecule)) return; } // Find dummy atom in template and get all necessary info // for haptic ligands, we pick the dummy atom that's // furthest from the centroid of the carbon atoms Vector3 centroid(0.0, 0.0, 0.0); unsigned carbonCount = 0; for (size_t i = 0; i < templateMolecule.atomCount(); ++i) { if (templateMolecule.atomicNumber(i) == 6) { carbonCount++; centroid += templateMolecule.atomPosition3d(i); } } if (carbonCount > 1) centroid = centroid / carbonCount; size_t templateDummyIndex = 0; std::vector templateLigandIndices; std::vector templateLigandUIDs; float maxDistance = 0.0; for (size_t i = 0; i < templateMolecule.atomCount(); ++i) { // in some ligands (e.g., haptic) we might have two dummy atoms // so we only select the one furthest from the ligand centroid if (templateMolecule.atomicNumber(i) == 0) { Vector3 delta = templateMolecule.atomPosition3d(i) - centroid; if (delta.squaredNorm() < maxDistance) continue; // too close to the centroid maxDistance = delta.squaredNorm(); templateDummyIndex = i; templateLigandIndices.clear(); templateLigandUIDs.clear(); for (const auto& bond : templateMolecule.bonds(i)) { size_t newIndex = bond.getOtherAtom(i).index(); templateLigandIndices.push_back(newIndex); templateLigandUIDs.push_back(templateMolecule.atomUniqueId(newIndex)); } } } // Find center atom in our current molecule and get all necessary info // - first check to see if there is a bond Vector3 moleculeLigandOutVector(0.0, 0.0, 0.0); Vector3 displacement(0.0, 0.0, 0.0); [[maybe_unused]] Vector3 centerPosition = m_molecule->atomPosition3d(selectedIndex); size_t moleculeCenterIndex = selectedIndex; size_t moleculeCenterUID = m_molecule->atomUniqueId(moleculeCenterIndex); if (m_molecule->bonds(selectedIndex).size() != 0) { moleculeCenterIndex = m_molecule->bonds(selectedIndex)[0].getOtherAtom(selectedIndex).index(); moleculeCenterUID = m_molecule->atomUniqueId(moleculeCenterIndex); for (size_t UID : m_toolWidget->selectedUIDs()) { size_t index = m_molecule->atomByUniqueId(UID).index(); Vector3 newPos = m_molecule->atomPosition3d(index); moleculeLigandOutVector += newPos - m_molecule->atomPosition3d(moleculeCenterIndex); } // Estimate and try to realize bond distances for (size_t i = 0; i < templateLigandIndices.size(); i++) { unsigned char ligandAtomicNumber = templateMolecule.atomicNumber(templateLigandIndices[i]); ligandAtomicNumber = (ligandAtomicNumber == 0) ? 6 : ligandAtomicNumber; // Estimate as the sum of covalent radii double bondDistance = Elements::radiusCovalent(ligandAtomicNumber) + Elements::radiusCovalent( m_molecule->atomicNumber(moleculeCenterIndex)); Vector3 inVector = templateMolecule.atomPosition3d(templateDummyIndex) - templateMolecule.atomPosition3d(templateLigandIndices[i]); Vector3 correctionVector = inVector; correctionVector.normalize(); correctionVector *= bondDistance - inVector.norm(); displacement += correctionVector; } displacement *= 1.0 / templateLigandIndices.size(); } else { // direction can be random displacement = Eigen::Vector3d::Random(); } Vector3 newPos = templateMolecule.atomPosition3d(templateDummyIndex) + displacement; templateMolecule.setAtomPosition3d(templateDummyIndex, newPos); // Translate template so dummy atom is brought to center atom for (size_t i = 0; i < templateMolecule.atomCount(); i++) { if (i != templateDummyIndex) { templateMolecule.setAtomPosition3d( i, templateMolecule.atomPosition3d(i) - templateMolecule.atomPosition3d(templateDummyIndex) + m_molecule->atomPosition3d(moleculeCenterIndex)); } } if (m_molecule->bonds(selectedIndex).size() != 0) { // Create arrays with the points to align and apply Kabsch algorithm std::vector templateLigandPositions; for (size_t index : templateLigandIndices) templateLigandPositions.push_back( templateMolecule.atomPosition3d(index) - m_molecule->atomPosition3d(moleculeCenterIndex)); std::vector moleculeLigandPositions; for (size_t UID : m_toolWidget->selectedUIDs()) moleculeLigandPositions.push_back( m_molecule->atomPosition3d(m_molecule->atomByUniqueId(UID).index()) - m_molecule->atomPosition3d(moleculeCenterIndex)); Matrix3 rotation = applyKabsch(templateLigandPositions, moleculeLigandPositions); for (size_t i = 0; i < templateMolecule.atomCount(); i++) { if (i != templateDummyIndex) { templateMolecule.setAtomPosition3d( i, rotation * (templateMolecule.atomPosition3d(i) - m_molecule->atomPosition3d(moleculeCenterIndex)) + m_molecule->atomPosition3d(moleculeCenterIndex)); } } // Rotate partially aligned template to align "out" vectors Vector3 templateLigandOutVector(0.0, 0.0, 0.0); for (size_t index : templateLigandIndices) { Vector3 pos = templateMolecule.atomPosition3d(index); templateLigandOutVector += pos - m_molecule->atomPosition3d(moleculeCenterIndex); } for (size_t i = 0; i < templateMolecule.atomCount(); i++) { if (templateMolecule.atomicNumber(i) != 0) { templateMolecule.setAtomPosition3d( i, rotateLigandCoords( templateMolecule.atomPosition3d(i) - m_molecule->atomPosition3d(moleculeCenterIndex), templateLigandOutVector, moleculeLigandOutVector) + m_molecule->atomPosition3d(moleculeCenterIndex)); } } } // Remove dummy atoms for (size_t i = 0; i < templateMolecule.atomCount(); i++) { if (templateMolecule.atomicNumber(i) == 0) { templateMolecule.removeAtom(i); i--; // repeat index to counteract swapping } } std::vector templateNewLigandIndices; for (size_t UID : templateLigandUIDs) { auto atom = templateMolecule.atomByUniqueId(UID); if (atom.isValid()) templateNewLigandIndices.push_back(atom.index()); } // Remove selected atoms and insert ligand // (unless there wasn't a bond to begin with) if (m_molecule->bonds(selectedIndex).size() != 0) { for (size_t UID : m_toolWidget->selectedUIDs()) m_molecule->removeAtom(m_molecule->atomByUniqueId(UID).index()); } size_t moleculeBaseIndex = m_molecule->atomCount(); m_molecule->appendMolecule(templateMolecule, tr("Insert Ligand")); // Create new bonds size_t moleculeCenterNewIndex = m_molecule->atomByUniqueId(moleculeCenterUID).index(); for (size_t index : templateNewLigandIndices) m_molecule->addBond(index + moleculeBaseIndex, moleculeCenterNewIndex); m_toolWidget->selectedUIDs().clear(); } } void TemplateTool::atomRightClick(QMouseEvent* e) { e->accept(); m_molecule->removeAtom(m_clickedObject.index); m_molecule->emitChanged(Molecule::Atoms | Molecule::Removed); } void TemplateTool::atomLeftDrag(QMouseEvent*) { // by default, don't allow drags for bonds return; } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/templatetool.h000066400000000000000000000061041506155467400255670ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_TEMPLATE_H #define AVOGADRO_QTPLUGINS_TEMPLATE_H #include #include #include #include #include namespace Avogadro { namespace QtPlugins { class TemplateToolWidget; /** * @class TemplateTool templatetool.h * @brief The Template tool inserts fragments, including metal centers. * @author Geoffrey R. Hutchison, Aritz, Erkiaga */ class TemplateTool : public QtGui::ToolPlugin { Q_OBJECT public: explicit TemplateTool(QObject* parent_ = NULL); ~TemplateTool() override; QString name() const override { return tr("Template tool"); } QString description() const override { return tr("Template tool"); } unsigned char priority() const override { return 21; } QAction* activateAction() const override { return m_activateAction; } QWidget* toolWidget() const override; void setIcon(bool darkTheme = false) override; void setMolecule(QtGui::Molecule* mol) override { if (mol) m_molecule = mol->undoMolecule(); } void setEditMolecule(QtGui::RWMolecule* mol) override { m_molecule = mol; } void setGLWidget(QtOpenGL::GLWidget* widget) override { m_glWidget = widget; } void setGLRenderer(Rendering::GLRenderer* renderer) override { m_renderer = renderer; } QUndoCommand* mousePressEvent(QMouseEvent* e) override; QUndoCommand* mouseReleaseEvent(QMouseEvent* e) override; QUndoCommand* mouseMoveEvent(QMouseEvent* e) override; QUndoCommand* keyPressEvent(QKeyEvent* e) override; void draw(Rendering::GroupNode& node) override; private slots: void clearKeyPressBuffer() { m_keyPressBuffer.clear(); } private: /** * Update the currently pressed buttons, accounting for modifier keys. * @todo Account for modifier keys. */ void updatePressedButtons(QMouseEvent*, bool release); /** * Reset all state for this tool. */ void reset(); void emptyLeftClick(QMouseEvent* e); void atomLeftClick(QMouseEvent* e); void bondLeftClick(QMouseEvent* e); void atomRightClick(QMouseEvent* e); void bondRightClick(QMouseEvent* e); void atomLeftDrag(QMouseEvent* e); QAction* m_activateAction; QtGui::RWMolecule* m_molecule; QtOpenGL::GLWidget* m_glWidget; Rendering::GLRenderer* m_renderer; TemplateToolWidget* m_toolWidget; Rendering::Identifier m_clickedObject; Rendering::Identifier m_newObject; Rendering::Identifier m_bondedAtom; Qt::MouseButtons m_pressedButtons; QPoint m_clickPosition; unsigned char m_clickedAtomicNumber; bool m_bondAdded; bool m_fixValenceLater; QString m_keyPressBuffer; Real m_bondDistance; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_NAVIGATOR_H avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/templatetoolwidget.cpp000066400000000000000000000367511506155467400273410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "templatetoolwidget.h" #include "ui_templatetoolwidget.h" #include #include #include #include #include #include #include #include namespace { // The ItemData of the "Other" entry in the combo box const int ELEMENT_SELECTOR_TAG = 255; } // namespace namespace Avogadro { namespace QtPlugins { enum TabType { Elements = 0, Ligands = 1, FunctionalGroups = 2 }; enum LigandType { Monodentate = 0, Bidentate = 1, Tridentate = 2, Tetradentate = 3, Hexadentate = 4, Haptic = 5, Clipboard = 6 }; TemplateToolWidget::TemplateToolWidget(QWidget* parent_) : QWidget(parent_), m_ui(new Ui::TemplateToolWidget), m_fragmentDialog(nullptr), m_elementSelector(nullptr), m_currentElement(26) { m_ui->setupUi(this); buildElements(); // Get default options QSettings settings; settings.beginGroup("templatetool"); unsigned int currentElement = settings.value("element", 26).toUInt(); selectElement(currentElement); // In the same order of the coordinationComboBox // append ".svg" for the icon and ".cjson" for the template m_centers << "1-lin" << "2-lin" << "3-tpl" << "4-tet" << "4-sqp" << "5-tbp" << "5-spy" << "6-oct" << "6-tpr" << "7-pbp" << "8-sqa"; m_groups << "amide" << "carboxylate" << "ester" << "ethylene" << "ethyne" << "nitro" << "phenyl" << "phosphate" << "sulfonate" << tr("Other…"); connect(m_ui->elementComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(elementChanged(int))); connect(m_ui->coordinationComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(coordinationChanged(int))); connect(m_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); connect(m_ui->ligandComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(ligandChanged(int))); connect(m_ui->groupComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(groupChanged(int))); // default coordination = octahedral QString currentCoord = settings.value("coordination", "6-oct").toString(); int index = m_centers.indexOf(currentCoord); if (index < 0) index = 7; // octahedral m_ui->coordinationComboBox->setCurrentIndex(index); // update the preview icon QString iconPath = QString(":/icons/centers/%1.svg").arg(currentCoord); m_ui->centerPreview->setIcon(QIcon(iconPath)); unsigned int ligandType = settings.value("ligandType", 0).toUInt(); m_ui->typeComboBox->setCurrentIndex(ligandType); // update the ligand combo box typeChanged(ligandType); groupChanged(0); } TemplateToolWidget::~TemplateToolWidget() { delete m_ui; } void TemplateToolWidget::setAtomicNumber(unsigned char atomicNum) { selectElement(atomicNum); if (m_elementSelector) m_elementSelector->setElement(static_cast(atomicNum)); } unsigned char TemplateToolWidget::atomicNumber() const { int curIndex = m_ui->elementComboBox->currentIndex(); QVariant itemData = m_ui->elementComboBox->itemData(curIndex); if (!itemData.isValid()) return 0; unsigned char atomicNum = static_cast(itemData.toUInt()); // "Other..." selected.... if (atomicNum == 0 && m_elementSelector) atomicNum = static_cast(m_elementSelector->element()); return atomicNum; } signed char TemplateToolWidget::formalCharge() const { return m_ui->chargeSpinBox->value(); } void TemplateToolWidget::setFormalCharge(int charge) { QSpinBox* spinBox = m_ui->chargeSpinBox; if (charge < spinBox->minimum() || charge > spinBox->maximum()) { return; } spinBox->setValue(charge); } void TemplateToolWidget::setCoordination(unsigned char order) { if (order < m_ui->coordinationComboBox->count()) m_ui->coordinationComboBox->setCurrentIndex(static_cast(order)); } unsigned char TemplateToolWidget::coordination() const { return static_cast(m_ui->coordinationComboBox->currentIndex()); } QString TemplateToolWidget::coordinationString() const { return m_centers.at(m_ui->coordinationComboBox->currentIndex()); } int TemplateToolWidget::currentTab() const { return m_ui->tabWidget->currentIndex(); } void TemplateToolWidget::setCurrentTab(int index) { if (index < 0) index = m_ui->tabWidget->count() - 1; else if (index >= m_ui->tabWidget->count()) index = 0; m_ui->tabWidget->setCurrentIndex(index); } unsigned char TemplateToolWidget::ligand() const { return static_cast(m_ui->ligandComboBox->currentIndex()); } QString TemplateToolWidget::ligandString() const { // first check which tab is open int tabIndex = m_ui->tabWidget->currentIndex(); if (tabIndex == TabType::FunctionalGroups) { int index = m_ui->groupComboBox->currentIndex(); // check if it's "other" if (index == m_ui->groupComboBox->count() - 1) return m_ligandPath; else return m_groups[index]; } // tell us if we are using the clipboard if (m_ui->typeComboBox->currentIndex() == LigandType::Clipboard) return "Clipboard"; // check if it's "other" if (m_ligands.at(m_ui->ligandComboBox->currentIndex()).endsWith("other")) return m_ligandPath; return m_ligands.at(m_ui->ligandComboBox->currentIndex()); } void TemplateToolWidget::coordinationChanged(int index) { if (index < 0 || index > m_ui->coordinationComboBox->count()) return; // get the icon name QString iconName = m_centers[index]; QSettings settings; settings.setValue("templatetool/coordination", iconName); m_ui->centerPreview->setIcon(QIcon(":/icons/centers/" + iconName + ".svg")); } void TemplateToolWidget::groupChanged(int index) { // get the current name from the text int i = m_ui->groupComboBox->currentIndex(); const QString& groupName = m_groups[i]; const QString& iconName = groupName; m_denticity = 1; // check if it's "other" if (index == m_ui->groupComboBox->count() - 1) { QString path = "fragments/groups"; if (m_fragmentDialog != nullptr) m_fragmentDialog->deleteLater(); m_fragmentDialog = new QtGui::InsertFragmentDialog(this, path); connect(m_fragmentDialog, SIGNAL(performInsert(const QString&, bool)), this, SLOT(otherLigandInsert(const QString&, bool))); m_fragmentDialog->show(); return; } m_ui->groupPreview->setIcon(QIcon(":/icons/ligands/" + iconName + ".svg")); } void TemplateToolWidget::ligandChanged(int index) { // we need to check if it's "other" if (index < 0 || index > m_ui->ligandComboBox->count() - 1) return; // get the icon name QString iconName = m_ligands[index]; // check if it's "other" if (iconName.endsWith("other")) { // figure out the ligand type and the resulting path // to the fragment files int ligandType = m_ui->typeComboBox->currentIndex(); QString path = "fragments"; switch (ligandType) { case LigandType::Monodentate: path += "/ligands/monodentate"; break; case LigandType::Bidentate: path += "/ligands/bidentate"; break; case LigandType::Tridentate: path += "/ligands/tridentate"; break; case LigandType::Tetradentate: path += "/ligands/tetradentate"; break; case LigandType::Hexadentate: path += "/ligands/hexadentate"; break; case LigandType::Haptic: path += "/ligands/haptic"; break; } if (m_fragmentDialog != nullptr) m_fragmentDialog->deleteLater(); m_fragmentDialog = new QtGui::InsertFragmentDialog(this, path); connect(m_fragmentDialog, SIGNAL(performInsert(const QString&, bool)), this, SLOT(otherLigandInsert(const QString&, bool))); m_fragmentDialog->show(); return; } m_ui->ligandPreview->setIcon(QIcon(":/icons/ligands/" + iconName + ".svg")); } void TemplateToolWidget::otherLigandInsert(const QString& fileName, [[maybe_unused]] bool crystal) { if (m_fragmentDialog == nullptr) return; // get the ligand name QString ligandName = m_fragmentDialog->fileName(); m_ligandPath = ligandName; m_fragmentDialog->hide(); // it will be deleted later // update the icon from the filename (so check for .svg) QString iconName = fileName; if (iconName.endsWith(".cjson")) iconName.chop(6); iconName += ".svg"; // check which tab is active m_ui->ligandPreview->setIcon(QIcon(iconName)); } void TemplateToolWidget::typeChanged(int index) { QSettings settings; settings.beginGroup("templatetool"); settings.setValue("ligandType", index); m_selectedUIDs.clear(); m_ui->ligandComboBox->clear(); m_ligands = QStringList(); QStringList ligandNames; switch (index) { case LigandType::Monodentate: // Monodentate ligandNames << "ammine" << "aqua" << "carbonyl" << "cyano" << "phosphine" << "thiol" << tr("Other…"); m_ligands << "1-ammine" << "1-aqua" << "1-carbonyl" << "1-cyano" << "1-phosphine" << "1-thiol" << "1-other"; m_denticity = 1; break; case LigandType::Bidentate: // Bidentate ligandNames << "acetylacetonate" << "bipyridine" << "ethylenediamine" << tr("Other…"); m_ligands << "2-acetylacetonate" << "2-bipyridine" << "2-ethylenediamine" << "2-other"; m_denticity = 2; break; case LigandType::Tridentate: // Tridentate ligandNames << "terpyridine" << tr("Other…"); m_ligands << "3-terpyridine" << "3-other"; m_denticity = 3; break; case LigandType::Tetradentate: // Tetradentate ligandNames << "phthalocyanine" << "porphin" << "salen" << tr("Other…"); m_ligands << "4-phthalocyanine" << "4-porphin" << "4-salen" << "4-other"; m_denticity = 4; break; case LigandType::Hexadentate: // Hexadentate ligandNames << "edta" << tr("Other…"); m_ligands << "6-edta" << "6-other"; m_denticity = 6; break; case LigandType::Haptic: // Haptic ligandNames << "η2-ethylene" << "η5-cyclopentyl" << "η6-benzene" << tr("Other…"); m_ligands << "eta2-ethylene" << "eta5-cyclopentyl" << "eta6-benzene" << "eta-other"; m_denticity = 1; break; case LigandType::Clipboard: // Clipboard ligandNames << "clipboard"; m_ligands = ligandNames; // technically, we should check the clipboard m_denticity = 1; break; } m_ui->ligandComboBox->addItems(ligandNames); ligandChanged(0); } void TemplateToolWidget::elementChanged(int index) { QVariant itemData = m_ui->elementComboBox->itemData(index); if (itemData.isValid()) { if (itemData.toInt() == ELEMENT_SELECTOR_TAG) { if (!m_elementSelector) { m_elementSelector = new QtGui::PeriodicTableView(this); connect(m_elementSelector, SIGNAL(elementChanged(int)), this, SLOT(elementSelectedFromTable(int))); } m_elementSelector->setElement(m_currentElement); m_elementSelector->show(); } else { if (m_elementSelector) m_elementSelector->setElement(itemData.toInt()); m_currentElement = static_cast(itemData.toInt()); } } QSettings settings; settings.setValue("templatetool/element", m_currentElement); } void TemplateToolWidget::updateElementCombo() { // Build set of all elements: QList allElements; allElements << m_defaultElements; allElements << m_userElements; std::sort(allElements.begin(), allElements.end()); // Cache selected atomic number for later QVariant selectedData; int curIndex = m_ui->elementComboBox->currentIndex(); if (curIndex >= 0) selectedData = m_ui->elementComboBox->itemData(curIndex); // Clear and repopulate combo m_ui->elementComboBox->clear(); foreach (unsigned char atomicNum, allElements) { m_ui->elementComboBox->addItem( QString("%1 (%2)").arg(Core::Elements::name(atomicNum)).arg(atomicNum), atomicNum); } m_ui->elementComboBox->insertSeparator(m_ui->elementComboBox->count()); m_ui->elementComboBox->addItem(tr("Other…"), ELEMENT_SELECTOR_TAG); // Reset the element if it still exists selectElement(static_cast( selectedData.isValid() ? selectedData.toInt() : -1)); } void TemplateToolWidget::addUserElement(unsigned char element) { // Never add any of the common elements to the user list. if (m_defaultElements.contains(element)) return; // If the element is already in the user list, move it to the back of the // list. if (m_userElements.removeOne(element)) { m_userElements << element; return; } m_userElements << element; // Limit the number of user elements /// @todo Make this number of user elements configurable. while (m_userElements.size() > 15) m_userElements.pop_front(); updateElementCombo(); saveElements(); } void TemplateToolWidget::elementSelectedFromTable(int element) { addUserElement(static_cast(element)); selectElement(static_cast(element)); } void TemplateToolWidget::selectElement(unsigned char element) { int curIndex = element > 0 ? m_ui->elementComboBox->findData(element) : -1; if (curIndex >= 0) m_ui->elementComboBox->setCurrentIndex(curIndex); else { addUserElement(element); curIndex = m_ui->elementComboBox->findData(element); if (curIndex >= 0) m_ui->elementComboBox->setCurrentIndex(curIndex); // if we can't find it after adding it, something is very wrong! } } void TemplateToolWidget::buildElements() { // Common elements that are always shown in the combo box. if (m_defaultElements.isEmpty()) { m_defaultElements.append(15); // Phosphorus m_defaultElements.append(16); // Sulfur m_defaultElements.append(22); // Ti m_defaultElements.append(23); // V m_defaultElements.append(24); // Cr m_defaultElements.append(25); // Mn m_defaultElements.append(26); // Fe m_defaultElements.append(27); // Co m_defaultElements.append(28); // Ni m_defaultElements.append(29); // Cu m_defaultElements.append(30); // Zn } // User-added elements QVariantList userElementsVar = QSettings().value("templatetool/userElements").toList(); foreach (const QVariant& var, userElementsVar) m_userElements << static_cast(var.toUInt()); updateElementCombo(); } void TemplateToolWidget::saveElements() { QVariantList atomicNums; for (int i = 0; i < m_userElements.size(); ++i) atomicNums << QVariant(m_userElements[i]); QSettings().setValue("templatetool/userElements", atomicNums); } int TemplateToolWidget::denticity() const { return m_denticity; } std::vector& TemplateToolWidget::selectedUIDs() { return m_selectedUIDs; } } // namespace QtPlugins } // namespace Avogadro avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/templatetoolwidget.h000066400000000000000000000042711506155467400267760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_TEMPLATETOOLWIDGET_H #define AVOGADRO_QTPLUGINS_TEMPLATETOOLWIDGET_H #include namespace Avogadro { namespace QtGui { class PeriodicTableView; class InsertFragmentDialog; } // namespace QtGui namespace QtPlugins { namespace Ui { class TemplateToolWidget; } class TemplateToolWidget : public QWidget { Q_OBJECT public: explicit TemplateToolWidget(QWidget* parent_ = 0); ~TemplateToolWidget() override; void setAtomicNumber(unsigned char atomicNum); unsigned char atomicNumber() const; void setFormalCharge(int charge); signed char formalCharge() const; void setCoordination(unsigned char geometry); unsigned char coordination() const; QString coordinationString() const; unsigned char ligand() const; QString ligandString() const; int denticity() const; std::vector& selectedUIDs(); int currentTab() const; void setCurrentTab(int index); private slots: void elementChanged(int index); void updateElementCombo(); void addUserElement(unsigned char element); void elementSelectedFromTable(int element); void selectElement(unsigned char element); void coordinationChanged(int index); void typeChanged(int index); void ligandChanged(int index); void groupChanged(int index); void otherLigandInsert(const QString& fileName, bool crystal); private: void buildElements(); void buildBondOrders(); void saveElements(); Ui::TemplateToolWidget* m_ui; QtGui::InsertFragmentDialog* m_fragmentDialog; QtGui::PeriodicTableView* m_elementSelector; QList m_defaultElements; QList m_userElements; unsigned char m_currentElement; QStringList m_centers; QStringList m_ligands; QStringList m_groups; QString m_ligandPath; int m_denticity; std::vector m_selectedUIDs; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_TEMPLATETOOLWIDGET_H avogadrolibs-1.101.0/avogadro/qtplugins/templatetool/templatetoolwidget.ui000066400000000000000000000366321506155467400271720ustar00rootroot00000000000000 Avogadro::QtPlugins::TemplateToolWidget 0 0 361 318 0 0 Form 326 285 0 320 256 Centers Element: 2 Hydrogen Carbon Iron Cobalt Qt::Horizontal 0 0 Formal Charge: -7 7 Qt::Horizontal 0 0 Coordination: 184 16777215 6: Octahedral 7 2147483646 1: Linear 2: Linear 3: Trigonal Planar 4: Tetrahedral 4: Square Planar 5: Trigonal Bipyramidal 5: Square Pyramidal 6: Octahedral 6: Trigonal Prism 7: Pentagonal Bipyramidal 8: Square Antiprism Qt::Horizontal 0 20 64 64 Qt::Horizontal 0 20 Qt::Vertical 20 0 320 254 Ligands Type: Monodentate Bidentate Tridentate Tetradentate Hexadentate Haptic From Clipboard Qt::Horizontal 0 0 Ligand: Qt::Horizontal 0 0 96 96 Qt::Horizontal 0 20 Qt::Vertical 20 0 Groups Group: amide carboxylate ester ethylene ethyne nitro phenyl phosphate sulfonate Other… Qt::Horizontal 40 20 96 96 Qt::Horizontal 40 20 Qt::Vertical 20 40 avogadrolibs-1.101.0/avogadro/qtplugins/vanderwaals/000077500000000000000000000000001506155467400225025ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/vanderwaals/CMakeLists.txt000066400000000000000000000003031506155467400252360ustar00rootroot00000000000000avogadro_plugin(VanDerWaals "Van der Waals rendering scheme" ScenePlugin vanderwaals.h VanDerWaals vanderwaals.cpp "") target_link_libraries(VanDerWaals PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/vanderwaals/vanderwaals.cpp000066400000000000000000000107351506155467400255230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vanderwaals.h" #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Elements; using QtGui::PluginLayerManager; using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::SphereGeometry; struct LayerVdW : Core::LayerData { QWidget* widget; float opacity; LayerVdW() { widget = nullptr; QSettings settings; opacity = settings.value("vdw/opacity", 1.0).toFloat(); } LayerVdW(std::string settings) { widget = nullptr; deserialize(settings); } LayerData* clone() final { return new LayerVdW(*this); } ~LayerVdW() override { if (widget) widget->deleteLater(); } std::string serialize() final { return std::to_string(opacity); } void deserialize(std::string text) final { std::stringstream ss(text); std::string aux; ss >> aux; opacity = std::stof(aux); } void setupWidget(VanDerWaals* slot) { if (!widget) { widget = new QWidget(qobject_cast(slot->parent())); auto* form = new QFormLayout; // Opacity auto* slider = new QSlider(Qt::Horizontal); slider->setRange(0, 100); slider->setTickInterval(1); slider->setValue(round(opacity * 100)); QObject::connect(slider, &QSlider::valueChanged, slot, &VanDerWaals::setOpacity); form->addRow(QObject::tr("Opacity:"), slider); widget->setLayout(form); } } }; VanDerWaals::VanDerWaals(QObject* p) : ScenePlugin(p) { m_layerManager = PluginLayerManager(m_name); QSettings settings; } VanDerWaals::~VanDerWaals() {} void VanDerWaals::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { m_layerManager.load(); // Add a sphere node to contain all of the VdW spheres. auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; spheres->identifier().molecule = &molecule; spheres->identifier().type = Rendering::AtomType; auto* translucentSpheres = new SphereGeometry; translucentSpheres->setRenderPass(Rendering::TranslucentPass); translucentSpheres->identifier().molecule = &molecule; translucentSpheres->identifier().type = Rendering::AtomType; auto selectedSpheres = new SphereGeometry; selectedSpheres->setOpacity(0.42); selectedSpheres->setRenderPass(Rendering::TranslucentPass); geometry->addDrawable(spheres); geometry->addDrawable(selectedSpheres); geometry->addDrawable(translucentSpheres); for (Index i = 0; i < molecule.atomCount(); ++i) { Core::Atom atom = molecule.atom(i); if (!m_layerManager.atomEnabled(i)) { continue; } unsigned char atomicNumber = atom.atomicNumber(); Vector3ub color = atom.color(); auto radius = static_cast(Elements::radiusVDW(atomicNumber)); auto* interface = m_layerManager.getSetting(m_layerManager.getLayerID(i)); float opacity = interface->opacity; if (opacity < 1.0f) { translucentSpheres->addSphere(atom.position3d().cast(), color, radius, i); translucentSpheres->setOpacity(opacity); } else { spheres->addSphere(atom.position3d().cast(), color, radius, i); } if (atom.selected()) { color = Vector3ub(0, 0, 255); radius += 0.3f; selectedSpheres->addSphere(atom.position3d().cast(), color, radius, i); } } } void VanDerWaals::setOpacity(int opacity) { m_opacity = static_cast(opacity) / 100.0f; auto* interface = m_layerManager.getSetting(); if (m_opacity != interface->opacity) { interface->opacity = m_opacity; emit drawablesChanged(); } QSettings settings; settings.setValue("vdw/opacity", m_opacity); } QWidget* VanDerWaals::setupWidget() { auto* interface = m_layerManager.getSetting(); interface->setupWidget(this); return interface->widget; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/vanderwaals/vanderwaals.h000066400000000000000000000025751506155467400251730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_VANDERWAALS_H #define AVOGADRO_QTPLUGINS_VANDERWAALS_H #include namespace Avogadro { namespace QtPlugins { /** * @brief Render the molecule as Van der Waals spheres. * @author Marcus D. Hanwell */ class VanDerWaals : public QtGui::ScenePlugin { Q_OBJECT public: explicit VanDerWaals(QObject* parent = nullptr); ~VanDerWaals() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Van der Waals"); } QString description() const override { return tr("Simple display of VdW spheres."); } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } public slots: void setOpacity(int opacity); private: std::string m_name = "Van der Waals"; QWidget* m_setupWidget = nullptr; float m_opacity = 1.0; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_VANDERWAALS_H avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/000077500000000000000000000000001506155467400223535ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/CMakeLists.txt000066400000000000000000000003231506155467400251110ustar00rootroot00000000000000set(plugin_srcs vibrationdialog.cpp vibrationmodel.cpp vibrations.cpp ) avogadro_plugin(Vibrations "Vibrations" ExtensionPlugin vibrations.h Vibrations "${plugin_srcs}" "vibrationdialog.ui" ) avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/vibrationdialog.cpp000066400000000000000000000042431506155467400262370ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vibrationdialog.h" #include "ui_vibrationdialog.h" #include "vibrationmodel.h" #include namespace Avogadro::QtPlugins { VibrationDialog::VibrationDialog(QWidget* parent_, Qt::WindowFlags f) : QDialog(parent_, f), m_ui(new Ui::VibrationDialog) { m_ui->setupUi(this); m_ui->tableView->verticalHeader()->setVisible(true); m_ui->tableView->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch); m_ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_ui->amplitudeSlider, SIGNAL(sliderMoved(int)), SIGNAL(amplitudeChanged(int))); connect(m_ui->startButton, SIGNAL(clicked(bool)), SIGNAL(startAnimation())); connect(m_ui->stopButton, SIGNAL(clicked(bool)), SIGNAL(stopAnimation())); } VibrationDialog::~VibrationDialog() { delete m_ui; } void VibrationDialog::setMolecule(QtGui::Molecule* molecule) { if (m_ui->tableView->selectionModel()) { disconnect(m_ui->tableView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SLOT(selectRow(QModelIndex))); } auto* model = new VibrationModel(this); model->setMolecule(molecule); m_ui->tableView->setModel(model); connect(m_ui->tableView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), SLOT(selectRow(QModelIndex))); Core::Array freqs = molecule->vibrationFrequencies(); for (size_t i = 0; i < freqs.size(); ++i) { if (freqs[i] > 0.5) { m_ui->tableView->selectRow(static_cast(i)); emit modeChanged(i); break; } } } int VibrationDialog::currentMode() const { return m_ui->tableView->currentIndex().row(); } void VibrationDialog::selectRow(QModelIndex idx) { emit modeChanged(idx.row()); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/vibrationdialog.h000066400000000000000000000023411506155467400257010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_VIBRATIONDIALOG_H #define AVOGADRO_QTPLUGINS_VIBRATIONDIALOG_H #include #include #include namespace Ui { class VibrationDialog; } namespace Avogadro { namespace QtPlugins { /** * @brief The VibrationDialog presents vibrational modes. */ class VibrationDialog : public QDialog { Q_OBJECT public: explicit VibrationDialog(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~VibrationDialog() override; void setMolecule(QtGui::Molecule* molecule); int currentMode() const; protected slots: void selectRow(QModelIndex); signals: void modeChanged(int mode); void amplitudeChanged(int amplitude); void startAnimation(); void stopAnimation(); private: Ui::VibrationDialog* m_ui; }; } // End namespace QtPlugins } // End namespace Avogadro #endif // AVOGADRO_QTPLUGINS_VibrationDialog_H avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/vibrationdialog.ui000066400000000000000000000055561506155467400261020ustar00rootroot00000000000000 VibrationDialog 0 0 500 600 Vibrational Modes 5 5 5 5 QAbstractItemView::NoEditTriggers false false true QAbstractItemView::ContiguousSelection QAbstractItemView::SelectRows Amplitude: 20 Qt::Horizontal QSlider::TicksBothSides 10 Qt::Horizontal 40 20 Start Animation Stop Animation avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/vibrationmodel.cpp000066400000000000000000000063001506155467400260740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vibrationmodel.h" #include namespace Avogadro::QtPlugins { VibrationModel::VibrationModel(QObject* p) : QAbstractItemModel(p), m_molecule(nullptr), m_hasRaman(false) { } QModelIndex VibrationModel::parent(const QModelIndex&) const { return QModelIndex(); } int VibrationModel::rowCount(const QModelIndex& p) const { if (p.isValid() || !m_molecule) return 0; else return m_molecule->vibrationFrequencies().size(); } int VibrationModel::columnCount(const QModelIndex&) const { // do we have raman data? if (m_molecule && m_hasRaman) return 3; return 2; } Qt::ItemFlags VibrationModel::flags(const QModelIndex&) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } void VibrationModel::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; m_hasRaman = mol->vibrationRamanIntensities().size() > 0; } QVariant VibrationModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { switch (section) { case 0: return QString("Frequency (cm⁻¹)"); case 1: return QString("Intensity (KM/mol)"); case 2: return QString("Raman Intensity (Å⁴/amu)"); } } else if (orientation == Qt::Vertical) { return QString::number(section + 1); } } return QVariant(); } bool VibrationModel::setData(const QModelIndex&, const QVariant&, int) { return false; } QVariant VibrationModel::data(const QModelIndex& idx, int role) const { if (!idx.isValid() || idx.column() > 2 || !m_molecule || static_cast(m_molecule->vibrationFrequencies().size()) <= idx.row()) { return QVariant(); } if (role == Qt::DisplayRole) { switch (idx.column()) { case 0: if (static_cast(m_molecule->vibrationFrequencies().size()) > idx.row()) return m_molecule->vibrationFrequencies()[idx.row()]; else return "No value"; case 1: if (static_cast(m_molecule->vibrationIRIntensities().size()) > idx.row()) return m_molecule->vibrationIRIntensities()[idx.row()]; else return "No value"; case 2: if (static_cast(m_molecule->vibrationRamanIntensities().size()) > idx.row()) return m_molecule->vibrationRamanIntensities()[idx.row()]; else return "No value"; default: return "Invalid"; } } return QVariant(); } QModelIndex VibrationModel::index(int row, int column, const QModelIndex& p) const { if (!p.isValid()) if (row >= 0 && m_molecule && row < static_cast(m_molecule->vibrationFrequencies().size())) return createIndex(row, column); return QModelIndex(); } void VibrationModel::clear() {} } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/vibrationmodel.h000066400000000000000000000027361506155467400255520ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_VIBRATIONMODEL_H #define AVOGADRO_QTPLUGINS_VIBRATIONMODEL_H #include namespace Avogadro { namespace QtGui { class Molecule; } namespace QtPlugins { class VibrationModel : public QAbstractItemModel { public: explicit VibrationModel(QObject* p = nullptr); QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; bool setData(const QModelIndex& index, const QVariant& value, int role) override; QVariant data(const QModelIndex& index, int role) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; void clear(); void setMolecule(QtGui::Molecule* mol); signals: public slots: private: QtGui::Molecule* m_molecule; bool m_hasRaman; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_VIBRATIONMODEL_H avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/vibrations.cpp000066400000000000000000000172211506155467400252420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vibrations.h" #include "vibrationdialog.h" #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { Vibrations::Vibrations(QObject* p) : ExtensionPlugin(p), m_molecule(nullptr), m_dialog(nullptr), m_timer(nullptr), m_mode(0), m_amplitude(20) { auto* action = new QAction(this); action->setEnabled(false); action->setText(tr("Vibrational Modes…")); connect(action, SIGNAL(triggered()), SLOT(openDialog())); m_actions.push_back(action); } Vibrations::~Vibrations() {} QList Vibrations::actions() const { return m_actions; } QStringList Vibrations::menuPath(QAction*) const { QStringList path; path << tr("&Analyze"); return path; } void Vibrations::setMolecule(QtGui::Molecule* mol) { if (mol == nullptr) return; if (m_molecule != nullptr) m_molecule->disconnect(this); bool isVibrational(false); if (mol->vibrationFrequencies().size()) isVibrational = true; m_actions[0]->setEnabled(isVibrational); m_molecule = mol; if (m_dialog) m_dialog->setMolecule(mol); if (isVibrational) openDialog(); connect(m_molecule, SIGNAL(changed(unsigned int)), SLOT(moleculeChanged(unsigned int))); } void Vibrations::moleculeChanged([[maybe_unused]] unsigned int changes) { if (m_molecule == nullptr) return; bool currentVibrational = m_actions[0]->isEnabled(); bool isVibrational = (m_molecule->vibrationFrequencies().size() > 0); if (currentVibrational != isVibrational) { m_actions[0]->setEnabled(isVibrational); if (m_dialog) m_dialog->setMolecule(m_molecule); // update the dialog if (isVibrational) openDialog(); } } void Vibrations::registerCommands() { emit registerCommand("showVibrations", tr("Show the vibrational modes dialog.")); emit registerCommand("setVibrationalMode", tr("Set the vibrational mode.")); emit registerCommand("setVibrationalAmplitude", tr("Set the vibrational amplitude.")); emit registerCommand("startVibrationAnimation", tr("Start the vibrational animation.")); emit registerCommand("stopVibrationAnimation", tr("Stop the vibrational animation.")); } bool Vibrations::handleCommand(const QString& command, const QVariantMap& options) { if (m_molecule == nullptr) return false; // No molecule to handle the command. if (command == "showVibrations") { openDialog(); return true; } else if (command == "setVibrationalMode") { if (options.contains("mode")) { setMode(options["mode"].toInt()); return true; } } else if (command == "setVibrationalAmplitude") { if (options.contains("amplitude")) { setAmplitude(options["amplitude"].toInt()); return true; } } else if (command == "startVibrationAnimation") { startVibrationAnimation(); return true; } else if (command == "stopVibrationAnimation") { stopVibrationAnimation(); return true; } return false; } void Vibrations::setMode(int mode) { if (mode >= 0 && mode < static_cast(m_molecule->vibrationFrequencies().size())) { m_mode = mode; // Now calculate the frames and set them on the molecule. m_molecule->setCoordinate3d(0); Core::Array atomPositions = m_molecule->atomPositions3d(); Core::Array atomDisplacements = m_molecule->vibrationLx(mode); // TODO: needs an option (show forces or not) double factor = 0.01 * m_amplitude; Index atom = 0; for (Vector3& v : atomDisplacements) { v *= 10.0 * factor; m_molecule->setForceVector(atom, v); ++atom; } int frames = 5; // TODO: needs an option int frameCounter = 0; m_molecule->clearCoordinate3d(); m_molecule->setCoordinate3d(atomPositions, frameCounter++); // Current coords + displacement. for (int i = 1; i <= frames; ++i) { Core::Array framePositions; for (atom = 0; atom < m_molecule->atomCount(); ++atom) { framePositions.push_back(atomPositions[atom] + atomDisplacements[atom] * factor * (double(i) / frames)); } m_molecule->setCoordinate3d(framePositions, frameCounter++); } // + displacement back to original. for (int i = frames - 1; i >= 0; --i) { Core::Array framePositions; for (atom = 0; atom < m_molecule->atomCount(); ++atom) { framePositions.push_back(atomPositions[atom] + atomDisplacements[atom] * factor * (double(i) / frames)); } m_molecule->setCoordinate3d(framePositions, frameCounter++); } // Current coords - displacement. for (int i = 1; i <= frames; ++i) { Core::Array framePositions; for (atom = 0; atom < m_molecule->atomCount(); ++atom) { framePositions.push_back(atomPositions[atom] - atomDisplacements[atom] * factor * (double(i) / frames)); } m_molecule->setCoordinate3d(framePositions, frameCounter++); } // - displacement back to original. for (int i = frames - 1; i >= 0; --i) { Core::Array framePositions; for (atom = 0; atom < m_molecule->atomCount(); ++atom) { framePositions.push_back(atomPositions[atom] - atomDisplacements[atom] * factor * (double(i) / frames)); } m_molecule->setCoordinate3d(framePositions, frameCounter++); } } } void Vibrations::setAmplitude(int amplitude) { m_amplitude = amplitude; setMode(m_mode); } void Vibrations::startVibrationAnimation() { // First calculate our frames, and then start our timer. m_totalFrames = m_molecule->coordinate3dCount(); m_currentFrame = 0; if (!m_timer) { m_timer = new QTimer(this); connect(m_timer, SIGNAL(timeout()), SLOT(advanceFrame())); } if (!m_timer->isActive()) { m_timer->start(50); } } void Vibrations::stopVibrationAnimation() { if (m_timer && m_timer->isActive()) { m_timer->stop(); m_molecule->setCoordinate3d(0); m_currentFrame = 0; m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } } void Vibrations::openDialog() { if (!m_dialog) { m_dialog = new VibrationDialog(qobject_cast(parent())); connect(m_dialog, SIGNAL(modeChanged(int)), SLOT(setMode(int))); connect(m_dialog, SIGNAL(amplitudeChanged(int)), SLOT(setAmplitude(int))); connect(m_dialog, SIGNAL(startAnimation()), SLOT(startVibrationAnimation())); connect(m_dialog, SIGNAL(stopAnimation()), SLOT(stopVibrationAnimation())); } if (m_molecule) m_dialog->setMolecule(m_molecule); m_dialog->show(); } void Vibrations::advanceFrame() { if (++m_currentFrame >= m_totalFrames) m_currentFrame = 0; m_molecule->setCoordinate3d(m_currentFrame); m_molecule->emitChanged(QtGui::Molecule::Atoms | QtGui::Molecule::Added); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/vibrations/vibrations.h000066400000000000000000000033121506155467400247030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_VIBRATIONS_H #define AVOGADRO_QTPLUGINS_VIBRATIONS_H #include class QAction; class QDialog; class QTimer; namespace Avogadro { namespace QtPlugins { class VibrationDialog; /** * @brief The Vibration plugin handles vibration animations. */ class Vibrations : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit Vibrations(QObject* parent = nullptr); ~Vibrations() override; QString name() const override { return tr("Vibrations"); } QString description() const override { return tr("Display vibrational modes."); } QList actions() const override; QStringList menuPath(QAction*) const override; void setMolecule(QtGui::Molecule* mol) override; bool handleCommand(const QString& command, const QVariantMap& options) override; void registerCommands() override; public slots: void setMode(int mode); void setAmplitude(int amplitude); void startVibrationAnimation(); void stopVibrationAnimation(); void openDialog(); void moleculeChanged(unsigned int changes); private slots: void advanceFrame(); private: QList m_actions; QtGui::Molecule* m_molecule; VibrationDialog* m_dialog; QTimer* m_timer; int m_currentFrame; int m_totalFrames; int m_mode; int m_amplitude; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_VIBRATIONS_H avogadrolibs-1.101.0/avogadro/qtplugins/vrml/000077500000000000000000000000001506155467400211535ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/vrml/CMakeLists.txt000066400000000000000000000002451506155467400237140ustar00rootroot00000000000000avogadro_plugin(VRML "Render the scene using VRML." ExtensionPlugin vrml.h VRML "vrml.cpp" "" ) target_link_libraries(VRML PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/vrml/vrml.cpp000066400000000000000000000036771506155467400226540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vrml.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { VRML::VRML(QObject* p) : Avogadro::QtGui::ExtensionPlugin(p), m_molecule(nullptr), m_scene(nullptr), m_camera(nullptr), m_action(new QAction(tr("VRML Render…"), this)) { connect(m_action, SIGNAL(triggered()), SLOT(render())); } VRML::~VRML() {} QList VRML::actions() const { QList result; return result << m_action; } QStringList VRML::menuPath(QAction*) const { return QStringList() << tr("&File") << tr("&Export"); } void VRML::setMolecule(QtGui::Molecule* mol) { m_molecule = mol; } void VRML::setScene(Rendering::Scene* scene) { m_scene = scene; } void VRML::setCamera(Rendering::Camera* camera) { m_camera = camera; } void VRML::render() { if (!m_scene || !m_camera) return; QString filename = QFileDialog::getSaveFileName( qobject_cast(parent()), tr("Save File"), QDir::homePath(), tr("VRML (*.wrl);;Text file (*.txt)")); QFile file(filename); if (!file.open(QIODevice::WriteOnly)) return; QTextStream fileStream(&file); Rendering::VRMLVisitor visitor(*m_camera); visitor.begin(); m_scene->rootNode().accept(visitor); fileStream << visitor.end().c_str(); file.close(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/vrml/vrml.h000066400000000000000000000024761506155467400223150ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_VRML_H #define AVOGADRO_QTPLUGINS_VRML_H #include #include namespace Avogadro { namespace QtPlugins { /** * @brief The VRML class performs VRML operations */ class VRML : public QtGui::ExtensionPlugin { Q_OBJECT public: explicit VRML(QObject* p = nullptr); ~VRML() override; QString name() const override { return tr("VRML"); } QString description() const override { return tr("Render the scene using VRML."); } QList actions() const override; QStringList menuPath(QAction* action) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void setScene(Rendering::Scene* scene) override; void setCamera(Rendering::Camera* camera) override; private slots: void render(); private: QtGui::Molecule* m_molecule; Rendering::Scene* m_scene; Rendering::Camera* m_camera; QAction* m_action; }; } // namespace QtPlugins } // namespace Avogadro #endif // AVOGADRO_QTPLUGINS_VRML_H avogadrolibs-1.101.0/avogadro/qtplugins/wireframe/000077500000000000000000000000001506155467400221545ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/wireframe/CMakeLists.txt000066400000000000000000000002651506155467400247170ustar00rootroot00000000000000avogadro_plugin(Wireframe "Wireframe rendering scheme" ScenePlugin wireframe.h Wireframe wireframe.cpp "") target_link_libraries(Wireframe PRIVATE Avogadro::Rendering) avogadrolibs-1.101.0/avogadro/qtplugins/wireframe/wireframe.cpp000066400000000000000000000154711506155467400246510ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "wireframe.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::QtPlugins { using Core::Array; using Core::Elements; using QtGui::PluginLayerManager; using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::LineStripGeometry; using Rendering::SphereGeometry; struct LayerWireframe : Core::LayerData { QWidget* widget; bool multiBonds; bool showHydrogens; float lineWidth; LayerWireframe() { widget = nullptr; QSettings settings; multiBonds = settings.value("wireframe/multiBonds", true).toBool(); showHydrogens = settings.value("wireframe/showHydrogens", true).toBool(); lineWidth = settings.value("wireframe/lineWidth", 1.0).toDouble(); } LayerWireframe(std::string settings) { widget = nullptr; deserialize(settings); } LayerData* clone() final { return new LayerWireframe(*this); } ~LayerWireframe() override { if (widget) widget->deleteLater(); } std::string serialize() final { return boolToString(multiBonds) + " " + boolToString(showHydrogens) + " " + std::to_string(lineWidth); } void deserialize(std::string text) final { std::stringstream ss(text); std::string aux; ss >> aux; multiBonds = stringToBool(aux); ss >> aux; showHydrogens = stringToBool(aux); ss >> aux; lineWidth = std::stof(aux); } void setupWidget(Wireframe* slot) { if (!widget) { widget = new QWidget(qobject_cast(slot->parent())); auto* v = new QVBoxLayout; // line width auto* spin = new QDoubleSpinBox; spin->setRange(0.5, 5.0); spin->setSingleStep(0.25); spin->setDecimals(2); spin->setValue(lineWidth); QObject::connect(spin, SIGNAL(valueChanged(double)), slot, SLOT(setWidth(double))); auto* form = new QFormLayout; form->addRow(QObject::tr("Line width:"), spin); v->addLayout(form); // options auto* check = new QCheckBox(QObject::tr("Show multiple bonds")); check->setChecked(multiBonds); QObject::connect(check, &QCheckBox::clicked, slot, &Wireframe::multiBonds); v->addWidget(check); check = new QCheckBox(QObject::tr("Show hydrogens")); check->setChecked(showHydrogens); QObject::connect(check, &QCheckBox::clicked, slot, &Wireframe::showHydrogens); v->addWidget(check); v->addStretch(1); widget->setLayout(v); } } }; Wireframe::Wireframe(QObject* p) : ScenePlugin(p), m_group(nullptr) { m_layerManager = PluginLayerManager(m_name); } Wireframe::~Wireframe() {} void Wireframe::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { m_layerManager.load(); // Add a node to contain all of the lines. m_group = &node; auto* geometry = new GeometryNode; node.addChild(geometry); auto* lines = new LineStripGeometry; lines->identifier().molecule = &molecule; lines->identifier().type = Rendering::BondType; // add tiny atom sites for selection auto atoms = new SphereGeometry; atoms->identifier().molecule = &molecule; atoms->identifier().type = Rendering::AtomType; auto selectedAtoms = new SphereGeometry; selectedAtoms->setOpacity(0.42); Vector3ub selectedColor(0, 0, 255); geometry->addDrawable(lines); geometry->addDrawable(atoms); geometry->addDrawable(selectedAtoms); for (Index i = 0; i < molecule.bondCount(); ++i) { Core::Bond bond = molecule.bond(i); if (!m_layerManager.bondEnabled(bond.atom1().index(), bond.atom2().index())) { continue; } auto* interface1 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom1().index())); auto* interface2 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom2().index())); if (!interface1->showHydrogens && !interface2->showHydrogens && (bond.atom1().atomicNumber() == 1 || bond.atom2().atomicNumber() == 1)) { continue; } Vector3f pos1 = bond.atom1().position3d().cast(); Vector3f pos2 = bond.atom2().position3d().cast(); Vector3ub color1(Elements::color(bond.atom1().atomicNumber())); Vector3ub color2(Elements::color(bond.atom2().atomicNumber())); Array points; Array colors; points.push_back(pos1); points.push_back(pos2); colors.push_back(color1); colors.push_back(color2); float lineWidth = interface1->lineWidth; if (interface1->multiBonds || interface2->multiBonds) lineWidth *= bond.order(); lines->addLineStrip(points, colors, lineWidth); // add small spheres to allow the selection tool to work // smaller than this gets ignored atoms->addSphere(pos1, color1, 0.001f, bond.atom1().index()); atoms->addSphere(pos2, color2, 0.001f, bond.atom2().index()); if (bond.atom1().selected()) selectedAtoms->addSphere(pos1, selectedColor, 0.3f, i); if (bond.atom2().selected()) selectedAtoms->addSphere(pos2, selectedColor, 0.3f, i); } } QWidget* Wireframe::setupWidget() { auto* interface = m_layerManager.getSetting(); interface->setupWidget(this); return interface->widget; } void Wireframe::multiBonds(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->multiBonds) { interface->multiBonds = show; emit drawablesChanged(); } QSettings settings; settings.setValue("wireframe/multiBonds", show); } void Wireframe::showHydrogens(bool show) { auto* interface = m_layerManager.getSetting(); if (show != interface->showHydrogens) { interface->showHydrogens = show; emit drawablesChanged(); } QSettings settings; settings.setValue("wireframe/showHydrogens", show); } void Wireframe::setWidth(double width) { auto* interface = m_layerManager.getSetting(); interface->lineWidth = float(width); emit drawablesChanged(); QSettings settings; settings.setValue("wireframe/lineWidth", width); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/wireframe/wireframe.h000066400000000000000000000025701506155467400243120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_WIREFRAME_H #define AVOGADRO_QTPLUGINS_WIREFRAME_H #include namespace Avogadro { namespace QtPlugins { /** * @brief Render a molecule in the wireframe style. */ class Wireframe : public QtGui::ScenePlugin { Q_OBJECT public: explicit Wireframe(QObject* parent = nullptr); ~Wireframe() override; void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; QString name() const override { return tr("Wireframe"); } QString description() const override { return tr("Render the molecule as a wireframe."); } QWidget* setupWidget() override; bool hasSetupWidget() const override { return true; } DefaultBehavior defaultBehavior() const override { return DefaultBehavior::False; } public slots: void multiBonds(bool show); void showHydrogens(bool show); void setWidth(double width); private: Rendering::GroupNode* m_group; std::string m_name = "Wireframe"; }; } // end namespace QtPlugins } // end namespace Avogadro #endif // AVOGADRO_QTPLUGINS_WIREFRAME_H avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/000077500000000000000000000000001506155467400216355ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/CMakeLists.txt000066400000000000000000000005261506155467400244000ustar00rootroot00000000000000 set(yaehmop_srcs banddialog.cpp specialkpoints.cpp yaehmop.cpp yaehmopout.cpp ) set(yaehmop_uis banddialog.ui ) avogadro_plugin(Yaehmop "Use yaehmop to perform extended Hückel calculations." ExtensionPlugin yaehmop.h Yaehmop "${yaehmop_srcs}" "${yaehmop_uis}" ) target_link_libraries(Yaehmop PRIVATE Avogadro::Vtk) avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/banddialog.cpp000066400000000000000000000042061506155467400244270ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "banddialog.h" #include "ui_banddialog.h" #include namespace Avogadro::QtPlugins { BandDialog::BandDialog(QWidget* aParent, YaehmopSettings& yaehmopSettings) : QDialog(aParent), m_ui(new Ui::BandDialog), m_yaehmopSettings(yaehmopSettings) { m_ui->setupUi(this); } // Destructor must be defined after Ui::BandDialog has been resovled BandDialog::~BandDialog() = default; int BandDialog::exec() { // Load the settings then exec m_ui->spin_numKPoints->setValue(m_yaehmopSettings.numBandKPoints); m_ui->edit_specialKPoints->setText(m_yaehmopSettings.specialKPoints); m_ui->cb_displayYaehmopInput->setChecked( m_yaehmopSettings.displayYaehmopInput); m_ui->cb_limitY->setChecked(m_yaehmopSettings.limitY); m_ui->spin_minY->setValue(m_yaehmopSettings.minY); m_ui->spin_maxY->setValue(m_yaehmopSettings.maxY); m_ui->cb_plotFermi->setChecked(m_yaehmopSettings.plotFermi); m_ui->spin_fermi->setValue(m_yaehmopSettings.fermi); m_ui->cb_zeroFermi->setChecked(m_yaehmopSettings.zeroFermi); m_ui->spin_numDim->setValue(m_yaehmopSettings.numDim); return QDialog::exec(); } void BandDialog::accept() { // Save the settings and accept m_yaehmopSettings.numBandKPoints = m_ui->spin_numKPoints->value(); m_yaehmopSettings.specialKPoints = m_ui->edit_specialKPoints->toPlainText(); m_yaehmopSettings.displayYaehmopInput = m_ui->cb_displayYaehmopInput->isChecked(); m_yaehmopSettings.limitY = m_ui->cb_limitY->isChecked(); m_yaehmopSettings.minY = m_ui->spin_minY->value(); m_yaehmopSettings.maxY = m_ui->spin_maxY->value(); m_yaehmopSettings.plotFermi = m_ui->cb_plotFermi->isChecked(); m_yaehmopSettings.fermi = m_ui->spin_fermi->value(); m_yaehmopSettings.zeroFermi = m_ui->cb_zeroFermi->isChecked(); m_yaehmopSettings.numDim = m_ui->spin_numDim->value(); QDialog::accept(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/banddialog.h000066400000000000000000000017741506155467400241030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_YAEHMOPBANDDIALOG_H #define AVOGADRO_QTPLUGINS_YAEHMOPBANDDIALOG_H #include "yaehmopsettings.h" #include #include namespace Avogadro::QtPlugins { namespace Ui { class BandDialog; } /** * @brief Dialog to perform a band structure calculation with yaehmop. */ class BandDialog : public QDialog { Q_OBJECT public: explicit BandDialog(QWidget* parent, YaehmopSettings& yaehmopSettings); ~BandDialog() override; public slots: int exec() override; protected slots: void accept() override; private: std::unique_ptr m_ui; YaehmopSettings& m_yaehmopSettings; }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_YAEHMOPBANDDIALOG_H avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/banddialog.ui000066400000000000000000000305631506155467400242670ustar00rootroot00000000000000 Avogadro::QtPlugins::BandDialog 0 0 599 406 Yaehmop Band Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok false Min y: eV 5 -10000.000000000000000 10000.000000000000000 <html><head/><body><p>The Fermi level should be known before checking this box. You can discover the Fermi level by performing a density of states calculation and displaying the data (it will be at the top of the data). In addition, if a density of states calculation is performed, the Fermi level here will automatically be set to what was detected during the density of states calculation.</p><p>If this box is checked, be sure the correct Fermi level is set in the spinbox on the right.</p><p>Default: off</p></body></html> Qt::RightToLeft Plot Fermi? false <html><head/><body><p>Adjust the energies so that the zero is the Fermi? Only available if we are plotting the Fermi level.</p><p><br/></p><p>Default: off</p></body></html> Qt::RightToLeft Zero Fermi? # of k-points connecting special k-points: Special k-points false Max y: eV 5 -1000.000000000000000 1000.000000000000000 false <html><head/><body><p>The Fermi Level</p></body></html> eV 5 -10000.000000000000000 10000.000000000000000 0.100000000000000 <html><head/><body><p>Enter special k-points as such:</p><p>L 0.5 0.5 0.5</p><p>G 0.0 0.0 0.0</p><p>X 0.5 0.0 0.5</p><p>That is, &lt;symbol&gt; &lt;x&gt; &lt;y&gt; &lt;z&gt; where x, y, and z are fractional reciprocal space coordinates. Lines will be drawn connecting these k-points on the graph in the order you put them in. Please note that the orientation of your cell may have an effect on the locations of these reciprocal space points.</p><p>If the space group of the crystal has been perceived or set, the special k points will be automatically filled up with the primitive cell high symmetry points for that space group. There are a few space groups will different high symmetry points depending on the lattice (such as if a &gt; b or a &lt; b) - that is taken into account automatically.</p></body></html> <!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" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; 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;"><span style=" font-family:'Ubuntu'; font-size:11pt;">GM 0.0 0.0 0.0</span></p></body></html> <html><head/><body><p>Enter the number of k-points that will be connecting the special k-points. More of these k-points will smooth out the graph, but the calculation may take longer.</p><p>Default: 40</p></body></html> Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 0 999999 40 <html><head/><body><p>Limit the y-range in the plot?</p><p>Default: off</p></body></html> Limit y-range? <html><head/><body><p>The number of periodic dimensions.</p><p><br/></p><p>If this is set to 1, the material will be periodic only along the A vector of the crystal.</p><p><br/></p><p>If this is set to 2, the material will be periodic along both the A and B vectors of the crystal.</p><p><br/></p><p>If this is set to 3, the material will be periodic along the A, B, and C vectors of the crystal.</p><p><br/></p></body></html> Qt::LeftToRight Number of Dimensions: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter <html><head/><body><p>The number of periodic dimensions.</p><p><br/></p><p>If this is set to 1, the material will be periodic only along the A vector of the crystal.</p><p><br/></p><p>If this is set to 2, the material will be periodic along both the A and B vectors of the crystal.</p><p><br/></p><p>If this is set to 3, the material will be periodic along the A, B, and C vectors of the crystal.</p><p><br/></p></body></html> 1 3 3 Display Yaehmop Input? spin_numKPoints edit_specialKPoints cb_displayYaehmopInput cb_limitY spin_minY spin_maxY cb_plotFermi spin_fermi cb_zeroFermi spin_numDim buttonBox accepted() Avogadro::QtPlugins::BandDialog accept() 248 254 157 274 buttonBox rejected() Avogadro::QtPlugins::BandDialog reject() 316 260 286 274 cb_limitY toggled(bool) spin_minY setEnabled(bool) 449 180 449 211 cb_limitY toggled(bool) spin_maxY setEnabled(bool) 449 180 449 244 cb_plotFermi toggled(bool) spin_fermi setEnabled(bool) 170 250 449 251 cb_plotFermi toggled(bool) cb_zeroFermi setEnabled(bool) 170 225 170 255 avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/specialkpoints.cpp000066400000000000000000000133321506155467400253730ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #include #include #include #include #include #include #include #include "specialkpoints.h" #include "specialkpointsdata.h" using Avogadro::Vector3; using Avogadro::Core::AvoSpglib; using Avogadro::Core::Molecule; using Avogadro::Core::SpaceGroups; using Avogadro::Core::UnitCell; namespace Avogadro::QtPlugins { QString SpecialKPoints::getSpecialKPoints(Molecule& mol) { UnitCell* cell = mol.unitCell(); if (cell) { // Check to see if the space group is set. unsigned short hallNumber = mol.hallNumber(); // Try to find the space group if it is not set if (!hallNumber) { hallNumber = AvoSpglib::getHallNumber(mol); // If we still can't find the hall number, just return if (!hallNumber) return QString(); } unsigned short spgNum = SpaceGroups::internationalNumber(hallNumber); if (spgNum != 0 && spgNum < 231) { QString specialKPoints = special_k_points[spgNum]; if (specialKPoints.contains("#")) processConditionKPoints(specialKPoints, mol, spgNum); return specialKPoints.replace(',', '\n'); } } // If we made it here, we failed return QString(); } void SpecialKPoints::processConditionKPoints(QString& specialKPoints, Molecule& mol, unsigned short spgNum) { // Check to see if the space group is set. UnitCell* cell = mol.unitCell(); if (!cell) { qDebug() << "Error in " << __FUNCTION__ << ": cell is NULL!"; specialKPoints = ""; return; } // Ensure the space group number is valid if (spgNum == 0 || spgNum > 230) { qDebug() << "Error in " << __FUNCTION__ << ": spg is invalid!"; specialKPoints = ""; return; } QStringList stringSplit = specialKPoints.split("#"); switch (spgNum) { // a > b or a < b case 20: case 21: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 63: case 64: case 65: case 66: case 67: case 68: { if (stringSplit.size() != 2) { qDebug() << "Error in " << __FUNCTION__ << ": for spgNum " << QString::number(spgNum) << ", size is not correct!"; specialKPoints = ""; } else { unsigned short ind = 0; // a > b? if (cell->a() >= cell->b()) ind = 0; else ind = 1; specialKPoints = stringSplit[ind]; } return; } // a^-2 > b^-2 + c^-2, c^-2 > a^-2 + b^-2, or other case 22: case 42: case 43: case 69: case 70: { if (stringSplit.size() != 3) { qDebug() << "Error in " << __FUNCTION__ << ": for spgNum " << QString::number(spgNum) << ", size is not correct!"; specialKPoints = ""; } else { unsigned short ind = 0; double a = cell->a(); double b = cell->b(); double c = cell->c(); if (pow(a, -2.0) >= pow(b, -2.0) + pow(c, -2.0)) ind = 0; else if (pow(c, -2.0) >= pow(a, -2.0) + pow(b, -2.0)) ind = 1; else ind = 2; specialKPoints = stringSplit[ind]; } return; } // (b > a > c or b > c > a) or other case 23: case 24: case 44: case 45: case 46: case 71: case 72: case 73: case 74: { if (stringSplit.size() != 2) { qDebug() << "Error in " << __FUNCTION__ << ": for spgNum " << QString::number(spgNum) << ", size is not correct!"; specialKPoints = ""; } else { unsigned short ind = 0; double a = cell->a(); double b = cell->b(); double c = cell->c(); if ((b >= a && a >= c) || (b >= c && c >= a)) ind = 0; else ind = 1; specialKPoints = stringSplit[ind]; } return; } // c/a > 1 or other case 79: case 80: case 82: case 87: case 88: case 97: case 98: case 107: case 108: case 109: case 110: case 119: case 120: case 121: case 122: case 139: case 140: case 141: case 142: { if (stringSplit.size() != 2) { qDebug() << "Error in " << __FUNCTION__ << ": for spgNum " << QString::number(spgNum) << ", size is not correct!"; specialKPoints = ""; } else { unsigned short ind = 0; if (cell->c() / cell->a() >= 1) ind = 0; else ind = 1; specialKPoints = stringSplit[ind]; } return; } // sqrt3a > sqrt2c or other case 146: case 148: case 155: case 160: case 161: case 166: case 167: { if (stringSplit.size() != 2) { qDebug() << "Error in " << __FUNCTION__ << ": for spgNum " << QString::number(spgNum) << ", size is not correct!"; specialKPoints = ""; } else { unsigned short ind = 0; if (sqrt(3.0 * cell->a()) >= sqrt(2.0 * cell->c())) ind = 0; else ind = 1; specialKPoints = stringSplit[ind]; } return; } // This shouldn't happen default: { qDebug() << "Error in" << __FUNCTION__ << ": failed to process spg " << QString::number(spgNum); specialKPoints = ""; return; } } } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/specialkpoints.h000066400000000000000000000033151506155467400250400ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_YAEHMOP_SPECIALKPOINTS_H #define AVOGADRO_QTPLUGINS_YAEHMOP_SPECIALKPOINTS_H namespace Avogadro { namespace Core { class Molecule; } } // namespace Avogadro namespace Avogadro { namespace QtPlugins { #include class SpecialKPoints { public: /* Get the special k points for a particular space group. It will * be returned like the following: * * GM 0 0 0 * Y 0 0.5 0 * etc. * * @param mol The molecule for which to get special k points. If the * space group is cached, that will be used. If not, this * function will attempt to determine the space group via * avospglib. If that fails, an empty string is returned. * * @return A QString containing the data for the special kpoint. */ static QString getSpecialKPoints(Core::Molecule& mol); private: /* Process a special k point with a condition. These special k points * are separated in specialkpointsdata.h by a '#' symbol. This function * has stored in it all of the cases for different conditions. It will * process and change the @param specialKPoints to be the correct * QString for the @param molecule. * */ static void processConditionKPoints(QString& specialKPoints, Core::Molecule& mol, unsigned short spgNum); }; } // namespace QtPlugins } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/specialkpointsdata.h000066400000000000000000000607031506155467400256760ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_YAEHMOP_SPECIALKPOINTSDATA_H #define AVOGADRO_QTPLUGINS_YAEHMOP_SPECIALKPOINTSDATA_H namespace Avogadro::QtPlugins { // There are 230 of these (not including the first value, which is null) // One point for each space group. // If there are multiple ways to express a space group's special k points // depending on some conditions like if a > b or a < b, there is a # // separating the different conditions const char* special_k_points[] = { "", "GM 0 0 0", // 1 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,X 0.5 0 0,V 0.5 0.5 0,U 0.5 0 0.5,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 2 "GM 0 0 0,Z 0 0.5 0,B 0 0 0.5,D 0 0.5 0.5,Y 0.5 0 0,C 0.5 0.5 0,A -0.5 0 " "0.5,E -0.5 0.5 0.5", // 3 "GM 0 0 0,Z 0 0.5 0,B 0 0 0.5,D 0 0.5 0.5,Y 0.5 0 0,C 0.5 0.5 0,A -0.5 0 " "0.5,E -0.5 0.5 0.5", // 4 "GM 0 0 0,Y 0.5 0.5 0,A 0 0 0.5,M 0.5 0.5 0.5", // 5 "GM 0 0 0,B 0 0 0.5,Y 0.5 0 0,A -0.5 0 0.5,Z 0 0.5 0,C 0.5 0.5 0,D 0 0.5 " "0.5,E -0.5 0.5 0.5", // 6 "GM 0 0 0,B 0 0 0.5,Y 0.5 0 0,A -0.5 0 0.5,Z 0 0.5 0,C 0.5 0.5 0,D 0 0.5 " "0.5,E -0.5 0.5 0.5", // 7 "GM 0 0 0,Y 0.5 0.5 0,A 0 0 0.5,M 0.5 0.5 0.5", // 8 "GM 0 0 0,Y 0.5 0.5 0,A 0 0 0.5,M 0.5 0.5 0.5", // 9 "GM 0 0 0,Z 0 0.5 0,B 0 0 0.5,Y 0.5 0 0,C 0.5 0.5 0,D 0 0.5 0.5,A -0.5 0 " "0.5,E -0.5 0.5 0.5", // 10 "GM 0 0 0,Z 0 0.5 0,B 0 0 0.5,Y 0.5 0 0,C 0.5 0.5 0,D 0 0.5 0.5,A -0.5 0 " "0.5,E -0.5 0.5 0.5", // 11 "GM 0 0 0,Y 0.5 0.5 0,A 0 0 0.5,M 0.5 0.5 0.5,V 0.5 0 0,L 0.5 0 0.5", // 12 "GM 0 0 0,Z 0 0.5 0,B 0 0 0.5,Y 0.5 0 0,C 0.5 0.5 0,D 0 0.5 0.5,A -0.5 0 " "0.5,E -0.5 0.5 0.5", // 13 "GM 0 0 0,Z 0 0.5 0,B 0 0 0.5,Y 0.5 0 0,C 0.5 0.5 0,D 0 0.5 0.5,A -0.5 0 " "0.5,E -0.5 0.5 0.5", // 14 "GM 0 0 0,Y 0.5 0.5 0,A 0 0 0.5,M 0.5 0.5 0.5,V 0.5 0 0,L 0.5 0 0.5", // 15 "GM 0 0 0,X 0.5 0 0,Y 0 0.5 0,Z 0 0 0.5,S 0.5 0.5 0,U 0.5 0 0.5,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 16 "GM 0 0 0,X 0.5 0 0,Y 0 0.5 0,Z 0 0 0.5,S 0.5 0.5 0,U 0.5 0 0.5,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 17 "GM 0 0 0,X 0.5 0 0,Y 0 0.5 0,Z 0 0 0.5,S 0.5 0.5 0,U 0.5 0 0.5,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 18 "GM 0 0 0,X 0.5 0 0,Y 0 0.5 0,Z 0 0 0.5,S 0.5 0.5 0,U 0.5 0 0.5,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 19 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 20 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 21 "GM 0 0 0,T 1 0.5 0.5,Z 0.5 0.5 0,Y 0.5 0 0.5#GM 0 0 0,T 0 0.5 0.5,Z 0.5 0.5 " "1,Y 0.5 0 0.5#GM 0 0 0,T 0 0.5 0.5,Z 0.5 0.5 0,Y 0.5 0 0.5", // 22 "GM 0 0 0,X 0.5 -0.5 0.5,W 0.25 0.25 0.25,WA -0.25 -0.25 0.75,T 0 0 0.5,R 0 " "0.5 0,S 0.5 0 0#GM 0 0 0,X 0.5 0.5 -0.5,W 0.25 0.25 0.25,WA -0.25 -0.25 " "0.75,T 0 0 0.5,R 0 0.5 0,S 0.5 0 0", // 23 "GM 0 0 0,X 0.5 -0.5 0.5,W 0.25 0.25 0.25,WA -0.25 -0.25 0.75,T 0 0 0.5,R 0 " "0.5 0,S 0.5 0 0#GM 0 0 0,X 0.5 0.5 -0.5,W 0.25 0.25 0.25,WA -0.25 -0.25 " "0.75,T 0 0 0.5,R 0 0.5 0,S 0.5 0 0", // 24 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 25 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 26 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 27 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 28 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 29 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 30 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 31 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 32 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 33 "GM 0 0 0,Z 0 0 0.5,Y 0 0.5 0,T 0 0.5 0.5,X 0.5 0 0,U 0.5 0 0.5,S 0.5 0.5 " "0,R 0.5 0.5 0.5", // 34 "GM 0 0 0,Z 0 0 0.5,Y 0.5 0.5 0,T 0.5 0.5 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Z 0 0 0.5,Y -0.5 0.5 0,T -0.5 0.5 0.5,S 0 0.5 0,R 0 0.5 0.5", // 35 "GM 0 0 0,Z 0 0 0.5,Y 0.5 0.5 0,T 0.5 0.5 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Z 0 0 0.5,Y -0.5 0.5 0,T -0.5 0.5 0.5,S 0 0.5 0,R 0 0.5 0.5", // 36 "GM 0 0 0,Z 0 0 0.5,Y 0.5 0.5 0,T 0.5 0.5 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Z 0 0 0.5,Y -0.5 0.5 0,T -0.5 0.5 0.5,S 0 0.5 0,R 0 0.5 0.5", // 37 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 38 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 39 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 40 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 41 "GM 0 0 0,Z 0.5 0.5 0,T 1 0.5 0.5,Y 0.5 0 0.5#GM 0 0 0,Z 0.5 0.5 1,T 0 0.5 " "0.5,Y 0.5 0 0.5#GM 0 0 0,Z 0.5 0.5 0,T 0 0.5 0.5,Y 0.5 0 0.5", // 42 "GM 0 0 0,Z 0.5 0.5 0,T 1 0.5 0.5,Y 0.5 0 0.5#GM 0 0 0,Z 0.5 0.5 1,T 0 0.5 " "0.5,Y 0.5 0 0.5#GM 0 0 0,Z 0.5 0.5 0,T 0 0.5 0.5,Y 0.5 0 0.5", // 43 "GM 0 0 0,X 0.5 -0.5 0.5,T 0 0 0.5,W 0.25 0.25 0.25,S 0.5 0 0,R 0 0.5 0#GM 0 " "0 0,X 0.5 0.5 -0.5,T 0 0 0.5,W 0.25 0.25 0.25,S 0.5 0 0,R 0 0.5 0", // 44 "GM 0 0 0,X 0.5 -0.5 0.5,T 0 0 0.5,W 0.25 0.25 0.25,S 0.5 0 0,R 0 0.5 0#GM 0 " "0 0,X 0.5 0.5 -0.5,T 0 0 0.5,W 0.25 0.25 0.25,S 0.5 0 0,R 0 0.5 0", // 45 "GM 0 0 0,X 0.5 -0.5 0.5,T 0 0 0.5,W 0.25 0.25 0.25,S 0.5 0 0,R 0 0.5 0#GM 0 " "0 0,X 0.5 0.5 -0.5,T 0 0 0.5,W 0.25 0.25 0.25,S 0.5 0 0,R 0 0.5 0", // 46 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 47 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 48 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 49 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 50 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 51 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 52 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 53 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 54 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 55 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 56 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 57 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 58 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 59 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 60 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 61 "GM 0 0 0,X 0.5 0 0,Z 0 0 0.5,U 0.5 0 0.5,Y 0 0.5 0,S 0.5 0.5 0,T 0 0.5 " "0.5,R 0.5 0.5 0.5", // 62 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 63 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 64 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 65 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 66 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 67 "GM 0 0 0,Y 0.5 0.5 0,T 0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5#GM 0 0 " "0,Y -0.5 0.5 0,T -0.5 0.5 0.5,Z 0 0 0.5,S 0 0.5 0,R 0 0.5 0.5", // 68 "GM 0 0 0,T 1 0.5 0.5,Z 0.5 0.5 0,Y 0.5 0 0.5,L 0.5 0.5 0.5#GM 0 0 0,T 0 0.5 " "0.5,Z 0.5 0.5 1,Y 0.5 0 0.5,L 0.5 0.5 0.5#GM 0 0 0,T 0 0.5 0.5,Z 0.5 0.5 " "0,Y 0.5 0 0.5,L 0.5 0.5 0.5", // 69 "GM 0 0 0,T 1 0.5 0.5,Z 0.5 0.5 0,Y 0.5 0 0.5,L 0.5 0.5 0.5#GM 0 0 0,T 0 0.5 " "0.5,Z 0.5 0.5 1,Y 0.5 0 0.5,L 0.5 0.5 0.5#GM 0 0 0,T 0 0.5 0.5,Z 0.5 0.5 " "0,Y 0.5 0 0.5,L 0.5 0.5 0.5", // 70 "GM 0 0 0,X 0.5 -0.5 0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25#GM 0 " "0 0,X 0.5 0.5 -0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25", // 71 "GM 0 0 0,X 0.5 -0.5 0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25#GM 0 " "0 0,X 0.5 0.5 -0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25", // 72 "GM 0 0 0,X 0.5 -0.5 0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25#GM 0 " "0 0,X 0.5 0.5 -0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25", // 73 "GM 0 0 0,X 0.5 -0.5 0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25#GM 0 " "0 0,X 0.5 0.5 -0.5,S 0.5 0 0,R 0 0.5 0,T 0 0 0.5,W 0.25 0.25 0.25", // 74 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 75 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 76 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 77 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 78 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25#GM 0 0 0,M -0.5 0.5 " "0.5,X 0 0 0.5,P 0.25 0.25 0.25", // 79 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25#GM 0 0 0,M -0.5 0.5 " "0.5,X 0 0 0.5,P 0.25 0.25 0.25", // 80 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 81 "GM 0 0 0,M 0.5 0.5 -0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,X 0 0 0.5#GM 0 " "0 0,M -0.5 0.5 0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,X 0 0 0.5", // 82 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 83 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 84 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 85 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 86 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 87 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 88 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 89 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 90 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 91 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 92 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 93 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 94 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 95 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 96 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 97 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 98 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 99 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 100 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 101 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 102 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 103 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 104 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 105 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,X 0 0.5 0,R 0 0.5 0.5", // 106 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 107 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 108 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 109 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 110 "GM 0 0 0,A 0.5 0.5 0.5,Z 0 0 0.5,M 0.5 0.5 0,X 0 0.5 0,R 0 0.5 0.5", // 111 "GM 0 0 0,A 0.5 0.5 0.5,Z 0 0 0.5,M 0.5 0.5 0,X 0 0.5 0,R 0 0.5 0.5", // 112 "GM 0 0 0,A 0.5 0.5 0.5,Z 0 0 0.5,M 0.5 0.5 0,X 0 0.5 0,R 0 0.5 0.5", // 113 "GM 0 0 0,A 0.5 0.5 0.5,Z 0 0 0.5,M 0.5 0.5 0,X 0 0.5 0,R 0 0.5 0.5", // 114 "GM 0 0 0,M 0.5 0.5 0,A 0.5 0.5 0.5,Z 0 0 0.5,X 0 0.5 0,R 0 0.5 0.5", // 115 "GM 0 0 0,M 0.5 0.5 0,A 0.5 0.5 0.5,Z 0 0 0.5,X 0 0.5 0,R 0 0.5 0.5", // 116 "GM 0 0 0,M 0.5 0.5 0,A 0.5 0.5 0.5,Z 0 0 0.5,X 0 0.5 0,R 0 0.5 0.5", // 117 "GM 0 0 0,M 0.5 0.5 0,A 0.5 0.5 0.5,Z 0 0 0.5,X 0 0.5 0,R 0 0.5 0.5", // 118 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 119 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 120 "GM 0 0 0,M 0.5 0.5 -0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,X 0 0 0.5,N 0 " "0.5 0#GM 0 0 0,M -0.5 0.5 0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,X 0 0 " "0.5,N 0 0.5 0", // 121 "GM 0 0 0,M 0.5 0.5 -0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,X 0 0 0.5,N 0 " "0.5 0#GM 0 0 0,M -0.5 0.5 0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,X 0 0 " "0.5,N 0 0.5 0", // 122 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 123 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 124 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 125 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 126 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 127 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 128 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 129 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 130 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 131 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 132 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 133 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 134 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 135 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 136 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 137 "GM 0 0 0,Z 0 0 0.5,M 0.5 0.5 0,A 0.5 0.5 0.5,R 0 0.5 0.5,X 0 0.5 0", // 138 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 139 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 140 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 141 "GM 0 0 0,M 0.5 0.5 -0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0#GM 0 0 0,M " "-0.5 0.5 0.5,X 0 0 0.5,P 0.25 0.25 0.25,N 0 0.5 0", // 142 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5", // 143 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5", // 144 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5", // 145 "GM 0 0 0,T 0.5 0.5 -0.5#GM 0 0 0,T 0.5 0.5 0.5", // 146 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 147 "GM 0 0 0,T 0.5 0.5 -0.5,L 0 0.5 0,FA 0 0.5 -0.5#GM 0 0 0,T 0.5 0.5 -0.5,L 0 " "0.5 0,FB 0.5 0.5 0", // 148 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 149 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 150 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 151 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 152 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 153 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 154 "GM 0 0 0,T 0.5 0.5 -0.5,FA 0 0.5 -0.5,L 0 0.5 0#GM 0 0 0,T 0.5 0.5 0.5,FB " "0.5 0.5 0,L 0 0.5 0", // 155 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 156 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 157 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 158 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 159 "GM 0 0 0,T 0.5 0.5 -0.5,L 0 0.5 0,FA 0 0.5 -0.5#GM 0 0 0,T 0.5 0.5 -0.5,L 0 " "0.5 0,FB 0.5 0.5 0", // 160 "GM 0 0 0,T 0.5 0.5 -0.5,L 0 0.5 0,FA 0 0.5 -0.5#GM 0 0 0,T 0.5 0.5 -0.5,L 0 " "0.5 0,FB 0.5 0.5 0", // 161 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 162 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 163 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 164 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 165 "GM 0 0 0,T 0.5 0.5 -0.5,L 0 0.5 0,FA 0 0.5 -0.5#GM 0 0 0,T 0.5 0.5 0.5,L 0 " "0.5 0,FB 0 0.5 0.5", // 166 "GM 0 0 0,T 0.5 0.5 -0.5,L 0 0.5 0,FA 0 0.5 -0.5#GM 0 0 0,T 0.5 0.5 0.5,L 0 " "0.5 0,FB 0 0.5 0.5", // 167 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 168 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 169 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 170 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 171 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 172 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 173 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 174 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 175 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 176 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 177 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 178 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 179 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 180 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 181 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 182 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 183 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 184 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 185 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 186 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 187 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 188 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 189 "GM 0 0 0,A 0 0 0.5,KA 0.666667 -0.333333 0,HA 0.666667 -0.333333 0.5,K " "0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 0,L 0.5 0 0.5", // 190 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 191 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 192 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 193 "GM 0 0 0,A 0 0 0.5,K 0.333333 0.333333 0,H 0.333333 0.333333 0.5,M 0.5 0 " "0,L 0.5 0 0.5", // 194 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 195 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 196 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,N 0 0 0.5", // 197 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 198 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,N 0 0 0.5", // 199 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 200 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 201 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 202 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 203 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,N 0 0 0.5", // 204 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 205 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,N 0 0 0.5", // 206 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 207 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 208 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 209 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 210 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,N 0 0 0.5", // 211 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 212 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 213 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,N 0 0 0.5", // 214 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 215 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 216 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,N 0 0 0.5", // 217 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 218 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 219 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,PA -0.25 -0.25 0.75,N 0 0 0.5", // 220 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 221 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 222 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 223 "GM 0 0 0,R 0.5 0.5 0.5,M 0.5 0.5 0,X 0 0.5 0", // 224 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 225 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 226 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 227 "GM 0 0 0,X 0.5 0 0.5,L 0.5 0.5 0.5,W 0.5 0.25 0.75", // 228 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,N 0 0 0.5", // 229 "GM 0 0 0,H 0.5 -0.5 0.5,P 0.25 0.25 0.25,N 0 0 0.5" // 230 }; } // namespace Avogadro::QtPlugins #endif avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/yaehmop.cpp000066400000000000000000000446371506155467400240210ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "yaehmop.h" #include "banddialog.h" #include "specialkpoints.h" #include "yaehmopout.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Avogadro::Vector3; using Avogadro::Vector3i; using Avogadro::Core::Array; using Avogadro::Core::Elements; using Avogadro::Core::UnitCell; using Avogadro::QtGui::Molecule; namespace Avogadro::QtPlugins { Yaehmop::Yaehmop(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_actions(QList()), m_molecule(nullptr), m_yaehmopSettings(), m_bandDialog( new BandDialog(qobject_cast(parent()), m_yaehmopSettings)), m_displayBandDialogAction(new QAction(this)) { // first off, look for the program // either as eht_bind or yaehmop bool found = false; // If the YAEHMOP_EXECUTABLE environment variable is set, then // use that QByteArray yaehmopExec = qgetenv("YAEHMOP_EXECUTABLE"); if (!yaehmopExec.isEmpty()) { m_programPath = yaehmopExec; } else { // otherwise look in the current directory, ../bin and the PATH environment #ifdef _WIN32 QStringList programs = QStringList() << "eht_bind.exe" << "yaehmop.exe"; #else QStringList programs = QStringList() << "eht_bind" << "yaehmop"; #endif QStringList paths; paths << QCoreApplication::applicationDirPath() << QCoreApplication::applicationDirPath() + "/../bin"; // add the PATH environment variable auto environment = QProcessEnvironment::systemEnvironment().value("PATH"); paths << environment.split(':'); foreach (QString binary, programs) { foreach (QString path, paths) { if (QFile::exists(path + "/" + binary)) { m_programPath = path + "/" + binary; found = true; break; } } if (found) break; } } m_displayBandDialogAction->setText(tr("Calculate Band Structure…")); m_displayBandDialogAction->setEnabled(found); connect(m_displayBandDialogAction.get(), &QAction::triggered, this, &Yaehmop::displayBandDialog); m_actions.push_back(m_displayBandDialogAction.get()); m_displayBandDialogAction->setProperty("menu priority", 90); updateActions(); readSettings(); } Yaehmop::~Yaehmop() = default; QList Yaehmop::actions() const { return m_actions; } QStringList Yaehmop::menuPath(QAction*) const { return QStringList() << tr("&Extensions") << tr("&Yaehmop"); } void Yaehmop::setMolecule(QtGui::Molecule* mol) { if (m_molecule == mol) return; if (m_molecule) m_molecule->disconnect(this); m_molecule = mol; if (m_molecule) connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint))); updateActions(); } void Yaehmop::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); auto changes = static_cast(c); if (changes & Molecule::UnitCell) { if (changes & Molecule::Added || changes & Molecule::Removed) updateActions(); } } void Yaehmop::updateActions() { // Disable everything for nullptr molecules. if (!m_molecule) { foreach (QAction* action, m_actions) action->setEnabled(false); return; } // Only display the actions if there is a unit cell if (m_molecule->unitCell()) { foreach (QAction* action, m_actions) action->setEnabled(true); } else { foreach (QAction* action, m_actions) action->setEnabled(false); } } void Yaehmop::readSettings() { QSettings settings; m_yaehmopSettings.numBandKPoints = settings.value("yaehmop/bandOptions/numBandKPoints", 40).toUInt(); m_yaehmopSettings.displayYaehmopInput = settings.value("yaehmop/general/displayYaehmopInput", false).toBool(); m_yaehmopSettings.limitY = settings.value("yaehmop/general/limitY", false).toBool(); m_yaehmopSettings.minY = settings.value("yaehmop/general/minY", 0.0).toDouble(); m_yaehmopSettings.maxY = settings.value("yaehmop/general/maxY", 0.0).toDouble(); m_yaehmopSettings.plotFermi = settings.value("yaehmop/general/plotFermi", false).toBool(); m_yaehmopSettings.fermi = settings.value("yaehmop/general/fermi", 0.0).toDouble(); m_yaehmopSettings.zeroFermi = settings.value("yaehmop/general/zeroFermi", false).toBool(); m_yaehmopSettings.numDim = settings.value("yaehmop/general/numDim", 3).toUInt(); } void Yaehmop::writeSettings() { QSettings settings; settings.setValue("yaehmop/bandOptions/numBandKPoints", m_yaehmopSettings.numBandKPoints); settings.setValue("yaehmop/general/displayYaehmopInput", m_yaehmopSettings.displayYaehmopInput); settings.setValue("yaehmop/general/limitY", m_yaehmopSettings.limitY); settings.setValue("yaehmop/general/minY", m_yaehmopSettings.minY); settings.setValue("yaehmop/general/maxY", m_yaehmopSettings.maxY); settings.setValue("yaehmop/general/plotFermi", m_yaehmopSettings.plotFermi); settings.setValue("yaehmop/general/fermi", m_yaehmopSettings.fermi); settings.setValue("yaehmop/general/zeroFermi", m_yaehmopSettings.zeroFermi); settings.setValue("yaehmop/general/numDim", m_yaehmopSettings.numDim); } void Yaehmop::displayBandDialog() { if (!m_molecule) { qDebug() << "Error in " << __FUNCTION__ << ": the molecule is not set"; return; } if (!m_molecule->unitCell()) { QMessageBox::warning(nullptr, tr("Avogadro2"), tr("Cannot calculate band structure: no unit cell!")); qDebug() << "Error in " << __FUNCTION__ << ": there is no unit cell"; return; } // Generate the special k points before displaying the dialog m_yaehmopSettings.specialKPoints = SpecialKPoints::getSpecialKPoints(*m_molecule); if (m_yaehmopSettings.specialKPoints.isEmpty()) m_yaehmopSettings.specialKPoints = YAEHMOP_DEFAULT_SPECIAL_KPOINTS; // Do nothing if the user cancels if (m_bandDialog->exec() != QDialog::Accepted) return; // Save the settings for future use writeSettings(); calculateBandStructure(); } // Get the distance between two k points double Yaehmop::kpointDistance(const Vector3& a, const Vector3& b) { if (!m_molecule) { qDebug() << "Error in" << __FUNCTION__ << ": the molecule is not set"; return -1.0; } UnitCell* cell = m_molecule->unitCell(); if (!cell) { qDebug() << "Error in " << __FUNCTION__ << ": there is no unit cell"; return -1.0; } // We need to find the reciprocal space basis vectors, and we // need to find the actual k point location in reciprocal space. const Vector3& aVec = cell->aVector(); const Vector3& bVec = cell->bVector(); const Vector3& cVec = cell->cVector(); Vector3 b1 = bVec.cross(cVec); Vector3 b2 = cVec.cross(aVec); Vector3 b3 = aVec.cross(bVec); // This is how VASP does it. // If it's good enough for VASP, it's good enough for me! double omega = aVec[0] * b1[0] + aVec[1] * b1[1] + aVec[2] * b1[2]; b1 /= omega; b2 /= omega; b3 /= omega; // Calculate the reciprocal points const Vector3& recA(a[0] * b1 + a[1] * b2 + a[2] * b3); const Vector3& recB(b[0] * b1 + b[1] * b2 + b[2] * b3); return (recA - recB).norm(); } void Yaehmop::calculateBandStructure() { // First, create the input QString input; input += "Title\n"; // Title input += createGeometryAndLatticeInput(); // Here we describe the number of k points connecting each special // k point, the number of special k points, and their locations // in reciprocal space. This is something we will let the user change input += "Band\n"; // This is the number of kpoints connecting each special k point input += (QString::number(m_yaehmopSettings.numBandKPoints) + "\n"); // Num special k points int numSK = m_yaehmopSettings.specialKPoints .split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts) .size(); input += (QString::number(numSK) + "\n"); // num special k points // Add the whole string from user input input += m_yaehmopSettings.specialKPoints; // Perform the yaehmop calculation QByteArray output; QString err; if (!executeYaehmop(input.toLocal8Bit(), output, err)) { QMessageBox::warning( nullptr, tr("Avogadro2"), tr("Yaehmop execution failed with the following error:\n%1").arg(err)); qDebug() << "Yaehmop execution failed with the following error:\n" << err; return; } // Now, read the output QVector> bands; QVector kpoints; QVector specialKPoints; if (!YaehmopOut::readBandData(output, bands, kpoints, specialKPoints)) { QString message = tr("Failed to read band structure output from Yaehmop!"); QMessageBox::warning(nullptr, tr("Avogadro2"), message); qDebug() << message; } int numKPoints = kpoints.size(); int numSpecialKPoints = specialKPoints.size(); // If there is only one special k point, there is nothing to graph. Just // return. if (numSpecialKPoints <= 1) { QString message = tr("Only one special k point was found in Yaehmop output! Two or more " "are required!"); QMessageBox::warning(nullptr, tr("Avogadro2"), message); qDebug() << message; return; } // Calculate the k-space distances std::vector xVals{ 0.0 }; for (int i = 1; i < numKPoints; ++i) xVals.push_back(kpointDistance(kpoints[i - 1], kpoints[i]) + xVals.back()); // Calculate the special k point distances std::vector specialKPointVals{ 0.0 }; for (int i = 1; i < numSpecialKPoints; ++i) { specialKPointVals.push_back( kpointDistance(specialKPoints[i - 1].coords, specialKPoints[i].coords) + specialKPointVals.back()); } // This is to make sure vtk shows the symbols on the far left and right specialKPointVals.front() += 0.0001; specialKPointVals.back() -= 0.0001; // Make a vector of labels for the special k points std::vector specialKPointLabels; for (int i = 0; i < numSpecialKPoints; ++i) { std::string label = specialKPoints[i].label.toStdString(); specialKPointLabels.push_back(label); } // Now generate a plot with the data std::vector> data; data.push_back(xVals); // Make some labels std::vector lineLabels; // Set the color std::array color = { 255, 0, 0, 255 }; std::vector> lineColors; size_t curBandNum = 1; for (const auto& band : bands) { std::vector tmp(band.begin(), band.end()); data.push_back(tmp); lineLabels.push_back(tr("Band %1").arg(curBandNum).toStdString()); lineColors.push_back(color); ++curBandNum; } // Should we plot the fermi level? if (m_yaehmopSettings.plotFermi) { // Adjust all the energies if we are to zero the fermi level double fermi = m_yaehmopSettings.fermi; if (m_yaehmopSettings.zeroFermi) { // Skip the first vector because that is the x values for (size_t i = 1; i < data.size(); ++i) { for (auto& datum : data[i]) datum -= fermi; } // Fermi is now zero fermi = 0.0; } // Create a horizontal, black, dashed line for the fermi level lineLabels.push_back(tr("Fermi Level").toStdString()); lineColors.push_back({ 0, 0, 0, 255 }); std::vector fermiVals(xVals.size(), fermi); data.push_back(std::move(fermiVals)); } const char* xTitle = ""; const char* yTitle = "Energy (eV)"; const char* windowName = "YAeHMOP Band Structure"; if (!m_chartDialog) { m_chartDialog.reset( new VTK::ChartDialog(qobject_cast(this->parent()))); } m_chartDialog->setWindowTitle(windowName); auto* chart = m_chartDialog->chartWidget(); chart->clearPlots(); chart->addPlots(data, VTK::color4ub{ 255, 0, 0, 255 }); chart->setXAxisTitle(xTitle); chart->setYAxisTitle(yTitle); chart->setTickLabels(VTK::ChartWidget::Axis::x, specialKPointVals, specialKPointLabels); chart->setAxisLimits(VTK::ChartWidget::Axis::x, xVals.front(), xVals.back()); if (m_yaehmopSettings.limitY) { chart->setAxisLimits(VTK::ChartWidget::Axis::y, m_yaehmopSettings.minY, m_yaehmopSettings.maxY); } m_chartDialog->show(); // Show the yaehmop input if we are to do so if (m_yaehmopSettings.displayYaehmopInput) showYaehmopInput(input); } QString Yaehmop::createGeometryAndLatticeInput() const { if (!m_molecule) { qDebug() << "Error in " << __FUNCTION__ << ": the molecule is not set"; return ""; } UnitCell* cell = m_molecule->unitCell(); if (!cell) { QMessageBox::warning(nullptr, tr("Avogadro2"), tr("Cannot calculate band structure: no unit cell!")); qDebug() << "Error in " << __FUNCTION__ << ": there is no unit cell"; return ""; } const Array& atomicNumbers = m_molecule->atomicNumbers(); const Array& atomicPositions = m_molecule->atomPositions3d(); // This is the minimum number we allow doubles. If a number's float // absolute value is smaller than this, we will round it to 0. double minNum = 1e-8; QString input; input += "Geometry\n"; // Begin geometry section int numAtoms = atomicNumbers.size(); // Num atoms plus (numDimensions + 1) dummies. // Dummies are for defining the lattice unsigned short numDim = m_yaehmopSettings.numDim; input += (QString::number(numAtoms + numDim + 1) + QString("\n")); // Now loop through atom positions and add them for (int i = 0; i < numAtoms; ++i) { QString symbol = Elements::symbol(atomicNumbers[i]); const Vector3& pos = atomicPositions[i]; input += (QString::number(i + 1) + " "); input += (symbol + " "); for (size_t j = 0; j < 3; ++j) // If the position is small, just use 0 input += (QString::number((fabs(pos[j]) > 1e-8 ? pos[j] : 0)) + " "); input += "\n"; } // Get the cell matrix const Matrix3& cellMatrix = cell->cellMatrix(); // Add the dummy atoms - these tell the program where the lattice is for (unsigned short i = 0; i <= numDim; ++i) { input += (QString::number(numAtoms + i + 1) + " "); // Symbol for dummy atoms input += "& "; // First dummy is at 0,0,0, the other dummies are at the ends of the // lattice if (i == 0) { input += "0 0 0\n"; } else { // We only get here if i > 0. // i - 1 is equal to the index of the vector we are looking at. for (unsigned short j = 0; j < 3; ++j) { double val = cellMatrix(i - 1, j); if (fabs(val) < minNum) val = 0.0; input += (QString::number(val) + " "); } input += "\n"; } } // Let's calculate the number of overlaps to use // The manual says that numOverlaps * latticeVecLength should be between // 10 and 20 Angstroms. Let's always use a numOverlaps of at least 3 and // then use more if numOverlaps * latticeVecLength < 20. Vector3i overlaps(3, 3, 3); Vector3 latticeLengths(cell->a(), cell->b(), cell->c()); for (unsigned short i = 0; i < 3; ++i) { while (overlaps[i] * latticeLengths[i] < 20) ++overlaps[i]; } // Lattice section to define the lattice input += "lattice\n"; input += QString::number(numDim) + "\n"; // Add numbers of overlaps for (size_t i = 0; i < numDim; ++i) input += (QString::number(overlaps[i]) + " "); input += "\n"; // If we have "4 5" here, that means the vector is defined // from atom 4 to atom 5. We use dummy atoms for this. The first dummy // atom (numAtoms + 1) is always at the origin, and the other dummy atoms // are at the ends of the a, b, and c axes. for (size_t i = 0; i < numDim; ++i) { input += (QString::number(numAtoms + 1) + " " + QString::number(numAtoms + i + 2) + "\n"); } return input; } // Uncomment this for executeYaehmop debugging // #define AVOGADRO_YAEHMOP_EXECUTE_DEBUG bool Yaehmop::executeYaehmop(const QByteArray& input, QByteArray& output, QString& err) { #ifdef AVOGADRO_YAEHMOP_EXECUTE_DEBUG qDebug() << "executeYaehmop() input is:\n" << qPrintable(input); #endif QString program = m_programPath; if (program.isEmpty()) { QMessageBox::warning(nullptr, tr("Avogadro"), tr("Cannot find Yaehmop")); return false; } QStringList args; args << "--use_stdin_stdout"; QProcess p; p.start(program, args); if (!p.waitForStarted()) { qDebug() << tr("Error: %1 failed to start").arg(program); return false; } // Give it the input! p.write(input.data()); // Close the write channel p.closeWriteChannel(); if (!p.waitForFinished()) { err = tr("Error: " + program.toLocal8Bit() + " failed to finish"); qDebug() << err; output = p.readAllStandardOutput(); qDebug() << "Output is as follows:\n" << qPrintable(output); return false; } int exitStatus = p.exitStatus(); output = p.readAllStandardOutput(); if (exitStatus == QProcess::CrashExit) { qDebug() << "Error: Yahemop crashed!"; qDebug() << "Output is as follows:\n" << qPrintable(output); return false; } if (exitStatus != QProcess::NormalExit) { qDebug() << "Error: Yahemop finished abnormally with exit code " << exitStatus; qDebug() << "Output is as follows:\n" << qPrintable(output); return false; } #ifdef AVOGADRO_YAEHMOP_EXECUTE_DEBUG qDebug() << "executeYaehmop() output is:\n" << qPrintable(output); #endif // We did it! return true; } void Yaehmop::showYaehmopInput(const QString& input) { auto* dialog = new QDialog(qobject_cast(this->parent())); // Make sure this gets deleted upon closing dialog->setAttribute(Qt::WA_DeleteOnClose); auto* layout = new QVBoxLayout(dialog); dialog->setLayout(layout); dialog->setWindowTitle(tr("Yaehmop Input")); auto* edit = new QTextEdit(dialog); layout->addWidget(edit); dialog->resize(500, 500); edit->setText(input); dialog->show(); } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/yaehmop.h000066400000000000000000000045771506155467400234650ustar00rootroot00000000000000/******************************************************************************* This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). *******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_YAEHMOP_H #define AVOGADRO_QTPLUGINS_YAEHMOP_H #include "banddialog.h" #include "yaehmopsettings.h" #include #include #include #include // Forward declarations class QByteArray; namespace Avogadro::QtPlugins { /** * @brief Perform extended Hückel calculations with yaehmop. */ class Yaehmop : public Avogadro::QtGui::ExtensionPlugin { Q_OBJECT public: explicit Yaehmop(QObject* parent_ = nullptr); ~Yaehmop() override; QString name() const override { return tr("Yaehmop"); } QString description() const override; QList actions() const override; QStringList menuPath(QAction*) const override; public slots: void setMolecule(QtGui::Molecule* mol) override; void moleculeChanged(unsigned int changes); private slots: void updateActions(); void displayBandDialog(); private: void readSettings(); void writeSettings(); // This pops up a dialog box with the yaehmop input inside void showYaehmopInput(const QString& input); // Get the distance between two k points double kpointDistance(const Avogadro::Vector3& a, const Avogadro::Vector3& b); void calculateBandStructure(); QString createGeometryAndLatticeInput() const; // Use QProcess to execute yaehmop // If the YAEHMOP_EXECUTABLE environment variable is set, that will be // used for the executable. Otherwise, it will search for the executable in // some common places and use it if it can be found. bool executeYaehmop(const QByteArray& input, QByteArray& output, QString& err); QString m_programPath; QList m_actions; QtGui::Molecule* m_molecule; YaehmopSettings m_yaehmopSettings; std::unique_ptr m_bandDialog; std::unique_ptr m_displayBandDialogAction; QScopedPointer m_chartDialog; }; inline QString Yaehmop::description() const { return tr("Perform extended Hückel calculations with yaehmop."); } } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_YAEHMOPEXTENSION_H avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/yaehmopout.cpp000066400000000000000000000105741506155467400245420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "yaehmopout.h" #include #include #include #include #include namespace Avogadro::QtPlugins { inline bool printAndReturnFalse(const QString& error) { qDebug() << "Error in YaehmopOut:" << error; return false; } bool YaehmopOut::readBandData(const QString& data, QVector>& bands, QVector& kpoints, QVector& specialKPoints) { bands.clear(); kpoints.clear(); specialKPoints.clear(); QStringList lines = data.split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); while (!lines.isEmpty() && !lines[0].contains("#BAND_DATA")) lines.removeFirst(); if (lines.isEmpty()) return printAndReturnFalse("Band Data not found in readBandData()!"); // These get printed from the status file and are not needed... foreach (const QString& line, lines) { if (line.contains("Error value from Diagonalization")) lines.removeOne(line); if (line.contains( "On entry to ZHEGV , parameter number 6 had an illegal value")) lines.removeOne(line); } // Right here, lines.size() must be at least 8 if (lines.size() < 8) return printAndReturnFalse("Band data is too few lines!"); long long ind = 2; if (!lines[ind].contains("Special points")) return printAndReturnFalse("Special points missing"); int numSpecialPoints = lines[ind].split(" ")[0].toInt(); // Now we know more details about how many lines should be here if (lines.size() < 7 + numSpecialPoints) return printAndReturnFalse("Too few lines of data in band data!"); ++ind; if (!lines[ind].contains("k points")) return printAndReturnFalse("k points missing"); size_t numKPoints = lines[ind].split(" ")[0].toInt(); ++ind; if (!lines[ind].contains("orbitals in")) return printAndReturnFalse("orbitals in missing"); ; size_t numOrbitals = lines[ind].split(" ")[0].toInt(); for (int i = 0; i < numSpecialPoints; ++i) { ++ind; specialKPoint kp; if (lines[ind].split(" ").size() < 4) return printAndReturnFalse("Special points line too small"); kp.label = lines[ind].split(" ")[0]; // If the label is "GM", use "Γ" instead // FIXME: our vtk can't currently display a unicode gamma like this. When // we add a font that can, we can uncomment this. // if (kp.label.toLower() == "gm") // kp.label = QString::fromUtf8("Γ"); kp.coords = Vector3(lines[ind].split(" ")[1].toDouble(), lines[ind].split(" ")[2].toDouble(), lines[ind].split(" ")[3].toDouble()); specialKPoints.append(kp); } ++ind; if (!lines[ind].contains("Begin band data")) return printAndReturnFalse("Begin band data missing"); // This should be equal in size to the number of orbitals bands.reserve(numOrbitals); for (size_t i = 0; i < numOrbitals; ++i) { bands.append(QVector()); // This is how many points we should have in total bands[i].reserve(numKPoints * (numSpecialPoints - 1) + 1); } // This is how many points we should have in total kpoints.reserve(numKPoints * (numSpecialPoints - 1) + 1); ++ind; while (true) { // Did we make it to the end without finishing the band data? if (ind >= lines.size()) return printAndReturnFalse("END_BAND_DATA is missing!"); // Did we make it to the end? if (lines[ind].contains("END_BAND_DATA")) break; // Read the k-point info if (!lines[ind].contains("K point") || lines[ind].split(" ").size() < 6) { return printAndReturnFalse("K point missing"); } Vector3 kp = Vector3(lines[ind].split(" ")[3].toDouble(), lines[ind].split(" ")[4].toDouble(), lines[ind].split(" ")[5].toDouble()); kpoints.append(kp); // Get the orbital energies for (size_t j = 0; j < numOrbitals; ++j) { ++ind; bands[j].append(lines[ind].trimmed().toDouble()); } ++ind; } // We made it! return true; } } // namespace Avogadro::QtPlugins avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/yaehmopout.h000066400000000000000000000023351506155467400242030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_YAEHMOP_OUT_H #define AVOGADRO_QTPLUGINS_YAEHMOP_OUT_H #include #include #include namespace Avogadro::QtPlugins { typedef struct { QString label; Vector3 coords; } specialKPoint; // Static class for Yaehmop output class YaehmopOut { public: // Pass the yaehmop output in as 'data'. It would be faster if this only // included the section from BAND_DATA to END_BAND_DATA, but it is not // necessary. This sets bands, kpoints, and specialKPoints to be the // bands, the kpoints, and the special k points. Returns true if the // read was successful, and false if the read failed static bool readBandData(const QString& data, QVector>& bands, QVector& kpoints, QVector& specialKPoints); }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_YAEHMOPOUT_H avogadrolibs-1.101.0/avogadro/qtplugins/yaehmop/yaehmopsettings.h000066400000000000000000000020471506155467400252340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QTPLUGINS_YAEHMOPSETTINGS_H #define AVOGADRO_QTPLUGINS_YAEHMOPSETTINGS_H #include namespace Avogadro::QtPlugins { static const char* YAEHMOP_DEFAULT_SPECIAL_KPOINTS = "GM 0 0 0"; struct YaehmopSettings { YaehmopSettings() : numBandKPoints(40), specialKPoints(YAEHMOP_DEFAULT_SPECIAL_KPOINTS), displayYaehmopInput(false), limitY(false), minY(0.0), maxY(0.0), plotFermi(false), fermi(0.0), zeroFermi(false), numDim(3) {}; unsigned long long numBandKPoints; QString specialKPoints; bool displayYaehmopInput; bool limitY; double minY; double maxY; bool plotFermi; double fermi; bool zeroFermi; unsigned short numDim; }; } // namespace Avogadro::QtPlugins #endif // AVOGADRO_QTPLUGINS_YAEHMOPSETTINGS_H avogadrolibs-1.101.0/avogadro/quantumio/000077500000000000000000000000001506155467400201675ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/quantumio/CMakeLists.txt000066400000000000000000000011041506155467400227230ustar00rootroot00000000000000add_library(QuantumIO) avogadro_headers(QuantumIO gamessus.h gaussianfchk.h gaussiancube.h genericjson.h genericoutput.h molden.h mopacaux.h nwchemjson.h nwchemlog.h orca.h qcschema.h ) # Source files for our data. target_sources(QuantumIO PRIVATE gamessus.cpp gaussianfchk.cpp gaussiancube.cpp genericjson.cpp genericoutput.cpp molden.cpp mopacaux.cpp nwchemjson.cpp nwchemlog.cpp orca.cpp qcschema.cpp ) avogadro_add_library(QuantumIO) target_link_libraries(QuantumIO PUBLIC Avogadro::IO PRIVATE nlohmann_json::nlohmann_json ) avogadrolibs-1.101.0/avogadro/quantumio/gamessukout.cpp000066400000000000000000000577731506155467400232650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gamessukout.h" #include #include #include #include #include using Eigen::Vector3d; using std::vector; namespace Avogadro { namespace QuantumIO { using Quantum::D; using Quantum::F; using Quantum::P; using Quantum::S; using Quantum::SP; using Quantum::UU; using Quantum::orbital; //! Break a string (supplied as the second argument) into tokens, returned //! in the first argument. Tokens are determined by the delimiters supplied bool tokenize(std::vector& vcr, const char* buf, const char* delimstr) { // Not the most efficient way to do this, but this avoids using GPL code // from openbabel. if (!buf || !delimstr) return false; QString tmp(buf); tmp += "\n"; // for compatibility with old behavior vcr.clear(); QString splitString("["); splitString += QString(delimstr); splitString += QString("]"); QRegularExpression splitter(splitString); foreach (const QString& str, tmp.split(splitter, Qt::SkipEmptyParts)) vcr.push_back(str.toStdString()); return true; } //! Removes white space from front and back of string std::string& Trim(std::string& txt) { // Not the most efficient way to do this, but this avoids using GPL code // from openbabel. txt = QString::fromStdString(txt).trimmed().toStdString(); return txt; } /** * This purloined from: http://www.codeguru.com/forum/showthread.php?t=231054 */ template bool from_string(T& t, const std::string& s, std::ios_base& (*f)(std::ios_base&)) { std::istringstream iss(s); return !(iss >> f >> t).fail(); } void GUKBasisSet::outputCoord() { std::cout << "Coordinates:\n"; for (unsigned int i = 0; i < coordinates.size(); i++) { printf("%d: %3s %10f %10f %10f\n", i, atomLabels[i].c_str(), coordinates[i][0], coordinates[i][1], coordinates[i][2]); } } // end outputCoord void GUKBasisSet::outputBasis() { std::cout << "Basis functions" << std::endl; int prev; for (unsigned int i = 0; i < shellLabels.size(); i++) { std::cout << "Atom(" << i << "): " << shellLabels.at(i) << std::endl; // std::cout << "shells at(i).size() " << shells.at(i).size() << std::endl; for (unsigned int j = 0; j < shells.at(i).size(); j++) { // The first indexes are different as they are the indexes held at the // end of the last shell or 0 for the first one if (i == 0 && j == 0) prev = 0; else if (j == 0) prev = gtoIndicies.at(i - 1).back(); else prev = gtoIndicies.at(i).at(j - 1); std::cout << "shell type " << shells.at(i).at(j) << std::endl; for (unsigned int k = prev; k < gtoIndicies.at(i).at(j); k++) { std::cout << " e = " << gtoExponents.at(k) << " c = " << gtoCoefficients.at(k) << std::endl; } } } std::cout << "Read in nShell " << nShell << std::endl; std::cout << "Read in nBasisFunctions " << nBasisFunctions << std::endl; std::cout << "Read in nElectrons " << nElectrons << std::endl; } // end outputBasis bool GUKBasisSet::labelIndex(std::string label) { for (unsigned int i = 0; i < shellLabels.size(); i++) if (shellLabels.at(i) == label) return true; return false; } // end labelIndex orbital GUKBasisSet::shellTypeFromString(std::string label) { /** * Return the enum from basis.h for the supplied label as a string * basisset.h: enum orbital { S, SP, P, D, D5, F, F7, UU }; * The label is from the GAMESS-UK basis set label, which is printed as shown below: * shell type prim exponents contraction coefficients * 1 1s 3 5.533350 1.801737 ( 0.700713 ) * or * 2 2sp 4 3.664980 -0.747384 ( -0.395897 ) 1.709178 ( 0.236460 ) */ // Could be e.g. 's', 'l', '2sp' or '1s'. // We assume that if it is longer then 1 character long, the rest is the label if (label.size() > 1) { // Remove the shell number label = label.substr(1, std::string::npos); } // Check for sp if (label.size() == 2) { if (label.compare(0, 2, "sp") == 0) return SP; } if (label.size() == 1) { if (label == "l") return SP; if (label == "s") return S; if (label == "p") return P; if (label == "d") return D; } // end label of length 1 // if we get here, it's all gone wrong... std::cerr << "ERROR: shellTypeFromString with label: " << label << std::endl; return UU; } GamessukOut::GamessukOut(const QString& qtfilename, GaussianSet* basis) { std::string filename; filename = qtfilename.toStdString(); GamessukOutNoQt(filename, basis); } // end GamessukOut GamessukOut::~GamessukOut() {} // end ~GamessukOut void GamessukOut::GamessukOutNoQt(const std::string& filename, GaussianSet* basis) { bool ok; std::ifstream ifs; ifs.open(filename.c_str()); if (!ifs) { std::cerr << "Cannot open: " << filename << "\n"; return; } // Initialise the basis set object that holds the parsed data before we // convert // it into Avogadro form gukBasis = GUKBasisSet(); // Now read the file ok = parseFile(ifs); ifs.close(); if (ok) { // outputParsedData(); // Create the Avogadro basis set object load(basis); } else std::cerr << "ERROR READING ORBITALS FROM FILE: " << filename << std::endl; } // end GamessukOutNoQt void GamessukOut::outputParsedData() { gukBasis.outputCoord(); gukBasis.outputBasis(); } // end outputParsedData bool GamessukOut::parseFile(std::ifstream& ifs) { /** * Loop through the file, calling routines that read in the data of interest * into the GUKBasisSet object * Is currently pretty rudimentary - could do with lots of error trapping to * check all o.k. */ bool gotMOs = false; // used as return value - indicates if we have valid // orbitals for the coordinates we've read in while (ifs.good() && ifs.getline(buffer, BUFF_SIZE)) { // First find oriented geometry - use this for single-point calculations if (strstr(buffer, " * atom atomic coordinates") != nullptr) { readInitialCoordinates(ifs); } // The basis set definition if (strstr(buffer, " atom shell type prim exponents " " contraction coefficients") != nullptr) { readBasisSet(ifs); } // Determine the scftype - can't do uhf yet if (strstr(buffer, " * SCF TYPE") != nullptr) { tokenize(tokens, buffer, " \t\n"); if (tokens[3].compare(0, 6, "rhf") != 0) { std::cerr << "ERROR: can currently only do rhf!\n"; return false; } } // The converged geometry if (strstr(buffer, "optimization converged") != nullptr) { readOptimisedCoordinates(ifs); if (gotMOs) gotMOs = false; // If we read in some MOs they are now redundant } // The molecular orbitals if (strstr( buffer, " eigenvectors") != nullptr || strstr(buffer, " molecular orbitals") != nullptr) { readMOs(ifs); gotMOs = true; } } return gotMOs; } void GamessukOut::readInitialCoordinates(std::ifstream& ifs) { // string to mark end of the coordinates char coordEnd[86] = " " "********************************************************" "********************"; double x = 0.0, y = 0.0, z = 0.0; // skip five lines ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE); while (strstr(buffer, coordEnd) == nullptr) { // std::cout << "COORD line" << buffer << std::endl; // ifs.getline(buffer, BUFF_SIZE); tokenize(tokens, buffer, " \t\n"); if (tokens.size() == 8) { // std::cout << "Coord line" << buffer << std::endl; gukBasis.atomLabels.push_back(tokens.at(1)); from_string(x, tokens.at(3), std::dec); from_string(y, tokens.at(4), std::dec); from_string(z, tokens.at(5), std::dec); gukBasis.coordinates.push_back( Eigen::Vector3d(x, y, z)); // Want coordinates in Bohr } ifs.getline(buffer, BUFF_SIZE); } } void GamessukOut::readBasisSet(std::ifstream& ifs) { // std::cout << "readBasisSet\n"; bool newAtom = true, gotAtom = false, firstAtom = true; std::string atomLabel; double exponent, coefficient; orbital stype; int nshell = -1; int lastNshell = -1; // the last shell number - is same as nshell when looping // through a shell int lastStype = -1; // need to keep track of sp shells as we split into s & p // For separating sp-shells std::vector s_coeff; std::vector p_coeff; std::vector sp_exponents; // skip 2 lines to be just before the first atom label ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE); // now loop through the basis sets till we hit the end while (!ifs.eof()) { ifs.getline(buffer, BUFF_SIZE); line = buffer; if (line.compare(0, 10, " =========") == 0) { // End of basis - add indicies of where the coffecients and exponents of // the GTOs for the last shell end gukBasis.gtoIndicies.at(gukBasis.shellLabels.size() - 1) .push_back(static_cast(gukBasis.gtoExponents.size())); break; } // Remove blank space line = Trim(line); // skip blank lines if (line.size() == 0) continue; // Separate into tokens if (!tokenize(tokens, line.c_str(), " \t\n") || tokens.size() == 0) { // If the string couldn't be tokenised, set tokens[0] to the entire string tokens.clear(); tokens.push_back(line); } if (tokens.size() == 1) { // This means a new atomLabel newAtom = true; if (firstAtom) firstAtom = false; else { // Check if the last shell was sp - if so add the temp sp data // structures // to the main ones if (lastStype == SP) { addSpBasis(s_coeff, p_coeff, sp_exponents); // Clear the temp data structures for separating out sp into s & p s_coeff.clear(); p_coeff.clear(); sp_exponents.clear(); lastStype = -1; } else { // Add the index for where the GTO coffecients and exponents for the // previous shell start gukBasis.gtoIndicies.at(gukBasis.shellLabels.size() - 1) .push_back(static_cast(gukBasis.gtoExponents.size())); } } // end firstAtom // Check if we've already processed an atom of this type if (!gukBasis.labelIndex(tokens.at(0))) { // std::cout << "Processing atom label: " << tokens.at(0) << std::endl; gotAtom = false; // we'll be processing this atom gukBasis.shellLabels.push_back(tokens.at(0)); gukBasis.shells.push_back(std::vector()); gukBasis.gtoIndicies.push_back(std::vector()); } else gotAtom = true; continue; } // End new atomLabel // if we're not processing an atom we can skip this line if (gotAtom) continue; /* Here we are reading in a line of the format: shell type prim exponents contraction coefficients 1 1s 3 5.533350 1.801737 ( 0.700713 ) or 2 2sp 4 3.664980 -0.747384 ( -0.395897 ) 1.709178 ( 0.236460 ) */ from_string(nshell, tokens.at(0), std::dec); if (nshell != lastNshell) { // Reading a new shell if (!newAtom) { // Add the data for the last shell processed // First check if last shell was sp & we need to add the data we've // gathered to the main structures if (lastStype == SP) { addSpBasis(s_coeff, p_coeff, sp_exponents); // Clear the temp data structures for separating out sp into s & p s_coeff.clear(); p_coeff.clear(); sp_exponents.clear(); } else { // Add the index for where the primitives for the last shell finish gukBasis.gtoIndicies.at(gukBasis.shellLabels.size() - 1) .push_back(static_cast(gukBasis.gtoExponents.size())); } // end sp shell } // end newAtom // need to determine type stype = gukBasis.shellTypeFromString(tokens.at(1)); // std::cout << "Reading new shell of type " << stype << std::endl; if (stype != SP) { // Add shell to symmetry list and AtomToShellIndex if not sp shell as we // do that separately gukBasis.shells.at(gukBasis.shellLabels.size() - 1).push_back(stype); } } // end new shell // Now check for coefficients - we take the second lot to match Gaussian from_string(exponent, tokens.at(3), std::dec); if (stype == SP) { if (tokens.size() != 12) std::cerr << "PROBLEM READING SP LINE!\n"; from_string(coefficient, tokens.at(6), std::dec); s_coeff.push_back(coefficient); from_string(coefficient, tokens.at(10), std::dec); p_coeff.push_back(coefficient); sp_exponents.push_back(exponent); } else { // std::cout << "Adding exponent " << exponent << std::endl; gukBasis.gtoExponents.push_back(exponent); from_string(coefficient, tokens.at(6), std::dec); gukBasis.gtoCoefficients.push_back(coefficient); } // end type == SP lastNshell = nshell; lastStype = stype; newAtom = false; } // end while // Finished reading the basis data - now just collect some data from the // summary - mainly for checking ifs.getline(buffer, BUFF_SIZE); // blank // nShell ifs.getline(buffer, BUFF_SIZE); if (strstr(buffer, " total number of shells") == nullptr) std::cerr << "Error reading nShell!: " << line << std::endl; // reuse nshell from above as temporary variable tokenize(tokens, buffer, " \t\n"); from_string(nshell, tokens.at(4), std::dec); gukBasis.nShell = nshell; // nBasisFunctions ifs.getline(buffer, BUFF_SIZE); if (strstr(buffer, " total number of basis") == nullptr) std::cerr << "Error reading nBasisFunctions!: " << line << std::endl; tokenize(tokens, buffer, " \t\n"); // reuse nshell from above as temporary variable from_string(nshell, tokens.at(5), std::dec); gukBasis.nBasisFunctions = nshell; // nElectrons ifs.getline(buffer, BUFF_SIZE); if (strstr(buffer, " number of electrons") == nullptr) std::cerr << "Error reading nElectrons!: " << line << std::endl; tokenize(tokens, buffer, " \t\n"); // reuse nshell from above as temporary variable from_string(nshell, tokens.at(3), std::dec); gukBasis.nElectrons = nshell; } // end readBasisSet inline void GamessukOut::addSpBasis(std::vector s_coeff, std::vector p_coeff, std::vector sp_exponents) { // Convenience function for adding separated sp basis // Add s gukBasis.shells.at(gukBasis.shellLabels.size() - 1).push_back(S); for (unsigned int i = 0; i < s_coeff.size(); i++) { gukBasis.gtoExponents.push_back(sp_exponents[i]); gukBasis.gtoCoefficients.push_back(s_coeff[i]); } gukBasis.gtoIndicies.at(gukBasis.shellLabels.size() - 1) .push_back(static_cast(gukBasis.gtoExponents.size())); // Add p gukBasis.shells.at(gukBasis.shellLabels.size() - 1).push_back(P); for (unsigned int i = 0; i < p_coeff.size(); i++) { gukBasis.gtoExponents.push_back(sp_exponents[i]); gukBasis.gtoCoefficients.push_back(p_coeff[i]); } gukBasis.gtoIndicies.at(gukBasis.shellLabels.size() - 1) .push_back(static_cast(gukBasis.gtoExponents.size())); } // end addSpBasis void GamessukOut::readOptimisedCoordinates(std::ifstream& ifs) { // std::cout << "readOptimisedCoordinates\n"; double x, y, z; // Nuke the old coordinates gukBasis.atomLabels.clear(); gukBasis.coordinates.clear(); ifs.getline(buffer, BUFF_SIZE); while (!ifs.eof()) { // This for some optimize runtypes if (strstr( buffer, " x y z chg tag") != nullptr) { // std::cout << "start of opt coord\n"; // Skip 2 lines - should then be at the coordinates ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE); while (!ifs.eof()) { // End of geometry block if (strstr(buffer, " " "===================================================" "=========") != nullptr) return; tokenize(tokens, buffer, " \t\n"); from_string(x, tokens.at(0), std::dec); from_string(y, tokens.at(1), std::dec); from_string(z, tokens.at(2), std::dec); gukBasis.coordinates.push_back(Eigen::Vector3d(x, y, z)); gukBasis.atomLabels.push_back(tokens.at(4)); ifs.getline(buffer, BUFF_SIZE); } // end while } else if (strstr(buffer, "atom znuc x y z") != nullptr) { // print "start of opt coord 2" // Skip 3 lines - should then be at the coordinates ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE); while (!ifs.eof()) { // End of geometry block if (strstr(buffer, "*************************") != nullptr) return; tokenize(tokens, buffer, " \t\n"); gukBasis.atomLabels.push_back(tokens.at(0)); from_string(x, tokens.at(3), std::dec); from_string(y, tokens.at(4), std::dec); from_string(z, tokens.at(5), std::dec); gukBasis.coordinates.push_back(Eigen::Vector3d(x, y, z)); ifs.getline(buffer, BUFF_SIZE); } // end of while } ifs.getline(buffer, BUFF_SIZE); } // end of read loop } // end readOptimisedCoordinates void GamessukOut::readMOs(std::ifstream& ifs) { /* Read the Molecular Orbitals as printed out by util1.m subroutine prev */ int orbitalsRead, orbitalsRead1; // Nuke any old set - fix when look at alpha & beta gukBasis.moVectors.clear(); // Skip 3 lines to be just before first header ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE); orbitalsRead1 = readMOVectors(ifs); orbitalsRead = orbitalsRead1; while (orbitalsRead == orbitalsRead1 || orbitalsRead != 0) orbitalsRead = readMOVectors(ifs); } // end readMos int GamessukOut::readMOVectors(std::ifstream& ifs) { /* Loop through a series of columns of printed MO vectors & return the number of orbitals read in */ unsigned int norbitals, norbitalsRead; double energy; ifs.getline(buffer, BUFF_SIZE); // std::cout << "HeaderLine " << buffer << std::endl; // Check we're not at the end if (strstr(buffer, "end of") != 0) return 0; tokenize(tokens, buffer, " \t\n"); // How many orbital columns: norbitals = static_cast(tokens.size()); for (unsigned int i = 0; i < tokens.size(); i++) { from_string(energy, tokens.at(i), std::dec); // std::cout << "adding e " << energy << std::endl; gukBasis.moEnergies.push_back(energy); } // Add the lists to hold this set of coefficients // How many were read in previously: norbitalsRead = static_cast(gukBasis.moVectors.size()); // Create the arrays to hold the coefficients for each orbital for (unsigned int i = 0; i < norbitals; i++) gukBasis.moVectors.push_back(std::vector()); // skip 5 lines to just before where first set of orbitals are printed ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE) && ifs.getline(buffer, BUFF_SIZE); // loop nBasisFunctions times to read in up to norbitals coefficients for (int i = 0; i < gukBasis.nBasisFunctions; i++) { ifs.getline(buffer, BUFF_SIZE); // std::cout << "MO line " << buffer << std::endl; tokenize(tokens, buffer, " \t\n"); for (unsigned int j = 0; j < norbitals; j++) { // reuse variable energy to hold coefficient from_string(energy, tokens.at(j + 4), std::dec); gukBasis.moVectors.at(norbitalsRead + j).push_back(energy); // std::cout << "Adding " << energy << " to vector " << norbitalsRead+j << // std::endl; } } // skip 2 lines to where the next set of headers are printed ifs.getline(buffer, BUFF_SIZE); ifs.getline(buffer, BUFF_SIZE); // If we are printed out after an optimisation under the control of "iprint // vectors", // the next line with be filled with " =================" if we've finished if (strstr(buffer, " ===============================") != 0) return 0; return norbitals; } // end readMOVectors void GamessukOut::addBasisForLabel(unsigned int atomIndex, std::string label, GaussianSet* basis) { /* Add the basis functions for the atom label */ unsigned int s; int prev; for (unsigned int i = 0; i < gukBasis.shellLabels.size(); i++) { if (gukBasis.shellLabels.at(i) != label) continue; for (unsigned int j = 0; j < gukBasis.shells.at(i).size(); j++) { s = basis->addBasis(atomIndex, gukBasis.shells.at(i).at(j)); // The first indexes are different as they are the indexes held at the end // of the last shell or // 0 for the first one if (i == 0 && j == 0) prev = 0; else if (j == 0) prev = gukBasis.gtoIndicies.at(i - 1).back(); else prev = gukBasis.gtoIndicies.at(i).at(j - 1); for (unsigned int k = prev; k < gukBasis.gtoIndicies.at(i).at(j); k++) { basis->addGTO(s, gukBasis.gtoCoefficients.at(k), gukBasis.gtoExponents.at(k)); } } } return; } // end addBasisForLabel void GamessukOut::load(GaussianSet* basis) { /* We will only have read in a basis set for atoms of each type Loop through the list of all the atoms & add the basis functions for each individual atom */ basis->setNumElectrons(gukBasis.nElectrons); // Add the basis for each atom for (unsigned int i = 0; i < gukBasis.atomLabels.size(); i++) { basis->addAtom(gukBasis.coordinates.at(i)); addBasisForLabel(i, gukBasis.atomLabels.at(i), basis); } // Now to load in the MO coefficients // This currently a dirty hack - basisset addMO just expects a long vector of // doubles, which // it then converts into a square matrix. // For this test, we convert our vector of vectors to a single vector and fill // the remaining // virtual orbitals with zeros. std::vector MOs; unsigned int moEnd, nBasis = static_cast(gukBasis.nBasisFunctions); for (unsigned int i = 0; i < nBasis; i++) { if (i >= gukBasis.moVectors.size()) { // std::cout << "Adding blank vectors for non-printed MOs " << i << // std::endl; moEnd = static_cast(MOs.size()) + nBasis; for (unsigned int j = static_cast(MOs.size()); j < moEnd; j++) MOs.push_back(0.0); } else { // std::cout << "Adding actual vector " << i << std::endl; MOs.insert(MOs.end(), gukBasis.moVectors.at(i).begin(), gukBasis.moVectors.at(i).end()); } } // Need to multiply by -1 to bring into accordance with Gaussian. // for( unsigned int i=0; i addMOs(MOs); // basis->initCalculation(); } // end load } // namespace QuantumIO } // namespace Avogadro avogadrolibs-1.101.0/avogadro/quantumio/gamessukout.h000066400000000000000000000070321506155467400227110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_GAMESSUKOUT_H #define AVOGADRO_QUANTUMIO_GAMESSUKOUT_H #include #include #include #include #define BUFF_SIZE 32768 class QString; namespace Avogadro { namespace QuantumIO { using Quantum::GaussianSet; using Quantum::orbital; /** * This class is used to hold the data parsed from the GAMESS-UK output * before we create the Avogadro basis set object */ class GUKBasisSet { public: GUKBasisSet() {} ~GUKBasisSet() {} void outputCoord(); void outputBasis(); /** * Return true if we have already processed an atom of this label type */ bool labelIndex(std::string label); /** * Return the enum from basis.h for the supplied label as a string * basisset.h: enum orbital { S, SP, P, D, D5, F, F7, UU }; */ orbital shellTypeFromString(std::string label); std::vector atomLabels; // ordered list of atom labels mapping to coordinates std::vector coordinates; // Coordinates in Bohr std::vector shellLabels; // list of atom labels for the basis functions std::vector> shells; // vector of shell types for each // atom (use basisset.h orbital enum // here) std::vector> gtoIndicies; // Vector of vector of // index of where the GTO // for a particular shell // ends std::vector gtoExponents; // list of exponents std::vector gtoCoefficients; // list of contraction coefficients // Need to look at what to do about uhf calculations std::vector moEnergies; // list of the energies of the MOs std::vector> moVectors; // list of list of MO vectors in order of moEnergies // These are read in after the basis is printed and used by readMOVectors - // could use to check the basis has been parsed correctly too int nShell; int nBasisFunctions; int nElectrons; }; class GamessukOut { public: GamessukOut(const QString& filename, GaussianSet* basis); ~GamessukOut(); void GamessukOutNoQt(const std::string& filename, GaussianSet* basis); void outputParsedData(); private: bool parseFile(std::ifstream& ifs); void readInitialCoordinates(std::ifstream& ifs); void readBasisSet(std::ifstream& ifs); inline void addSpBasis(std::vector s_coeff, std::vector p_coeff, std::vector sp_exponents); void readOptimisedCoordinates(std::ifstream& ifs); void readMOs(std::ifstream& ifs); int readMOVectors(std::ifstream& ifs); void load(GaussianSet* basis); void addBasisForLabel(unsigned int atomIndex, std::string label, GaussianSet* basis); // This holds the basis data parsed in from the file and which is used to // create the BasisSet GUKBasisSet gukBasis; // For parsing the file char buffer[BUFF_SIZE]; std::string line; std::vector tokens; }; } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/gamessus.cpp000066400000000000000000000336541506155467400225350ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gamessus.h" #include #include #include using std::cout; using std::endl; using std::string; using std::vector; namespace Avogadro::QuantumIO { using Core::Atom; using Core::BasisSet; using Core::GaussianSet; using Core::Rhf; using Core::Rohf; using Core::Uhf; GAMESSUSOutput::GAMESSUSOutput() : m_coordFactor(1.0), m_scftype(Rhf) {} GAMESSUSOutput::~GAMESSUSOutput() {} std::vector GAMESSUSOutput::fileExtensions() const { std::vector extensions; extensions.emplace_back("gamout"); extensions.emplace_back("gamess"); return extensions; } std::vector GAMESSUSOutput::mimeTypes() const { return std::vector(); } bool GAMESSUSOutput::read(std::istream& in, Core::Molecule& molecule) { // Read the log file line by line, most sections are terminated by an empty // line, so they should be retained. string buffer; while (getline(in, buffer)) { if (Core::contains(buffer, "COORDINATES (BOHR)")) { readAtomBlock(in, molecule, false); } else if (Core::contains(buffer, "COORDINATES OF ALL ATOMS ARE (ANGS)")) { readAtomBlock(in, molecule, true); } else if (Core::contains(buffer, "ATOMIC BASIS SET")) { readBasisSet(in); } else if (Core::contains(buffer, "CHARGE OF MOLECULE")) { vector parts = Core::split(buffer, '='); if (parts.size() == 2) molecule.setData("totalCharge", Core::lexicalCast(parts[1])); } else if (Core::contains(buffer, "SPIN MULTIPLICITY")) { vector parts = Core::split(buffer, '='); if (parts.size() == 2) molecule.setData("totalSpinMultiplicity", Core::lexicalCast(parts[1])); } else if (Core::contains(buffer, "NUMBER OF ELECTRONS")) { vector parts = Core::split(buffer, '='); if (parts.size() == 2) m_electrons = Core::lexicalCast(parts[1]); else cout << "error" << buffer << endl; } /*else if (Core::contains(buffer, "NUMBER OF OCCUPIED ORBITALS (ALPHA)")) { cout << "Found alpha orbitals\n"; } else if (Core::contains(buffer, "NUMBER OF OCCUPIED ORBITALS (BETA )")) { cout << "Found alpha orbitals\n"; } else if (Core::contains(buffer, "SCFTYP=")) { cout << "Found SCF type\n"; }*/ else if (Core::contains(buffer, "EIGENVECTORS")) { readEigenvectors(in); } } // f functions and beyond need to be reordered reorderMOs(); molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); auto* basis = new GaussianSet; load(basis); molecule.setBasisSet(basis); basis->setMolecule(&molecule); // outputAll(); return true; } void GAMESSUSOutput::readAtomBlock(std::istream& in, Core::Molecule& molecule, bool angs) { // We read the atom block in until it terminates with a blank line. double coordFactor = angs ? 1.0 : BOHR_TO_ANGSTROM_D; string buffer; bool atomsExist = molecule.atomCount() > 0; Index index = 0; //@TODO - store all the coordinates while (getline(in, buffer)) { if (Core::contains(buffer, "CHARGE") || Core::contains(buffer, "------")) continue; else if (buffer.length() == 0 || buffer == "\n") // Our work here is done. return; vector parts = Core::split(buffer, ' '); if (parts.size() != 5) { appendError("Poorly formed atom line: " + buffer); return; } bool ok(false); Vector3 pos; auto atomicNumber( static_cast(Core::lexicalCast(parts[1], ok))); if (!ok) appendError("Failed to cast to int for atomic number: " + parts[1]); pos.x() = Core::lexicalCast(parts[2], ok) * coordFactor; if (!ok) appendError("Failed to cast to double for position: " + parts[2]); pos.y() = Core::lexicalCast(parts[3], ok) * coordFactor; if (!ok) appendError("Failed to cast to double for position: " + parts[3]); pos.z() = Core::lexicalCast(parts[4], ok) * coordFactor; if (!ok) appendError("Failed to cast to double for position: " + parts[4]); Atom atom; if (!atomsExist) { atom = molecule.addAtom(atomicNumber, pos); } else { atom = molecule.atom(index); atom.setPosition3d(pos); index++; } } } void GAMESSUSOutput::readBasisSet(std::istream& in) { // Basic strategy is to use the number of parts in a line to determine the // type, where atom has 1 part, and a GTO has 5 (or 6 for SP/L). Termination // of the block when we hit the summary information at the end. string buffer; int currentAtom(0); bool header(true); while (getline(in, buffer)) { if (header) { // Skip the header lines until we hit the last header line. if (Core::contains(buffer, "SHELL")) header = false; continue; } vector parts = Core::split(buffer, ' '); if (Core::contains(buffer, "TOTAL NUMBER OF BASIS SET SHELLS")) { // End of the basis set block. return; } else if (parts.size() == 1) { // Currently just incrememt the current atom, we should probably at least // verify the element matches in the future too. ++currentAtom; } else if (parts.size() == 5 || parts.size() == 6) { if (parts[1].size() != 1) { appendError("Error parsing basis set line, unrecognized type" + parts[1]); continue; } // Determine the shell type. GaussianSet::orbital shellType(GaussianSet::UU); switch (parts[1][0]) { case 'S': shellType = GaussianSet::S; break; case 'L': shellType = GaussianSet::SP; break; case 'P': shellType = GaussianSet::P; break; case 'D': shellType = GaussianSet::D; break; case 'F': shellType = GaussianSet::F; break; default: shellType = GaussianSet::UU; appendError("Unrecognized shell type: " + parts[1]); } // Read in the rest of the shell, terminate when the number of tokens // is not 5 or 6 in a line. int numGTOs(0); while (parts.size() == 5 || parts.size() == 6) { ++numGTOs; m_a.push_back(Core::lexicalCast(parts[3])); m_c.push_back(Core::lexicalCast(parts[4])); if (shellType == GaussianSet::SP && parts.size() == 6) m_csp.push_back(Core::lexicalCast(parts[5])); if (!getline(in, buffer)) break; parts = Core::split(buffer, ' '); } // Now add this to our data structure. m_shellNums.push_back(numGTOs); m_shellTypes.push_back(shellType); m_shelltoAtom.push_back(currentAtom); } } } void GAMESSUSOutput::readEigenvectors(std::istream& in) { string buffer; getline(in, buffer); getline(in, buffer); getline(in, buffer); vector parts = Core::split(buffer, ' '); vector> eigenvectors; bool ok(false); size_t numberOfMos(0); bool newBlock(true); while (!Core::contains(buffer, "END OF") || Core::contains(buffer, "--------")) { // Any line with actual information in it will contain >= 5 parts. if (parts.size() > 5 && buffer.substr(0, 16) != " ") { if (newBlock) { // Reorder the columns/rows, add them and then prepare for (auto& eigenvector : eigenvectors) for (double j : eigenvector) m_MOcoeffs.push_back(j); eigenvectors.clear(); eigenvectors.resize(parts.size() - 4); numberOfMos += eigenvectors.size(); newBlock = false; } for (size_t i = 0; i < parts.size() - 4; ++i) { eigenvectors[i].push_back(Core::lexicalCast(parts[i + 4], ok)); if (!ok) appendError("Failed to cast to double for eigenvector: " + parts[i]); } } else { // Note that we are either ending or entering a new block of orbitals. newBlock = true; } if (!getline(in, buffer)) break; parts = Core::split(buffer, ' '); } m_nMOs = numberOfMos; for (auto& eigenvector : eigenvectors) for (double j : eigenvector) m_MOcoeffs.push_back(j); // Now we just need to transpose the matrix, as GAMESS uses a different order. // We know the number of columns (MOs), and the number of rows (primitives). if (eigenvectors.size() != numberOfMos * m_a.size()) { appendError("Incorrect number of eigenvectors loaded."); return; } } void GAMESSUSOutput::load(GaussianSet* basis) { // Now load up our basis set basis->setElectronCount(m_electrons); // Set up the GTO primitive counter, go through the shells and add them int nGTO = 0; int nSP = 0; // number of SP shells for (unsigned int i = 0; i < m_shellTypes.size(); ++i) { // Handle the SP case separately - this should possibly be a distinct type if (m_shellTypes.at(i) == GaussianSet::SP) { // SP orbital type - currently have to unroll into two shells int tmpGTO = nGTO; int s = basis->addBasis(m_shelltoAtom.at(i) - 1, GaussianSet::S); for (int j = 0; j < m_shellNums.at(i); ++j) { basis->addGto(s, m_c.at(nGTO), m_a.at(nGTO)); ++nGTO; } int p = basis->addBasis(m_shelltoAtom.at(i) - 1, GaussianSet::P); for (int j = 0; j < m_shellNums.at(i); ++j) { basis->addGto(p, m_csp.at(nSP), m_a.at(tmpGTO)); ++tmpGTO; ++nSP; } } else { int b = basis->addBasis(m_shelltoAtom.at(i) - 1, m_shellTypes.at(i)); for (int j = 0; j < m_shellNums.at(i); ++j) { basis->addGto(b, m_c.at(nGTO), m_a.at(nGTO)); ++nGTO; } } } // qDebug() << " loading MOs " << m_MOcoeffs.size(); // Now to load in the MO coefficients if (m_MOcoeffs.size()) basis->setMolecularOrbitals(m_MOcoeffs); if (m_alphaMOcoeffs.size()) basis->setMolecularOrbitals(m_alphaMOcoeffs, BasisSet::Alpha); if (m_betaMOcoeffs.size()) basis->setMolecularOrbitals(m_betaMOcoeffs, BasisSet::Beta); // generateDensity(); // if (m_density.rows()) // basis->setDensityMatrix(m_density); basis->setScfType(m_scftype); } void GAMESSUSOutput::reorderMOs() { unsigned int GTOcounter = 0; for (int iMO = 0; iMO < m_nMOs; iMO++) { // loop over the basis set shells for (auto& m_shellType : m_shellTypes) { // The angular momentum of the shell // determines the number of primitive GTOs. // GAMESS always prints the full cartesian set. double yyy, zzz, xxy, xxz, yyx, yyz, zzx, zzy, xyz; unsigned int nPrimGTOs = 0; switch (m_shellType) { case GaussianSet::S: nPrimGTOs = 1; GTOcounter += nPrimGTOs; break; case GaussianSet::P: nPrimGTOs = 3; GTOcounter += nPrimGTOs; break; // L? case GaussianSet::D: nPrimGTOs = 6; GTOcounter += nPrimGTOs; break; case GaussianSet::F: nPrimGTOs = 10; // f functions are the first set to be reordered. // double xxx = m_MOcoeffs.at(GTOcounter); yyy = m_MOcoeffs.at(GTOcounter + 1); zzz = m_MOcoeffs.at(GTOcounter + 2); xxy = m_MOcoeffs.at(GTOcounter + 3); xxz = m_MOcoeffs.at(GTOcounter + 4); yyx = m_MOcoeffs.at(GTOcounter + 5); yyz = m_MOcoeffs.at(GTOcounter + 6); zzx = m_MOcoeffs.at(GTOcounter + 7); zzy = m_MOcoeffs.at(GTOcounter + 8); xyz = m_MOcoeffs.at(GTOcounter + 9); // xxx is unchanged m_MOcoeffs.at(GTOcounter + 1) = xxy; // xxy m_MOcoeffs.at(GTOcounter + 2) = xxz; // xxz m_MOcoeffs.at(GTOcounter + 3) = yyx; // xyy m_MOcoeffs.at(GTOcounter + 4) = xyz; // xyz m_MOcoeffs.at(GTOcounter + 5) = zzx; // xzz m_MOcoeffs.at(GTOcounter + 6) = yyy; // yyy m_MOcoeffs.at(GTOcounter + 7) = yyz; // yyz m_MOcoeffs.at(GTOcounter + 8) = zzy; // yzz m_MOcoeffs.at(GTOcounter + 9) = zzz; // zzz GTOcounter += nPrimGTOs; break; case GaussianSet::G: nPrimGTOs = 15; GTOcounter += nPrimGTOs; break; case GaussianSet::H: nPrimGTOs = 21; GTOcounter += nPrimGTOs; break; case GaussianSet::I: nPrimGTOs = 28; GTOcounter += nPrimGTOs; break; default: cout << "Basis set not handled - results may be incorrect.\n"; } } } } void GAMESSUSOutput::outputAll() { switch (m_scftype) { case Rhf: cout << "SCF type = RHF" << endl; break; case Uhf: cout << "SCF type = UHF" << endl; break; case Rohf: cout << "SCF type = ROHF" << endl; break; default: cout << "SCF typ = Unknown" << endl; } cout << "Shell mappings\n"; for (unsigned int i = 0; i < m_shellTypes.size(); ++i) { cout << i << ": type = " << m_shellTypes.at(i) << ", number = " << m_shellNums.at(i) << ", atom = " << m_shelltoAtom.at(i) << endl; } int nGTOs = 0; if (m_MOcoeffs.size()) { nGTOs = m_MOcoeffs.size() / m_nMOs; cout << m_nMOs << " MOs, " << nGTOs << " GTOs" << endl; } for (unsigned int iMO = 0; iMO < 10; iMO++) { for (unsigned int i = iMO * nGTOs; i < nGTOs * iMO + 10; ++i) // m_MOcoeffs.size(); ++i) cout << m_MOcoeffs.at(i) << "\t"; cout << "\n"; } if (m_alphaMOcoeffs.size()) cout << "Alpha MO coefficients.\n"; for (double m_alphaMOcoeff : m_alphaMOcoeffs) cout << m_alphaMOcoeff; if (m_betaMOcoeffs.size()) cout << "Beta MO coefficients.\n"; for (double m_betaMOcoeff : m_betaMOcoeffs) cout << m_betaMOcoeff; cout << std::flush; } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/gamessus.h000066400000000000000000000056231506155467400221750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_GAMESSUS_H #define AVOGADRO_QUANTUMIO_GAMESSUS_H #include "avogadroquantumioexport.h" #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT GAMESSUSOutput : public Io::FileFormat { public: GAMESSUSOutput(); ~GAMESSUSOutput() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new GAMESSUSOutput; } std::string identifier() const override { return "Avogadro: GAMESS"; } std::string name() const override { return "GAMESS"; } std::string description() const override { return "GAMESS US log file output parser."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not write out GAMESS log files. return false; } private: /** * @brief Read the atom block of the log file. * @param in Our input stream. * @param molecule The molecule to add the atoms to. * @param angs Whether the units are Angstroms (true) or Bohr (false). */ void readAtomBlock(std::istream& in, Core::Molecule& molecule, bool angs); /** * Read in the basis set block. */ void readBasisSet(std::istream& in); /** * Read in the molecular orbitals. */ void readEigenvectors(std::istream& in); /** * Reorder the molecular orbitals. */ void reorderMOs(); /** * Outpull all known properties that have been read, useful for debugging. */ void outputAll(); /** * Load the basis with the properties read in from the file. */ void load(Core::GaussianSet* basis); double m_coordFactor; int m_electrons; int m_electronsA; int m_electronsB; int m_nMOs; Core::ScfType m_scftype; unsigned int m_numBasisFunctions; std::vector m_shellTypes; std::vector m_shellNums; std::vector m_shelltoAtom; std::vector m_a; std::vector m_c; std::vector m_csp; std::vector m_orbitalEnergy; std::vector m_alphaOrbitalEnergy; std::vector m_betaOrbitalEnergy; std::vector m_MOcoeffs; std::vector m_alphaMOcoeffs; std::vector m_betaMOcoeffs; MatrixX m_density; /// Total density matrix }; } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/gaussiancube.cpp000066400000000000000000000137771506155467400233630ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gaussiancube.h" #include #include #include #include #include namespace Avogadro::QuantumIO { GaussianCube::GaussianCube() {} GaussianCube::~GaussianCube() {} std::vector GaussianCube::fileExtensions() const { std::vector extensions; extensions.emplace_back("cube"); return extensions; } std::vector GaussianCube::mimeTypes() const { return std::vector(); } bool GaussianCube::read(std::istream& in, Core::Molecule& molecule) { // Variables we will need std::string line; std::vector list; int nAtoms; Vector3 min; Vector3 spacing; Vector3i dim; // Gaussian Cube format is very specific // Read and set name getline(in, line); molecule.setData("name", line); // Read and skip field title (we may be able to use this to setCubeType in the // future) getline(in, line); // Next line contains nAtoms and m_min in >> nAtoms; for (unsigned int i = 0; i < 3; ++i) in >> min(i); getline(in, line); // capture newline before continuing // Next 3 lines contains spacing and dim for (unsigned int i = 0; i < 3; ++i) { getline(in, line); line = Core::trimmed(line); list = Core::split(line, ' '); dim(i) = Core::lexicalCast(list[0]); spacing(i) = Core::lexicalCast(list[i + 1]); } // Geometry block Vector3 pos; for (int i = 0; i < abs(nAtoms); ++i) { getline(in, line); line = Core::trimmed(line); list = Core::split(line, ' '); auto atomNum = Core::lexicalCast(list[0]); Core::Atom a = molecule.addAtom(static_cast(atomNum)); for (unsigned int j = 2; j < 5; ++j) pos(j - 2) = Core::lexicalCast(list[j]); pos = pos * BOHR_TO_ANGSTROM; a.setPosition3d(pos); } // If the nAtoms were negative there is another line before // the data which is necessary, maybe contain 1 or more cubes unsigned int nCubes = 1; if (nAtoms < 0) { in >> nCubes; std::vector moList(nCubes); for (unsigned int i = 0; i < nCubes; ++i) in >> moList[i]; // clear buffer getline(in, line); } // Render molecule molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); // Cube block, set limits and populate data // min and spacing are in bohr units, convert to ANGSTROM min *= BOHR_TO_ANGSTROM; spacing *= BOHR_TO_ANGSTROM; for (unsigned int i = 0; i < nCubes; ++i) { // Get a cube object from molecule Core::Cube* cube = molecule.addCube(); cube->setCubeType(Core::Cube::Type::FromFile); cube->setLimits(min, dim, spacing); std::vector values; // push_back is slow for this, resize vector first values.resize(dim(0) * dim(1) * dim(2)); for (float& value : values) in >> value; // clear buffer, if more than one cube getline(in, line); cube->setData(values); } return true; } void writeFixedFloat(std::ostream& outStream, Real number) { outStream << std::setw(12) << std::fixed << std::right << std::setprecision(6) << number; } void writeFixedInt(std::ostream& outStream, unsigned int number) { outStream << std::setw(5) << std::fixed << std::right << number; } bool GaussianCube::write(std::ostream& outStream, const Core::Molecule& mol) { if (mol.cubeCount() == 0) return false; // no cubes to write const Core::Cube* cube = mol.cube(0); // eventually need to write all the cubes Vector3 min = cube->min() * ANGSTROM_TO_BOHR; Vector3 spacing = cube->spacing() * ANGSTROM_TO_BOHR; Vector3i dim = cube->dimensions(); // number of points in each direction // might be useful to use the 2nd line, but it's just a comment // e.g. write out the cube type outStream << "Gaussian Cube file generated by Avogadro.\n"; if (mol.data("name").toString().length()) outStream << mol.data("name").toString() << "\n"; else outStream << "\n"; // Write out the number of atoms and the minimum coordinates size_t numAtoms = mol.atomCount(); writeFixedInt(outStream, numAtoms); writeFixedFloat(outStream, min[0]); writeFixedFloat(outStream, min[1]); writeFixedFloat(outStream, min[2]); writeFixedInt(outStream, 1); // one value per point (i.e., not vector) outStream << "\n"; // now write the size and spacing of the cube writeFixedInt(outStream, dim[0]); writeFixedFloat(outStream, spacing[0]); writeFixedFloat(outStream, 0.0); writeFixedFloat(outStream, 0.0); outStream << "\n"; writeFixedInt(outStream, dim[1]); writeFixedFloat(outStream, 0.0); writeFixedFloat(outStream, spacing[1]); writeFixedFloat(outStream, 0.0); outStream << "\n"; writeFixedInt(outStream, dim[2]); writeFixedFloat(outStream, 0.0); writeFixedFloat(outStream, 0.0); writeFixedFloat(outStream, spacing[2]); outStream << "\n"; for (size_t i = 0; i < numAtoms; ++i) { Core::Atom atom = mol.atom(i); if (!atom.isValid()) { appendError("Internal error: Atom invalid."); return false; } writeFixedInt(outStream, static_cast(atom.atomicNumber())); writeFixedFloat(outStream, 0.0); // charge writeFixedFloat(outStream, atom.position3d()[0] * ANGSTROM_TO_BOHR); writeFixedFloat(outStream, atom.position3d()[1] * ANGSTROM_TO_BOHR); writeFixedFloat(outStream, atom.position3d()[2] * ANGSTROM_TO_BOHR); outStream << "\n"; } // write the raw cube values const std::vector* values = cube->data(); for (unsigned int i = 0; i < values->size(); ++i) { outStream << std::setw(13) << std::right << std::scientific << std::setprecision(5) << (*values)[i]; if (i % 6 == 5) outStream << "\n"; } return true; } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/gaussiancube.h000066400000000000000000000027511506155467400230160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_GAUSSIANCUBE_H #define AVOGADRO_QUANTUMIO_GAUSSIANCUBE_H #include "avogadroquantumioexport.h" #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT GaussianCube : public Io::FileFormat { public: GaussianCube(); ~GaussianCube() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new GaussianCube; } std::string identifier() const override { return "Avogadro: Gaussian Cube"; } std::string name() const override { return "Gaussian"; } std::string description() const override { return "Gaussian cube file format."; } std::string specificationUrl() const override { return "https://gaussian.com/cubegen/"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; private: void outputAll(); }; // End GaussianCube } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/gaussianfchk.cpp000066400000000000000000000531401506155467400233440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "gaussianfchk.h" #include #include #include #include #include using std::cout; using std::endl; using std::string; using std::vector; namespace Avogadro::QuantumIO { using Core::Atom; using Core::BasisSet; using Core::GaussianSet; using Core::Rhf; using Core::Rohf; using Core::Uhf; // https://physics.nist.gov/cgi-bin/cuu/Value?hrev const double hartreeToEV = 27.211386245981; GaussianFchk::GaussianFchk() : m_scftype(Rhf) {} GaussianFchk::~GaussianFchk() {} std::vector GaussianFchk::fileExtensions() const { std::vector extensions; extensions.emplace_back("fchk"); return extensions; } std::vector GaussianFchk::mimeTypes() const { return std::vector(); } bool GaussianFchk::read(std::istream& in, Core::Molecule& molecule) { // Read the log file line by line, most sections are terminated by an empty // line, so they should be retained. while (!in.eof()) processLine(in); auto* basis = new GaussianSet; int nAtom = 0; for (unsigned int i = 0; i < m_aPos.size(); i += 3) { Atom a = molecule.addAtom(static_cast(m_aNums[nAtom++])); a.setPosition3d(Vector3(m_aPos[i] * BOHR_TO_ANGSTROM, m_aPos[i + 1] * BOHR_TO_ANGSTROM, m_aPos[i + 2] * BOHR_TO_ANGSTROM)); } if (m_frequencies.size() > 0 && m_frequencies.size() == m_vibDisplacements.size() && m_frequencies.size() == m_IRintensities.size()) { molecule.setVibrationFrequencies(m_frequencies); molecule.setVibrationIRIntensities(m_IRintensities); molecule.setVibrationLx(m_vibDisplacements); if (m_RamanIntensities.size()) molecule.setVibrationRamanIntensities(m_RamanIntensities); } // set the total charge molecule.setData("totalCharge", m_charge); // set the spin multiplicity molecule.setData("totalSpinMultiplicity", m_spin); // dipole moment // TODO: This should be a Vector3d Core::Variant dipole(m_dipoleMoment.x(), m_dipoleMoment.y(), m_dipoleMoment.z()); molecule.setData("dipoleMoment", dipole); // Do simple bond perception. molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); molecule.setBasisSet(basis); basis->setMolecule(&molecule); load(basis); return true; } void GaussianFchk::processLine(std::istream& in) { // First truncate the line, remove trailing white space and check any line of // the required length. We are looking for keyword lines of the form, // Charge I 0 // If we are in any other kind of block that is not known skip through until // we find a recognized block. string line; if (!getline(in, line) || line.size() < 44) return; string key = line.substr(0, 42); // cout << "Key:\t" << key << endl; key = Core::trimmed(key); string tmp = line.substr(43); vector list = Core::split(tmp, ' '); std::vector tmpVec; // Big switch statement checking for various things we are interested in if (Core::contains(key, "RHF")) { m_scftype = Rhf; } else if (Core::contains(key, "UHF")) { m_scftype = Uhf; } else if (key == "Number of atoms" && list.size() > 1) { m_numAtoms = Core::lexicalCast(list[1]); } else if (key == "Charge" && list.size() > 1) { m_charge = Core::lexicalCast(list[1]); } else if (key == "Multiplicity" && list.size() > 1) { m_spin = Core::lexicalCast(list[1]); } else if (key == "Dipole Moment" && list.size() > 2) { vector dipole = readArrayD(in, Core::lexicalCast(list[2])); m_dipoleMoment = Vector3(dipole[0], dipole[1], dipole[2]); // convert from au m_dipoleMoment *= 2.541746; } else if (key == "Number of electrons" && list.size() > 1) { m_electrons = Core::lexicalCast(list[1]); } else if (key == "Number of alpha electrons" && list.size() > 1) { m_electronsAlpha = Core::lexicalCast(list[1]); } else if (key == "Number of beta electrons" && list.size() > 1) { m_electronsBeta = Core::lexicalCast(list[1]); } else if (key == "Number of basis functions" && list.size() > 1) { m_numBasisFunctions = Core::lexicalCast(list[1]); // cout << "Number of basis functions = " << m_numBasisFunctions << endl; } else if (key == "Atomic numbers" && list.size() > 2) { m_aNums = readArrayI(in, Core::lexicalCast(list[2])); if (static_cast(m_aNums.size()) != Core::lexicalCast(list[2])) cout << "Reading atomic numbers failed.\n"; } // Now we get to the meat of it - coordinates of the atoms else if (key == "Current cartesian coordinates" && list.size() > 2) { m_aPos = readArrayD(in, Core::lexicalCast(list[2]), 16); } // The real meat is here - basis sets etc! else if (key == "Shell types" && list.size() > 2) { m_shellTypes = readArrayI(in, Core::lexicalCast(list[2])); } else if (key == "Number of primitives per shell" && list.size() > 2) { m_shellNums = readArrayI(in, Core::lexicalCast(list[2])); } else if (key == "Shell to atom map" && list.size() > 2) { m_shelltoAtom = readArrayI(in, Core::lexicalCast(list[2])); } // Now to get the exponents and coefficients( else if (key == "Primitive exponents" && list.size() > 2) { m_a = readArrayD(in, Core::lexicalCast(list[2]), 16); } else if (key == "Contraction coefficients" && list.size() > 2) { m_c = readArrayD(in, Core::lexicalCast(list[2]), 16); } else if (key == "P(S=P) Contraction coefficients" && list.size() > 2) { m_csp = readArrayD(in, Core::lexicalCast(list[2]), 16); } else if (key == "Alpha Orbital Energies" || key == "orbital energies") { if (m_scftype == Rhf) { m_orbitalEnergy = readArrayD(in, Core::lexicalCast(list[2]), 16, hartreeToEV); } else if (m_scftype == Uhf) { m_alphaOrbitalEnergy = readArrayD(in, Core::lexicalCast(list[2]), 16, hartreeToEV); } } else if (key == "Beta Orbital Energies") { if (m_scftype != Uhf) { m_scftype = Uhf; m_alphaOrbitalEnergy = m_orbitalEnergy; m_orbitalEnergy = vector(); m_alphaMOcoeffs = m_MOcoeffs; m_MOcoeffs = vector(); } m_betaOrbitalEnergy = readArrayD(in, Core::lexicalCast(list[2]), 16, hartreeToEV); } else if ((key == "Alpha MO coefficients" || key == "MO coefficients (C)") && list.size() > 2) { if (m_scftype == Rhf) { m_MOcoeffs = readArrayD(in, Core::lexicalCast(list[2]), 16); } else if (m_scftype == Uhf) { m_alphaMOcoeffs = readArrayD(in, Core::lexicalCast(list[2]), 16); } else { cout << "Error, alpha MO coefficients, n = " << m_MOcoeffs.size() << endl; } } else if (key == "Beta MO coefficients" && list.size() > 2) { m_betaMOcoeffs = readArrayD(in, Core::lexicalCast(list[2]), 16); } else if (key == "Total SCF Density" && list.size() > 2) { if (!readDensityMatrix(in, Core::lexicalCast(list[2]), 16)) cout << "Error reading in the SCF density matrix.\n"; } else if (key == "Spin SCF Density" && list.size() > 2) { if (!readSpinDensityMatrix(in, Core::lexicalCast(list[2]), 16)) cout << "Error reading in the SCF spin density matrix.\n"; } else if (key == "Number of Normal Modes" && list.size() > 1) { m_normalModes = Core::lexicalCast(list[1]); } else if (key == "Vib-E2" && list.size() > 2) { m_frequencies.clear(); m_IRintensities.clear(); m_RamanIntensities.clear(); unsigned threeN = m_numAtoms * 3; // degrees of freedom tmpVec = readArrayD(in, Core::lexicalCast(list[2]), 16); // read in the first 3N-6 elements as frequencies for (unsigned int i = 0; i < static_cast(m_normalModes); ++i) { m_frequencies.push_back(tmpVec[i]); } // skip to after threeN elements then read IR intensities for (unsigned int i = threeN; i < threeN + static_cast(m_normalModes); ++i) { m_IRintensities.push_back(tmpVec[i]); } // now check if we have Raman intensities if (tmpVec[threeN + m_normalModes] != 0.0) { for (unsigned int i = threeN + m_normalModes; i < threeN + 2 * static_cast(m_normalModes); ++i) { m_RamanIntensities.push_back(tmpVec[i]); } } } else if (key == "Vib-Modes" && list.size() > 2) { tmpVec = readArrayD(in, Core::lexicalCast(list[2]), 16); m_vibDisplacements.clear(); if (tmpVec.size() == static_cast(m_numAtoms) * 3 * static_cast(m_normalModes)) { for (unsigned int i = 0; i < static_cast(m_normalModes); ++i) { Core::Array mode; for (unsigned int j = 0; j < static_cast(m_numAtoms); ++j) { Vector3 v(tmpVec[i * m_numAtoms * 3 + j * 3], tmpVec[i * m_numAtoms * 3 + j * 3 + 1], tmpVec[i * m_numAtoms * 3 + j * 3 + 2]); mode.push_back(v); } m_vibDisplacements.push_back(mode); } } } } void GaussianFchk::load(GaussianSet* basis) { // Now load up our basis set basis->setElectronCount(m_electrons); // basis->setElectronCount(m_electronsAlpha, Core::GaussianSet::alpha); // basis->setElectronCount(m_electronsBeta, Core::GaussianSet::beta); // Set up the GTO primitive counter, go through the shells and add them int nGTO = 0; for (unsigned int i = 0; i < m_shellTypes.size(); ++i) { // Handle the SP case separately - this should possibly be a distinct type if (m_shellTypes[i] == -1) { // SP orbital type - actually have to add two shells int s = basis->addBasis(m_shelltoAtom[i] - 1, GaussianSet::S); int tmpGTO = nGTO; for (unsigned int j = 0; j < static_cast(m_shellNums[i]); ++j) { basis->addGto(s, m_c[nGTO], m_a[nGTO]); ++nGTO; } int p = basis->addBasis(m_shelltoAtom[i] - 1, GaussianSet::P); for (unsigned int j = 0; j < static_cast(m_shellNums[i]); ++j) { basis->addGto(p, m_csp[tmpGTO], m_a[tmpGTO]); ++tmpGTO; } } else { GaussianSet::orbital type; switch (m_shellTypes[i]) { case 0: type = GaussianSet::S; break; case 1: type = GaussianSet::P; break; case 2: type = GaussianSet::D; break; case -2: type = GaussianSet::D5; break; case 3: type = GaussianSet::F; break; case -3: type = GaussianSet::F7; break; case 4: type = GaussianSet::G; break; case -4: type = GaussianSet::G9; break; case 5: type = GaussianSet::H; break; case -5: type = GaussianSet::H11; break; case 6: type = GaussianSet::I; break; case -6: type = GaussianSet::I13; break; default: // If we encounter GTOs we do not understand, the basis is likely // invalid type = GaussianSet::UU; /// basis->setValid(false); } if (type != GaussianSet::UU) { int b = basis->addBasis(m_shelltoAtom[i] - 1, type); for (unsigned int j = 0; j < static_cast(m_shellNums[i]); ++j) { basis->addGto(b, m_c[nGTO], m_a[nGTO]); ++nGTO; } } } } // Now to load in the MO coefficients if (basis->isValid()) { if (m_MOcoeffs.size()) basis->setMolecularOrbitals(m_MOcoeffs); else cout << "Error no MO coefficients...\n"; if (m_alphaMOcoeffs.size()) basis->setMolecularOrbitals(m_alphaMOcoeffs, BasisSet::Alpha); if (m_betaMOcoeffs.size()) basis->setMolecularOrbitals(m_betaMOcoeffs, BasisSet::Beta); if (m_density.rows()) basis->setDensityMatrix(m_density); if (m_spinDensity.rows()) basis->setSpinDensityMatrix(m_spinDensity); if (m_orbitalEnergy.size()) // restricted calculation basis->setMolecularOrbitalEnergy(m_orbitalEnergy); else { if (m_alphaOrbitalEnergy.size()) basis->setMolecularOrbitalEnergy(m_alphaOrbitalEnergy, BasisSet::Alpha); if (m_betaOrbitalEnergy.size()) basis->setMolecularOrbitalEnergy(m_betaOrbitalEnergy, BasisSet::Beta); } } else { cout << "Basis set is not valid!\n"; } } vector GaussianFchk::readArrayI(std::istream& in, unsigned int n) { vector tmp; tmp.reserve(n); bool ok(false); while (tmp.size() < n) { if (in.eof()) { cout << "GaussianFchk::readArrayI could not read all elements " << n << " expected " << tmp.size() << " parsed.\n"; return tmp; } string line; if (getline(in, line), line.empty()) return tmp; vector list = Core::split(line, ' '); for (auto& i : list) { if (tmp.size() >= n) { cout << "Too many variables read in. File may be inconsistent. " << tmp.size() << " of " << n << endl; return tmp; } tmp.push_back(Core::lexicalCast(i, ok)); if (!ok) { cout << "Warning: problem converting string to integer: " << i << " in GaussianFchk::readArrayI.\n"; return tmp; } } } return tmp; } vector GaussianFchk::readArrayD(std::istream& in, unsigned int n, int width, double factor) { vector tmp; tmp.reserve(n); bool ok(false); while (tmp.size() < n) { if (in.eof()) { cout << "GaussianFchk::readArrayD could not read all elements " << n << " expected " << tmp.size() << " parsed.\n"; return tmp; } string line; if (getline(in, line), line.empty()) return tmp; if (width == 0) { // we can split by spaces vector list = Core::split(line, ' '); for (auto& i : list) { if (tmp.size() >= n) { cout << "Too many variables read in. File may be inconsistent. " << tmp.size() << " of " << n << endl; return tmp; } tmp.push_back(Core::lexicalCast(i, ok) * factor); if (!ok) { cout << "Warning: problem converting string to integer: " << i << " in GaussianFchk::readArrayD.\n"; return tmp; } } } else { // Q-Chem files use 16 character fields size_t maxColumns = 80 / width; for (size_t i = 0; i < maxColumns; ++i) { string substring = line.substr(i * width, width); if (static_cast(substring.length()) != width) break; if (tmp.size() >= n) { cout << "Too many variables read in. File may be inconsistent. " << tmp.size() << " of " << n << endl; return tmp; } tmp.push_back(Core::lexicalCast(substring, ok) * factor); if (!ok) { cout << "Warning: problem converting string to double: " << substring << " in GaussianFchk::readArrayD.\n"; return tmp; } } } } return tmp; } bool GaussianFchk::readDensityMatrix(std::istream& in, unsigned int n, int width) { // This function reads in the lower triangular density matrix m_density.resize(m_numBasisFunctions, m_numBasisFunctions); unsigned int cnt = 0; unsigned int i = 0, j = 0; unsigned int f = 1; bool ok = false; while (cnt < n) { if (in.eof()) { cout << "GaussianFchk::readDensityMatrix could not read all elements " << n << " expected " << cnt << " parsed.\n"; return false; } string line; if (getline(in, line), line.empty()) return false; if (width == 0) { // we can split by spaces vector list = Core::split(line, ' '); for (auto& k : list) { if (cnt >= n) { cout << "Too many variables read in. File may be inconsistent. " << cnt << " of " << n << endl; return false; } // Read in lower half matrix m_density(i, j) = Core::lexicalCast(k, ok); if (ok) { // Valid double converted, carry on ++j; ++cnt; if (j == f) { // We need to move down to the next row and increment f - lower tri j = 0; ++f; ++i; } } else { // Invalid conversion of a string to double cout << "Warning: problem converting string to double: " << k << "\nIn GaussianFchk::readDensityMatrix.\n"; return false; } } } else { // Q-Chem files use 16-character fields size_t maxColumns = 80 / width; for (size_t c = 0; c < maxColumns; ++c) { string substring = line.substr(c * width, width); if (static_cast(substring.length()) != width) { break; } else if (cnt >= n) { cout << "Too many variables read in. File may be inconsistent. " << cnt << " of " << n << endl; return false; } // Read in lower half matrix m_density(i, j) = Core::lexicalCast(substring, ok); if (ok) { // Valid double converted, carry on ++j; ++cnt; if (j == f) { // We need to move down to the next row and increment f - lower tri j = 0; ++f; ++i; } } else { // Invalid conversion of a string to double cout << "Warning: problem converting string to double: " << substring << "\nIn GaussianFchk::readDensityMatrix.\n"; return false; } } } } return true; } bool GaussianFchk::readSpinDensityMatrix(std::istream& in, unsigned int n, int width) { // This function reads in the lower triangular density matrix m_spinDensity.resize(m_numBasisFunctions, m_numBasisFunctions); unsigned int cnt = 0; unsigned int i = 0, j = 0; unsigned int f = 1; bool ok = false; while (cnt < n) { if (in.eof()) { cout << "GaussianFchk::readSpinDensityMatrix could not read all elements " << n << " expected " << cnt << " parsed.\n"; return false; } string line; if (getline(in, line), line.empty()) return false; if (width == 0) { // we can split by spaces vector list = Core::split(line, ' '); for (auto& k : list) { if (cnt >= n) { cout << "Too many variables read in. File may be inconsistent. " << cnt << " of " << n << endl; return false; } // Read in lower half matrix m_spinDensity(i, j) = Core::lexicalCast(k, ok); if (ok) { // Valid double converted, carry on ++j; ++cnt; if (j == f) { // We need to move down to the next row and increment f - lower tri j = 0; ++f; ++i; } } else { // Invalid conversion of a string to double cout << "Warning: problem converting string to double: " << k << "\nIn GaussianFchk::readDensityMatrix.\n"; return false; } } } else { // Q-Chem files use 16-character fields size_t maxColumns = 80 / width; for (size_t c = 0; c < maxColumns; ++c) { string substring = line.substr(c * width, width); if (static_cast(substring.length()) != width) { break; } else if (cnt >= n) { cout << "Too many variables read in. File may be inconsistent. " << cnt << " of " << n << endl; return false; } // Read in lower half matrix m_spinDensity(i, j) = Core::lexicalCast(substring, ok); if (ok) { // Valid double converted, carry on ++j; ++cnt; if (j == f) { // We need to move down to the next row and increment f - lower tri j = 0; ++f; ++i; } } else { // Invalid conversion of a string to double cout << "Warning: problem converting string to double: " << substring << "\nIn GaussianFchk::readSpinDensityMatrix.\n"; return false; } } } } return true; } void GaussianFchk::outputAll() { switch (m_scftype) { case Rhf: cout << "SCF type = RHF\n"; break; case Uhf: cout << "SCF type = UHF\n"; break; case Rohf: cout << "SCF type = ROHF\n"; break; default: cout << "SCF type = Unknown\n"; } cout << "Shell mappings:\n"; for (unsigned int i = 0; i < m_shellTypes.size(); ++i) cout << i << " : type = " << m_shellTypes.at(i) << ", number = " << m_shellNums.at(i) << ", atom = " << m_shelltoAtom.at(i) << endl; if (m_MOcoeffs.size()) { cout << "MO coefficients:\n"; for (double m_MOcoeff : m_MOcoeffs) cout << m_MOcoeff << "\t"; cout << endl << endl; } if (m_alphaMOcoeffs.size()) { cout << "Alpha MO coefficients:\n"; for (double m_alphaMOcoeff : m_alphaMOcoeffs) cout << m_alphaMOcoeff << "\t"; cout << endl << endl; } if (m_betaMOcoeffs.size()) { cout << "Beta MO coefficients:\n"; for (double m_betaMOcoeff : m_betaMOcoeffs) cout << m_betaMOcoeff << "\t"; cout << endl << endl; } } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/gaussianfchk.h000066400000000000000000000062231506155467400230110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_GAUSSIANFCHK_H #define AVOGADRO_QUANTUMIO_GAUSSIANFCHK_H #include "avogadroquantumioexport.h" #include #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT GaussianFchk : public Io::FileFormat { public: GaussianFchk(); ~GaussianFchk() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new GaussianFchk; } std::string identifier() const override { return "Avogadro: FCHK"; } std::string name() const override { return "Gaussian FCHK"; } std::string description() const override { return "Gaussian formatted checkpoint reader."; } std::string specificationUrl() const override { return "http://www.gaussian.com/g_tech/g_ur/f_formchk.htm"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not write out Gaussian FCHK files. return false; } void outputAll(); private: void processLine(std::istream& in); void load(Core::GaussianSet* basis); std::vector readArrayI(std::istream& in, unsigned int n); std::vector readArrayD(std::istream& in, unsigned int n, int width = 0, double factor = 1.0); bool readDensityMatrix(std::istream& in, unsigned int n, int width = 0); bool readSpinDensityMatrix(std::istream& in, unsigned int n, int width = 0); /** * Use either m_electrons, or m_electronsAlpha and m_electronsBeta. * This then carries through to the energy, coefficients etc. */ int m_electrons; int m_electronsAlpha; int m_electronsBeta; int m_normalModes; int m_numAtoms; int m_spin; int m_charge; unsigned int m_numBasisFunctions; std::vector m_aNums; std::vector m_aPos; std::vector m_shellTypes; std::vector m_shellNums; std::vector m_shelltoAtom; std::vector m_a; std::vector m_c; std::vector m_csp; std::vector m_orbitalEnergy; std::vector m_alphaOrbitalEnergy; std::vector m_betaOrbitalEnergy; std::vector m_MOcoeffs; std::vector m_alphaMOcoeffs; std::vector m_betaMOcoeffs; MatrixX m_density; /// Total density matrix MatrixX m_spinDensity; /// Spin density matrix Vector3 m_dipoleMoment; Core::ScfType m_scftype; Core::Array m_frequencies; Core::Array m_IRintensities; Core::Array m_RamanIntensities; Core::Array> m_vibDisplacements; }; } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/genericjson.cpp000066400000000000000000000051421506155467400232030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "genericjson.h" #include #include #include "nwchemjson.h" #include "qcschema.h" #include #include namespace Avogadro::QuantumIO { using json = nlohmann::json; using std::string; GenericJson::GenericJson() {} GenericJson::~GenericJson() {} std::vector GenericJson::fileExtensions() const { std::vector extensions; extensions.emplace_back("json"); return extensions; } std::vector GenericJson::mimeTypes() const { return std::vector(); } bool GenericJson::read(std::istream& in, Core::Molecule& molecule) { // this should be JSON so look for key attributes FileFormat* reader = nullptr; // all of these formats expect a JSON object json root; try { in >> root; } catch (json::parse_error& e) { appendError("Error parsing JSON: " + string(e.what())); return false; } if (!root.is_object()) { appendError("Error: Input is not a JSON object."); return false; } // Okay, look for particular keys if (root.find("schema_name") != root.end()) { if (root["schema_name"].get() == "QC_JSON") reader = new QCSchema(); } else if (root.find("simulation") != root.end()) { reader = new NWChemJson(); } // if we didn't find a program name, check for cclib or OpenBabel // prefer cclib if it's available if (reader == nullptr) { // check what other json support is available std::vector readers = Io::FileFormatManager::instance().fileFormatsFromFileExtension( "json", FileFormat::File | FileFormat::Read); // loop through readers to check for "cclib" or "Open Babel" for (const FileFormat* r : readers) { if (r->name() == "cclib") { reader = r->newInstance(); break; } else if (r->identifier().compare(0, 9, "OpenBabel") == 0) { reader = r->newInstance(); break; } } } // rewind the stream in.seekg(0, std::ios::beg); in.clear(); if (reader) { bool success = reader->readFile(fileName(), molecule); delete reader; return success; } else { appendError("Could not determine the program used to generate this file."); delete reader; return false; } } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/genericjson.h000066400000000000000000000027701506155467400226540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_GENERICJSON_H #define AVOGADRO_QUANTUMIO_GENERICJSON_H #include "avogadroquantumioexport.h" #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT GenericJson : public Io::FileFormat { public: GenericJson(); ~GenericJson() override; Operations supportedOperations() const override { return Read | File | Stream | String; // TODO add write operation } FileFormat* newInstance() const override { return new GenericJson; } std::string identifier() const override { return "Avogadro: Generic JSON"; } std::string name() const override { return "Generic JSON"; } std::string description() const override { return "Generic JSON format."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not currently write QC_SCHEMA files. return false; } }; } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/genericoutput.cpp000066400000000000000000000056521506155467400236000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "genericoutput.h" #include #include #include #include "gamessus.h" #include "molden.h" #include "nwchemlog.h" #include "orca.h" #include namespace Avogadro::QuantumIO { GenericOutput::GenericOutput() {} GenericOutput::~GenericOutput() {} std::vector GenericOutput::fileExtensions() const { std::vector extensions; extensions.emplace_back("out"); extensions.emplace_back("output"); extensions.emplace_back("log"); return extensions; } std::vector GenericOutput::mimeTypes() const { return std::vector(); } bool GenericOutput::read(std::istream& in, Core::Molecule& molecule) { // check the stream line-by-line until we see the program name FileFormat* reader = nullptr; std::string line; while (std::getline(in, line)) { if (line.find("Northwest Computational Chemistry Package") != std::string::npos) { // NWChem reader = new NWChemLog; break; } else if (line.find("GAMESS VERSION") != std::string::npos) { // GAMESS-US .. don't know if we can read Firefly or GAMESS-UK reader = new GAMESSUSOutput; break; } else if (line.find("[Molden Format]") != std::string::npos) { // molden with .out extension reader = new MoldenFile; break; } else if (line.find("O R C A") != std::string::npos) { // ORCA reader reader = new ORCAOutput; break; } else if (line.find("xtb:") != std::string::npos) { // xtb reader reader = new Io::XyzFormat; break; } } // if we didn't find a program name, check for cclib or OpenBabel // prefer cclib if it's available if (reader == nullptr) { // check what output is available std::vector readers = Io::FileFormatManager::instance().fileFormatsFromFileExtension( "out", FileFormat::File | FileFormat::Read); // loop through writers to check for "cclib" or "Open Babel" for (const FileFormat* r : readers) { if (r->name() == "cclib") { reader = r->newInstance(); break; } else if (r->identifier().compare(0, 9, "OpenBabel") == 0) { reader = r->newInstance(); break; } } } // rewind the stream in.seekg(0, std::ios::beg); in.clear(); if (reader) { bool success = reader->readFile(fileName(), molecule); delete reader; return success; } else { appendError( "Could not determine the program used to generate this output file."); delete reader; return false; } } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/genericoutput.h000066400000000000000000000027351506155467400232440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_GENERICOUTPUT_H #define AVOGADRO_QUANTUMIO_GENERICOUTPUT_H #include "avogadroquantumioexport.h" #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT GenericOutput : public Io::FileFormat { public: GenericOutput(); ~GenericOutput() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new GenericOutput; } std::string identifier() const override { return "Avogadro: Generic Output"; } std::string name() const override { return "Generic Output"; } std::string description() const override { return "Generic output format."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not write output files. return false; } }; } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/molden.cpp000066400000000000000000000263401506155467400221560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "molden.h" #include #include #include #include using std::cout; using std::endl; using std::string; using std::vector; namespace Avogadro::QuantumIO { using Core::Atom; using Core::GaussianSet; MoldenFile::MoldenFile() : m_coordFactor(1.0), m_electrons(0), m_mode(Unrecognized) { } MoldenFile::~MoldenFile() {} std::vector MoldenFile::fileExtensions() const { std::vector extensions; extensions.emplace_back("mold"); extensions.emplace_back("molf"); extensions.emplace_back("molden"); return extensions; } std::vector MoldenFile::mimeTypes() const { return std::vector(); } bool MoldenFile::read(std::istream& in, Core::Molecule& molecule) { // Read the log file line by line, most sections are terminated by an empty // line, so they should be retained. while (!in.eof() && in.good()) { processLine(in); } auto* basis = new GaussianSet; int nAtom = 0; for (unsigned int i = 0; i < m_aPos.size(); i += 3) { Atom a = molecule.addAtom(static_cast(m_aNums[nAtom++])); a.setPosition3d(Vector3(m_aPos[i], m_aPos[i + 1], m_aPos[i + 2])); } // Do simple bond perception. molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); molecule.setBasisSet(basis); basis->setMolecule(&molecule); load(basis); if (m_frequencies.size() > 0 && m_frequencies.size() == m_vibDisplacements.size()) { molecule.setVibrationFrequencies(m_frequencies); molecule.setVibrationLx(m_vibDisplacements); // if we don't have intensities, set them all to zero if (m_IRintensities.size() != m_frequencies.size()) { m_IRintensities.resize(m_frequencies.size()); for (unsigned int i = 0; i < m_frequencies.size(); i++) m_IRintensities[i] = 0.0; } molecule.setVibrationIRIntensities(m_IRintensities); if (m_RamanIntensities.size()) molecule.setVibrationRamanIntensities(m_RamanIntensities); } return true; } void MoldenFile::processLine(std::istream& in) { // First truncate the line, remove trailing white space and check for blanks. string line; if (!getline(in, line) || Core::trimmed(line).empty()) return; vector list = Core::split(line, ' '); // Big switch statement checking for various things we are interested in. The // Molden file format uses sections, each starts with a header line of the // form [Atoms], and the beginning of a new section denotes the end of the // last. if (Core::contains(line, "[Atoms]") || Core::contains(line, "[ATOMS]")) { if (list.size() > 1 && Core::contains(list[1], "AU")) m_coordFactor = BOHR_TO_ANGSTROM_D; m_mode = Atoms; } else if (Core::contains(line, "[GTO]")) { m_mode = GTO; } else if (Core::contains(line, "[MO]")) { m_mode = MO; } else if (Core::contains(line, "[FREQ]")) { m_mode = Frequencies; } else if (Core::contains(line, "[FR-NORM-COORD]")) { m_mode = VibrationalModes; } else if (Core::contains(line, "[INT]")) { m_mode = Intensities; } else if (Core::contains(line, "[")) { // unknown section m_mode = Unrecognized; } else { // We are in a section, and must parse the lines in that section. string shell; GaussianSet::orbital shellType; std::streampos currentPos = in.tellg(); // Parsing a line of data in a section - what mode are we in? switch (m_mode) { case Atoms: readAtom(list); break; case GTO: { // TODO: detect dead files and make bullet-proof int atom = Core::lexicalCast(list[0]); getline(in, line); line = Core::trimmed(line); while (!line.empty()) { // Read the shell types in this GTO. list = Core::split(line, ' '); if (list.size() < 1) break; shell = list[0]; shellType = GaussianSet::UU; if (shell == "sp") shellType = GaussianSet::SP; else if (shell == "s") shellType = GaussianSet::S; else if (shell == "p") shellType = GaussianSet::P; else if (shell == "d") shellType = GaussianSet::D; else if (shell == "f") shellType = GaussianSet::F; else if (shell == "g") shellType = GaussianSet::G; if (shellType != GaussianSet::UU) { m_shellTypes.push_back(shellType); m_shelltoAtom.push_back(atom); } else { return; } int numGTOs = Core::lexicalCast(list[1]); m_shellNums.push_back(numGTOs); // Now read all the exponents and contraction coefficients. for (int gto = 0; gto < numGTOs; ++gto) { getline(in, line); line = Core::trimmed(line); list = Core::split(line, ' '); if (list.size() > 1) { m_a.push_back(Core::lexicalCast(list[0])); m_c.push_back(Core::lexicalCast(list[1])); } if (shellType == GaussianSet::SP && list.size() > 2) m_csp.push_back(Core::lexicalCast(list[2])); } // Start reading the next shell. getline(in, line); line = Core::trimmed(line); } } break; case MO: // Parse the occupation, spin, energy, etc (Occup, Spin, Ene). while (!line.empty() && Core::contains(line, "=")) { getline(in, line); line = Core::trimmed(line); list = Core::split(line, ' '); if (Core::contains(line, "Occup")) m_electrons += Core::lexicalCast(list[1]); else if (Core::contains(line, "Ene")) m_orbitalEnergy.push_back(Core::lexicalCast(list[1])); // TODO: track alpha beta spin } // Parse the molecular orbital coefficients. while (!line.empty() && !Core::contains(line, "=") && !Core::contains(line, "[")) { list = Core::split(line, ' '); if (list.size() < 2) break; m_MOcoeffs.push_back(Core::lexicalCast(list[1])); getline(in, line); line = Core::trimmed(line); list = Core::split(line, ' '); } // go back to previous line in.seekg(currentPos); break; case Frequencies: // Parse the frequencies. m_frequencies.clear(); while (!line.empty() && !Core::contains(line, "[")) { line = Core::trimmed(line); m_frequencies.push_back(Core::lexicalCast(line)); currentPos = in.tellg(); getline(in, line); } // go back to previous line in.seekg(currentPos); break; case VibrationalModes: // Parse the vibrational modes. // should be "vibration 1" etc. // then the normal mode displacements m_vibDisplacements.clear(); // shouldn't be more than the number of frequencies while (!line.empty() && !Core::contains(line, "[")) { if (Core::contains(line, "vibration")) { m_vibDisplacements.push_back(Core::Array()); getline(in, line); line = Core::trimmed(line); while (!line.empty() && !Core::contains(line, "[") && !Core::contains(line, "vibration")) { list = Core::split(line, ' '); if (list.size() < 3) break; m_vibDisplacements.back().push_back(Vector3( Core::lexicalCast(list[0]) * BOHR_TO_ANGSTROM_D, Core::lexicalCast(list[1]) * BOHR_TO_ANGSTROM_D, Core::lexicalCast(list[2]) * BOHR_TO_ANGSTROM_D)); currentPos = in.tellg(); getline(in, line); line = Core::trimmed(line); } } else { // we shouldn't hit this, but better to be safe break; } // okay, we're either done reading // or we're at the next vibration if (m_vibDisplacements.size() == m_frequencies.size()) { // reset to make sure we don't miss any other sections // (e.g., intensities) in.seekg(currentPos); break; } } break; case Intensities: // could be just IR or two pieces including Raman while (!line.empty() && !Core::contains(line, "[")) { list = Core::split(line, ' '); m_IRintensities.push_back(Core::lexicalCast(list[0])); if (list.size() == 2) m_RamanIntensities.push_back(Core::lexicalCast(list[1])); if (m_IRintensities.size() == m_frequencies.size()) { // we're done break; } currentPos = in.tellg(); getline(in, line); line = Core::trimmed(line); } break; default: break; } } } void MoldenFile::readAtom(const vector& list) { // element_name number atomic_number x y z if (list.size() < 6) return; m_aNums.push_back(Core::lexicalCast(list[2])); m_aPos.push_back(Core::lexicalCast(list[3]) * m_coordFactor); m_aPos.push_back(Core::lexicalCast(list[4]) * m_coordFactor); m_aPos.push_back(Core::lexicalCast(list[5]) * m_coordFactor); } void MoldenFile::load(GaussianSet* basis) { // Now load up our basis set basis->setElectronCount(m_electrons); // Set up the GTO primitive counter, go through the shells and add them int nGTO = 0; int nSP = 0; // number of SP shells for (unsigned int i = 0; i < m_shellTypes.size(); ++i) { // Handle the SP case separately - this should possibly be a distinct type if (m_shellTypes.at(i) == GaussianSet::SP) { // SP orbital type - currently have to unroll into two shells int s = basis->addBasis(m_shelltoAtom[i] - 1, GaussianSet::S); int p = basis->addBasis(m_shelltoAtom[i] - 1, GaussianSet::P); for (int j = 0; j < m_shellNums[i]; ++j) { basis->addGto(s, m_c[nGTO], m_a[nGTO]); basis->addGto(p, m_csp[nSP], m_a[nGTO]); ++nSP; ++nGTO; } } else { int b = basis->addBasis(m_shelltoAtom[i] - 1, m_shellTypes[i]); for (int j = 0; j < m_shellNums[i]; ++j) { basis->addGto(b, m_c[nGTO], m_a[nGTO]); ++nGTO; } } } // Now to load in the MO coefficients if (m_MOcoeffs.size()) basis->setMolecularOrbitals(m_MOcoeffs); if (m_orbitalEnergy.size()) basis->setMolecularOrbitalEnergy(m_orbitalEnergy); } void MoldenFile::outputAll() { cout << "Shell mappings:\n"; for (unsigned int i = 0; i < m_shellTypes.size(); ++i) cout << i << ": type = " << m_shellTypes.at(i) << ", number = " << m_shellNums.at(i) << ", atom = " << m_shelltoAtom.at(i) << endl; cout << "MO coefficients:\n"; for (double m_MOcoeff : m_MOcoeffs) cout << m_MOcoeff << "\t"; cout << endl; } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/molden.h000066400000000000000000000046401506155467400216220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_MOLDEN_H #define AVOGADRO_QUANTUMIO_MOLDEN_H #include "avogadroquantumioexport.h" #include #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT MoldenFile : public Io::FileFormat { public: MoldenFile(); ~MoldenFile() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new MoldenFile; } std::string identifier() const override { return "Avogadro: Molden"; } std::string name() const override { return "Molden"; } std::string description() const override { return "Molden file format."; } std::string specificationUrl() const override { return "http://www.cmbi.ru.nl/molden/molden_format.html"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not write out Molden files. return false; } private: void outputAll(); void processLine(std::istream& in); void readAtom(const std::vector& list); void load(Core::GaussianSet* basis); double m_coordFactor; int m_electrons; unsigned int m_numBasisFunctions; std::vector m_aNums; std::vector m_aPos; std::vector m_shellTypes; std::vector m_shellNums; std::vector m_shelltoAtom; std::vector m_a; std::vector m_c; std::vector m_csp; std::vector m_orbitalEnergy; std::vector m_MOcoeffs; Core::Array m_frequencies; Core::Array m_IRintensities; Core::Array m_RamanIntensities; Core::Array> m_vibDisplacements; enum Mode { Atoms, GTO, MO, Frequencies, VibrationalModes, Intensities, Unrecognized }; Mode m_mode; }; } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/mopacaux.cpp000066400000000000000000000361601506155467400225160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "mopacaux.h" #include #include #include #include using std::cout; using std::endl; using std::string; using std::vector; namespace Avogadro::QuantumIO { using Core::Atom; using Core::SlaterSet; MopacAux::MopacAux() : m_electrons(0) {} MopacAux::~MopacAux() {} std::vector MopacAux::fileExtensions() const { std::vector extensions; extensions.emplace_back("aux"); return extensions; } std::vector MopacAux::mimeTypes() const { return std::vector(); } bool MopacAux::read(std::istream& in, Core::Molecule& molecule) { // Read the log file line by line, most sections are terminated by an empty // line, so they should be retained. while (!in.eof()) processLine(in); auto* basis = new SlaterSet; for (unsigned int i = 0; i < m_atomPos.size(); ++i) { Atom a = molecule.addAtom(static_cast(m_atomNums[i])); a.setPosition3d(m_atomPos[i]); } // Do simple bond perception. molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); molecule.setBasisSet(basis); basis->setMolecule(&molecule); load(basis); // check if there is vibrational data if (m_frequencies.size() > 0) { // convert the std::vector to Array Core::Array frequencies(m_frequencies.size()); for (unsigned int i = 0; i < m_frequencies.size(); ++i) frequencies[i] = m_frequencies[i]; molecule.setVibrationFrequencies(frequencies); // convert the std::vector to Array Core::Array intensities(m_frequencies.size(), 0.0); if (m_irIntensities.size() == m_frequencies.size()) { for (unsigned int i = 0; i < m_irIntensities.size(); ++i) intensities[i] = m_irIntensities[i]; } molecule.setVibrationIRIntensities(intensities); // wrap the normal modes into a vector of vectors Core::Array> normalModes; Core::Array normalMode; Index atomCount = molecule.atomCount(); for (unsigned int i = 0; i < m_normalModes.size(); ++i) { normalMode.push_back(m_normalModes[i]); if (i % atomCount == 0 && normalMode.size() > 0) { normalModes.push_back(normalMode); normalMode.clear(); } } molecule.setVibrationLx(normalModes); } // add charges and properties molecule.setData("totalCharge", m_charge); molecule.setData("totalSpinMultiplicity", m_spin); molecule.setData("dipoleMoment", m_dipoleMoment); molecule.setData("DeltaH", m_heatOfFormation); molecule.setData("Area", m_area); molecule.setData("Volume", m_volume); if (m_partialCharges.size() > 0) { MatrixX charges(m_partialCharges.size(), 1); for (size_t i = 0; i < m_partialCharges.size(); ++i) charges(i, 0) = m_partialCharges[i]; molecule.setPartialCharges("MOPAC", charges); } // if we have more than one coordinate set if (m_coordSets.size() > 1) { for (unsigned int i = 0; i < m_coordSets.size(); ++i) { Core::Array positions; positions.reserve(molecule.atomCount()); for (size_t j = 0; j < molecule.atomCount(); ++j) { positions.push_back(m_coordSets[i][j]); } molecule.setCoordinate3d(positions, i); } } return true; } void MopacAux::processLine(std::istream& in) { // First truncate the line, remove trailing white space and check string line; if (!getline(in, line) || Core::trimmed(line).empty()) return; string key = Core::trimmed(line); // Big switch statement checking for various things we are interested in if (Core::contains(key, "ATOM_EL")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 4)); cout << "Number of atoms = " << tmp << endl; m_atomNums = readArrayElements(in, tmp); } else if (Core::contains(key, "HEAT_OF_FORMATION:KCAL/MOL")) { vector list = Core::split(line, '='); if (list.size() > 1) { std::replace(list[1].begin(), list[1].end(), 'D', 'E'); m_heatOfFormation = Core::lexicalCast(list[1]); cout << "Heat of formation = " << m_heatOfFormation << " kcal/mol" << endl; } } else if (Core::contains(key, "AREA:SQUARE ANGSTROMS")) { vector list = Core::split(line, '='); if (list.size() > 1) { std::replace(list[1].begin(), list[1].end(), 'D', 'E'); m_area = Core::lexicalCast(list[1]); cout << "Area = " << m_area << " square Angstroms" << endl; } } else if (Core::contains(key, "VOLUME:CUBIC ANGSTROMS")) { vector list = Core::split(line, '='); if (list.size() > 1) { std::replace(list[1].begin(), list[1].end(), 'D', 'E'); m_volume = Core::lexicalCast(list[1]); cout << "Volume = " << m_volume << " cubic Angstroms" << endl; } } else if (Core::contains(key, "KEYWORDS=")) { // parse for charge and spin std::vector list = Core::split(key, ' '); for (size_t i = 0; i < list.size(); ++i) { if (Core::contains(list[i], "CHARGE=")) { m_charge = Core::lexicalCast(list[i].substr(7)); } else if (Core::contains(list[i], "DOUBLET")) { m_spin = 2; } else if (Core::contains(list[i], "TRIPLET")) { m_spin = 3; } else if (Core::contains(list[i], "QUARTET")) { m_spin = 4; } else if (Core::contains(list[i], "QUINTET")) { m_spin = 5; } else if (Core::contains(list[i], "SEXTET")) { m_spin = 6; } else if (Core::contains(list[i], "SEPTET")) { m_spin = 7; } else if (Core::contains(list[i], "OCTET")) { m_spin = 8; } else if (Core::contains(list[i], "NONET")) { m_spin = 9; } } } else if (Core::contains(key, "DIP_VEC:DEBYE")) { vector list = Core::split(line, '='); if (list.size() > 1) { // split based on spaces std::replace(list[1].begin(), list[1].end(), 'D', 'E'); vector dipole = Core::split(list[1], ' '); if (dipole.size() == 3) { m_dipoleMoment = Vector3(Core::lexicalCast(dipole[0]), Core::lexicalCast(dipole[1]), Core::lexicalCast(dipole[2])); } } cout << "Dipole moment " << m_dipoleMoment.norm() << " Debye" << endl; } else if (Core::contains(key, "AO_ATOMINDEX")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 4)); cout << "Number of atomic orbitals = " << tmp << endl; m_atomIndex = readArrayI(in, tmp); for (int& i : m_atomIndex) --i; } else if (Core::contains(key, "ATOM_SYMTYPE")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 4)); cout << "Number of atomic orbital types = " << tmp << endl; m_atomSym = readArraySym(in, tmp); } else if (Core::contains(key, "AO_ZETA")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 4)); cout << "Number of zeta values = " << tmp << endl; m_zeta = readArrayD(in, tmp); } else if (Core::contains(key, "ATOM_CHARGES")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 4)); cout << "Number of atomic charges = " << tmp << endl; m_partialCharges = readArrayD(in, tmp); } else if (Core::contains(key, "ATOM_PQN")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 4)); cout << "Number of PQN values =" << tmp << endl; m_pqn = readArrayI(in, tmp); } else if (Core::contains(key, "NUM_ELECTRONS")) { vector list = Core::split(line, '='); if (list.size() > 1) { m_electrons = Core::lexicalCast(list[1]); cout << "Number of electrons = " << m_electrons << endl; } } else if (Core::contains(key, "ATOM_X")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 4)); cout << "Number of atomic coordinates = " << tmp << endl; m_atomPos = readArrayVec(in, tmp); m_coordSets.push_back(m_atomPos); } else if (Core::contains(key, "OVERLAP_MATRIX")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 6)); cout << "Size of lower half triangle of overlap matrix = " << tmp << endl; readOverlapMatrix(in, tmp); } else if (Core::contains(key, "EIGENVECTORS")) { // For large molecules the Eigenvectors counter overflows to [*****] // So just use the square of the m_atomIndex array // QString tmp = key.mid(key.indexOf('[')+1, 6); cout << "Size of eigen vectors matrix = " << m_atomIndex.size() * m_atomIndex.size() << endl; readEigenVectors(in, static_cast(m_atomIndex.size() * m_atomIndex.size())); } else if (Core::contains(key, "TOTAL_DENSITY_MATRIX")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 6)); cout << "Size of lower half triangle of density matrix = " << tmp << endl; readDensityMatrix(in, tmp); } else if (Core::contains(key, "VIB._FREQ")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 6)); readVibrationFrequencies(in, tmp); } else if (Core::contains(key, "VIB._T_DIP")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 6)); readVibrationIntensities(in, tmp); } else if (Core::contains(key, "NORMAL_MODES")) { int tmp = Core::lexicalCast(key.substr(key.find('[') + 1, 6)); readNormalModes(in, tmp); } } void MopacAux::load(SlaterSet* basis) { if (m_atomPos.size() == 0) { cout << "No atoms found in .aux file. Bailing out." << endl; // basis->setIsValid(false); return; } // Now load up our basis set basis->addSlaterIndices(m_atomIndex); basis->addSlaterTypes(m_atomSym); basis->addZetas(m_zeta); basis->addPQNs(m_pqn); basis->setElectronCount(m_electrons); basis->addOverlapMatrix(m_overlap); basis->addEigenVectors(m_eigenVectors); basis->addDensityMatrix(m_density); } vector MopacAux::readArrayElements(std::istream& in, unsigned int n) { vector tmp; while (tmp.size() < n) { string line; getline(in, line); vector list = Core::split(line, ' '); for (auto& i : list) { tmp.push_back( static_cast(Core::Elements::atomicNumberFromSymbol(i))); } } return tmp; } vector MopacAux::readArrayI(std::istream& in, unsigned int n) { vector tmp; while (tmp.size() < n) { string line; getline(in, line); vector list = Core::split(line, ' '); for (auto& i : list) tmp.push_back(Core::lexicalCast(i)); } return tmp; } vector MopacAux::readArrayD(std::istream& in, unsigned int n) { vector tmp; while (tmp.size() < n) { string line; getline(in, line); vector list = Core::split(line, ' '); for (auto& i : list) tmp.push_back(Core::lexicalCast(i)); } return tmp; } vector MopacAux::readArraySym(std::istream& in, unsigned int n) { int type; vector tmp; while (tmp.size() < n) { string line; getline(in, line); vector list = Core::split(line, ' '); for (auto& i : list) { if (i == "S") type = SlaterSet::S; else if (i == "PX") type = SlaterSet::PX; else if (i == "PY") type = SlaterSet::PY; else if (i == "PZ") type = SlaterSet::PZ; else if (i == "X2") type = SlaterSet::X2; else if (i == "XZ") type = SlaterSet::XZ; else if (i == "Z2") type = SlaterSet::Z2; else if (i == "YZ") type = SlaterSet::YZ; else if (i == "XY") type = SlaterSet::XY; else type = SlaterSet::UU; tmp.push_back(type); } } return tmp; } vector MopacAux::readArrayVec(std::istream& in, unsigned int n) { vector tmp(n / 3); double* ptr = tmp[0].data(); unsigned int cnt = 0; while (cnt < n) { string line; getline(in, line); vector list = Core::split(line, ' '); for (auto& i : list) ptr[cnt++] = Core::lexicalCast(i); } return tmp; } bool MopacAux::readVibrationFrequencies(std::istream& in, unsigned int n) { vector tmp = readArrayD(in, n); m_frequencies.insert(m_frequencies.end(), tmp.begin(), tmp.end()); return true; } bool MopacAux::readVibrationIntensities(std::istream& in, unsigned int n) { vector tmp = readArrayD(in, n); m_irIntensities.insert(m_irIntensities.end(), tmp.begin(), tmp.end()); return true; } bool MopacAux::readNormalModes(std::istream& in, unsigned int n) { vector tmp = readArrayVec(in, n); m_normalModes.insert(m_normalModes.end(), tmp.begin(), tmp.end()); return true; } bool MopacAux::readOverlapMatrix(std::istream& in, unsigned int n) { m_overlap.resize(m_zeta.size(), m_zeta.size()); unsigned int cnt = 0; unsigned int i = 0, j = 0; unsigned int f = 1; // Skip the first comment line... string line; getline(in, line); while (cnt < n) { getline(in, line); vector list = Core::split(line, ' '); for (auto& k : list) { // m_overlap.part()(i, j) = list.at(k).toDouble(); m_overlap(i, j) = m_overlap(j, i) = Core::lexicalCast(k); ++i; ++cnt; if (i == f) { // We need to move down to the next row and increment f - lower tri i = 0; ++f; ++j; } } } return true; } bool MopacAux::readEigenVectors(std::istream& in, unsigned int n) { m_eigenVectors.resize(m_zeta.size(), m_zeta.size()); unsigned int cnt = 0; unsigned int i = 0, j = 0; while (cnt < n) { string line; getline(in, line); vector list = Core::split(line, ' '); for (auto& k : list) { m_eigenVectors(i, j) = Core::lexicalCast(k); ++i; ++cnt; if (i == m_zeta.size()) { // We need to move down to the next row and increment f - lower tri i = 0; ++j; } } } return true; } bool MopacAux::readDensityMatrix(std::istream& in, unsigned int n) { m_density.resize(m_zeta.size(), m_zeta.size()); unsigned int cnt = 0; unsigned int i = 0, j = 0; unsigned int f = 1; // Skip the first comment line... string line; getline(in, line); while (cnt < n) { getline(in, line); vector list = Core::split(line, ' '); for (auto& k : list) { // m_overlap.part()(i, j) = list.at(k).toDouble(); m_density(i, j) = m_density(j, i) = Core::lexicalCast(k); ++i; ++cnt; if (i == f) { // We need to move down to the next row and increment f - lower tri i = 0; ++f; ++j; } } } return true; } void MopacAux::outputAll() { cout << "Shell mappings:\n"; for (unsigned int i = 0; i < m_shellTypes.size(); ++i) cout << i << ": type = " << m_shellTypes.at(i) << ", number = " << m_shellNums.at(i) << ", atom = " << m_shelltoAtom.at(i) << endl; cout << "MO coefficients:\n"; for (double m_MOcoeff : m_MOcoeffs) cout << m_MOcoeff << "\t"; cout << endl; } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/mopacaux.h000066400000000000000000000063721506155467400221650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_MOPACAUX_H #define AVOGADRO_QUANTUMIO_MOPACAUX_H #include "avogadroquantumioexport.h" #include #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT MopacAux : public Io::FileFormat { public: MopacAux(); ~MopacAux() override; void outputAll(); Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new MopacAux; } std::string identifier() const override { return "Avogadro: MOPAC"; } std::string name() const override { return "MOPAC AUX"; } std::string description() const override { return "MOPAC AUX file format."; } std::string specificationUrl() const override { return "http://openmopac.net/manual/auxiliary.html"; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not write out MOPAC AUX files. return false; } private: void processLine(std::istream& in); void load(Core::SlaterSet* basis); std::vector readArrayElements(std::istream& in, unsigned int n); std::vector readArrayI(std::istream& in, unsigned int n); std::vector readArrayD(std::istream& in, unsigned int n); std::vector readArraySym(std::istream& in, unsigned int n); std::vector readArrayVec(std::istream& in, unsigned int n); bool readOverlapMatrix(std::istream& in, unsigned int n); bool readEigenVectors(std::istream& in, unsigned int n); bool readDensityMatrix(std::istream& in, unsigned int n); bool readVibrationFrequencies(std::istream& in, unsigned int n); bool readVibrationIntensities(std::istream& in, unsigned int n); bool readNormalModes(std::istream& in, unsigned int n); int m_electrons; int m_charge = 0; int m_spin = 1; Vector3 m_dipoleMoment; std::vector m_partialCharges; double m_heatOfFormation; double m_area; double m_volume; std::vector m_shellTypes; std::vector m_shellNums; std::vector m_shelltoAtom; std::vector m_c; std::vector m_csp; std::vector m_orbitalEnergy; std::vector m_MOcoeffs; std::vector m_atomIndex; std::vector m_atomSym; std::vector m_atomNums; std::vector m_zeta; std::vector m_pqn; std::vector m_atomPos; std::vector> m_coordSets; std::vector m_frequencies; std::vector m_irIntensities; std::vector m_normalModes; Eigen::MatrixXd m_overlap; /// Overlap matrix Eigen::MatrixXd m_eigenVectors; Eigen::MatrixXd m_density; /// Total density matrix }; } // End namespace QuantumIO } // End namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/nwchemjson.cpp000066400000000000000000000256301506155467400230540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "nwchemjson.h" #include #include #include #include #include #include #include #include namespace Avogadro::QuantumIO { using std::string; using std::vector; using nlohmann::json; using Core::Array; using Core::Atom; using Core::GaussianSet; using Core::Molecule; using Core::split; NWChemJson::NWChemJson() {} NWChemJson::~NWChemJson() {} bool NWChemJson::read(std::istream& file, Molecule& molecule) { json root; try { file >> root; } catch (json::parse_error& e) { appendError("Error parsing JSON: " + string(e.what())); return false; } if (!root.is_object()) { appendError("Error: Input is not a JSON object."); return false; } if (root.find("simulation") == root.end()) { appendError("Error: no \"simulation\" key found."); return false; } json simulation = root["simulation"]; if (simulation.find("calculations") == simulation.end() || !simulation["calculations"].is_array()) { appendError("Error: no \"calculations\" array found."); return false; } // Scan the calculations array for calculationSetup.molecule objects. json calculations = simulation["calculations"]; // Iterate through the objects in the array, and print out any molecules. json moleculeArray = json::array(); json basisSetArray = json::array(); json calculationVib; json molecularOrbitals; int numberOfElectrons = 0; string theory; string xcFunctional; for (const auto& calcObj : calculations) { if (calcObj.is_object()) { string calcType = calcObj.value("calculationType", ""); // Store the last vibrational frequencies calculation object. if (calcType == "vibrationalModes") calculationVib = calcObj; json calcSetup = calcObj.value("calculationSetup", json()); json calcMol = calcSetup.value("molecule", json()); numberOfElectrons = calcSetup.value("numberOfElectrons", -1); if (calcSetup.count("exchangeCorrelationFunctional") && calcSetup["exchangeCorrelationFunctional"].is_array() && !calcSetup["exchangeCorrelationFunctional"].empty()) { json functional = calcSetup["exchangeCorrelationFunctional"][0]; if (functional.is_object()) { xcFunctional = functional.value("xcName", ""); } if (xcFunctional == "B3LYP Method XC Potential") { xcFunctional = "b3lyp"; } } if (calcSetup.count("waveFunctionTheory")) { theory = calcSetup["waveFunctionTheory"].get(); if (theory == "Density Functional Theory") { theory = "dft"; } } if (!calcMol.is_null() && calcMol.is_object()) moleculeArray.push_back(calcMol); json basisSet = calcSetup.value("basisSet", json()); if (!basisSet.is_null() && basisSet.is_object()) basisSetArray.push_back(basisSet); json calcResults = calcObj.value("calculationResults", json()); calcMol = calcResults.value("molecule", json()); if (!calcMol.is_null() && calcMol.is_object()) moleculeArray.push_back(calcMol); // There is currently one id for all, just get the last one we find. if (calcResults.count("molecularOrbitals") && calcResults["molecularOrbitals"].is_object()) { molecularOrbitals = calcResults["molecularOrbitals"]; } } } // For now, we are just grabbing the "last" molecule, and using that. This // needs more complex logic to step through the file and do it properly. json atoms; if (!moleculeArray.empty()) { json finalMol = moleculeArray.back(); atoms = finalMol.value("atoms", json()); } if (atoms.is_array()) { for (auto jsonAtom : atoms) { if (jsonAtom.is_null() || !jsonAtom.is_object()) continue; Atom a = molecule.addAtom( static_cast(jsonAtom.value("elementNumber", 0))); json pos = jsonAtom["cartesianCoordinates"]["value"]; Vector3 position; if (pos.is_array() && pos.size() >= 3) position = Vector3(pos[0], pos[1], pos[2]); string units = jsonAtom["cartesianCoordinates"]["units"]; if (units == "bohr") position *= BOHR_TO_ANGSTROM_D; a.setPosition3d(position); } } // Perceive bonds for the molecule. molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); // Add in the electronic structure information if available. if (molecularOrbitals.is_object() && molecularOrbitals["atomicOrbitalDescriptions"].is_array()) { json basisSet; if (!basisSetArray.empty()) basisSet = basisSetArray.back(); json orbDesc = molecularOrbitals.value("atomicOrbitalDescriptions", json()); // Figure out the mapping of basis set to molecular orbitals. Array atomNumber; Array atomSymbol; for (auto& i : orbDesc) { string desc = i; vector parts = split(desc, ' '); assert(parts.size() == 3); int num = Core::lexicalCast(parts[0]); if (atomNumber.size() > 0 && atomNumber.back() == num) continue; atomNumber.push_back(num); atomSymbol.push_back(parts[1]); } // Now create the structure, and expand out the orbitals. auto* basis = new GaussianSet; basis->setMolecule(&molecule); string basisSetName; for (size_t i = 0; i < atomSymbol.size(); ++i) { string symbol = atomSymbol[i]; json basisFunctions = basisSet.value("basisFunctions", json()); json currentFunction; for (auto& basisFunction : basisFunctions) { currentFunction = basisFunction; string elementType; if (currentFunction.count("elementLabel")) elementType = currentFunction["elementLabel"].get(); else if (currentFunction.count("elementType")) elementType = currentFunction["elementType"].get(); if (elementType == symbol) break; currentFunction = json(); } if (currentFunction.is_null()) break; if (currentFunction.count("basisSetName")) { if (basisSetName.empty()) { basisSetName = currentFunction["basisSetName"].get(); } else if (basisSetName != currentFunction["basisSetName"]) { basisSetName = "Custom"; } } json contraction = currentFunction.value("basisSetContraction", json()); bool spherical = currentFunction.value("basisSetHarmonicType", "") == "spherical"; for (auto contractionShell : contraction) { string shellType; if (contractionShell.count("basisSetShell")) shellType = contractionShell["basisSetShell"].get(); else if (contractionShell.count("basisSetShellType")) shellType = contractionShell["basisSetShellType"].get(); json exponent = contractionShell.value("basisSetExponent", json()); json coefficient = contractionShell.value("basisSetCoefficient", json()); assert(exponent.size() == coefficient.size()); GaussianSet::orbital type = GaussianSet::UU; if (shellType == "s") type = GaussianSet::S; else if (shellType == "p") type = GaussianSet::P; else if (shellType == "d" && spherical) type = GaussianSet::D5; else if (shellType == "d") type = GaussianSet::D; else if (shellType == "f" && spherical) type = GaussianSet::F7; else if (shellType == "f") type = GaussianSet::F; if (type != GaussianSet::UU) { int b = basis->addBasis(i, type); for (size_t k = 0; k < exponent.size() && k < coefficient.size(); ++k) { basis->addGto(b, coefficient[k], exponent[k]); } } } } // Now to add the molecular orbital coefficients. json moCoeffs = molecularOrbitals.value("molecularOrbital", json()); vector coeffArray; vector energyArray; vector occArray; vector numArray; for (auto currentMO : moCoeffs) { json coeff = currentMO.value("moCoefficients", json()); for (auto& j : coeff) coeffArray.push_back(j); if (currentMO.count("orbitalEnergy")) { energyArray.push_back(currentMO["orbitalEnergy"].value("value", 0.0)); } if (currentMO.count("orbitalOccupancy")) { occArray.push_back( static_cast(currentMO["orbitalOccupancy"])); } if (currentMO.count("orbitalNumber")) { numArray.push_back( static_cast(currentMO["orbitalNumber"])); } } basis->setMolecularOrbitals(coeffArray); basis->setMolecularOrbitalEnergy(energyArray); basis->setMolecularOrbitalOccupancy(occArray); basis->setMolecularOrbitalNumber(numArray); basis->setElectronCount(numberOfElectrons); basis->setFunctionalName(xcFunctional); basis->setName(basisSetName); basis->setTheoryName(theory); molecule.setBasisSet(basis); } // Now to see if there was a vibrational frequencies calculation. if (!calculationVib.is_null() && calculationVib.is_object()) { json normalModes = calculationVib.value("calculationResults", json()) .value("normalModes", json()); if (!normalModes.is_null() && normalModes.is_array()) { Array frequencies; Array intensities; Array> Lx; for (const auto& mode : normalModes) { frequencies.push_back( mode.value("normalModeFrequency", json()).value("value", 0.0)); intensities.push_back(mode.value("normalModeInfraRedIntensity", json()) .value("value", 0.0)); json lx = mode.value("normalModeVector", json()).value("value", json()); if (!lx.empty() && lx.is_array()) { Array modeLx; modeLx.resize(lx.size() / 3); for (size_t k = 0; k < lx.size(); ++k) modeLx[k / 3][k % 3] = lx[k]; Lx.push_back(modeLx); } } molecule.setVibrationFrequencies(frequencies); molecule.setVibrationIRIntensities(intensities); molecule.setVibrationLx(Lx); } } return true; } bool NWChemJson::write(std::ostream&, const Molecule&) { return false; } vector NWChemJson::fileExtensions() const { vector ext; ext.emplace_back("nwjson"); return ext; } vector NWChemJson::mimeTypes() const { vector mime; mime.emplace_back("chemical/x-nwjson"); return mime; } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/nwchemjson.h000066400000000000000000000030551506155467400225160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_NWCHEMJSON_H #define AVOGADRO_QUANTUMIO_NWCHEMJSON_H #include "avogadroquantumioexport.h" #include namespace Avogadro { namespace QuantumIO { /** * @class NWChemJson nwchemjson.h * @brief Implementation of the NWChem JSON format. * @author Marcus D. Hanwell */ class AVOGADROQUANTUMIO_EXPORT NWChemJson : public Io::FileFormat { public: NWChemJson(); ~NWChemJson() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new NWChemJson; } std::string identifier() const override { return "Avogadro: NWCHEMJSON"; } std::string name() const override { return "NWChem JSON"; } std::string description() const override { return "TODO: Describe the format."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override; }; } // namespace QuantumIO } // namespace Avogadro #endif // AVOGADRO_IO_NWCHEMJSON_H avogadrolibs-1.101.0/avogadro/quantumio/nwchemlog.cpp000066400000000000000000000124541506155467400226640ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "nwchemlog.h" #include #include #include #include using std::string; using std::vector; namespace Avogadro::QuantumIO { using Core::Atom; NWChemLog::NWChemLog() {} NWChemLog::~NWChemLog() {} std::vector NWChemLog::fileExtensions() const { std::vector extensions; extensions.emplace_back("nwchem"); return extensions; } std::vector NWChemLog::mimeTypes() const { return std::vector(); } bool NWChemLog::read(std::istream& in, Core::Molecule& molecule) { // Read the log file line by line, most sections are terminated by an empty // line, so they should be retained. while (!in.eof()) processLine(in, molecule); if (0 == molecule.atomCount()) { appendError("Could not find any atomic coordinates! Are you sure this is " "an NWChem output file?"); return false; } if (m_frequencies.size() > 0 && m_frequencies.size() == m_Lx.size() && m_frequencies.size() == m_intensities.size()) { molecule.setVibrationFrequencies(m_frequencies); molecule.setVibrationIRIntensities(m_intensities); molecule.setVibrationLx(m_Lx); } // GaussianSet *basis = new GaussianSet; // Do simple bond perception. molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); return true; } void NWChemLog::processLine(std::istream& in, Core::Molecule& mol) { // First truncate the line, remove trailing white space and check string line; if (!getline(in, line) || Core::trimmed(line).empty()) return; string key = Core::trimmed(line); // Big switch statement checking for various things we are interested in if (Core::contains(key, "Output coordinates")) { if (mol.atomCount()) mol.clearAtoms(); readAtoms(in, mol); } else if (Core::contains(key, "P.Frequency")) { readFrequencies(line, in, mol); } else if (Core::contains(key, "Projected Infra")) { readIntensities(in, mol); } } void NWChemLog::readAtoms(std::istream& in, Core::Molecule& mol) { string line; // Skip the next three lines, headers, blanks... for (int i = 0; i < 3; ++i) if (!getline(in, line)) return; while (true) { if (!getline(in, line)) return; vector parts = Core::split(line, ' '); // Keep going until the expected number of components is not seen. if (parts.size() != 6) break; unsigned char element = Core::Elements::atomicNumberFromSymbol(parts[1]); if (element == Avogadro::InvalidElement) { appendError("Invalid element encountered: " + parts[1]); return; } Vector3 p; for (int i = 0; i < 3; ++i) { bool ok = false; p[i] = Core::lexicalCast(parts[i + 3], ok); if (!ok) { appendError("Couldn't convert coordinate component to double: " + parts[i + 3]); return; } } Core::Atom a = mol.addAtom(element); a.setPosition3d(p); } } void NWChemLog::readFrequencies(const std::string& firstLine, std::istream& in, Core::Molecule&) { string line = firstLine; bool ok = false; vector parts = Core::split(firstLine, ' '); if (parts.size() < 2) return; vector frequencies; for (size_t i = 1; i < parts.size(); ++i) frequencies.push_back(Core::lexicalCast(parts[i], ok)); if (!ok) { appendError("Error reading frequencies: " + firstLine); return; } // Skip the blank line after the frequencies. if (!getline(in, line)) return; if (!getline(in, line)) return; parts = Core::split(line, ' '); if (parts.size() < 2) return; vector> cols; cols.resize(parts.size() - 1); // Main block of numbers. while (parts.size() >= 2) { for (size_t i = 1; i < parts.size(); ++i) { cols[i - 1].push_back(Core::lexicalCast(parts[i], ok)); if (!ok) { appendError("Couldn't convert " + parts[i] + " to double."); return; } } if (!getline(in, line)) return; parts = Core::split(line, ' '); } for (size_t i = 0; i < frequencies.size(); ++i) { m_frequencies.push_back(frequencies[i]); Core::Array Lx; for (size_t j = 0; j < cols[i].size(); j += 3) { Lx.push_back(Vector3(cols[i][j + 0], cols[i][j + 1], cols[i][j + 2])); } m_Lx.push_back(Lx); } } void NWChemLog::readIntensities(std::istream& in, Core::Molecule&) { string line; bool ok = false; // Skip the next two lines, headers, blanks... for (int i = 0; i < 2; ++i) if (!getline(in, line)) return; while (true) { if (!getline(in, line)) return; vector parts = Core::split(line, ' '); // Keep going until the expected number of components is not seen. if (parts.size() != 7) break; m_intensities.push_back(Core::lexicalCast(parts[5], ok)); if (!ok) { appendError("Couldn't convert " + parts[5] + " to double."); return; } } } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/nwchemlog.h000066400000000000000000000040701506155467400223240ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_NWCHEMLOG_H #define AVOGADRO_QUANTUMIO_NWCHEMLOG_H #include "avogadroquantumioexport.h" #include #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT NWChemLog : public Io::FileFormat { public: NWChemLog(); ~NWChemLog() override; void outputAll(); Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new NWChemLog; } std::string identifier() const override { return "Avogadro: NWChem"; } std::string name() const override { return "NWChem Log"; } std::string description() const override { return "NWChem log file format."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not write out NWChem log files. return false; } private: void processLine(std::istream& in, Core::Molecule& mol); // Read the atoms, and their geometry. void readAtoms(std::istream& in, Core::Molecule& mol); // Read the projected frequencies. void readFrequencies(const std::string& line, std::istream& in, Core::Molecule& mol); // Read the projected frequency intensities. void readIntensities(std::istream& in, Core::Molecule& mol); Core::Array m_frequencies; Core::Array m_intensities; Core::Array> m_Lx; }; } // End namespace QuantumIO } // End namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/orca.cpp000066400000000000000000001142561506155467400216300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "orca.h" #include #include #include #include #include #include #include using std::regex; using std::string; using std::vector; namespace Avogadro::QuantumIO { using Core::Array; using Core::Atom; using Core::GaussianSet; ORCAOutput::ORCAOutput() {} ORCAOutput::~ORCAOutput() {} std::vector ORCAOutput::fileExtensions() const { std::vector extensions; extensions.emplace_back("orca"); return extensions; } std::vector ORCAOutput::mimeTypes() const { return std::vector(); } bool ORCAOutput::read(std::istream& in, Core::Molecule& molecule) { // Read the log file line by line auto* basis = new GaussianSet; while (!in.eof()) processLine(in, basis); // Set up the molecule int nAtom = 0; for (unsigned int i = 0; i < m_atomNums.size(); i++) { Vector3 pos = m_atomPos[i] * BOHR_TO_ANGSTROM; molecule.addAtom(static_cast(m_atomNums[nAtom++]), pos); } if (0 == molecule.atomCount()) { appendError("Could not find any atomic coordinates! Are you sure this is " "an ORCA output file?"); return false; } // this should be the final coordinate set (e.g. the optimized geometry) molecule.setCoordinate3d(molecule.atomPositions3d(), 0); if (m_coordSets.size() > 1) { for (unsigned int i = 0; i < m_coordSets.size(); i++) { Array positions; positions.reserve(molecule.atomCount()); for (size_t j = 0; j < molecule.atomCount(); ++j) { positions.push_back(m_coordSets[i][j] * BOHR_TO_ANGSTROM); } molecule.setCoordinate3d(positions, i + 1); } } // guess bonds and bond orders molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); if (m_frequencies.size() > 0 && m_frequencies.size() == m_vibDisplacements.size() && m_frequencies.size() == m_IRintensities.size()) { molecule.setVibrationFrequencies(m_frequencies); molecule.setVibrationIRIntensities(m_IRintensities); molecule.setVibrationLx(m_vibDisplacements); if (m_RamanIntensities.size()) molecule.setVibrationRamanIntensities(m_RamanIntensities); } if (m_electronicTransitions.size() > 0 && m_electronicTransitions.size() == m_electronicIntensities.size()) { MatrixX electronicData(m_electronicTransitions.size(), 2); for (size_t i = 0; i < m_electronicTransitions.size(); ++i) { electronicData(i, 0) = m_electronicTransitions[i]; electronicData(i, 1) = m_electronicIntensities[i]; } molecule.setSpectra("Electronic", electronicData); if (m_electronicRotations.size() == m_electronicTransitions.size()) { MatrixX electronicRotations(m_electronicTransitions.size(), 2); for (size_t i = 0; i < m_electronicTransitions.size(); ++i) { electronicRotations(i, 0) = m_electronicTransitions[i]; electronicRotations(i, 1) = m_electronicRotations[i]; } molecule.setSpectra("CircularDichroism", electronicRotations); } } if (m_nmrShifts.size() > 0) { MatrixX nmrData(m_nmrShifts.size(), 2); // nmr_shifts has an entry for every atom even if not computed for (size_t i = 0; i < m_nmrShifts.size(); ++i) { nmrData(i, 0) = m_nmrShifts[i]; nmrData(i, 1) = 1.0; } molecule.setSpectra("NMR", nmrData); } // check bonds from calculated bond orders if (m_bondOrders.size() > 0) { for (unsigned int i = 0; i < m_bondOrders.size(); i++) { // m_bondOrders[i][0] is the first atom // m_bondOrders[i][1] is the second atom // m_bondOrders[i][2] is the bond order if (m_bondOrders[i].size() > 2) { auto bond = molecule.bond(m_bondOrders[i][0], m_bondOrders[i][1]); if (bond.isValid() && bond.order() != m_bondOrders[i][2]) { // if the bond order is different, change it bond.setOrder(static_cast(m_bondOrders[i][2])); } } } } molecule.setBasisSet(basis); basis->setMolecule(&molecule); load(basis); // we have to do a few things *after* any modifications to bonds / atoms // because those automatically clear partial charges and data // add the partial charges if (m_partialCharges.size() > 0) { for (auto it = m_partialCharges.begin(); it != m_partialCharges.end(); ++it) { molecule.setPartialCharges(it->first, it->second); } } molecule.setData("totalCharge", m_charge); molecule.setData("totalSpinMultiplicity", m_spin); molecule.setData("dipoleMoment", m_dipoleMoment); molecule.setData("totalEnergy", m_totalEnergy); if (m_energies.size() > 1) molecule.setData("energies", m_energies); return true; } void ORCAOutput::processLine(std::istream& in, [[maybe_unused]] GaussianSet* basis) { // First truncate the line, remove trailing white space and check string line; if (!getline(in, line) || Core::trimmed(line).empty()) return; string key = Core::trimmed(line); vector list; int nGTOs = 0; [[maybe_unused]] float vibScaling = 1.0f; if (Core::contains(key, "CARTESIAN COORDINATES (A.U.)")) { m_coordFactor = 1.; // leave the coords in BOHR .... m_currentMode = Atoms; // if there are any current coordinates, push them back if (m_atomPos.size() > 0) { m_coordSets.push_back(m_atomPos); } m_atomPos.clear(); m_atomNums.clear(); m_atomLabel.clear(); getline(in, key); // skip ----- line getline(in, key); // column titles } else if (Core::contains(key, "BASIS SET INFORMATION")) { if (!Core::contains(key, "AUXILIARY")) { // skip auxiliary basis set infos m_currentMode = GTO; getline(in, key); // skip ----- line // Number of groups of distinct atoms getline(in, key); list = Core::split(key, ' '); if (list.size() > 3) { m_nGroups = Core::lexicalCast(list[2]); } else { return; } getline(in, key); // skip blank line for (int i = 0; i < m_nGroups; ++i) { getline(in, key); // skip group information } getline(in, key); // skip blank line for (unsigned int i = 0; i < m_atomNums.size(); ++i) { getline(in, key); // skip group information } // now skip // blank line // ---------------------------- // # Basis set for element : x // ---------------------------- // blank line for (unsigned int i = 0; i < 6; ++i) { getline(in, key); } } } else if (Core::contains(key, "Total Charge")) { list = Core::split(key, ' '); if (list.size() > 4) m_charge = Core::lexicalCast(list[4]); } else if (Core::contains(key, "Multiplicity")) { list = Core::split(key, ' '); if (list.size() > 3) m_spin = Core::lexicalCast(list[3]); } else if (Core::contains(key, "FINAL SINGLE POINT ENERGY")) { list = Core::split(key, ' '); if (list.size() > 4) m_totalEnergy = Core::lexicalCast(list[4]); m_energies.push_back(m_totalEnergy); } else if (Core::contains(key, "TOTAL NUMBER OF BASIS SET")) { m_currentMode = NotParsing; // no longer reading GTOs } else if (Core::contains(key, "NUMBER OF CARTESIAN GAUSSIAN BASIS")) { m_currentMode = NotParsing; // no longer reading GTOs } else if (Core::contains(key, "Number of Electrons")) { list = Core::split(key, ' '); m_electrons = Core::lexicalCast(list[5]); } else if (Core::contains(key, "Total Dipole Moment")) { list = Core::split(key, ' '); m_dipoleMoment = Eigen::Vector3d(Core::lexicalCast(list[4]), Core::lexicalCast(list[5]), Core::lexicalCast(list[6])); // convert from atomic units to Debye // e.g. https://en.wikipedia.org/wiki/Debye m_dipoleMoment *= 2.54174628; } else if (Core::contains(key, "Mayer bond orders")) { m_currentMode = BondOrders; // starts at the next line } else if (Core::contains( key, "ABSORPTION SPECTRUM VIA TRANSITION ELECTRIC DIPOLE MOMENTS")) { m_currentMode = Electronic; for (int i = 0; i < 4; ++i) { getline(in, key); // skip header } // starts at the next line } else if (Core::contains(key, "CD SPECTRUM") && !Core::contains(key, "TRANSITION VELOCITY DIPOLE")) { m_currentMode = ECD; for (int i = 0; i < 4; ++i) { getline(in, key); // skip header } } else if (Core::contains(key, "ORBITAL ENERGIES")) { m_currentMode = OrbitalEnergies; getline(in, key); // skip ------------ getline(in, key); // check if SPIN UP ORBITALS are present if (Core::contains(key, "SPIN UP ORBITALS")) { m_openShell = true; m_readBeta = false; } else { m_openShell = false; m_readBeta = false; } getline(in, key); // skip column titles } else if (Core::contains(key, "SPIN DOWN ORBITALS")) { m_currentMode = OrbitalEnergies; m_openShell = true; m_readBeta = true; getline(in, key); // skip column headers } else if (Core::contains(key, "MOLECULAR ORBITALS")) { m_currentMode = MO; getline(in, key); //------------ } else if (Core::contains(key, "HIRSHFELD ANALYSIS")) { m_currentMode = HirshfeldCharges; m_chargeType = "Hirshfeld"; for (unsigned int i = 0; i < 6; ++i) { getline(in, key); // skip header } } else if (Core::contains(key, "MBIS ANALYSIS")) { // MBIS analysis is similar to Hirshfeld, but with different headers m_currentMode = HirshfeldCharges; m_chargeType = "MBIS"; for (unsigned int i = 0; i < 9; ++i) { getline(in, key); // skip header } } else if (Core::contains(key, "CHELPG Charges")) { // similar to standard charges m_currentMode = Charges; m_chargeType = "CHELPG"; getline(in, key); // skip ------------ } else if (Core::contains(key, "RESP Charges")) { m_currentMode = Charges; m_chargeType = "RESP"; getline(in, key); // skip ------------ } else if (Core::contains(key, "ATOMIC CHARGES")) { m_currentMode = Charges; // figure out what type of charges we have list = Core::split(key, ' '); if (list.size() > 2) { m_chargeType = Core::trimmed(list[0]); // e.g. MULLIKEN or LOEWDIN } // lowercase everything except the first letter for (unsigned int i = 1; i < m_chargeType.size(); ++i) { m_chargeType[i] = tolower(m_chargeType[i]); } getline(in, key); // skip ------------ } else if (Core::contains(key, "VIBRATIONAL FREQUENCIES")) { m_currentMode = Frequencies; getline(in, key); // skip ------------ getline(in, key); // skip blank line getline(in, key); // scaling factor // Scaling factor for frequencies = 1.000000000 list = Core::split(key, ' '); if (list.size() > 6) vibScaling = Core::lexicalCast(list[5]); getline(in, key); // skip blank line } else if (Core::contains(key, "NORMAL MODES")) { m_currentMode = VibrationalModes; getline(in, key); // skip ------------ getline(in, key); // skip blank line getline(in, key); // skip comment getline(in, key); // skip more comments getline(in, key); // skip even more comment getline(in, key); // skip blank line } else if (Core::contains(key, "IR SPECTRUM")) { m_currentMode = IR; getline(in, key); // skip ------------ getline(in, key); // skip blank line getline(in, key); // skip column titles getline(in, key); // skip more column titles getline(in, key); // skip ------------ } else if (Core::contains(key, "RAMAN SPECTRUM")) { m_currentMode = Raman; getline(in, key); // skip ------------ getline(in, key); // skip blank line getline(in, key); // skip column titles getline(in, key); // skip ------------ } else if (Core::contains(key, "CHEMICAL SHIELDING SUMMARY (ppm)")) { m_currentMode = NMR; for (int i = 0; i < 4; ++i) { getline(in, key); // skip header } } else { vector> columns; unsigned int numColumns, numRows; numColumns = 0; numRows = 0; // parsing a line -- what mode are we in? switch (m_currentMode) { case Atoms: { if (key.empty()) break; list = Core::split(key, ' '); while (!key.empty()) { if (list.size() < 8) { break; } Eigen::Vector3d pos( Core::lexicalCast(list[5]) * m_coordFactor, Core::lexicalCast(list[6]) * m_coordFactor, Core::lexicalCast(list[7]) * m_coordFactor); unsigned char atomicNum = Core::Elements::atomicNumberFromSymbol(Core::trimmed(list[1])); m_atomNums.push_back(atomicNum); m_atomPos.push_back(pos); m_atomLabel.push_back(Core::trimmed(list[1])); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_currentMode = NotParsing; break; } case HirshfeldCharges: { // should start at the first atom if (key.empty()) break; Eigen::MatrixXd charges(m_atomNums.size(), 1); charges.setZero(); list = Core::split(key, ' '); while (!key.empty()) { if (list.size() < 4) { break; } // e.g. index atom charge spin // e.g. 0 O -0.714286 0.000 int atomIndex = Core::lexicalCast(list[0]); double charge = Core::lexicalCast(list[2]); charges(atomIndex, 0) = charge; getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_partialCharges[m_chargeType] = charges; m_currentMode = NotParsing; break; } case Charges: { // should start at the first atom if (key.empty()) break; Eigen::MatrixXd charges(m_atomNums.size(), 1); charges.setZero(); list = Core::split(key, ' '); while (!key.empty()) { if (list.size() != 4) { break; } // e.g. 0 O : -0.714286 int atomIndex = Core::lexicalCast(list[0]); double charge = Core::lexicalCast(list[3]); charges(atomIndex, 0) = charge; getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_partialCharges[m_chargeType] = charges; m_currentMode = NotParsing; break; } case BondOrders: { if (key.empty()) break; m_bondOrders.clear(); while (key[0] == 'B') { // @todo .. parse the bonds based on character position // e.g. B( 0-Ru, 1-C ) : 0.4881 B( 0-Ru, 4-C ) : 0.6050 Index firstAtom = Core::lexicalCast(key.substr(2, 3)); Index secondAtom = Core::lexicalCast(key.substr(9, 3)); double bondOrder = Core::lexicalCast(key.substr(18, 9)); if (bondOrder > 1.6) { std::vector bond; bond.push_back(static_cast(firstAtom)); bond.push_back(static_cast(secondAtom)); bond.push_back(static_cast(std::round(bondOrder))); m_bondOrders.push_back(bond); } if (key.size() > 54 && key[28] == 'B') { firstAtom = Core::lexicalCast(key.substr(30, 3)); secondAtom = Core::lexicalCast(key.substr(37, 3)); bondOrder = Core::lexicalCast(key.substr(46, 9)); if (bondOrder > 1.6) { std::vector bond; bond.push_back(static_cast(firstAtom)); bond.push_back(static_cast(secondAtom)); bond.push_back(static_cast(std::round(bondOrder))); m_bondOrders.push_back(bond); } } if (key.size() > 82 && key[56] == 'B') { firstAtom = Core::lexicalCast(key.substr(58, 3)); secondAtom = Core::lexicalCast(key.substr(65, 3)); bondOrder = Core::lexicalCast(key.substr(74, 9)); if (bondOrder > 1.6) { std::vector bond; bond.push_back(static_cast(firstAtom)); bond.push_back(static_cast(secondAtom)); bond.push_back(static_cast(std::round(bondOrder))); m_bondOrders.push_back(bond); } } getline(in, key); key = Core::trimmed(key); } m_currentMode = NotParsing; break; } case OrbitalEnergies: { if (key.empty()) break; // should start at the first orbital if (!m_readBeta) m_orbitalEnergy.clear(); else m_betaOrbitalEnergy.clear(); list = Core::split(key, ' '); while (!key.empty()) { if (list.size() != 4) { break; } // energy in Hartree in 3rd column in eV in 4th column double energy = Core::lexicalCast(list[3]); if (!m_readBeta) m_orbitalEnergy.push_back(energy); else m_betaOrbitalEnergy.push_back(energy); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_currentMode = NotParsing; break; } case Frequencies: { // should start at the first frequency - include zeros if (key.empty()) break; list = Core::split(key, ' '); while (!key.empty()) { // imaginary frequencies can have an additional comment: // ***imaginary mode*** if (list.size() != 3 && (list.size() != 5 || list[3] != "***imaginary" || list[4] != "mode***")) { break; } // e.g. 0: 0.00 cm**-1 double freq = Core::lexicalCast(list[1]); m_frequencies.push_back(freq); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } // okay, now set up the normal mode arrays m_vibDisplacements.resize(m_frequencies.size()); m_IRintensities.resize(m_frequencies.size()); // we don't bother with Raman, because that's less common for (unsigned int i = 0; i < m_frequencies.size(); i++) { m_IRintensities[i] = 0.0; m_vibDisplacements[i].resize(m_atomNums.size()); for (unsigned int j = 0; j < m_atomNums.size(); j++) m_vibDisplacements[i].push_back(Eigen::Vector3d()); } m_currentMode = NotParsing; break; } case VibrationalModes: { if (key.empty()) break; list = Core::split(key, ' '); vector modeIndex; bool invalid_index = false; while (!key.empty()) { // first we get a set of column numbers // e.g. 1 2 3 4 5 6 7 8 9 10 modeIndex.clear(); for (const auto& index_str : list) { auto index = Core::lexicalCast(index_str); if (index >= m_frequencies.size()) { invalid_index = true; break; } modeIndex.push_back(index); } // invalid column index if (invalid_index) break; // now we read the displacements .. there should be 3N lines // x,y,z for each atom getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); for (unsigned int i = 0; i < 3 * m_atomNums.size(); i++) { unsigned int atomIndex = i / 3; unsigned int coordIndex = i % 3; for (unsigned int j = 0; j < modeIndex.size(); j++) { m_vibDisplacements[modeIndex[j]][atomIndex][coordIndex] = Core::lexicalCast(list[j + 1]); } getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } } m_currentMode = NotParsing; break; } case IR: { if (key.empty()) break; list = Core::split(key, ' '); while (!key.empty()) { // e.g. 6: 1711.76 0.014736 74.47 0.002686 (-0.021704 0.027180 // 0.038427) if (list.size() < 7) { break; } // the first entry might be 5 or 6 because of removed rotations / // translations auto index = Core::lexicalCast(list[0]); // invalid index if (index >= m_frequencies.size()) break; double intensity = Core::lexicalCast(list[3]); m_IRintensities[index] = intensity; getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_currentMode = NotParsing; break; } case Raman: { if (key.empty()) break; list = Core::split(key, ' '); while (!key.empty()) { // e.g. 6: 76.62 0.000000 0.465517 if (list.size() != 4) { break; } // the first entry might be 5 or 6 because of removed rotations / // translations auto index = Core::lexicalCast(list[0]); // invalid index if (index >= m_frequencies.size()) break; if (m_RamanIntensities.empty()) { while (m_RamanIntensities.size() < index) { m_RamanIntensities.push_back(0.0); } } // index, frequency, activity, depolarization double activity = Core::lexicalCast(list[2]); m_RamanIntensities.push_back(activity); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_currentMode = NotParsing; break; } case Electronic: { if (key.empty()) break; list = Core::split(key, ' '); double wavenumbers; while (!key.empty()) { // should have 8 columns if (list.size() != 8) { getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); continue; // skip any spin-forbidden transitions } wavenumbers = Core::lexicalCast(list[1]); // convert to eV m_electronicTransitions.push_back(wavenumbers / 8065.544); m_electronicIntensities.push_back(Core::lexicalCast(list[3])); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); if (list.size() < 2) break; // hit the blank line } m_currentMode = NotParsing; break; } case ECD: { if (key.empty()) break; list = Core::split(key, ' '); [[maybe_unused]] double wavenumbers; while (!key.empty()) { // should have 7 columns if (list.size() != 7) { getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); continue; // skip any spin-forbidden transitions } wavenumbers = Core::lexicalCast(list[1]); // convert to eV // m_electronicTransitions.push_back(wavenumbers / 8065.544); m_electronicRotations.push_back(Core::lexicalCast(list[3])); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); if (list.size() < 2) break; // hit the blank line } m_currentMode = NotParsing; break; } case NMR: { if (key.empty()) break; list = Core::split(key, ' '); // default to filling m_nmrShifts with zeros m_nmrShifts.resize(m_atomNums.size(), 0.0); while (!key.empty()) { // should have 4 columns if (list.size() != 4) { break; } // e.g. 1 C 0.0000 0.0000 0.0000 0.0000 int atomIndex = Core::lexicalCast(list[0]); double shift = Core::lexicalCast(list[2]); // ignore the anisotropy for now m_nmrShifts[atomIndex] = shift; getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_currentMode = NotParsing; break; } case GTO: { // // should start at the first newGTO if (key.empty()) break; nGTOs = 0; list = Core::split(key, ' '); int nShells; // init all vectors etc. m_basisAtomLabel.clear(); m_orcaNumShells.resize(0); m_basisFunctions.resize(0); m_orcaShellTypes.resize(0); m_a.resize(0); m_c.resize(0); m_shellNums.resize(0); m_shellTypes.resize(0); m_shelltoAtom.resize(0); while (Core::trimmed(list[0]) == "NewGTO") { m_basisAtomLabel.push_back(Core::trimmed(list[1])); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); nShells = 0; m_basisFunctions.push_back( new std::vector*>); shellFunctions.resize(0); shellTypes.resize(0); while (Core::trimmed(list[0]) != "end;") { int nFunc = Core::lexicalCast(Core::trimmed(list[1])); shellTypes.push_back(orbitalIdx(Core::trimmed(list[0]))); shellFunctions.push_back(nFunc); m_basisFunctions.at(nGTOs)->push_back( new std::vector(nFunc)); for (int i = 0; i < nFunc; i++) { getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); m_basisFunctions.at(nGTOs)->at(nShells)->at(i).x() = Core::lexicalCast(list[1]); // exponent m_basisFunctions.at(nGTOs)->at(nShells)->at(i).y() = Core::lexicalCast(list[2]); // coeff } nShells++; getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); } m_orcaShellTypes.push_back( std::vector(shellTypes.size())); m_orcaShellTypes.at(nGTOs) = shellTypes; m_orcaNumShells.push_back(std::vector(shellFunctions.size())); m_orcaNumShells.at(nGTOs) = shellFunctions; nGTOs++; getline(in, key); getline(in, key); getline(in, key); key = Core::trimmed(key); list = Core::split(key, ' '); if (list.size() == 0) break; // unexpected structure - suppose no more NewGTOs } // create input for gaussian basisset int nBasis = nGTOs; int nAtoms = m_atomLabel.size(); m_currentAtom = 0; for (int i = 0; i < nAtoms; i++) { m_currentAtom++; for (int j = 0; j < nBasis; j++) { if (m_atomLabel.at(i) == m_basisAtomLabel.at(j)) { for (unsigned int k = 0; k < m_orcaNumShells.at(j).size(); k++) { for (int l = 0; l < m_orcaNumShells.at(j).at(k); l++) { m_a.push_back(m_basisFunctions.at(j)->at(k)->at(l).x()); m_c.push_back(m_basisFunctions.at(j)->at(k)->at(l).y()); } m_shellNums.push_back(m_orcaNumShells.at(j).at(k)); m_shellTypes.push_back(m_orcaShellTypes.at(j).at(k)); m_shelltoAtom.push_back(m_currentAtom); } break; } } } m_currentMode = NotParsing; break; } case MO: { m_MOcoeffs.clear(); // if the orbitals were punched multiple times m_orbitalEnergy.clear(); // we can get the energies here std::vector orcaOrbitals; while (!Core::trimmed(key).empty()) { // currently reading the sequence number getline(in, key); // energies list = Core::split(key, ' '); // convert these all to double and add to m_orbitalEnergy for (unsigned int i = 0; i < list.size(); i++) { // convert from Hartree to eV m_orbitalEnergy.push_back(Core::lexicalCast(list[i]) * 27.2114); } getline(in, key); // occupations getline(in, key); // skip ----------- getline(in, key); // now we've got coefficients // coefficients are optionally a -, one or two digits, a decimal // point, and then 6 digits or just one or two digits a decimal point // and then 6 digits we can use a regex to split the line regex rx("[-]?[0-9]{1,2}[.][0-9]{6}"); auto key_begin = std::sregex_iterator(key.begin(), key.end(), rx); auto key_end = std::sregex_iterator(); list.clear(); for (std::sregex_iterator i = key_begin; i != key_end; ++i) { list.push_back(i->str()); } numColumns = list.size(); columns.resize(numColumns); while (list.size() > 0) { // get the '2s' or '1dx2y2' piece from the line // so we can re-order the orbitals later std::vector pieces = Core::split(key, ' '); orcaOrbitals.push_back(pieces[1]); for (unsigned int i = 0; i < numColumns; ++i) { columns[i].push_back(Core::lexicalCast(list[i])); } getline(in, key); key_begin = std::sregex_iterator(key.begin(), key.end(), rx); key_end = std::sregex_iterator(); list.clear(); for (std::sregex_iterator i = key_begin; i != key_end; ++i) { list.push_back(i->str()); } if (list.size() != numColumns) break; } // ok, we've finished one batch of MO coeffs // now reorder the p orbitals from "orcaStyle" (pz, px,py) // to expected Avogadro (px,py,pz) std::size_t idx = 0; while (idx < orcaOrbitals.size()) { if (Core::contains(orcaOrbitals.at(idx), "pz")) { for (unsigned int i = 0; i < numColumns; i++) { if (idx + 1 >= columns[i].size()) break; std::swap(columns[i].at(idx), columns[i].at(idx + 1)); } idx++; for (unsigned int i = 0; i < numColumns; i++) { if (idx + 1 >= columns[i].size()) break; std::swap(columns[i].at(idx), columns[i].at(idx + 1)); } idx++; idx++; } else { idx++; } } // Now we need to re-order the MO coeffs, so we insert one MO at a // time for (unsigned int i = 0; i < numColumns; ++i) { numRows = columns[i].size(); for (unsigned int j = 0; j < numRows; ++j) { m_MOcoeffs.push_back(columns[i][j]); } } columns.clear(); orcaOrbitals.clear(); } // finished parsing MOs if (m_MOcoeffs.size() != numRows * numRows) { m_orcaSuccess = false; } m_numBasisFunctions = numRows; if (m_openShell) { // TODO: parse both alpha and beta orbitals m_BetaMOcoeffs.clear(); // if the orbitals were punched multiple times m_betaOrbitalEnergy.clear(); // we can get the energies here getline(in, key); while (!Core::trimmed(key).empty()) { // currently reading the sequence number getline(in, key); // energies list = Core::split(key, ' '); // convert these all to double and add to m_orbitalEnergy for (unsigned int i = 0; i < list.size(); i++) { // convert from Hartree to eV m_orbitalEnergy.push_back(Core::lexicalCast(list[i]) * 27.2114); } getline(in, key); // symmetries getline(in, key); // skip ----------- getline(in, key); // now we've got coefficients regex rx("[-]?[0-9]{1,2}[.][0-9]{6}"); auto key_begin = std::sregex_iterator(key.begin(), key.end(), rx); auto key_end = std::sregex_iterator(); list.clear(); for (std::sregex_iterator i = key_begin; i != key_end; ++i) { list.push_back(i->str()); } numColumns = list.size(); columns.resize(numColumns); while (list.size() > 0) { // get the '2s' or '1dx2y2' piece from the line // so we can re-order the orbitals later std::vector pieces = Core::split(key, ' '); orcaOrbitals.push_back(pieces[1]); // columns.resize(numColumns); for (unsigned int i = 0; i < numColumns; ++i) { columns[i].push_back(Core::lexicalCast(list[i])); } auto inner_key_begin = std::sregex_iterator(key.begin(), key.end(), rx); auto inner_key_end = std::sregex_iterator(); list.clear(); for (std::sregex_iterator i = inner_key_begin; i != inner_key_end; ++i) { list.push_back(i->str()); } if (list.size() != numColumns) break; } // ok, we've finished one batch of MO coeffs // now reorder the p orbitals from "orcaStyle" (pz, px,py) to // expected Avogadro (px,py,pz) std::size_t idx = 0; while (idx < orcaOrbitals.size()) { if (Core::contains(orcaOrbitals.at(idx), "pz")) { for (unsigned int i = 0; i < numColumns; i++) { if (idx + 1 >= columns[i].size()) break; std::swap(columns[i].at(idx), columns[i].at(idx + 1)); } idx++; for (unsigned int i = 0; i < numColumns; i++) { if (idx + 1 >= columns[i].size()) break; std::swap(columns[i].at(idx), columns[i].at(idx + 1)); } idx++; idx++; } else { idx++; } } // Now we need to re-order the MO coeffs, so we insert one MO at a // time for (unsigned int i = 0; i < numColumns; ++i) { numRows = columns[i].size(); for (unsigned int j = 0; j < numRows; ++j) { m_BetaMOcoeffs.push_back(columns[i][j]); } } columns.clear(); orcaOrbitals.clear(); if (Core::trimmed(key).empty()) getline(in, key); // skip the blank line after the MOs } // finished parsing 2nd. MOs if (m_MOcoeffs.size() != numRows * numRows) { m_orcaSuccess = false; } m_numBasisFunctions = numRows; } m_currentMode = NotParsing; break; } default:; } // end switch } // end if (mode) } void ORCAOutput::load(GaussianSet* basis) { // Now load up our basis set basis->setElectronCount(m_electrons); // Set up the GTO primitive counter, go through the shells and add them int nGTO = 0; int nSP = 0; // number of SP shells for (unsigned int i = 0; i < m_shellTypes.size(); ++i) { // Handle the SP case separately - this should possibly be a distinct type if (m_shellTypes.at(i) == GaussianSet::SP) { // SP orbital type - currently have to unroll into two shells int tmpGTO = nGTO; int s = basis->addBasis(m_shelltoAtom.at(i) - 1, GaussianSet::S); for (int j = 0; j < m_shellNums.at(i); ++j) { basis->addGto(s, m_c.at(nGTO), m_a.at(nGTO)); ++nGTO; } int p = basis->addBasis(m_shelltoAtom.at(i) - 1, GaussianSet::P); for (int j = 0; j < m_shellNums.at(i); ++j) { basis->addGto(p, m_csp.at(nSP), m_a.at(tmpGTO)); ++tmpGTO; ++nSP; } } else { int b = basis->addBasis(m_shelltoAtom.at(i) - 1, m_shellTypes.at(i)); for (int j = 0; j < m_shellNums.at(i); ++j) { basis->addGto(b, m_c.at(nGTO), m_a.at(nGTO)); ++nGTO; } } } // Now to load in the MO coefficients if (m_MOcoeffs.size()) basis->setMolecularOrbitals(m_MOcoeffs); if (m_BetaMOcoeffs.size()) basis->setMolecularOrbitals(m_BetaMOcoeffs, Core::BasisSet::Beta); if (m_orbitalEnergy.size()) basis->setMolecularOrbitalEnergy(m_orbitalEnergy); if (m_betaOrbitalEnergy.size()) basis->setMolecularOrbitalEnergy(m_betaOrbitalEnergy, Core::BasisSet::Beta); // TODO: set orbital symmetries m_homo = ceil(m_electrons / 2.0); basis->generateDensityMatrix(); } GaussianSet::orbital ORCAOutput::orbitalIdx(std::string txt) { if (txt == "S") return GaussianSet::S; if (txt == "SP") return GaussianSet::SP; if (txt == "P") return GaussianSet::P; if (txt == "D") return GaussianSet::D5; //// orca only uses Spherical - 5 d components if (txt == "D5") return GaussianSet::D5; if (txt == "F") return GaussianSet::F7; //// orca only uses Spherical - 7 f components if (txt == "F7") return GaussianSet::F7; if (txt == "G") return GaussianSet::G9; //// orca only uses Spherical - 9 g components if (txt == "G9") return GaussianSet::G9; if (txt == "H") return GaussianSet::H11; //// orca only uses Spherical - 11 h /// components if (txt == "H11") return GaussianSet::H11; if (txt == "I") return GaussianSet::I13; //// orca only uses Spherical - 13 i /// components if (txt == "I13") return GaussianSet::I13; return GaussianSet::UU; } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/orca.h000066400000000000000000000073061506155467400212720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_ORCA_H #define AVOGADRO_QUANTUMIO_ORCA_H #include "avogadroquantumioexport.h" #include #include #include #include #include namespace Avogadro { namespace QuantumIO { class AVOGADROQUANTUMIO_EXPORT ORCAOutput : public Io::FileFormat { public: ORCAOutput(); ~ORCAOutput() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new ORCAOutput; } std::string identifier() const override { return "Avogadro: Orca"; } std::string name() const override { return "Orca"; } std::string description() const override { return "Orca output format."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream&, const Core::Molecule&) override { // Empty, as we do not write out Orca output files. return false; } private: void outputAll(); void processLine(std::istream& in, Core::GaussianSet* basis); void load(Core::GaussianSet* basis); // OrcaStuff void orcaWarningMessage(const std::string& m); Core::GaussianSet::orbital orbitalIdx(std::string txt); bool m_orcaSuccess; std::vector m_atomLabel; std::vector m_basisAtomLabel; std::vector m_atomNums; std::vector m_atomPos; std::vector> m_coordSets; std::vector m_energies; Vector3 m_dipoleMoment; std::vector> m_bondOrders; std::vector shellFunctions; std::vector shellTypes; std::vector> m_orcaNumShells; std::vector> m_orcaShellTypes; int m_nGroups; std::vector*>*> m_basisFunctions; enum mode { Atoms, GTO, MO, OrbitalEnergies, Charges, HirshfeldCharges, Frequencies, VibrationalModes, IR, Raman, Electronic, ECD, // electronic circular dichroism NMR, BondOrders, NotParsing, Unrecognized }; double m_coordFactor; mode m_currentMode; int m_electrons; bool m_openShell; bool m_readBeta; int m_homo; int m_charge; int m_spin; double m_totalEnergy; int m_currentAtom; unsigned int m_numBasisFunctions; std::vector m_shellTypes; std::vector m_shellNums; std::vector m_shelltoAtom; std::vector m_a; std::vector m_c; std::vector m_csp; std::vector m_orbitalEnergy; std::vector m_MOcoeffs; std::vector m_betaOrbitalEnergy; std::vector m_BetaMOcoeffs; std::string m_chargeType; std::map m_partialCharges; Core::Array m_frequencies; Core::Array m_IRintensities; Core::Array m_RamanIntensities; Core::Array> m_vibDisplacements; Core::Array m_electronicTransitions; // in eV Core::Array m_electronicIntensities; Core::Array m_electronicRotations; // for CD Core::Array m_nmrShifts; // for NMR (in ppm) }; } // namespace QuantumIO } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/quantumio/qcschema.cpp000066400000000000000000000306111506155467400224600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "qcschema.h" #include #include #include #include #include #include #include #include using json = nlohmann::json; using std::string; namespace Avogadro::QuantumIO { using Core::Array; using Core::Atom; using Core::Elements; bool isNumericArray(json& j) { if (j.is_array() && j.size() > 0) { for (const auto& v : j) { if (!v.is_number()) { return false; } } return true; } return false; } bool isBooleanArray(json& j) { if (j.is_array() && j.size() > 0) { for (const auto& v : j) { if (!v.is_boolean()) { return false; } } return true; } return false; } QCSchema::QCSchema() {} QCSchema::~QCSchema() {} std::vector QCSchema::fileExtensions() const { std::vector extensions; extensions.emplace_back("qcjson"); return extensions; } std::vector QCSchema::mimeTypes() const { return std::vector(); } bool QCSchema::read(std::istream& in, Core::Molecule& molecule) { // This should be JSON so look for key attributes json root; try { in >> root; } catch (json::parse_error& e) { appendError("Error parsing JSON: " + string(e.what())); return false; } if (!root.is_object()) { appendError("Error: Input is not a JSON object."); return false; } // check for 'schema_name' if (root.find("schema_name") == root.end() || root["schema_name"].get() != "QC_JSON") { appendError("Error: Input is not a QC_JSON object."); return false; } // get the elements if (root.find("symbols") == root.end() || !root["symbols"].is_array()) { appendError("Error: no \"symbols\" array found."); return false; } json elements = root["symbols"]; unsigned char atomicNum(0); for (const auto& element : elements) { // convert from a string to atomic number std::string symbol = element.get(); atomicNum = Elements::atomicNumberFromSymbol(symbol); molecule.addAtom(atomicNum); } Index atomCount = molecule.atomCount(); if (atomCount == 0) { appendError("Error: no atoms found."); return false; } // look for geometry for coordinates // stored as a numeric array of all coordinates // as [atom1x, atom1y, atom1z, atom2x, atom2y, atom2z, ...] if (root.find("geometry") == root.end() || !root["geometry"].is_array()) { appendError("Error: no \"geometry\" array found."); return false; } json geometry = root["geometry"]; // check the length of the array if (geometry.size() != molecule.atomCount() * 3) { appendError("Error: \"geometry\" array has incorrect length."); return false; } for (Index i = 0; i < molecule.atomCount(); ++i) { auto a = molecule.atom(i); a.setPosition3d( Vector3(geometry[3 * i], geometry[3 * i + 1], geometry[3 * i + 2])); } // check for (optional) connectivity if (root.find("connectivity") != root.end() && root["connectivity"].is_array()) { // read the bonds and orders json connectivity = root["connectivity"]; // stored as an array of 3-value arrays start, end, order for (const auto& bond : connectivity) { Index start = bond[0].get() - 1; Index end = bond[1].get() - 1; unsigned char order = bond[2].get(); molecule.addBond(start, end, order); } } else { // perceive connectivity molecule.perceiveBondsSimple(); molecule.perceiveBondOrders(); } // check for optional comment / name if (root.find("comment") != root.end()) molecule.setData("name", root["comment"].get()); // check for molecular_charge and molecular_multiplicity if (root.find("molecular_charge") != root.end()) { molecule.setData("totalCharge", root["molecular_charge"].get()); } if (root.find("molecular_multiplicity") != root.end()) { molecule.setData("totalSpinMultiplicity", root["molecular_multiplicity"].get()); } // if the "properties object exists" look for properties if (root.find("properties") != root.end() && root["properties"].is_object()) { json properties = root["properties"]; if (properties.find("dipole_moment") != properties.end()) { // read the numeric array json dipole = properties["dipole_moment"]; if (dipole.size() == 3) { Core::Variant dipoleMoment(dipole[0].get(), dipole[1].get(), dipole[2].get()); molecule.setData("dipoleMoment", dipoleMoment); } } if (properties.find("partial_charges") != properties.end() && properties["partial_charges"].is_object()) { // keys are types, values are arrays of charges json partialCharges = properties["partial_charges"]; for (auto& kv : partialCharges.items()) { MatrixX charges(atomCount, 1); if (isNumericArray(kv.value()) && kv.value().size() == atomCount) { for (size_t i = 0; i < kv.value().size(); ++i) { charges(i, 0) = kv.value()[i]; } molecule.setPartialCharges(kv.key(), charges); } } } // energy // e.g. total_energy": { // "units": "Hartree", // "value": -26.173033542939 if (properties.find("total_energy") != properties.end() && properties["total_energy"].is_object()) { json totalEnergy = properties["total_energy"]; if (totalEnergy.find("value") != totalEnergy.end()) molecule.setData("totalEnergy", totalEnergy["value"].get()); } // trajectory or geometry optimization if (properties.find("geometry_sequence") != properties.end() && properties["geometry_sequence"].is_object()) { json sequence = properties["geometry_sequence"]; // energies and geometries // energies should be a numeric array if (sequence.find("energies") != sequence.end() && sequence["energies"].is_array()) { std::vector energies; for (unsigned int i = 0; i < sequence["energies"].size(); ++i) { energies.push_back(sequence["energies"][i].get()); } molecule.setData("energies", energies); } json coordSets = properties["geometry_sequence"]["geometries"]; if (coordSets.is_array() && coordSets.size()) { for (unsigned int i = 0; i < coordSets.size(); ++i) { Array setArray; json set = coordSets[i]; if (isNumericArray(set)) { for (unsigned int j = 0; j < set.size() / 3; ++j) { setArray.push_back( Vector3(set[3 * j], set[3 * j + 1], set[3 * j + 2])); } molecule.setCoordinate3d(setArray, i); } } // Make sure the first step is active once we are done loading the sets. molecule.setCoordinate3d(0); } } // vibrations if (properties.find("vibrations") != properties.end() && properties["vibrations"].is_object()) { json vib = properties["vibrations"]; Array freqs, irIntens, ramanIntens; Array> disps; // frequencies if (vib.find("frequencies") != vib.end() && vib["frequencies"].is_array()) { json frequencies = vib["frequencies"]; if (isNumericArray(frequencies)) { for (auto& frequency : frequencies) { freqs.push_back(static_cast(frequency)); } } } if (vib.find("intensities") != vib.end() && vib["intensities"].is_object()) { json ir = vib["intensities"]["IR"]; if (isNumericArray(ir)) { for (auto& i : ir) { irIntens.push_back(static_cast(i)); } } json raman = vib["intensities"]["raman"]; if (isNumericArray(raman)) { for (auto& i : raman) { ramanIntens.push_back(static_cast(i)); } } } json displacements = vib["displacement"]; if (displacements.is_array()) { for (auto arr : displacements) { if (isNumericArray(arr)) { Array mode; mode.resize(arr.size() / 3); double* ptr = &mode[0][0]; for (auto& j : arr) { *(ptr++) = static_cast(j); } disps.push_back(mode); } } } // sanity check // make sure these all have the same length Index size = freqs.size(); if (size > 0 && irIntens.size() == size && disps.size() == size) { molecule.setVibrationFrequencies(freqs); molecule.setVibrationIRIntensities(irIntens); molecule.setVibrationLx(disps); } // check to make sure raman intensities are not all zero bool allZero = true; for (auto& i : ramanIntens) { if (i != 0.0) { allZero = false; break; } } if (ramanIntens.size() == size && !allZero) molecule.setVibrationRamanIntensities(ramanIntens); } // excitation energies if (properties.find("excited_states") != properties.end() && properties["excited_states"].is_object()) { json excitedStates = properties["excited_states"]; // check units (defaults to nm) std::string units = "nm"; if (excitedStates.find("units") != excitedStates.end()) { units = excitedStates["units"].get(); } std::vector energies; std::vector intensities; // transition_energies if (excitedStates.find("transition_energies") != excitedStates.end() && excitedStates["transition_energies"].is_array()) { json transition_energies = excitedStates["transition_energies"]; if (isNumericArray(transition_energies)) { for (auto& i : transition_energies) { if (units == "nm") // convert to eV, i.e. eV = 1239.8 / wavelength energies.push_back(1239.841984 / static_cast(i)); else if (units == "cm^-1") energies.push_back(static_cast(i) / 8065.544); else if (units == "eV") energies.push_back(static_cast(i)); } } } if (excitedStates.find("intensities") != excitedStates.end() && excitedStates["intensities"].is_array()) { json intensities = excitedStates["intensities"]; if (isNumericArray(intensities)) { for (auto& i : intensities) { intensities.push_back(static_cast(i)); } } } // sanity check // make sure these all have the same length Index size = energies.size(); if (size > 0 && intensities.size() == size) { // create the matrix MatrixX electronicData(energies.size(), 2); // copy the data for (std::size_t i = 0; i < energies.size(); ++i) { electronicData(i, 0) = energies[i]; electronicData(i, 1) = intensities[i]; } // set the data molecule.setSpectra("Electronic", electronicData); } } // NMR spectra if (properties.find("nmr_shifts") != properties.end() && properties["nmr_shifts"].is_object()) { json nmrShifts = properties["nmr_shifts"]; // get the isotropic shifts as an array std::vector nmrShiftsIsotropic; if (nmrShifts.find("isotropic") != nmrShifts.end() && nmrShifts["isotropic"].is_array()) { json isotropic = nmrShifts["isotropic"]; if (isNumericArray(isotropic)) { for (auto& i : isotropic) { nmrShiftsIsotropic.push_back(static_cast(i)); } } } MatrixX nmrData(nmrShiftsIsotropic.size(), 1); for (std::size_t i = 0; i < nmrShiftsIsotropic.size(); ++i) { nmrData(i, 0) = nmrShiftsIsotropic[i]; } molecule.setSpectra("NMR", nmrData); } // todo // - orbital energies // - other properties } return true; } } // namespace Avogadro::QuantumIO avogadrolibs-1.101.0/avogadro/quantumio/qcschema.h000066400000000000000000000032131506155467400221230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_QUANTUMIO_QCSCHEMA_H #define AVOGADRO_QUANTUMIO_QCSCHEMA_H #include "avogadroquantumioexport.h" #include namespace Avogadro { namespace QuantumIO { /** * @class QCSchema qcschema.h * @brief Implementation of the MolSSI QCSchema format and WebMO variant * @author Geoffrey R. Hutchison */ class AVOGADROQUANTUMIO_EXPORT QCSchema : public Io::FileFormat { public: QCSchema(); ~QCSchema() override; Operations supportedOperations() const override { return Read | File | Stream | String; } FileFormat* newInstance() const override { return new QCSchema; } std::string identifier() const override { return "Avogadro: QCSchema"; } std::string name() const override { return "QCSchema JSON"; } std::string description() const override { return "MolSSI QCSchema JSON format."; } std::string specificationUrl() const override { return ""; } std::vector fileExtensions() const override; std::vector mimeTypes() const override; bool read(std::istream& in, Core::Molecule& molecule) override; bool write(std::ostream& out, const Core::Molecule& molecule) override { // Empty, as we do not currently write QC_SCHEMA files. return false; } }; } // namespace QuantumIO } // namespace Avogadro #endif // AVOGADRO_IO_NWCHEMJSON_H avogadrolibs-1.101.0/avogadro/rendering/000077500000000000000000000000001506155467400201225ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/rendering/CMakeLists.txt000066400000000000000000000060101506155467400226570ustar00rootroot00000000000000find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) if(WIN32 AND NOT BUILD_SHARED_LIBS) add_definitions(-DGLEW_STATIC) endif() add_library(Rendering) avogadro_headers(Rendering arcsector.h arcstrip.h arrowgeometry.h avogadrogl.h avogadrorendering.h beziergeometry.h bsplinegeometry.h bufferobject.h camera.h cartoongeometry.h curvegeometry.h cylindergeometry.h dashedlinegeometry.h drawable.h geometrynode.h geometryvisitor.h groupnode.h glrenderer.h glrendervisitor.h linestripgeometry.h meshgeometry.h node.h plyvisitor.h povrayvisitor.h primitive.h quad.h quadoutline.h scene.h shader.h shaderprogram.h solidpipeline.h spheregeometry.h textlabel2d.h textlabel3d.h textlabelbase.h textproperties.h textrenderstrategy.h texture2d.h transformnode.h visitor.h volumegeometry.h vrmlvisitor.h ) target_sources(Rendering PRIVATE arcsector.cpp arcstrip.cpp arrowgeometry.cpp beziergeometry.cpp bufferobject.cpp bsplinegeometry.cpp cartoongeometry.cpp camera.cpp curvegeometry.cpp cylindergeometry.cpp dashedlinegeometry.cpp drawable.cpp geometrynode.cpp geometryvisitor.cpp groupnode.cpp glrenderer.cpp glrendervisitor.cpp linestripgeometry.cpp meshgeometry.cpp node.cpp plyvisitor.cpp povrayvisitor.cpp quad.cpp quadoutline.cpp scene.cpp shader.cpp shaderprogram.cpp solidpipeline.cpp spheregeometry.cpp textlabel2d.cpp textlabel3d.cpp textlabelbase.cpp textproperties.cpp textrenderstrategy.cpp texture2d.cpp transformnode.cpp visitor.cpp volumegeometry.cpp vrmlvisitor.cpp ambientocclusionspheregeometry.cpp ) set(shader_files "arrow_vs.glsl" "cylinders_fs.glsl" "cylinders_vs.glsl" "dashedline_fs.glsl" "dashedline_vs.glsl" "linestrip_fs.glsl" "linestrip_vs.glsl" "mesh_fs.glsl" "mesh_opaque_fs.glsl" "mesh_vs.glsl" "solid_vs.glsl" "solid_first_fs.glsl" "spheres_fs.glsl" "spheres_vs.glsl" "sphere_ao_depth_vs.glsl" "sphere_ao_depth_fs.glsl" "sphere_ao_bake_vs.glsl" "sphere_ao_bake_fs.glsl" "sphere_ao_render_vs.glsl" "sphere_ao_render_fs.glsl" "textlabelbase_fs.glsl" "textlabelbase_vs.glsl" ) foreach(file ${shader_files}) get_filename_component(file_we ${file} NAME_WE) set(src ${CMAKE_CURRENT_SOURCE_DIR}/${file}) set(resh ${CMAKE_CURRENT_BINARY_DIR}/${file_we}.h) list(APPEND shader_h_files ${resh}) add_custom_command( OUTPUT ${resh} DEPENDS ${src} encodefile COMMAND encodefile ARGS ${src} ${resh} ${file_we} COMMENT "Encoding ${src}" ) endforeach() target_sources(Rendering PRIVATE FILE_SET shaders TYPE HEADERS BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR} FILES ${shader_h_files}) avogadro_add_library(Rendering) target_link_libraries(Rendering PUBLIC Avogadro::Headers PRIVATE Avogadro::Core # Needed for an emum in residue.h that we should move, maybe others. GLEW::GLEW OpenGL::GL) if(USE_3DCONNEXION AND (WIN32 OR APPLE)) target_compile_definitions(Rendering PUBLIC _3DCONNEXION) endif() avogadrolibs-1.101.0/avogadro/rendering/ambientocclusionspheregeometry.cpp000066400000000000000000001104211506155467400271460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "ambientocclusionspheregeometry.h" #include "camera.h" #include "scene.h" #include "bufferobject.h" #include "shader.h" #include "shaderprogram.h" #include "visitor.h" namespace { #include "sphere_ao_bake_fs.h" #include "sphere_ao_bake_vs.h" #include "sphere_ao_depth_fs.h" #include "sphere_ao_depth_vs.h" #include "sphere_ao_render_fs.h" #include "sphere_ao_render_vs.h" const int num_ao_points = 162; const float ao_points[] = { 0.850650808352f, 0.525731112119f, 0.0f, -0.850650808352f, 0.525731112119f, 0.0f, 0.850650808352f, -0.525731112119f, 0.0f, -0.850650808352f, -0.525731112119f, 0.0f, 0.525731112119f, 0.0f, 0.850650808352f, 0.525731112119f, 0.0f, -0.850650808352f, -0.525731112119f, 0.0f, 0.850650808352f, -0.525731112119f, 0.0f, -0.850650808352f, 0.0f, 0.850650808352f, 0.525731112119f, 0.0f, -0.850650808352f, 0.525731112119f, 0.0f, 0.850650808352f, -0.525731112119f, 0.0f, -0.850650808352f, -0.525731112119f, 0.5f, 0.809016994375f, 0.309016994375f, 0.309016994375f, 0.5f, 0.809016994375f, 0.809016994375f, 0.309016994375f, 0.5f, 0.809016994375f, 0.309016994375f, -0.5f, 0.309016994375f, 0.5f, -0.809016994375f, 0.5f, 0.809016994375f, -0.309016994375f, 0.809016994375f, -0.309016994375f, 0.5f, 0.309016994375f, -0.5f, 0.809016994375f, 0.5f, -0.809016994375f, 0.309016994375f, 0.5f, -0.809016994375f, -0.309016994375f, 0.309016994375f, -0.5f, -0.809016994375f, 0.809016994375f, -0.309016994375f, -0.5f, -0.809016994375f, 0.309016994375f, 0.5f, -0.309016994375f, 0.5f, 0.809016994375f, -0.5f, 0.809016994375f, 0.309016994375f, -0.5f, 0.809016994375f, -0.309016994375f, -0.309016994375f, 0.5f, -0.809016994375f, -0.809016994375f, 0.309016994375f, -0.5f, -0.5f, -0.809016994375f, 0.309016994375f, -0.309016994375f, -0.5f, 0.809016994375f, -0.809016994375f, -0.309016994375f, 0.5f, -0.809016994375f, -0.309016994375f, -0.5f, -0.309016994375f, -0.5f, -0.809016994375f, -0.5f, -0.809016994375f, -0.309016994375f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.702046444776f, 0.69378047756f, 0.16062203564f, 0.688190960236f, 0.587785252292f, 0.425325404176f, 0.862668480416f, 0.433888564553f, 0.259891913008f, 0.16062203564f, 0.702046444776f, 0.69378047756f, 0.425325404176f, 0.688190960236f, 0.587785252292f, 0.259891913008f, 0.862668480416f, 0.433888564553f, 0.69378047756f, 0.16062203564f, 0.702046444776f, 0.587785252292f, 0.425325404176f, 0.688190960236f, 0.433888564553f, 0.259891913008f, 0.862668480416f, 0.862668480416f, 0.433888564553f, -0.259891913008f, 0.688190960236f, 0.587785252292f, -0.425325404176f, 0.702046444776f, 0.69378047756f, -0.16062203564f, 0.433888564553f, 0.259891913008f, -0.862668480416f, 0.587785252292f, 0.425325404176f, -0.688190960236f, 0.69378047756f, 0.16062203564f, -0.702046444776f, 0.259891913008f, 0.862668480416f, -0.433888564553f, 0.425325404176f, 0.688190960236f, -0.587785252292f, 0.16062203564f, 0.702046444776f, -0.69378047756f, 0.862668480416f, -0.433888564553f, 0.259891913008f, 0.688190960236f, -0.587785252292f, 0.425325404176f, 0.702046444776f, -0.69378047756f, 0.16062203564f, 0.433888564553f, -0.259891913008f, 0.862668480416f, 0.587785252292f, -0.425325404176f, 0.688190960236f, 0.69378047756f, -0.16062203564f, 0.702046444776f, 0.259891913008f, -0.862668480416f, 0.433888564553f, 0.425325404176f, -0.688190960236f, 0.587785252292f, 0.16062203564f, -0.702046444776f, 0.69378047756f, 0.702046444776f, -0.69378047756f, -0.16062203564f, 0.688190960236f, -0.587785252292f, -0.425325404176f, 0.862668480416f, -0.433888564553f, -0.259891913008f, 0.16062203564f, -0.702046444776f, -0.69378047756f, 0.425325404176f, -0.688190960236f, -0.587785252292f, 0.259891913008f, -0.862668480416f, -0.433888564553f, 0.69378047756f, -0.16062203564f, -0.702046444776f, 0.587785252292f, -0.425325404176f, -0.688190960236f, 0.433888564553f, -0.259891913008f, -0.862668480416f, -0.862668480416f, 0.433888564553f, 0.259891913008f, -0.688190960236f, 0.587785252292f, 0.425325404176f, -0.702046444776f, 0.69378047756f, 0.16062203564f, -0.433888564553f, 0.259891913008f, 0.862668480416f, -0.587785252292f, 0.425325404176f, 0.688190960236f, -0.69378047756f, 0.16062203564f, 0.702046444776f, -0.259891913008f, 0.862668480416f, 0.433888564553f, -0.425325404176f, 0.688190960236f, 0.587785252292f, -0.16062203564f, 0.702046444776f, 0.69378047756f, -0.702046444776f, 0.69378047756f, -0.16062203564f, -0.688190960236f, 0.587785252292f, -0.425325404176f, -0.862668480416f, 0.433888564553f, -0.259891913008f, -0.16062203564f, 0.702046444776f, -0.69378047756f, -0.425325404176f, 0.688190960236f, -0.587785252292f, -0.259891913008f, 0.862668480416f, -0.433888564553f, -0.69378047756f, 0.16062203564f, -0.702046444776f, -0.587785252292f, 0.425325404176f, -0.688190960236f, -0.433888564553f, 0.259891913008f, -0.862668480416f, -0.702046444776f, -0.69378047756f, 0.16062203564f, -0.688190960236f, -0.587785252292f, 0.425325404176f, -0.862668480416f, -0.433888564553f, 0.259891913008f, -0.16062203564f, -0.702046444776f, 0.69378047756f, -0.425325404176f, -0.688190960236f, 0.587785252292f, -0.259891913008f, -0.862668480416f, 0.433888564553f, -0.69378047756f, -0.16062203564f, 0.702046444776f, -0.587785252292f, -0.425325404176f, 0.688190960236f, -0.433888564553f, -0.259891913008f, 0.862668480416f, -0.862668480416f, -0.433888564553f, -0.259891913008f, -0.688190960236f, -0.587785252292f, -0.425325404176f, -0.702046444776f, -0.69378047756f, -0.16062203564f, -0.433888564553f, -0.259891913008f, -0.862668480416f, -0.587785252292f, -0.425325404176f, -0.688190960236f, -0.69378047756f, -0.16062203564f, -0.702046444776f, -0.259891913008f, -0.862668480416f, -0.433888564553f, -0.425325404176f, -0.688190960236f, -0.587785252292f, -0.16062203564f, -0.702046444776f, -0.69378047756f, 0.525731112119f, 0.850650808352f, 0.0f, 0.0f, 0.961938357784f, -0.273266528913f, 0.26286555606f, 0.951056516295f, -0.162459848116f, 0.26286555606f, 0.951056516295f, 0.162459848116f, 0.0f, 0.961938357784f, 0.273266528913f, -0.525731112119f, 0.850650808352f, 0.0f, -0.26286555606f, 0.951056516295f, 0.162459848116f, -0.26286555606f, 0.951056516295f, -0.162459848116f, 0.525731112119f, -0.850650808352f, 0.0f, 0.0f, -0.961938357784f, 0.273266528913f, 0.26286555606f, -0.951056516295f, 0.162459848116f, 0.26286555606f, -0.951056516295f, -0.162459848116f, 0.0f, -0.961938357784f, -0.273266528913f, -0.525731112119f, -0.850650808352f, 0.0f, -0.26286555606f, -0.951056516295f, 0.162459848116f, -0.26286555606f, -0.951056516295f, -0.162459848116f, 0.850650808352f, 0.0f, 0.525731112119f, 0.961938357784f, -0.273266528913f, 0.0f, 0.951056516295f, -0.162459848116f, 0.26286555606f, 0.951056516295f, 0.162459848116f, 0.26286555606f, 0.961938357784f, 0.273266528913f, 0.0f, 0.850650808352f, 0.0f, -0.525731112119f, 0.951056516295f, 0.162459848116f, -0.26286555606f, 0.951056516295f, -0.162459848116f, -0.26286555606f, -0.850650808352f, 0.0f, 0.525731112119f, -0.961938357784f, 0.273266528913f, 0.0f, -0.951056516295f, 0.162459848116f, 0.26286555606f, -0.951056516295f, -0.162459848116f, 0.26286555606f, -0.961938357784f, -0.273266528913f, 0.0f, -0.850650808352f, 0.0f, -0.525731112119f, -0.951056516295f, -0.162459848116f, -0.26286555606f, -0.951056516295f, 0.162459848116f, -0.26286555606f, 0.0f, 0.525731112119f, 0.850650808352f, -0.273266528913f, 0.0f, 0.961938357784f, -0.162459848116f, 0.26286555606f, 0.951056516295f, 0.162459848116f, 0.26286555606f, 0.951056516295f, 0.273266528913f, 0.0f, 0.961938357784f, 0.0f, -0.525731112119f, 0.850650808352f, 0.162459848116f, -0.26286555606f, 0.951056516295f, -0.162459848116f, -0.26286555606f, 0.951056516295f, 0.0f, 0.525731112119f, -0.850650808352f, 0.273266528913f, 0.0f, -0.961938357784f, 0.162459848116f, 0.26286555606f, -0.951056516295f, -0.162459848116f, 0.26286555606f, -0.951056516295f, -0.273266528913f, 0.0f, -0.961938357784f, 0.0f, -0.525731112119f, -0.850650808352f, -0.162459848116f, -0.26286555606f, -0.951056516295f, 0.162459848116f, -0.26286555606f, -0.951056516295f, }; } // namespace #include "avogadrogl.h" #include using std::cout; using std::endl; namespace Avogadro::Rendering { class AmbientOcclusionRenderer { public: virtual ~AmbientOcclusionRenderer() {} virtual void renderDepth(const Eigen::Matrix4f& modelView, const Eigen::Matrix4f& projection) = 0; virtual void renderAO(const Eigen::Matrix4f& modelView, const Eigen::Matrix4f& projection, GLint textureSize, float numDirections) = 0; }; class AmbientOcclusionBaker { public: AmbientOcclusionBaker(AmbientOcclusionRenderer* renderer, GLint textureSize_) : m_renderer(renderer), m_textureSize(textureSize_), m_depthTexture(0), m_depthFBO(0), m_aoTexture(0), m_aoFBO(0) { initialize(); } void destroy() { // delete framebuffers glDeleteFramebuffers(1, &m_depthFBO); glDeleteFramebuffers(1, &m_aoFBO); // delete depth texture glDeleteTextures(1, &m_depthTexture); // note: ao texture not deleted since it contains the computed data... } GLint textureSize() const { return m_textureSize; } GLuint aoTexture() const { return m_aoTexture; } void accumulateAO(const Vector3f& center, float radius) { // save OpenGL state m_openglState.save(); // set the viewport glViewport(0, 0, m_textureSize, m_textureSize); // set the clear depth value glClearDepth(1.0f); // set the clear color glClearColor(0.0, 0.0, 0.0, 1.0); // enable polygon offset to resolve depth fighting glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(2.0f, 4.0f); // enable alpha blending glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); // bind the depth texture for depth lookup glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_depthTexture); Camera camera; camera.calculateOrthographic(-radius, radius, -radius, radius, -radius, radius); Eigen::Matrix4f projection(camera.projection().matrix()); // clear draw buffer once, AO wil be accumulated using blending glBindFramebuffer(GL_FRAMEBUFFER, m_aoFBO); glClear(GL_COLOR_BUFFER_BIT); glBindFramebuffer(GL_FRAMEBUFFER, 0); for (int i = 0; i < num_ao_points; ++i) { // random light direction Vector3f dir(ao_points[i * 3], ao_points[i * 3 + 1], ao_points[i * 3 + 2]); camera.lookAt(center + dir, center, Vector3f(0, 1, 0)); Eigen::Matrix4f modelView = camera.modelView().matrix(); // render depth to texture renderDepth(modelView, projection); // accumulate AO renderAO(modelView, projection, num_ao_points); } // load OpenGL state m_openglState.load(); } private: void initialize() { // create depth texture & FBO createDepthTexture(); createDepthFBO(); // create AO texture & FBO createAOTexture(); createAOFBO(); } void checkFramebufferStatus() { // check framebuffer status GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); switch (status) { case GL_FRAMEBUFFER_COMPLETE: std::cerr << "GL_FRAMEBUFFER_COMPLETE" << std::endl; break; case GL_FRAMEBUFFER_UNDEFINED: std::cerr << "GL_FRAMEBUFFER_UNDEFINED" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER" << std::endl; break; case GL_FRAMEBUFFER_UNSUPPORTED: std::cerr << "GL_FRAMEBUFFER_UNSUPPORTED" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" << std::endl; break; case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: std::cerr << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS" << std::endl; break; default: std::cerr << "GL_FRAMEBUFFER_???" << std::endl; break; } } void createDepthTexture(GLenum internalFormat = GL_DEPTH_COMPONENT32) { // create depth texture glGenTextures(1, &m_depthTexture); // bind the depth texture glBindTexture(GL_TEXTURE_2D, m_depthTexture); // allocate storage for the texture glTexImage2D(GL_TEXTURE_2D, // target 0, // level internalFormat, // internal format m_textureSize, m_textureSize, // texture size 0, // border GL_DEPTH_COMPONENT, // format GL_FLOAT, // type nullptr); // data // set the filtering modes glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // set depth comparison mode // this does not work with nvidia driver, works with intel 3000... // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, // GL_COMPARE_REF_TO_TEXTURE); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); // set wrap modes glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // unbind texture glBindTexture(GL_TEXTURE_2D, 0); } void createDepthFBO() { // create FBO to render depth into glGenFramebuffers(1, &m_depthFBO); glBindFramebuffer(GL_FRAMEBUFFER, m_depthFBO); // attach the depth texture to the depth FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0); // disable draw and read buffer glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); // check framebuffer status // checkFramebufferStatus(); // debug... // unbind framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); } void renderDepth(const Eigen::Matrix4f& modelView, const Eigen::Matrix4f& projection) { // bind framebuffer glBindFramebuffer(GL_FRAMEBUFFER, m_depthFBO); // enable depth test glEnable(GL_DEPTH_TEST); // clear depth buffer glClear(GL_DEPTH_BUFFER_BIT); // render the scene m_renderer->renderDepth(modelView, projection); // unbind framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); } void createAOTexture() { // create AO texture glGenTextures(1, &m_aoTexture); // bind the AO texture glBindTexture(GL_TEXTURE_2D, m_aoTexture); // allocate storage for the texture glTexImage2D(GL_TEXTURE_2D, // target 0, // level GL_RGBA, // internal format m_textureSize, m_textureSize, // texture size 0, // border GL_RGBA, // format GL_UNSIGNED_BYTE, // type nullptr); // data // set the filtering modes glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // set wrap modes glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // unbind texture glBindTexture(GL_TEXTURE_2D, 0); } void createAOFBO() { // create FBO to render depth into glGenFramebuffers(1, &m_aoFBO); glBindFramebuffer(GL_FRAMEBUFFER, m_aoFBO); // attach the depth texture to the depth FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_aoTexture, 0); // disable draw and read buffer glReadBuffer(GL_NONE); // check framebuffer status // checkFramebufferStatus(); // debug... // unbind framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); } void renderAO(const Eigen::Matrix4f& modelView, const Eigen::Matrix4f& projection, int numDirections) { // bind framebuffer glBindFramebuffer(GL_FRAMEBUFFER, m_aoFBO); // disable depth testing glDisable(GL_DEPTH_TEST); // render the scene m_renderer->renderAO(modelView, projection, m_textureSize, static_cast(numDirections)); // unbind framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); } struct OpenGLState { void save() { // bound texture glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); // viewport glGetIntegerv(GL_VIEWPORT, viewport); // depth glGetBooleanv(GL_DEPTH_TEST, &depthTest); glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepthValue); // color glGetBooleanv(GL_BLEND, &blend); glGetIntegerv(GL_BLEND_SRC, &blendSrc); glGetIntegerv(GL_BLEND_DST, &blendDst); glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor); // polygon offset polygonOffset = glIsEnabled(GL_POLYGON_OFFSET_FILL); glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &polygonOffsetFactor); glGetFloatv(GL_POLYGON_OFFSET_UNITS, &polygonOffsetUnits); } void load() { // bound texture glBindTexture(GL_TEXTURE_2D, boundTexture); // viewport glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); // depth if (!depthTest) glDisable(GL_DEPTH_TEST); else glEnable(GL_DEPTH_TEST); glClearDepth(clearDepthValue); // color glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); if (!blend) glDisable(GL_BLEND); else glEnable(GL_BLEND); glBlendFunc(blendSrc, blendDst); } // bound texture GLint boundTexture; // viewport GLint viewport[4]; // depth GLboolean depthTest; GLfloat clearDepthValue; // color GLfloat clearColor[4]; GLboolean blend; GLint blendSrc, blendDst; // polygon offset GLboolean polygonOffset; GLfloat polygonOffsetFactor; GLfloat polygonOffsetUnits; }; OpenGLState m_openglState; AmbientOcclusionRenderer* m_renderer; GLint m_textureSize; GLuint m_depthTexture; GLuint m_depthFBO; GLuint m_aoTexture; GLuint m_aoFBO; }; class SphereAmbientOcclusionRenderer : public AmbientOcclusionRenderer { public: SphereAmbientOcclusionRenderer(BufferObject& vbo, BufferObject& ibo, int numSpheres, int numVertices, int numIndices) : m_vbo(vbo), m_ibo(ibo), m_numSpheres(numSpheres), m_numVertices(numVertices), m_numIndices(numIndices) { initialize(); } void renderDepth(const Eigen::Matrix4f& modelView, const Eigen::Matrix4f& projection) override { // bind buffer objects m_vbo.bind(); m_ibo.bind(); m_depthProgram.bind(); // set the uniforms if (!m_depthProgram.setUniformValue("u_modelView", modelView)) { cout << m_depthProgram.error() << endl; } if (!m_depthProgram.setUniformValue("u_projection", projection)) { cout << m_depthProgram.error() << endl; } // set the attributes if (!m_depthProgram.enableAttributeArray("a_pos")) cout << m_depthProgram.error() << endl; if (!m_depthProgram.useAttributeArray("a_pos", ColorTextureVertex::vertexOffset(), sizeof(ColorTextureVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << m_depthProgram.error() << endl; } if (!m_depthProgram.enableAttributeArray("a_corner")) cout << m_depthProgram.error() << endl; if (!m_depthProgram.useAttributeArray( "a_corner", ColorTextureVertex::textureCoordOffset(), sizeof(ColorTextureVertex), FloatType, 2, ShaderProgram::NoNormalize)) { cout << m_depthProgram.error() << endl; } // draw glDrawRangeElements(GL_TRIANGLES, 0, static_cast(m_numVertices), static_cast(m_numIndices), GL_UNSIGNED_INT, (const GLvoid*)nullptr); m_vbo.release(); m_ibo.release(); m_depthProgram.disableAttributeArray("a_pos"); m_depthProgram.disableAttributeArray("a_corner"); m_depthProgram.release(); } void renderAO(const Eigen::Matrix4f& modelView, const Eigen::Matrix4f& projection, GLint textureSize, float numDirections) override { // bind buffer objects m_vbo.bind(); m_ibo.bind(); m_aoProgram.bind(); // set the uniforms if (!m_aoProgram.setUniformValue("u_modelView", modelView)) { cout << m_aoProgram.error() << endl; } if (!m_aoProgram.setUniformValue("u_projection", projection)) { cout << m_aoProgram.error() << endl; } if (!m_aoProgram.setUniformValue("u_textureSize", static_cast(textureSize))) { cout << m_aoProgram.error() << endl; } if (!m_aoProgram.setUniformValue( "u_tileSize", 1.0f / std::ceil(std::sqrt(static_cast(m_numSpheres))))) { cout << m_aoProgram.error() << endl; } if (!m_aoProgram.setUniformValue("u_depthTex", 0)) { cout << m_aoProgram.error() << endl; } if (!m_aoProgram.setUniformValue("u_intensity", 1.0f / (0.3f * numDirections))) { cout << m_aoProgram.error() << endl; } // set the attributes if (!m_aoProgram.enableAttributeArray("a_pos")) cout << m_aoProgram.error() << endl; if (!m_aoProgram.useAttributeArray("a_pos", ColorTextureVertex::vertexOffset(), sizeof(ColorTextureVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << m_aoProgram.error() << endl; } if (!m_aoProgram.enableAttributeArray("a_corner")) cout << m_aoProgram.error() << endl; if (!m_aoProgram.useAttributeArray("a_corner", ColorTextureVertex::textureCoordOffset(), sizeof(ColorTextureVertex), FloatType, 2, ShaderProgram::NoNormalize)) { cout << m_aoProgram.error() << endl; } if (!m_aoProgram.enableAttributeArray("a_tileOffset")) cout << m_aoProgram.error() << endl; if (!m_aoProgram.useAttributeArray( "a_tileOffset", ColorTextureVertex::textureCoord2Offset(), sizeof(ColorTextureVertex), FloatType, 2, ShaderProgram::NoNormalize)) { cout << m_aoProgram.error() << endl; } // draw glDrawRangeElements(GL_TRIANGLES, 0, static_cast(m_numVertices), static_cast(m_numIndices), GL_UNSIGNED_INT, (const GLvoid*)nullptr); m_vbo.release(); m_ibo.release(); m_aoProgram.disableAttributeArray("a_pos"); m_aoProgram.disableAttributeArray("a_corner"); m_aoProgram.disableAttributeArray("a_tileOffset"); m_aoProgram.release(); } void destroy() { // depth shader m_depthProgram.detachShader(m_depthVertexShader); m_depthProgram.detachShader(m_depthFragmentShader); m_depthFragmentShader.cleanup(); m_depthVertexShader.cleanup(); // ao shader m_aoProgram.detachShader(m_aoVertexShader); m_aoProgram.detachShader(m_aoFragmentShader); m_aoVertexShader.cleanup(); m_aoFragmentShader.cleanup(); } private: void initialize() { // compile depth shaders m_depthVertexShader.setType(Shader::Vertex); m_depthVertexShader.setSource(sphere_ao_depth_vs); if (!m_depthVertexShader.compile()) cout << m_depthVertexShader.error() << endl; m_depthFragmentShader.setType(Shader::Fragment); m_depthFragmentShader.setSource(sphere_ao_depth_fs); if (!m_depthFragmentShader.compile()) cout << m_depthFragmentShader.error() << endl; // link depth program m_depthProgram.attachShader(m_depthVertexShader); m_depthProgram.attachShader(m_depthFragmentShader); if (!m_depthProgram.link()) cout << m_depthProgram.error() << endl; // compile AO shaders m_aoVertexShader.setType(Shader::Vertex); m_aoVertexShader.setSource(sphere_ao_bake_vs); if (!m_aoVertexShader.compile()) cout << m_aoVertexShader.error() << endl; m_aoFragmentShader.setType(Shader::Fragment); m_aoFragmentShader.setSource(sphere_ao_bake_fs); if (!m_aoFragmentShader.compile()) cout << m_aoFragmentShader.error() << endl; // link AO program m_aoProgram.attachShader(m_aoVertexShader); m_aoProgram.attachShader(m_aoFragmentShader); if (!m_aoProgram.link()) cout << m_aoProgram.error() << endl; } Shader m_depthVertexShader; Shader m_depthFragmentShader; ShaderProgram m_depthProgram; Shader m_aoVertexShader; Shader m_aoFragmentShader; ShaderProgram m_aoProgram; BufferObject& m_vbo; BufferObject& m_ibo; int m_numSpheres; int m_numVertices; int m_numIndices; }; class AmbientOcclusionSphereGeometry::Private { public: Private() : aoTextureSize(1024) {} BufferObject vbo; BufferObject ibo; Shader vertexShader; Shader fragmentShader; ShaderProgram program; size_t numberOfVertices; size_t numberOfIndices; Eigen::Matrix4f translate; int aoTextureSize; int aoTexture; }; AmbientOcclusionSphereGeometry::AmbientOcclusionSphereGeometry() : m_dirty(false), d(new Private) { } AmbientOcclusionSphereGeometry::AmbientOcclusionSphereGeometry( const AmbientOcclusionSphereGeometry& other) : Drawable(other), m_spheres(other.m_spheres), m_indices(other.m_indices), m_dirty(true), d(new Private) { } AmbientOcclusionSphereGeometry::~AmbientOcclusionSphereGeometry() { delete d; } void AmbientOcclusionSphereGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void AmbientOcclusionSphereGeometry::update() { if (m_indices.empty() || m_spheres.empty()) return; // Check if the VBOs are ready, if not get them ready. if (!d->vbo.ready() || m_dirty) { std::vector sphereIndices; std::vector sphereVertices; sphereIndices.reserve(m_indices.size() * 4); sphereVertices.reserve(m_spheres.size() * 4); int nSpheres = static_cast(m_spheres.size()); int nSpheresSquared = static_cast(std::ceil(std::sqrt(static_cast(nSpheres)))); float tileSize = 1.0f / static_cast(nSpheresSquared); float halfTileSize = tileSize / 2.0f; int tileX = 0; int tileY = 0; auto itIndex = m_indices.begin(); auto itSphere = m_spheres.begin(); // calculate center Vector3f center(Vector3f::Zero()); for (auto& m_sphere : m_spheres) center += m_sphere.center; center /= static_cast(nSpheres); /* d->translate = Eigen::Matrix4f::Identity(); d->translate(0, 3) = center.x(); d->translate(1, 3) = center.y(); d->translate(2, 3) = center.z(); */ // calculate radius float radius = 0.0f; for (auto& m_sphere : m_spheres) if ((m_sphere.center - center).norm() > radius) radius = (m_sphere.center - center).norm(); for (unsigned int i = 0; itIndex != m_indices.end() && itSphere != m_spheres.end(); ++i, ++itIndex, ++itSphere) { // Use our packed data structure... float r = itSphere->radius; unsigned int index = 4 * static_cast(*itIndex); ColorTextureVertex vert( itSphere->center, itSphere->color, Vector2f(-r, -r), Vector2f(halfTileSize + tileSize * static_cast(tileX), halfTileSize + tileSize * static_cast(tileY))); sphereVertices.push_back(vert); vert.textureCoord = Vector2f(-r, r); sphereVertices.push_back(vert); vert.textureCoord = Vector2f(r, -r); sphereVertices.push_back(vert); vert.textureCoord = Vector2f(r, r); sphereVertices.push_back(vert); // 6 indexed vertices to draw a quad... sphereIndices.push_back(index + 0); sphereIndices.push_back(index + 1); sphereIndices.push_back(index + 2); sphereIndices.push_back(index + 3); sphereIndices.push_back(index + 2); sphereIndices.push_back(index + 1); ++tileX; if (tileX >= nSpheresSquared) { // start new tile row tileX = 0; ++tileY; } } d->vbo.upload(sphereVertices, BufferObject::ArrayBuffer); d->ibo.upload(sphereIndices, BufferObject::ElementArrayBuffer); d->numberOfVertices = sphereVertices.size(); d->numberOfIndices = sphereIndices.size(); SphereAmbientOcclusionRenderer aoSphereRenderer( d->vbo, d->ibo, static_cast(m_spheres.size()), static_cast(d->numberOfVertices), static_cast(d->numberOfIndices)); AmbientOcclusionBaker baker(&aoSphereRenderer, d->aoTextureSize); baker.accumulateAO(center, radius + 2.0f); d->aoTexture = baker.aoTexture(); baker.destroy(); aoSphereRenderer.destroy(); m_dirty = false; } // Build and link the shader if it has not been used yet. if (d->vertexShader.type() == Shader::Unknown) { d->vertexShader.setType(Shader::Vertex); d->vertexShader.setSource(sphere_ao_render_vs); d->fragmentShader.setType(Shader::Fragment); d->fragmentShader.setSource(sphere_ao_render_fs); if (!d->vertexShader.compile()) cout << d->vertexShader.error() << endl; if (!d->fragmentShader.compile()) cout << d->fragmentShader.error() << endl; d->program.attachShader(d->vertexShader); d->program.attachShader(d->fragmentShader); if (!d->program.link()) cout << d->program.error() << endl; } } void AmbientOcclusionSphereGeometry::render(const Camera& camera) { if (m_indices.empty() || m_spheres.empty()) return; // Prepare the VBOs, IBOs and shader program if necessary. update(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, d->aoTexture); if (!d->program.bind()) cout << d->program.error() << endl; d->vbo.bind(); d->ibo.bind(); // Set up our attribute arrays. if (!d->program.enableAttributeArray("a_pos")) cout << d->program.error() << endl; if (!d->program.useAttributeArray("a_pos", ColorTextureVertex::vertexOffset(), sizeof(ColorTextureVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program.error() << endl; } if (!d->program.enableAttributeArray("a_corner")) cout << d->program.error() << endl; if (!d->program.useAttributeArray( "a_corner", ColorTextureVertex::textureCoordOffset(), sizeof(ColorTextureVertex), FloatType, 2, ShaderProgram::NoNormalize)) { cout << d->program.error() << endl; } if (!d->program.enableAttributeArray("a_tileOffset")) cout << d->program.error() << endl; if (!d->program.useAttributeArray( "a_tileOffset", ColorTextureVertex::textureCoord2Offset(), sizeof(ColorTextureVertex), FloatType, 2, ShaderProgram::NoNormalize)) { cout << d->program.error() << endl; } if (!d->program.enableAttributeArray("a_color")) cout << d->program.error() << endl; if (!d->program.useAttributeArray( "a_color", ColorTextureVertex::colorOffset(), sizeof(ColorTextureVertex), UCharType, 3, ShaderProgram::Normalize)) { cout << d->program.error() << endl; } // Set up our uniforms if (!d->program.setUniformValue("u_modelView", camera.modelView().matrix())) { cout << d->program.error() << endl; } if (!d->program.setUniformValue( "u_invModelView", Eigen::Matrix3f( camera.modelView().matrix().block<3, 3>(0, 0).inverse()))) { cout << d->program.error() << endl; } if (!d->program.setUniformValue("u_projection", camera.projection().matrix())) { cout << d->program.error() << endl; } if (!d->program.setUniformValue("u_tex", 0)) { cout << d->program.error() << endl; } // To avoid texture interpolation from neighboring tiles, texture coords are // scaled such that half a texel is removed from all sides of a tile. // width of a singl texel in texture coordinates [0, 1] float texel = 1.0f / static_cast(d->aoTextureSize); // with of a single tile in texture coordinates [0, 1] float tile = 1.f / std::ceil(std::sqrt(static_cast(m_spheres.size()))); // The uv coordinates, centered around the tileOffset are originally in the // range [-1, 1]. The denominator below ensures that these are scaled to // values matching exactly one tile. The numerator is one minus a factor // to ensure half a tile on each side is never reached to avoid texture // interpolation taking values from neighboring texels into account. if (!d->program.setUniformValue( "u_texScale", (1.0f - 2.0f * texel / tile) / (2.0f * std::ceil(std::sqrt( static_cast(m_spheres.size())))))) { cout << d->program.error() << endl; } // Render the loaded spheres using the shader and bound VBO. glDrawRangeElements(GL_TRIANGLES, 0, static_cast(d->numberOfVertices), static_cast(d->numberOfIndices), GL_UNSIGNED_INT, (const GLvoid*)nullptr); d->vbo.release(); d->ibo.release(); d->program.disableAttributeArray("a_pos"); d->program.disableAttributeArray("a_color"); d->program.disableAttributeArray("a_corner"); d->program.disableAttributeArray("a_tileOffset"); d->program.release(); } std::multimap AmbientOcclusionSphereGeometry::hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const { std::multimap result; // Check for intersection. for (size_t i = 0; i < m_spheres.size(); ++i) { const SphereColor& sphere = m_spheres[i]; Vector3f distance = sphere.center - rayOrigin; float B = distance.dot(rayDirection); float C = distance.dot(distance) - (sphere.radius * sphere.radius); float D = B * B - C; // Test for intersection if (D < 0) continue; // Test for clipping if (B < 0 || (sphere.center - rayEnd).dot(rayDirection) > 0) continue; Identifier id; id.molecule = m_identifier.molecule; id.type = m_identifier.type; id.index = i; if (id.type != InvalidType) { auto rootD = static_cast(sqrt(D)); float depth = std::min(std::abs(B + rootD), std::abs(B - rootD)); result.insert(std::pair(depth, id)); } } return result; } void AmbientOcclusionSphereGeometry::addSphere(const Vector3f& position, const Vector3ub& color, float radius, size_t index) { m_dirty = true; m_spheres.push_back(SphereColor(position, radius, color)); m_indices.push_back(index == MaxIndex ? m_indices.size() : index); } void AmbientOcclusionSphereGeometry::clear() { m_spheres.clear(); m_indices.clear(); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/ambientocclusionspheregeometry.h000066400000000000000000000072701506155467400266220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_AMBIENTOCCLUSIONSPHEREGEOMETRY_H #define AVOGADRO_RENDERING_AMBIENTOCCLUSIONSPHEREGEOMETRY_H #include "drawable.h" #include #include #include namespace Avogadro { namespace Rendering { /** * @class AmbientOcclusionSphereGeometry ambientocclusionspheregeometry.h * * @brief The AmbientOcclusionSphereGeometry class contains one or more spheres. * @author Tim Vandermeersch * * This Drawable is capable of storing the geometry for one or more spheres. * A sphere is defined by a center point, a radius and a color. If the * spheres are not a densely packed one-to-one mapping with the objects indices * they can also optionally use an identifier that will point to some numeric * ID for the purposes of picking. * * Unlike the SphereGeometry class, this class also supports ambient occlusion. */ class AVOGADRORENDERING_EXPORT AmbientOcclusionSphereGeometry : public Drawable { public: AmbientOcclusionSphereGeometry(); AmbientOcclusionSphereGeometry(const AmbientOcclusionSphereGeometry& other); ~AmbientOcclusionSphereGeometry() override; AmbientOcclusionSphereGeometry& operator=(AmbientOcclusionSphereGeometry); friend void swap(AmbientOcclusionSphereGeometry& lhs, AmbientOcclusionSphereGeometry& rhs); /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Update the VBOs, IBOs etc ready for rendering. */ void update(); /** * @brief Render the sphere geometry. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; /** * Return the primitives that are hit by the ray. * @param rayOrigin Origin of the ray. * @param rayEnd End point of the ray. * @param rayDirection Normalized direction of the ray. * @return Sorted collection of primitives that were hit. */ std::multimap hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const override; /** * Add a sphere to the geometry object. */ void addSphere(const Vector3f& position, const Vector3ub& color, float radius, size_t index = MaxIndex); /** * Get a reference to the spheres. */ Core::Array& spheres() { return m_spheres; } const Core::Array& spheres() const { return m_spheres; } /** * Clear the contents of the node. */ void clear() override; /** * Get the number of spheres in the node object. */ size_t size() const { return m_spheres.size(); } private: Core::Array m_spheres; Core::Array m_indices; bool m_dirty; class Private; Private* d; }; inline AmbientOcclusionSphereGeometry& AmbientOcclusionSphereGeometry::operator=(AmbientOcclusionSphereGeometry other) { using std::swap; swap(*this, other); return *this; } inline void swap(AmbientOcclusionSphereGeometry& lhs, AmbientOcclusionSphereGeometry& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_spheres, rhs.m_spheres); swap(lhs.m_indices, rhs.m_indices); lhs.m_dirty = rhs.m_dirty = true; } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_AMBIENTOCCLUSIONSPHEREGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/arcsector.cpp000066400000000000000000000033201506155467400226110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "arcsector.h" using Avogadro::Core::Array; namespace Avogadro::Rendering { void ArcSector::setArcSector(const Vector3f& origin, const Vector3f& startEdge, const Vector3f& normal, float degreesCCW, float resolutionDeg) { // Prepare rotation, calculate sizes const auto numTriangles = static_cast(std::fabs(std::ceil(degreesCCW / resolutionDeg))); const auto numVerts = static_cast(numTriangles + 2); const float stepAngleRads = (degreesCCW / static_cast(numTriangles)) * DEG_TO_RAD_F; const Eigen::AngleAxisf rot(stepAngleRads, normal); // Generate normal array Array norms(numVerts, normal); // Generate vertices Array verts(numVerts); auto vertsInserter(verts.begin()); auto vertsEnd(verts.end()); Vector3f radial = startEdge; *(vertsInserter++) = origin; *(vertsInserter++) = origin + radial; while (vertsInserter != vertsEnd) *(vertsInserter++) = origin + (radial = rot * radial); // Generate indices Array indices(numTriangles * 3); auto indexInserter(indices.begin()); auto indexEnd(indices.end()); for (unsigned int i = 1; indexInserter != indexEnd; ++i) { *(indexInserter++) = 0; *(indexInserter++) = i; *(indexInserter++) = i + 1; } clear(); addVertices(verts, norms); addTriangles(indices); } } // End namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/arcsector.h000066400000000000000000000035201506155467400222600ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_ARCSECTOR_H #define AVOGADRO_RENDERING_ARCSECTOR_H #include "meshgeometry.h" #include namespace Avogadro { namespace Rendering { /** * @class ArcSector arcsector.h * * @brief The ArcSector class is a convenience class for creating an arc * disk (e.g., part of a circle) from a MeshGeometry. * @see ArcStrip for the edge of a circle. */ class AVOGADRORENDERING_EXPORT ArcSector : public MeshGeometry { public: ArcSector() {} ~ArcSector() override {} /** * Define the sector. * @param origin Center of the circle from which the arc is cut. * @param startEdge A vector defining an leading edge of the sector. The * direction is used to fix the sector's rotation about the origin, and the * length defines the radius of the sector. * @param normal The normal direction to the plane of the sector. * @param degreesCCW The extent of the sector, measured counter-clockwise from * startEdge in degrees. * @param resolutionDeg The radial width of each triangle used in the sector * approximation in degrees. This will be adjusted to fit an integral number * of triangles in the sector. Smaller triangles (better approximations) are * chosen if adjustment is needed. */ void setArcSector(const Vector3f& origin, const Vector3f& startEdge, const Vector3f& normal, float degreesCCW, float resolutionDeg); }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_ARCSECTOR_H avogadrolibs-1.101.0/avogadro/rendering/arcstrip.cpp000066400000000000000000000024031506155467400224540ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "arcstrip.h" namespace Avogadro::Rendering { void ArcStrip::setArc(const Vector3f& origin, const Vector3f& start, const Vector3f& normal, float degreesCCW, float resolutionDeg, float lineWidth) { // Prepare rotation, calculate sizes const auto resolution = static_cast(std::fabs(std::ceil(degreesCCW / resolutionDeg))); const auto numVerts = static_cast(resolution + 1); const float stepAngleRads = (degreesCCW / static_cast(resolution)) * DEG_TO_RAD_F; const Eigen::AngleAxisf rot(stepAngleRads, normal); // Generate vertices Core::Array verts(numVerts); auto vertsInserter(verts.begin()); auto vertsEnd(verts.end()); Vector3f radial = start; *(vertsInserter++) = origin + radial; while (vertsInserter != vertsEnd) *(vertsInserter++) = origin + (radial = rot * radial); clear(); addLineStrip(verts, lineWidth); } } // End namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/arcstrip.h000066400000000000000000000032631506155467400221260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_ARCSTRIP_H #define AVOGADRO_RENDERING_ARCSTRIP_H #include "linestripgeometry.h" #include namespace Avogadro { namespace Rendering { /** * @class ArcStrip arcstrip.h * * @brief The ArcStrip class is a convenience class for creating an arc * line (e.g., the edge of a circle). */ class AVOGADRORENDERING_EXPORT ArcStrip : public LineStripGeometry { public: ArcStrip() {} ~ArcStrip() override {} /** * Define the arc. * @param origin Center of the circle from which the arc is cut. * @param start A vector pointing from the origin to the start of the arc. * @param normal The normal direction to the plane of the circle. * @param degreesCCW The extent of the arc, measured counter-clockwise from * start in degrees. * @param resolutionDeg The radial width of each segment used in the arc * approximation, in degrees. This will be adjusted to fit an integral number * of segments into the arc. Smaller segments (better approximations) are * chosen if adjustment is needed. * @param lineWidth The width of the line. */ void setArc(const Vector3f& origin, const Vector3f& start, const Vector3f& normal, float degreesCCW, float resolutionDeg, float lineWidth); }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_ARCSTRIP_H avogadrolibs-1.101.0/avogadro/rendering/arrow_vs.glsl000066400000000000000000000002671506155467400226540ustar00rootroot00000000000000attribute vec4 vertex; uniform mat4 modelView; uniform mat4 projection; void main() { gl_FrontColor = vec4(0.0, 1.0, 0.0, 1.0); gl_Position = projection * modelView * vertex; } avogadrolibs-1.101.0/avogadro/rendering/arrowgeometry.cpp000066400000000000000000000120301506155467400235300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "arrowgeometry.h" #include "avogadrogl.h" #include "bufferobject.h" #include "camera.h" #include "scene.h" #include "shader.h" #include "shaderprogram.h" #include "visitor.h" #include #include #include #include namespace { #include "arrow_vs.h" } using Avogadro::Vector3f; using Avogadro::Vector3ub; using Avogadro::Vector4ub; using std::cout; using std::endl; namespace Avogadro::Rendering { class ArrowGeometry::Private { public: Private() {} Shader vertexShader; ShaderProgram program; }; ArrowGeometry::ArrowGeometry() : m_color(0, 255, 0), m_dirty(false), d(new Private) { } ArrowGeometry::ArrowGeometry(const ArrowGeometry& other) : Drawable(other), m_vertices(other.m_vertices), m_lineStarts(other.m_lineStarts), m_color(other.m_color), m_dirty(true), d(new Private) { } ArrowGeometry::~ArrowGeometry() { delete d; } void ArrowGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void ArrowGeometry::update() { if (m_vertices.empty()) return; // Build and link the shader if it has not been used yet. if (d->vertexShader.type() == Shader::Unknown) { d->vertexShader.setType(Shader::Vertex); d->vertexShader.setSource(arrow_vs); if (!d->vertexShader.compile()) cout << d->vertexShader.error() << endl; d->program.attachShader(d->vertexShader); if (!d->program.link()) cout << d->program.error() << endl; } } void ArrowGeometry::render(const Camera& camera) { if (m_vertices.empty()) return; // Prepare the shader program if necessary. update(); if (!d->program.bind()) cout << d->program.error() << endl; // Set up our uniforms (model-view and projection matrices right now). if (!d->program.setUniformValue("modelView", camera.modelView().matrix())) { cout << d->program.error() << endl; } if (!d->program.setUniformValue("projection", camera.projection().matrix())) { cout << d->program.error() << endl; } // Render the arrows using the shader. for (auto& m_vertice : m_vertices) { Vector3f v3 = m_vertice.first + 0.8 * (m_vertice.second - m_vertice.first); drawLine(m_vertice.first, v3, 2); drawCone(v3, m_vertice.second, 0.05, 1.0); } d->program.release(); } void ArrowGeometry::clear() { m_vertices.clear(); m_lineStarts.clear(); m_dirty = true; } void ArrowGeometry::drawLine(const Vector3f& start, const Vector3f& end, double lineWidth) { // Draw a line between two points of the specified thickness glPushAttrib(GL_LIGHTING_BIT); glDisable(GL_LIGHTING); glLineWidth(lineWidth); // Draw the line glBegin(GL_LINE_STRIP); glVertex3fv(start.data()); glVertex3fv(end.data()); glEnd(); glPopAttrib(); } void ArrowGeometry::drawCone(const Vector3f& base, const Vector3f& cap, double baseRadius, double) { const int CONE_TESS_LEVEL = 30; // This draws a cone which will be most useful for drawing arrows etc. Vector3f axis = cap - base; Vector3f axisNormalized = axis.normalized(); Vector3f ortho1, ortho2; ortho1 = axisNormalized.unitOrthogonal(); ortho1 *= baseRadius; ortho2 = axisNormalized.cross(ortho1); // Draw the cone // unfortunately we can't use a GL_TRIANGLE_FAN because this would force // having a common normal vector at the tip. for (int j = 0; j < CONE_TESS_LEVEL; j++) { const double alphaStep = 2.0 * M_PI / CONE_TESS_LEVEL; double alpha = j * alphaStep; double alphaNext = alpha + alphaStep; double alphaPrec = alpha - alphaStep; Vector3f v = sin(alpha) * ortho1 + cos(alpha) * ortho2 + base; Vector3f vNext = sin(alphaNext) * ortho1 + cos(alphaNext) * ortho2 + base; Vector3f vPrec = sin(alphaPrec) * ortho1 + cos(alphaPrec) * ortho2 + base; Vector3f n = (cap - v).cross(v - vPrec).normalized(); Vector3f nNext = (cap - vNext).cross(vNext - v).normalized(); glBegin(GL_TRIANGLES); glColor3ub(m_color[0], m_color[1], m_color[2]); glNormal3fv((n + nNext).normalized().data()); glVertex3fv(cap.data()); glNormal3fv(nNext.data()); glVertex3fv(vNext.data()); glNormal3fv(n.data()); glVertex3fv(v.data()); glEnd(); } // Now to draw the base glBegin(GL_TRIANGLE_FAN); glNormal3fv((-axisNormalized).eval().data()); glVertex3fv(base.data()); for (int j = 0; j <= CONE_TESS_LEVEL; j++) { double alpha = -j * M_PI / (CONE_TESS_LEVEL / 2.0); Vector3f v = cos(alpha) * ortho1 + sin(alpha) * ortho2 + base; glVertex3fv(v.data()); } glEnd(); } void ArrowGeometry::addSingleArrow(const Vector3f& pos1, const Vector3f& pos2) { m_vertices.reserve(m_vertices.size() + 1); m_vertices.push_back(std::pair(pos1, pos2)); m_dirty = true; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/arrowgeometry.h000066400000000000000000000050751506155467400232100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_ARROWGEOMETRY_H #define AVOGADRO_RENDERING_ARROWGEOMETRY_H #include "drawable.h" #include namespace Avogadro { namespace Rendering { /** * @class ArrowGeometry arrowgeometry.h * * @brief The ArrowGeometry class is used to store sets of line strips. */ class AVOGADRORENDERING_EXPORT ArrowGeometry : public Drawable { public: static const size_t InvalidIndex; ArrowGeometry(); ArrowGeometry(const ArrowGeometry& other); ~ArrowGeometry() override; ArrowGeometry& operator=(ArrowGeometry); friend void swap(ArrowGeometry& lhs, ArrowGeometry& rhs); /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Render the arrows. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; /** * Clear the contents of the node. */ void clear() override; void drawLine(const Vector3f& start, const Vector3f& end, double lineWidth); void drawCone(const Vector3f& base, const Vector3f& cap, double baseRadius, double); /** * Add a single arrow object. * @param pos1 The start coordinate of the arrow. * @param pos2 The end coordinate of the arrow. * @{ */ void addSingleArrow(const Vector3f& pos1, const Vector3f& pos2); /** @} */ /** The vertex array. */ Core::Array> vertices() const { return m_vertices; } /** Set the color of the arrow */ void setColor(const Vector3ub& c) { m_color = c; } private: /** * @brief Update the shaders ready for rendering. */ void update(); Core::Array> m_vertices; Core::Array m_lineStarts; Vector3ub m_color; bool m_dirty; class Private; Private* d; }; inline ArrowGeometry& ArrowGeometry::operator=(ArrowGeometry other) { using std::swap; swap(*this, other); return *this; } inline void swap(ArrowGeometry& lhs, ArrowGeometry& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_vertices, rhs.m_vertices); lhs.m_dirty = rhs.m_dirty = true; } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_ARROWGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/avogadrogl.h000066400000000000000000000013131506155467400224160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_AVOGADROGL_H #define AVOGADRO_RENDERING_AVOGADROGL_H /** * GLEW should always be in the same place, but Apple likes to move gl.h. * This header should be used instead of including the GL headers directly * in order to work around different layouts. */ #include #ifdef __APPLE__ #include #else #include #endif #endif // AVOGADRO_RENDERING_AVOGADROGL_H avogadrolibs-1.101.0/avogadro/rendering/avogadrorendering.h000066400000000000000000000035631506155467400240020ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRORENDERING_H #define AVOGADRORENDERING_H namespace Avogadro { namespace Rendering { /** * @brief The RenderPass enum is used to identify different stages in * multipass rendering. */ enum RenderPass { /** * Default value -- no rendering is currently happening. */ NotRendering = 0, /** * Solid geometry is being drawn. Alpha blending disabled, depth testing is * enabled. Additionally, any screen-space shaders act here. */ SolidPass, /** * Opaque geometry is being drawn. Alpha blending disabled, depth testing is * enabled. */ OpaquePass, /** * Translucent geometry is being drawn. Depth testing is disabled, and alpha * blending is enabled using the equivalent of the OpenGL call * @code glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) @endcode */ TranslucentPass, /** * 3D geometry is being drawn over the Opaque and Translucent rendering. * Depth testing is disabled, and alpha blending is enabled using the * equivalent of the OpenGL call * @code glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) @endcode */ Overlay3DPass, /** * 2D drawables are being rendered over the 3D scene. The * modelview matrix is identity, and the projection matrix is configured to * an orthographic projection that matches the viewport. Valid Z values are * [-1, 1], and the origin is in the bottom-left corner. Depth testing is * disabled, and alpha blending is performed the same as in TranslucentPass. */ Overlay2DPass }; } // end namespace Rendering } // end namespace Avogadro #endif // AVOGADRORENDERING_H avogadrolibs-1.101.0/avogadro/rendering/beziergeometry.cpp000066400000000000000000000016421506155467400236650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "beziergeometry.h" namespace Avogadro::Rendering { BezierGeometry::BezierGeometry() : CurveGeometry() {} Vector3f BezierGeometry::computeCurvePoint( float t, const std::list& points) const { Vector3f h = Vector3f::Ones(); float u = 1.0f - t; float n1 = points.size(); float w = 1.0f / n1; float k = 0.0f; Vector3f Q(w, w, w); for (const auto& p : points) { for (size_t i = 0; i < 3; ++i) { h[i] = h[i] * t * (n1 - k) * w; h[i] = h[i] / (k * u * w + h[i]); Q[i] = (1.0f - h[i]) * Q[i] + h[i] * p->pos[i]; } k += 1.0f; } return Q; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/beziergeometry.h000066400000000000000000000013561506155467400233340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_BEZIERGEOMETRY_H #define AVOGADRO_RENDERING_BEZIERGEOMETRY_H #include "curvegeometry.h" namespace Avogadro { namespace Rendering { class AVOGADRORENDERING_EXPORT BezierGeometry : public CurveGeometry { public: BezierGeometry(); protected: Vector3f computeCurvePoint(float t, const std::list& points) const override; }; } // End namespace Rendering } // End namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/rendering/bsplinegeometry.cpp000066400000000000000000000041261506155467400240410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "bsplinegeometry.h" namespace Avogadro::Rendering { BSplineGeometry::BSplineGeometry() : CurveGeometry() {} BSplineGeometry::BSplineGeometry(bool flat) : CurveGeometry(flat) {} float B(float i, float k, float t, float knot) { float ti = knot * i; // t_i float ti1 = knot * (i + 1.0f); // t_(i+1) if (k == 1) { if (ti <= t && t < ti1) { return 1.0f; } else { return 0.0f; } } float tik = knot * (i + k); // t_(i+k) float tik1 = knot * (i + k - 1.0f); // t_(i+k-1) float a1 = (t - ti) / (tik1 - ti); float a2 = B(i, k - 1, t, knot); float b1 = (tik - t) / (tik - ti1); float b2 = B(i + 1, k - 1, t, knot); return a1 * a2 + b1 * b2; } Vector3f BSplineGeometry::computeCurvePoint( float t, const std::list& points) const { // degree: linear = 1, quadratic = 2, cubic = 3 float k = 3.0f; // how many points in either way for approximation const int lookahead = 10; // #knot segments = #control points + #degree + 1 float m = 2 * lookahead + k + 1.0f; float knot = 1.0f / m; Vector3f Q = Vector3f::Zero(); float i = 0.0f; auto it = points.begin(); const auto end = points.end(); int size = static_cast(points.size()); // start from a lookbehind distance rather than at the beginning int startIndex = (size * t) - lookahead; if (startIndex < 0) startIndex = 0; else if (startIndex > size - 2 * lookahead) startIndex = size - 2 * lookahead; float t2 = (t - startIndex / (float)size) * size / (2 * lookahead); for (; startIndex > 0 && it != end; --startIndex, ++it) {} // only read a certain number of elements from here size_t count = 2 * lookahead; for (; count && it != end; --count, ++it) { Q += (*it)->pos * B(i, k, t2, knot); i += 1.0f; } return Q; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/bsplinegeometry.h000066400000000000000000000014201506155467400235000ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_BSPLINEGEOMETRY_H #define AVOGADRO_RENDERING_BSPLINEGEOMETRY_H #include "curvegeometry.h" namespace Avogadro { namespace Rendering { class AVOGADRORENDERING_EXPORT BSplineGeometry : public CurveGeometry { public: BSplineGeometry(); BSplineGeometry(bool flat); protected: Vector3f computeCurvePoint(float t, const std::list& points) const override; }; } // End namespace Rendering } // End namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/rendering/bufferobject.cpp000066400000000000000000000040641506155467400232720ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "bufferobject.h" #include "avogadrogl.h" namespace Avogadro::Rendering { namespace { inline GLenum convertType(BufferObject::ObjectType type) { switch (type) { default: case BufferObject::ArrayBuffer: return GL_ARRAY_BUFFER; case BufferObject::ElementArrayBuffer: return GL_ELEMENT_ARRAY_BUFFER; } } } // namespace struct BufferObject::Private { Private() : handle(0) {} GLenum type; GLuint handle; }; BufferObject::BufferObject(ObjectType type_) : d(new Private), m_dirty(true) { if (type_ == ArrayBuffer) d->type = GL_ARRAY_BUFFER; else d->type = GL_ELEMENT_ARRAY_BUFFER; } BufferObject::~BufferObject() { if (d->handle != 0) glDeleteBuffers(1, &d->handle); delete d; } BufferObject::ObjectType BufferObject::type() const { if (d->type == GL_ARRAY_BUFFER) return ArrayBuffer; else return ElementArrayBuffer; } Index BufferObject::handle() const { return static_cast(d->handle); } bool BufferObject::bind() { if (!d->handle) return false; glBindBuffer(d->type, d->handle); return true; } bool BufferObject::release() { if (!d->handle) return false; glBindBuffer(d->type, 0); return true; } bool BufferObject::uploadInternal(const void* buffer, size_t size, ObjectType objectType) { GLenum objectTypeGl = convertType(objectType); if (d->handle == 0) { glGenBuffers(1, &d->handle); d->type = objectTypeGl; } else if (d->type != objectTypeGl) { m_error = "Trying to upload array buffer to incompatible buffer."; return false; } glBindBuffer(d->type, d->handle); glBufferData(d->type, size, static_cast(buffer), GL_STATIC_DRAW); m_dirty = false; return true; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/bufferobject.h000066400000000000000000000054431506155467400227410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_BUFFEROBJECT_H #define AVOGADRO_RENDERING_BUFFEROBJECT_H #include "avogadrorenderingexport.h" #include #include "scene.h" #include // For member variables. #include // For API. namespace Avogadro { namespace Rendering { /** * @class BufferObject bufferobject.h * @brief Buffer object to store geometry/attribute data on the GPU. * @author Marcus D. Hanwell * * This class creates GPU buffer object, and uploads the data to the GPU. */ class AVOGADRORENDERING_EXPORT BufferObject { public: enum ObjectType { ArrayBuffer, ElementArrayBuffer }; BufferObject(ObjectType type = ArrayBuffer); ~BufferObject(); /** Get the type of the buffer object. */ ObjectType type() const; /** Get the handle of the buffer object. */ Index handle() const; /** Determine if the buffer object is ready to be used. */ bool ready() const { return m_dirty == false; } /** * Upload data to the buffer object. The BufferObject::type() must match * @a type or be uninitialized. * * The ContainerT type must have tightly packed values of * ContainerT::value_type accessible by reference via ContainerT::operator[]. * Additionally, the standard size() and empty() methods must be implemented. * The std::vector and Avogadro::Core::Array classes are examples of such * supported containers. */ template bool upload(const ContainerT& array, ObjectType type); /** Bind the buffer object ready for rendering. * @note Only one ARRAY_BUFFER and one ELEMENT_ARRAY_BUFFER may be bound at * any time. */ bool bind(); /** Release the buffer. This should be done after rendering is complete. */ bool release(); /** Return a string describing errors. */ std::string error() const { return m_error; } private: bool uploadInternal(const void* buffer, size_t size, ObjectType objectType); struct Private; Private* d; bool m_dirty; std::string m_error; }; template inline bool BufferObject::upload(const ContainerT& array, BufferObject::ObjectType objectType) { if (array.empty()) { m_error = "Refusing to upload empty array."; return false; } return uploadInternal(&array[0], array.size() * sizeof(typename ContainerT::value_type), objectType); } } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_BUFFEROBJECT_H avogadrolibs-1.101.0/avogadro/rendering/camera.cpp000066400000000000000000000137761506155467400220740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "camera.h" #include #include namespace Avogadro::Rendering { Camera::Camera() : m_width(0), m_height(0), m_projectionType(Perspective), m_orthographicScale(1.0), m_data(new EigenData), m_focus(NAN, NAN, NAN) { m_data->projection.setIdentity(); m_data->modelView.setIdentity(); } Camera::Camera(const Camera& o) : m_width(o.m_width), m_height(o.m_height), m_projectionType(o.m_projectionType), m_orthographicScale(o.m_orthographicScale), m_data(new EigenData(*o.m_data)) { } Camera& Camera::operator=(const Camera& o) { if (this != &o) { m_width = o.m_width; m_height = o.m_height; m_projectionType = o.m_projectionType; m_orthographicScale = o.m_orthographicScale; m_data = std::move(std::unique_ptr(new EigenData(*o.m_data))); } return *this; } Camera::~Camera() {} void Camera::translate(const Vector3f& translate_) { m_data->modelView.translate(translate_); } void Camera::preTranslate(const Vector3f& translate_) { m_data->modelView.pretranslate(translate_); } void Camera::rotate(float angle, const Vector3f& axis) { m_data->modelView.rotate(Eigen::AngleAxisf(angle, axis)); } void Camera::preRotate(float angle, const Vector3f& axis) { m_data->modelView.prerotate(Eigen::AngleAxisf(angle, axis)); } void Camera::scale(float s) { if (m_projectionType == Perspective) m_data->modelView.scale(s); else m_orthographicScale *= s; } void Camera::lookAt(const Vector3f& eye, const Vector3f& center, const Vector3f& up) { Vector3f f = (center - eye).normalized(); Vector3f u = up.normalized(); Vector3f s = f.cross(u).normalized(); u = s.cross(f); m_data->modelView.setIdentity(); m_data->modelView(0, 0) = s.x(); m_data->modelView(0, 1) = s.y(); m_data->modelView(0, 2) = s.z(); m_data->modelView(1, 0) = u.x(); m_data->modelView(1, 1) = u.y(); m_data->modelView(1, 2) = u.z(); m_data->modelView(2, 0) = -f.x(); m_data->modelView(2, 1) = -f.y(); m_data->modelView(2, 2) = -f.z(); m_data->modelView(0, 3) = -s.dot(eye); m_data->modelView(1, 3) = -u.dot(eye); m_data->modelView(2, 3) = f.dot(eye); } float Camera::distance(const Vector3f& point) const { return (m_data->modelView * point).norm(); } Vector3f Camera::project(const Vector3f& point) const { Eigen::Matrix4f mvp = m_data->projection.matrix() * m_data->modelView.matrix(); Vector4f tPoint(point.x(), point.y(), point.z(), 1.0f); tPoint = mvp * tPoint; Vector3f result( static_cast(m_width) * (tPoint.x() / tPoint.w() + 1.0f) / 2.0f, static_cast(m_height) * (tPoint.y() / tPoint.w() + 1.0f) / 2.0f, (tPoint.z() / tPoint.w() + 1.0f) / 2.0f); return result; } Vector3f Camera::unProject(const Vector3f& point) const { Eigen::Matrix4f mvp = m_data->projection.matrix() * m_data->modelView.matrix(); Vector4f result(2.0f * point.x() / static_cast(m_width) - 1.0f, 2.0f * (static_cast(m_height) - point.y()) / static_cast(m_height) - 1.0f, 2.0f * point.z() - 1.0f, 1.0f); result = mvp.matrix().inverse() * result; return Vector3f(result.x() / result.w(), result.y() / result.w(), result.z() / result.w()); } Vector3f Camera::unProject(const Vector2f& point, const Vector3f& reference) const { return unProject(Vector3f(point.x(), point.y(), project(reference).z())); } void Camera::calculatePerspective(float fieldOfView, float aspectRatio, float zNear, float zFar) { m_data->projection.setIdentity(); float f = 1.0f / std::tan(fieldOfView * float(M_PI) / 360.0f); m_data->projection(0, 0) = f / aspectRatio; m_data->projection(1, 1) = f; m_data->projection(2, 2) = (zNear + zFar) / (zNear - zFar); m_data->projection(2, 3) = (2.0f * zFar * zNear) / (zNear - zFar); m_data->projection(3, 2) = -1; m_data->projection(3, 3) = 0; } void Camera::calculatePerspective(float fieldOfView, float zNear, float zFar) { calculatePerspective( fieldOfView, static_cast(m_width) / static_cast(m_height), zNear, zFar); } void Camera::calculateOrthographic(float left, float right, float bottom, float top, float zNear, float zFar) { left *= m_orthographicScale; right *= m_orthographicScale; bottom *= m_orthographicScale; top *= m_orthographicScale; m_data->projection.setIdentity(); m_data->projection(0, 0) = 2.0f / (right - left); m_data->projection(0, 3) = -(right + left) / (right - left); m_data->projection(1, 1) = 2.0f / (top - bottom); m_data->projection(1, 3) = -(top + bottom) / (top - bottom); m_data->projection(2, 2) = -2.0f / (zFar - zNear); m_data->projection(2, 3) = -(zFar + zNear) / (zFar - zNear); m_data->projection(3, 3) = 1; } void Camera::setViewport(int w, int h) { m_width = w; m_height = h; } void Camera::setProjection(const Eigen::Affine3f& transform) { m_data->projection = transform; } void Camera::setModelView(const Eigen::Affine3f& transform) { m_data->modelView = transform; } void Camera::calculatePerspective(float left, float right, float bottom, float top, float zNear, float zFar) { m_data->projection.setIdentity(); m_data->projection(0, 0) = (2.0f * zNear) / (right - left); m_data->projection(1, 1) = (2.0f * zNear) / (top - bottom); m_data->projection(0, 2) = (right + left) / (right - left); m_data->projection(1, 2) = (top + bottom) / (top - bottom); m_data->projection(2, 2) = -(zFar + zNear) / (zFar - zNear); m_data->projection(3, 2) = -1.0f; m_data->projection(2, 3) = -(2.0f * zFar * zNear) / (zFar - zNear); m_data->projection(3, 3) = 0.0f; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/camera.h000066400000000000000000000160711506155467400215300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_CAMERA_H #define AVOGADRO_RENDERING_CAMERA_H #include "avogadrorenderingexport.h" #include // For vector types. #include // For member variables. #include namespace Avogadro { namespace Rendering { enum Projection { Perspective, Orthographic }; // Separate Eigen datastructures to ensure sufficient memory alignment. struct EigenData { EIGEN_MAKE_ALIGNED_OPERATOR_NEW Eigen::Affine3f projection; Eigen::Affine3f modelView; }; /** * @class Camera camera.h * @brief The Camera class provides utility functionality useful in camera's * used with 3D scenes. * @author Marcus D. Hanwell */ class AVOGADRORENDERING_EXPORT Camera { public: Camera(); Camera(const Camera& o); Camera& operator=(const Camera& o); ~Camera(); /** * Translate the camera's model view matrix using the supplied translation * vector @p translate. */ void translate(const Vector3f& translate); /** * Pretranslate the camera's model view matrix using the supplied translation * vector @p translate. */ void preTranslate(const Vector3f& translate); /** * Rotate the camera about the supplied @p axis by @p angle (degrees). */ void rotate(float angle, const Vector3f& axis); /** * Prerotate the camera about the supplied @p axis by @p angle (degrees). */ void preRotate(float angle, const Vector3f& axis); /** * Modify the matrix, to give the effect of zooming in or out. */ void scale(float scale); /** * Set the model-view matrix to the "look at" transformation matrix. * @param eye the position of the eye/camera. * @param center the position to look at. * @param up the vector pointing up. */ void lookAt(const Vector3f& eye, const Vector3f& center, const Vector3f& up); /** * Distance to supplied point @p point and the camera. */ float distance(const Vector3f& point) const; /** * Projects a point from the scene to the window. */ Vector3f project(const Vector3f& point) const; /** * Unprojects a point from the window to the scene. */ Vector3f unProject(const Vector3f& point) const; /** * Unprojects a point from the window to the scene, using the supplied * reference point (defaults to the origin if nothing is supplied). */ Vector3f unProject(const Vector2f& point, const Vector3f& reference = Vector3f::Zero()) const; /** * Calculate the perspective projection matrix. * @param fieldOfView angle in degrees in the y direction. * @param aspectRatio is the ratio of width to height. * @param zNear is the distance from the viewer to the near clipping plane. * @param zFar is the distance from the viewer to the far clipping plane. */ void calculatePerspective(float fieldOfView, float aspectRatio, float zNear, float zFar); /** * Calculate the perspective projection matrix. Computes the aspect ratio * from the width and height stored by the Camera object. * @param fieldOfView angle in degrees in the y direction. * @param zNear is the distance from the viewer to the near clipping plane. * @param zFar is the distance from the viewer to the far clipping plane. */ void calculatePerspective(float fieldOfView, float zNear, float zFar); /** * Calculate the perspective projection matrix using frustum planes * coordinates. * @param left left vertical clipping plane. * @param right right vertical clipping plane. * @param bottom bottom horizontal clipping plane. * @param top top horizontal clipping plane. * @param zNear distance to the near clipping plane. * @param zFar distance to the far clipping plane. */ void calculatePerspective(float left, float right, float bottom, float top, float zNear, float zFar); /** * Calculate the orthographic projection matrix. * @param left left vertical clipping plane. * @param right right vertical clipping plane. * @param bottom bottom horizontal clipping plane. * @param top top horizontal clipping plane. * @param zNear distance to the near clipping plane. * @param zFar distance to the far clipping plane. */ void calculateOrthographic(float left, float right, float bottom, float top, float zNear, float zFar); /** * Set the dimensions of the viewport in pixels. */ void setViewport(int w, int h); /** * Get the width of the viewport in pixels. */ int width() const { return m_width; } /** * Get the height of the viewport in pixels. */ int height() const { return m_height; } /** * Set the model view matrix to the identity. This resets the model view * matrix. */ void setIdentity() { m_data->modelView.setIdentity(); } /** * Set the projection transform. */ void setProjection(const Eigen::Affine3f& transform); /** * Get a reference to the projection matrix. */ const Eigen::Affine3f& projection() const; /** * Set the model view transform. */ void setModelView(const Eigen::Affine3f& transform); /** Get a reference to the model view matrix. */ const Eigen::Affine3f& modelView() const; /** * Set the projection type for this camera (Perspective or Orthographic). * @param proj The projection type to use. */ void setProjectionType(Projection proj) { m_projectionType = proj; } /** * Get the projection type the camera is using. * @return The current projection type. */ Projection projectionType() const { return m_projectionType; } /** * Set the orthographic scale, this defaults to 1.0. Affects calculation of * the orthographic projection matrix. * @param newScale The factor to scale orthographic projection by. */ void setOrthographicScale(float newScale) { m_orthographicScale = newScale; } /** * Get the value of the orthographic scale, defaults to 1.0. * @return The current value of the orthographic scale. */ float orthographicScale() const { return m_orthographicScale; } /** * Set focused point. Navigation actions shall occur relative to it. * Note that this method does not cause any change in camera matrices. */ void setFocus(const Eigen::Vector3f& newFocus) { m_focus = newFocus; } /** * Get focused point. Navigation actions shall occur relative to it. */ Vector3f focus() const { return m_focus; } private: int m_width; int m_height; Projection m_projectionType; float m_orthographicScale; std::unique_ptr m_data; Eigen::Vector3f m_focus; }; inline const Eigen::Affine3f& Camera::projection() const { return m_data->projection; } inline const Eigen::Affine3f& Camera::modelView() const { return m_data->modelView; } } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_CAMERA_H avogadrolibs-1.101.0/avogadro/rendering/cartoongeometry.cpp000066400000000000000000000102601506155467400240460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cartoongeometry.h" #include namespace Avogadro::Rendering { using Core::Residue; using std::vector; const float Cartoon::ELIPSE_RATIO = 0.75f; Cartoon::Cartoon() : BSplineGeometry(false), m_minRadius(-1.0f), m_maxRadius(-1.0f) { } Cartoon::Cartoon(float minRadius, float maxRadius) : BSplineGeometry(false), m_minRadius(minRadius), m_maxRadius(maxRadius) { } vector Cartoon::computeCirclePoints(const Eigen::Affine3f& a, const Eigen::Affine3f& b, bool flat) const { unsigned int circleResolution = flat ? 2 : 20; const float resolutionRadians = 2.0f * static_cast(M_PI) / static_cast(circleResolution); vector result; float elipseA = flat ? 0.999f : ELIPSE_RATIO; float elipseB = 1.0f - elipseA; float e = std::sqrt(1.0f - ((elipseB * elipseB) / (elipseA * elipseA))); float c = elipseA * e; for (unsigned int i = 0; i < circleResolution; ++i) { float theta = resolutionRadians * i; float r = (elipseA * (1.0f - (e * e))) / (1.0f + e * std::cos(theta)); Vector3f elipse = Vector3f(r * std::sin(theta), 0.0f, c + r * std::cos(theta)); ColorNormalVertex vert1; vert1.normal = a.linear() * elipse; vert1.vertex = a * elipse; result.push_back(vert1); ColorNormalVertex vert2; vert2.normal = b.linear() * elipse; vert2.vertex = b * elipse; result.push_back(vert2); } return result; } float arrowFunction(float t) { float result; const float maxPoint = 0.7f; if (t < maxPoint) { // normalize t using max point and scale it so that adding will be between // [minimumRadius, 1] result = t / maxPoint; } else { // starting with 1 and go decreassing t = (t - maxPoint) / (1.0f - maxPoint); result = 1.0f - t; result = result < 0.3 ? 0.3 : result; } return result; } float Cartoon::computeScale(size_t index, float p, float radius) const { if (index > m_type.size()) return radius; float t = (m_type[index].second + p) / 0.80f; t = t > 1.0f ? 1.0f : t; switch (m_type[index].first) { default: case Undefined: return radius; case Body: return m_minRadius; case Arrow: if (m_type[index].second == 0) { return (arrowFunction(1.0f - t) * m_maxRadius) + m_minRadius; } else { return 0.3 * m_maxRadius + m_minRadius; } case Head: return ((1.0f - t) * (m_maxRadius - m_minRadius)) + (1.0f * m_minRadius); case Tail: return (t * (m_maxRadius - m_minRadius)) + (1.0f * m_minRadius); } } CartoonType secondaryToCartoonType(Residue::SecondaryStructure sec) { switch (sec) { case Residue::SecondaryStructure::betaSheet: return Arrow; case Residue::SecondaryStructure::alphaHelix: return Tail; case Residue::SecondaryStructure::helix310: return Tail; case Residue::SecondaryStructure::piHelix: return Tail; default: return Body; } } void Cartoon::addPoint(const Vector3f& pos, const Vector3ub& color, size_t group, size_t id, Residue::SecondaryStructure sec) { CartoonType ct = secondaryToCartoonType(sec); size_t idCartoon = 0; if (m_type.size() > 0) { idCartoon = ct == m_type.back().first && m_type.size() > (SKIPPED + 1) ? m_type.back().second + 1 : 0; if (Tail == m_type.back().first && ct == Body) { for (size_t i = m_type.size(), j = 0; i > 0 && j < std::ceil(m_type.back().second / 2.0f); --i, ++j) { m_type[i - 1].first = Head; m_type[i - 1].second = j; } } if (ct == Arrow && m_type.back().first == Arrow) { m_type.back().second = 1; idCartoon = 0; } } m_type.emplace_back(ct, idCartoon); BSplineGeometry::addPoint(pos, color, m_minRadius, group, id); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/cartoongeometry.h000066400000000000000000000027551506155467400235250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_CARTOON_H #define AVOGADRO_RENDERING_CARTOON_H #include "bsplinegeometry.h" #include namespace Avogadro { namespace Rendering { enum CartoonType { Undefined = -1, // constant small radius Body = 0, // constant big radius Arrow = 3, // arrow head Head = 2, // decreasing Tail = 1 // increasing }; class AVOGADRORENDERING_EXPORT Cartoon : public BSplineGeometry { public: Cartoon(); Cartoon(float minRadius, float maxRadius); static const float ELIPSE_RATIO; void addPoint(const Vector3f& pos, const Vector3ub& color, size_t group, size_t id, Core::Residue::SecondaryStructure sec); protected: // create an ellipsis and adapt it to the affine A std::vector computeCirclePoints(const Eigen::Affine3f& a, const Eigen::Affine3f& b, bool flat) const override; float computeScale(size_t index, float t, float scale) const override; std::vector> m_type; float m_minRadius, m_maxRadius; }; } // namespace Rendering } // namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/rendering/curvegeometry.cpp000066400000000000000000000236161506155467400235360ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "curvegeometry.h" #include "camera.h" #include "scene.h" #include "visitor.h" namespace { #include "cylinders_fs.h" #include "cylinders_vs.h" } // namespace #include "avogadrogl.h" #include #include #include using std::cout; using std::endl; namespace Avogadro::Rendering { using Core::Array; const size_t CurveGeometry::SKIPPED = 1; CurveGeometry::CurveGeometry() : m_dirty(true), m_canBeFlat(true) { setRenderPass(SolidPass); } CurveGeometry::CurveGeometry(bool flat) : m_dirty(true), m_canBeFlat(flat) { setRenderPass(SolidPass); } CurveGeometry::~CurveGeometry() { for (auto& l : m_lines) { delete l; } } std::vector CurveGeometry::computeCirclePoints( const Eigen::Affine3f& a, const Eigen::Affine3f& b, bool flat) const { unsigned int circleResolution = flat ? 1 : 12; const float resolutionRadians = 2.0f * static_cast(M_PI) / static_cast(circleResolution); std::vector result; for (unsigned int i = 0; i < circleResolution; ++i) { float theta = i * resolutionRadians; Vector3f circle = Vector3f(std::cos(theta), 0.0f, std::sin(theta)); ColorNormalVertex vert1; vert1.normal = a.linear() * circle; vert1.vertex = a * circle; vert1.color = Vector3ub(0, 0, 0); result.push_back(vert1); ColorNormalVertex vert2; vert2.normal = b.linear() * circle; vert2.vertex = b * circle; vert2.color = Vector3ub(0, 0, 0); result.push_back(vert2); } return result; } float CurveGeometry::computeScale(size_t, float, float scale) const { return scale; } void CurveGeometry::update(int index) { // compute the middle points Line* line = m_lines[index]; unsigned int lineResolution = line->flat ? 20 : 15; size_t qttyPoints = line->points.size(); const size_t qttySegments = lineResolution * qttyPoints; Vector3f previous; std::vector points; size_t top = qttyPoints <= 4 ? 0 : line->points.size() - 4; auto it = line->points.begin(); for (size_t i = SKIPPED; i < top; ++i) { for (size_t j = 0; j < lineResolution; ++j) { float t = (i * lineResolution + j) / float(qttySegments); auto p = computeCurvePoint(t, line->points); if (i > SKIPPED) { Eigen::Matrix3f m; m.col(1) = (p - previous).normalized(); m.col(0) = m.col(1).unitOrthogonal() * -1.0f; if (i > SKIPPED + 1) { const auto& previousAngle = points.back().linear().col(0); float angle = previousAngle.dot(m.col(0)); // avoid nans if (std::isnan(angle)) { m.col(0) = previousAngle; break; // if angle > 90 flip it } else if (angle <= 0.0f) { m.col(0) *= -1.0f; } angle = previousAngle.dot(m.col(0)); float degrees = (std::acos(angle / (m.col(0).norm() * previousAngle.norm())) * RAD_TO_DEG_F); // if angle is > 25º get the bisector while (degrees > 25.0f) { m.col(0) = (m.col(0) + previousAngle).normalized(); angle = previousAngle.dot(m.col(0)); degrees = (std::acos(angle / (m.col(0).norm() * previousAngle.norm())) * RAD_TO_DEG_F); } } m.col(2) = m.col(0).cross(m.col(1)) * -1.0f; Eigen::Affine3f affine; affine.translation() = p; affine.linear() = m; float r = !m_canBeFlat || !line->flat ? computeScale(i, j / static_cast(lineResolution), line->radius) : 0.01f; affine.scale(r); points.push_back(affine); } previous = p; } ++it; } // prepare VBO and EBO std::vector indices; std::vector vertices; it = line->points.begin(); if (line->points.size() > 3) { it = std::next(it, 3); } for (size_t i = 1; i < points.size(); ++i) { if (i % lineResolution == 0) { ++it; } std::vector radials = computeCirclePoints(points[i], points[i - 1], line->flat); for (auto r : radials) { r.color = (*it)->color; vertices.push_back(r); } const auto tubeStart = static_cast( vertices.size() - (line->flat && m_canBeFlat ? radials.size() : 0)); for (unsigned int j = 0; j < radials.size() / 2; ++j) { unsigned int r1 = j + j; unsigned int r2 = (j != 0 ? r1 : radials.size()) - 2; indices.push_back(tubeStart + r1); indices.push_back(tubeStart + r1 + 1); indices.push_back(tubeStart + r2); indices.push_back(tubeStart + r2); indices.push_back(tubeStart + r1 + 1); indices.push_back(tubeStart + r2 + 1); } } line->vbo.upload(vertices, BufferObject::ArrayBuffer); line->ibo.upload(indices, BufferObject::ElementArrayBuffer); line->numberOfVertices = vertices.size(); line->numberOfIndices = indices.size(); line->dirty = false; } void CurveGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void checkShaderInfo(ShaderInfo& shaderInfo, const std::string& fs, const std::string& vs) { if (shaderInfo.vertexShader.type() == Shader::Unknown) { shaderInfo.vertexShader.setType(Shader::Vertex); shaderInfo.vertexShader.setSource(vs); shaderInfo.fragmentShader.setType(Shader::Fragment); shaderInfo.fragmentShader.setSource(fs); if (!shaderInfo.vertexShader.compile()) cout << shaderInfo.vertexShader.error() << endl; if (!shaderInfo.fragmentShader.compile()) cout << shaderInfo.fragmentShader.error() << endl; shaderInfo.program.attachShader(shaderInfo.vertexShader); shaderInfo.program.attachShader(shaderInfo.fragmentShader); if (!shaderInfo.program.link()) cout << shaderInfo.program.error() << endl; } } void CurveGeometry::processShaderError(bool error) { if (error) { cout << m_shaderInfo.program.error() << endl; } } void CurveGeometry::render(const Camera& camera) { if (m_dirty) { checkShaderInfo(m_shaderInfo, cylinders_fs, cylinders_vs); m_dirty = false; } if (!m_dirty) { processShaderError(!m_shaderInfo.program.bind()); processShaderError(!m_shaderInfo.program.setUniformValue( "modelView", camera.modelView().matrix())); processShaderError(!m_shaderInfo.program.setUniformValue( "projection", camera.projection().matrix())); Eigen::Matrix3f normalMatrix = camera.modelView().linear().inverse().transpose(); processShaderError( !m_shaderInfo.program.setUniformValue("normalMatrix", normalMatrix)); for (size_t i = 0; i < m_lines.size(); ++i) { Line* line = m_lines[i]; if (line->dirty) { update(i); } line->vbo.bind(); line->ibo.bind(); processShaderError(!m_shaderInfo.program.enableAttributeArray("vertex")); processShaderError(!m_shaderInfo.program.useAttributeArray( "vertex", ColorNormalVertex::vertexOffset(), sizeof(ColorNormalVertex), FloatType, 3, ShaderProgram::NoNormalize)); processShaderError(!m_shaderInfo.program.enableAttributeArray("color")); processShaderError(!m_shaderInfo.program.useAttributeArray( "color", ColorNormalVertex::colorOffset(), sizeof(ColorNormalVertex), UCharType, 3, ShaderProgram::Normalize)); processShaderError(!m_shaderInfo.program.enableAttributeArray("normal")); processShaderError(!m_shaderInfo.program.useAttributeArray( "normal", ColorNormalVertex::normalOffset(), sizeof(ColorNormalVertex), FloatType, 3, ShaderProgram::NoNormalize)); if (line->flat && m_canBeFlat) { glLineWidth(-line->radius); } glDrawRangeElements(line->flat && m_canBeFlat ? GL_LINE_STRIP : GL_TRIANGLES, 0, static_cast(line->numberOfVertices), static_cast(line->numberOfIndices), GL_UNSIGNED_INT, reinterpret_cast(0)); line->vbo.release(); line->ibo.release(); m_shaderInfo.program.disableAttributeArray("vector"); m_shaderInfo.program.disableAttributeArray("color"); m_shaderInfo.program.disableAttributeArray("normal"); } m_shaderInfo.program.release(); } } void CurveGeometry::addPoint(const Vector3f& pos, const Vector3ub& color, float radius, size_t group, size_t id) { if (m_indexMap.find(group) == m_indexMap.end()) { m_indexMap[group] = m_lines.size(); m_lines.push_back(new Line(radius)); } m_lines[m_indexMap[group]]->radius = radius; m_lines[m_indexMap[group]]->flat = radius < 0.0f; m_lines[m_indexMap[group]]->add(new Point(pos, color, id)); } Array CurveGeometry::areaHits(const Frustrum& f) const { Array result; // Check for intersection. for (const auto& line : m_lines) { size_t skip = 0; std::queue previous; for (const auto& point : line->points) { previous.push(point->id); if (skip < 2) { ++skip; continue; } int in = 0; for (; in < 4; ++in) { float dist = (point->pos - f.points[2 * in]).dot(f.planes[in]); if (dist > 0.0f) { // Outside of our frustrum, break. break; } } if (in == 4) { // The center is within the four planes that make our frustrum - hit. Identifier id; id.molecule = m_identifier.molecule; id.type = m_identifier.type; id.index = previous.front(); result.push_back(id); } previous.pop(); } } return result; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/curvegeometry.h000066400000000000000000000051521506155467400231760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_CURVEGEOMETRY_H #define AVOGADRO_RENDERING_CURVEGEOMETRY_H #include "bufferobject.h" #include "drawable.h" #include "shader.h" #include "shaderprogram.h" #include #include #include #include namespace Avogadro { namespace Rendering { struct ShaderInfo { Shader vertexShader; Shader fragmentShader; ShaderProgram program; }; struct Point { Point(const Vector3f& p, const Vector3ub& c, size_t i) : pos(p), color(c), id(i) { } Vector3f pos; Vector3ub color; size_t id; }; struct Line { Line() : dirty(true), flat(true), radius(0.0f) {} explicit Line(float r) : dirty(true), radius(r) { flat = r < 0.0f; } ~Line() { for (auto& p : points) { delete p; } } void add(Point* point) { points.push_back(point); dirty = true; } std::list points; bool dirty; bool flat; // use GL_POINTS float radius; BufferObject vbo; BufferObject ibo; // EBO/IBO size_t numberOfVertices; size_t numberOfIndices; }; class AVOGADRORENDERING_EXPORT CurveGeometry : public Drawable { public: CurveGeometry(); CurveGeometry(bool flat); ~CurveGeometry() override; /** * Accept a visit from our friendly visitor. */ void accept(Visitor& visitor) override; /** * @brief Render the cylinder geometry. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; void addPoint(const Vector3f& pos, const Vector3ub& color, float radius, size_t group, size_t id); const std::vector& lines() const { return m_lines; }; const static size_t SKIPPED; protected: std::vector m_lines; std::map m_indexMap; ShaderInfo m_shaderInfo; bool m_dirty; bool m_canBeFlat; virtual void update(int index); virtual Vector3f computeCurvePoint(float t, const std::list& points) const = 0; virtual std::vector computeCirclePoints( const Eigen::Affine3f& a, const Eigen::Affine3f& b, bool flat) const; virtual float computeScale(size_t index, float t, float scale) const; void processShaderError(bool error); Core::Array areaHits(const Frustrum& f) const override; }; } // End namespace Rendering } // End namespace Avogadro #endif avogadrolibs-1.101.0/avogadro/rendering/cylindergeometry.cpp000066400000000000000000000243721506155467400242230ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "cylindergeometry.h" #include "camera.h" #include "scene.h" #include "visitor.h" #include "bufferobject.h" #include "shader.h" #include "shaderprogram.h" namespace { #include "cylinders_fs.h" #include "cylinders_vs.h" } // namespace #include "avogadrogl.h" #include #include using std::cout; using std::endl; namespace Avogadro::Rendering { class CylinderGeometry::Private { public: Private() {} BufferObject vbo; BufferObject ibo; inline static Shader* vertexShader = nullptr; inline static Shader* fragmentShader = nullptr; inline static ShaderProgram* program = nullptr; size_t numberOfVertices; size_t numberOfIndices; }; CylinderGeometry::CylinderGeometry() : m_dirty(false), d(new Private) { setRenderPass(SolidPass); } CylinderGeometry::CylinderGeometry(const CylinderGeometry& other) : Drawable(other), m_cylinders(other.m_cylinders), m_indices(other.m_indices), m_indexMap(other.m_indexMap), m_dirty(true), d(new Private) { setRenderPass(SolidPass); } CylinderGeometry::~CylinderGeometry() { delete d; } void CylinderGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void CylinderGeometry::update() { if (m_indices.empty() || m_cylinders.empty()) return; // Check if the VBOs are ready, if not get them ready. if (!d->vbo.ready() || m_dirty) { // Set some defaults for our cylinders. const unsigned int resolution = 8; // points per circle const float resolutionRadians = 2.0f * static_cast(M_PI) / static_cast(resolution); std::vector radials; radials.reserve(resolution); std::vector cylinderIndices; std::vector cylinderVertices; // cylinderIndices.reserve(m_indices.size() * 4); // cylinderVertices.reserve(m_cylinders.size() * 4); auto itIndex = m_indices.begin(); auto itCylinder = m_cylinders.begin(); for (unsigned int i = 0; itIndex != m_indices.end() && itCylinder != m_cylinders.end(); ++i, ++itIndex, ++itCylinder) { const Vector3f& position1 = itCylinder->end1; const Vector3f& position2 = itCylinder->end2; const Vector3f direction = (position2 - position1).normalized(); float radius = itCylinder->radius; // Generate the radial vectors Vector3f radialVec = direction.unitOrthogonal() * radius; Eigen::AngleAxisf transform(resolutionRadians, direction); radials.clear(); for (unsigned int j = 0; j < resolution; ++j) { radials.push_back(radialVec); radialVec = transform * radialVec; } // Cylinder ColorNormalVertex vert(itCylinder->color, -direction, position1); ColorNormalVertex vert2(itCylinder->color2, -direction, position1); const auto tubeStart = static_cast(cylinderVertices.size()); for (auto& radial : radials) { vert.normal = radial; vert.vertex = position1 + radial; cylinderVertices.push_back(vert); vert2.normal = vert.normal; vert2.vertex = position2 + radial; cylinderVertices.push_back(vert2); } // Now to stitch it together. for (unsigned int j = 0; j < resolution; ++j) { unsigned int r1 = j + j; unsigned int r2 = (j != 0 ? r1 : resolution + resolution) - 2; cylinderIndices.push_back(tubeStart + r1); cylinderIndices.push_back(tubeStart + r1 + 1); cylinderIndices.push_back(tubeStart + r2); cylinderIndices.push_back(tubeStart + r2); cylinderIndices.push_back(tubeStart + r1 + 1); cylinderIndices.push_back(tubeStart + r2 + 1); } } d->vbo.upload(cylinderVertices, BufferObject::ArrayBuffer); d->ibo.upload(cylinderIndices, BufferObject::ElementArrayBuffer); d->numberOfVertices = cylinderVertices.size(); d->numberOfIndices = cylinderIndices.size(); m_dirty = false; } // Build and link the shader if it has not been used yet. if (d->vertexShader == nullptr) { d->vertexShader = new Shader; d->vertexShader->setType(Shader::Vertex); d->vertexShader->setSource(cylinders_vs); d->fragmentShader = new Shader; d->fragmentShader->setType(Shader::Fragment); d->fragmentShader->setSource(cylinders_fs); if (!d->vertexShader->compile()) cout << d->vertexShader->error() << endl; if (!d->fragmentShader->compile()) cout << d->fragmentShader->error() << endl; if (d->program == nullptr) d->program = new ShaderProgram; d->program->attachShader(*d->vertexShader); d->program->attachShader(*d->fragmentShader); if (!d->program->link()) cout << d->program->error() << endl; } } void CylinderGeometry::render(const Camera& camera) { if (m_indices.empty() || m_cylinders.empty()) return; // Prepare the VBOs, IBOs and shader program if necessary. update(); if (!d->program->bind()) cout << d->program->error() << endl; d->vbo.bind(); d->ibo.bind(); // Set up our attribute arrays. if (!d->program->enableAttributeArray("vertex")) cout << d->program->error() << endl; if (!d->program->useAttributeArray( "vertex", ColorNormalVertex::vertexOffset(), sizeof(ColorNormalVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program->error() << endl; } if (!d->program->enableAttributeArray("color")) cout << d->program->error() << endl; if (!d->program->useAttributeArray("color", ColorNormalVertex::colorOffset(), sizeof(ColorNormalVertex), UCharType, 3, ShaderProgram::Normalize)) { cout << d->program->error() << endl; } if (!d->program->enableAttributeArray("normal")) cout << d->program->error() << endl; if (!d->program->useAttributeArray( "normal", ColorNormalVertex::normalOffset(), sizeof(ColorNormalVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program->error() << endl; } // Set up our uniforms (model-view and projection matrices right now). if (!d->program->setUniformValue("modelView", camera.modelView().matrix())) { cout << d->program->error() << endl; } if (!d->program->setUniformValue("projection", camera.projection().matrix())) { cout << d->program->error() << endl; } if (!d->program->setUniformValue("opacity", m_opacity)) { cout << d->program->error() << endl; } Matrix3f normalMatrix = camera.modelView().linear().inverse().transpose(); if (!d->program->setUniformValue("normalMatrix", normalMatrix)) std::cout << d->program->error() << std::endl; // Render the loaded spheres using the shader and bound VBO. glDrawRangeElements(GL_TRIANGLES, 0, static_cast(d->numberOfVertices), static_cast(d->numberOfIndices), GL_UNSIGNED_INT, reinterpret_cast(0)); d->vbo.release(); d->ibo.release(); d->program->disableAttributeArray("vector"); d->program->disableAttributeArray("color"); d->program->disableAttributeArray("normal"); d->program->release(); } std::multimap CylinderGeometry::hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const { std::multimap result; for (size_t i = 0; i < m_cylinders.size(); ++i) { const CylinderColor& cylinder = m_cylinders[i]; // Check for cylinder intersection with the ray. Vector3f ao = rayOrigin - cylinder.end1; Vector3f ab = cylinder.end2 - cylinder.end1; Vector3f aoxab = ao.cross(ab); Vector3f vxab = rayDirection.cross(ab); float A = vxab.dot(vxab); float B = 2.0f * vxab.dot(aoxab); float C = aoxab.dot(aoxab) - ab.dot(ab) * (cylinder.radius * cylinder.radius); float D = B * B - 4.0f * A * C; // no intersection if (D < 0.0f) continue; float t = std::min((-B + std::sqrt(D)) / (2.0f * A), (-B - std::sqrt(D)) / (2.0f * A)); Vector3f ip = rayOrigin + (rayDirection * t); Vector3f ip1 = ip - cylinder.end1; Vector3f ip2 = ip - (cylinder.end1 + ab); // intersection below base or above top of the cylinder if (ip1.dot(ab) < 0.0f || ip2.dot(ab) > 0.0f) continue; // Test for clipping Vector3f distance = ip - rayOrigin; if (distance.dot(rayDirection) < 0.0f || (ip - rayEnd).dot(rayDirection) > 0.0f) continue; Identifier id; id.molecule = m_identifier.molecule; id.type = m_identifier.type; id.index = i; if (m_indexMap.size()) id.index = m_indexMap.find(i)->second; if (id.type != InvalidType) { float depth = distance.norm(); result.insert(std::pair(depth, id)); } } return result; } void CylinderGeometry::addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& color) { addCylinder(pos1, pos2, radius, color, color); } void CylinderGeometry::addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& colorStart, const Vector3ub& colorEnd) { m_dirty = true; m_cylinders.emplace_back(pos1, pos2, radius, colorStart, colorEnd); m_indices.push_back(m_indices.size()); } void CylinderGeometry::addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& color, size_t index) { m_indexMap[m_cylinders.size()] = index; addCylinder(pos1, pos2, radius, color, color); } void CylinderGeometry::addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& colorStart, const Vector3ub& colorEnd, size_t index) { m_indexMap[m_cylinders.size()] = index; addCylinder(pos1, pos2, radius, colorStart, colorEnd); } void CylinderGeometry::clear() { m_cylinders.clear(); m_indices.clear(); m_indexMap.clear(); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/cylindergeometry.h000066400000000000000000000121051506155467400236570ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_CYLINDERGEOMETRY_H #define AVOGADRO_RENDERING_CYLINDERGEOMETRY_H #include "drawable.h" #include namespace Avogadro { namespace Rendering { struct CylinderColor { CylinderColor(const Vector3f& pos1, const Vector3f& pos2, float r, const Vector3ub& c, const Vector3ub& c2 = Vector3ub::Zero()) : end1(pos1), end2(pos2), radius(r), color(c), color2(c2) { } Vector3f end1; Vector3f end2; float radius; Vector3ub color; Vector3ub color2; }; /** * @class CylinderGeometry cylindergeometry.h * * @brief The CylinderGeometry contains one or more cylinders. * @author Marcus D. Hanwell */ class AVOGADRORENDERING_EXPORT CylinderGeometry : public Drawable { public: CylinderGeometry(); CylinderGeometry(const CylinderGeometry& other); ~CylinderGeometry() override; CylinderGeometry& operator=(CylinderGeometry); friend void swap(CylinderGeometry& lhs, CylinderGeometry& rhs); /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Update the VBOs, IBOs etc ready for rendering. */ void update(); /** * @brief Render the cylinder geometry. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; /** * Return the primitives that are hit by the ray. * @param rayOrigin Origin of the ray. * @param rayEnd End point of the ray. * @param rayDirection Normalized direction of the ray. * @return Sorted collection of primitives that were hit. */ std::multimap hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const override; /** * @brief Add a cylinder to the geometry object. * @param pos1 Base of the cylinder axis. * @param pos2 Top of the cylinder axis. * @param radius Radius of the cylinder. * @param color Color the cylinder will be rendered. */ void addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& color); /** * @brief Add a cylinder to the geometry object. * @param pos1 Base of the cylinder axis. * @param pos2 Top of the cylinder axis. * @param radius Radius of the cylinder. * @param color1 Color the start of the base of the cylinder. * @param color2 Color of the end of the cylinder. */ void addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& color1, const Vector3ub& color2); /** * @brief Add a cylinder to the geometry object. * @param pos1 Base of the cylinder axis. * @param pos2 Top of the cylinder axis. * @param radius Radius of the cylinder. * @param color Color the cylinder will be rendered. * @param index The index of the cylinder being added. */ void addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& color, size_t index); /** * @brief Add a cylinder to the geometry object. * @param pos1 Base of the cylinder axis. * @param pos2 Top of the cylinder axis. * @param radius Radius of the cylinder. * @param color Color the start of the base of the cylinder. * @param color2 Color of the end of the cylinder. * @param index The index of the cylinder being added. */ void addCylinder(const Vector3f& pos1, const Vector3f& pos2, float radius, const Vector3ub& color, const Vector3ub& color2, size_t index); /** * Get a reference to the cylinders. */ std::vector& cylinders() { return m_cylinders; } const std::vector& cylinders() const { return m_cylinders; } /** * Clear the contents of the node. */ void clear() override; /** * Get the number of cylinders in the node object. */ size_t size() const { return m_cylinders.size(); } /** * Set the opacity of the cylinders in this group. */ void setOpacity(float o) { m_opacity = o; if (o < 1.0f) setRenderPass(TranslucentPass); } private: std::vector m_cylinders; std::vector m_indices; std::map m_indexMap; float m_opacity = 1.0f; bool m_dirty; class Private; Private* d; }; inline CylinderGeometry& CylinderGeometry::operator=(CylinderGeometry other) { using std::swap; swap(*this, other); return *this; } inline void swap(CylinderGeometry& lhs, CylinderGeometry& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_cylinders, rhs.m_cylinders); swap(lhs.m_indices, rhs.m_indices); swap(lhs.m_indexMap, rhs.m_indexMap); lhs.m_dirty = rhs.m_dirty = true; } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_CYLINDERGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/cylinders_fs.glsl000066400000000000000000000007301506155467400234710ustar00rootroot00000000000000varying vec3 fnormal; uniform float opacity; void main() { vec3 N = normalize(fnormal); vec3 L = normalize(vec3(0, 1, 1)); vec3 E = vec3(0, 0, 1); vec3 H = normalize(L + E); float df = max(0.0, dot(N, L)); float sf = max(0.0, dot(N, H)); vec4 ambient = 0.4 * gl_Color; vec4 diffuse = 0.55 * gl_Color; vec4 specular = 0.5 * (vec4(1, 1, 1, 1) - gl_Color); gl_FragColor = ambient + df * diffuse + pow(sf, 20.0) * specular; gl_FragColor.a = opacity; } avogadrolibs-1.101.0/avogadro/rendering/cylinders_vs.glsl000066400000000000000000000004741506155467400235160ustar00rootroot00000000000000attribute vec4 vertex; attribute vec3 color; attribute vec3 normal; uniform mat4 modelView; uniform mat4 projection; uniform mat3 normalMatrix; varying vec3 fnormal; void main() { gl_FrontColor = vec4(color, 1.0); gl_Position = projection * modelView * vertex; fnormal = normalize(normalMatrix * normal); } avogadrolibs-1.101.0/avogadro/rendering/dashedline_fs.glsl000066400000000000000000000000531506155467400235730ustar00rootroot00000000000000void main() { gl_FragColor = gl_Color; } avogadrolibs-1.101.0/avogadro/rendering/dashedline_vs.glsl000066400000000000000000000002721506155467400236160ustar00rootroot00000000000000attribute vec4 vertex; attribute vec4 color; uniform mat4 modelView; uniform mat4 projection; void main() { gl_FrontColor = color; gl_Position = projection * modelView * vertex; } avogadrolibs-1.101.0/avogadro/rendering/dashedlinegeometry.cpp000066400000000000000000000122741506155467400245100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "dashedlinegeometry.h" #include "avogadrogl.h" #include "bufferobject.h" #include "camera.h" #include "scene.h" #include "shader.h" #include "shaderprogram.h" #include "visitor.h" #include #include #include #include namespace { #include "dashedline_fs.h" #include "dashedline_vs.h" } // namespace using Avogadro::Vector3f; using Avogadro::Vector3ub; using Avogadro::Vector4ub; using std::cout; using std::endl; namespace Avogadro::Rendering { class DashedLineGeometry::Private { public: Private() {} BufferObject vbo; Shader vertexShader; Shader fragmentShader; ShaderProgram program; }; DashedLineGeometry::DashedLineGeometry() : m_lineWidth(1.0), m_lineCount(0), m_color(255, 0, 0), m_opacity(255), m_dirty(false), d(new Private) { } DashedLineGeometry::DashedLineGeometry(const DashedLineGeometry& other) : Drawable(other), m_vertices(other.m_vertices), m_lineWidth(other.m_lineWidth), m_lineCount(other.m_lineCount), m_dirty(true), d(new Private) { } DashedLineGeometry::~DashedLineGeometry() { delete d; } void DashedLineGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void DashedLineGeometry::update() { if (m_vertices.empty()) return; // Check if the VBOs are ready, if not get them ready. if (!d->vbo.ready() || m_dirty) { d->vbo.upload(m_vertices, BufferObject::ArrayBuffer); m_dirty = false; } // Build and link the shader if it has not been used yet. if (d->vertexShader.type() == Shader::Unknown) { d->vertexShader.setType(Shader::Vertex); d->vertexShader.setSource(dashedline_vs); d->fragmentShader.setType(Shader::Fragment); d->fragmentShader.setSource(dashedline_fs); if (!d->vertexShader.compile()) cout << d->vertexShader.error() << endl; if (!d->fragmentShader.compile()) cout << d->fragmentShader.error() << endl; d->program.attachShader(d->vertexShader); d->program.attachShader(d->fragmentShader); if (!d->program.link()) cout << d->program.error() << endl; d->program.detachShader(d->vertexShader); d->program.detachShader(d->fragmentShader); d->vertexShader.cleanup(); d->fragmentShader.cleanup(); } } void DashedLineGeometry::render(const Camera& camera) { if (m_vertices.empty()) return; // Prepare the VBO and shader program if necessary. update(); if (!d->program.bind()) cout << d->program.error() << endl; d->vbo.bind(); // Set up our attribute arrays. if (!d->program.enableAttributeArray("vertex")) cout << d->program.error() << endl; if (!d->program.useAttributeArray("vertex", PackedVertex::vertexOffset(), sizeof(PackedVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program.error() << endl; } if (!d->program.enableAttributeArray("color")) cout << d->program.error() << endl; if (!d->program.useAttributeArray("color", PackedVertex::colorOffset(), sizeof(PackedVertex), UCharType, 4, ShaderProgram::Normalize)) { cout << d->program.error() << endl; } // Set up our uniforms (model-view and projection matrices right now). if (!d->program.setUniformValue("modelView", camera.modelView().matrix())) { cout << d->program.error() << endl; } if (!d->program.setUniformValue("projection", camera.projection().matrix())) { cout << d->program.error() << endl; } glEnable(GL_LINE_SMOOTH); glLineWidth(m_lineWidth); // Render the lines using the shader and bound VBO. glDrawArrays(GL_LINES, static_cast(0), static_cast(m_vertices.size())); glDisable(GL_LINE_SMOOTH); d->vbo.release(); d->program.release(); } void DashedLineGeometry::clear() { m_vertices.clear(); m_dirty = true; } size_t DashedLineGeometry::addDashedLine(const Vector3f& start, const Vector3f& end, const Vector4ub& rgba, int dashCount) { const int vertexCount = 2 * dashCount; Vector3f delta = (end - start) / (vertexCount - 1); Vector3f current = start; for (int n = 0; n < vertexCount; n++) { m_vertices.push_back(PackedVertex(current, rgba)); current += delta; } m_lineCount++; m_dirty = true; return m_lineCount - 1; } size_t DashedLineGeometry::addDashedLine(const Vector3f& start, const Vector3f& end, const Vector3ub& rgb, int dashCount) { Vector4ub rgba = Vector4ub(rgb(0), rgb(1), rgb(2), m_opacity); return addDashedLine(start, end, rgba, dashCount); } size_t DashedLineGeometry::addDashedLine(const Vector3f& start, const Vector3f& end, int dashCount) { return addDashedLine(start, end, m_color, dashCount); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/dashedlinegeometry.h000066400000000000000000000101331506155467400241450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_DASHEDLINEGEOMETRY_H #define AVOGADRO_RENDERING_DASHEDLINEGEOMETRY_H #include "drawable.h" #include namespace Avogadro { namespace Rendering { /** * @class DashedLineGeometry DashedLinegeometry.h * * @brief The DashedLineGeometry class is used to store sets of dashed lines. */ class AVOGADRORENDERING_EXPORT DashedLineGeometry : public Drawable { public: struct PackedVertex { // 16 bytes total: Vector3f vertex; // 12 bytes Vector4ub color; // 4 bytes PackedVertex(const Vector3f& v, const Vector4ub& c) : vertex(v), color(c) {} static int vertexOffset() { return 0; } static int colorOffset() { return static_cast(sizeof(Vector3f)); } }; DashedLineGeometry(); DashedLineGeometry(const DashedLineGeometry& other); ~DashedLineGeometry() override; DashedLineGeometry& operator=(DashedLineGeometry); friend void swap(DashedLineGeometry& lhs, DashedLineGeometry& rhs); /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Render the line strips. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; /** * Clear the contents of the node. */ void clear() override; /** * Add a complete line strip to the object. * @param vertices The 3D vertices which will be connected to form the line * strip. * @param color Vertex color. If not specified, use the current color() and * opacity(). If the 3 component color is set, the current opacity() is used. * @param lineWidth The width of the line strip. * @note All arrays must be the same length, or this function call will fail, * returning InvalidIndex. * @return The index of the first vertex added by this call. * @{ */ size_t addDashedLine(const Vector3f& start, const Vector3f& end, const Vector4ub& color, int dashCount); size_t addDashedLine(const Vector3f& start, const Vector3f& end, const Vector3ub& color, int dashCount); size_t addDashedLine(const Vector3f& start, const Vector3f& end, int dashCount); /** @} */ /** * The default color of the lines. This is used to set the color of new * vertices when no explicit vertex color is specified. * @{ */ void setColor(const Vector3ub& c) { m_color = c; } Vector3ub color() const { return m_color; } /** @} */ /** * The default opacity of the lines. This is used when either no explicit * vertex color is specified, or a three component color is used. * @{ */ void setOpacity(unsigned char opacity_) { m_opacity = opacity_; } unsigned char opacity() const { return m_opacity; } /** @} */ /** * The line width used for all of the lines. * @{ */ void setLineWidth(unsigned char lineWidth_) { m_lineWidth = lineWidth_; } unsigned char lineWidth() const { return m_lineWidth; } /** @} */ private: /** * @brief Update the VBOs, IBOs etc ready for rendering. */ void update(); Core::Array m_vertices; float m_lineWidth; int m_lineCount; Vector3ub m_color; unsigned char m_opacity; bool m_dirty; class Private; Private* d; }; inline DashedLineGeometry& DashedLineGeometry::operator=( DashedLineGeometry other) { using std::swap; swap(*this, other); return *this; } inline void swap(DashedLineGeometry& lhs, DashedLineGeometry& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_vertices, rhs.m_vertices); swap(lhs.m_color, rhs.m_color); swap(lhs.m_opacity, rhs.m_opacity); lhs.m_dirty = rhs.m_dirty = true; } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_DASHEDLINEGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/drawable.cpp000066400000000000000000000023751506155467400224160ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "drawable.h" #include "visitor.h" namespace Avogadro::Rendering { using Core::Array; Drawable::Drawable() : m_parent(nullptr), m_visible(true), m_renderPass(OpaquePass) { } Drawable::Drawable(const Drawable& other) : m_parent(other.m_parent), m_visible(other.m_visible), m_renderPass(other.m_renderPass), m_identifier(other.m_identifier) { } Drawable::~Drawable() {} void Drawable::accept(Visitor& visitor) { visitor.visit(*this); } void Drawable::render(const Camera&) {} std::multimap Drawable::hits(const Vector3f&, const Vector3f&, const Vector3f&) const { return std::multimap(); } Array Drawable::areaHits(const Frustrum&) const { return Array(); } void Drawable::clear() {} void Drawable::setParent(GeometryNode* parent_) { m_parent = parent_; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/drawable.h000066400000000000000000000076331506155467400220650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_DRAWABLE_H #define AVOGADRO_RENDERING_DRAWABLE_H #include "avogadrorenderingexport.h" #include "avogadrorendering.h" #include "primitive.h" #include #include #include namespace Avogadro { namespace Rendering { class Camera; class GeometryNode; class Visitor; /** * @class Drawable drawable.h * @brief The base class for all drawable geometry and types. * @author Marcus D. Hanwell * * This class provides the common API for drawable objects on the scene. It is * not a Node object, and can only be attached to GeometryNode objects in the * Scene. */ class AVOGADRORENDERING_EXPORT Drawable { public: Drawable(); Drawable(const Drawable& other); virtual ~Drawable(); Drawable& operator=(Drawable); friend void swap(Drawable& lhs, Drawable& rhs); /** * Accept a visit from our friendly visitor. */ virtual void accept(Visitor&); /** * @brief Get a pointer to the drawable object's parent. * @return Pointer to the parent node, nullptr if no parent. */ const GeometryNode* parent() const { return m_parent; } GeometryNode* parent() { return m_parent; } /** * @brief Set the visibility of the drawable object. * @param visibility True if the drawable is visible, false if invisible. */ void setVisible(bool visibility) { m_visible = visibility; } /** * @brief Get the current visibility of the drawable. * @return True if visible. */ bool isVisible() const { return m_visible; } /** * The render pass in which this drawable should be rendered. * @sa Rendering::RenderPass * @{ */ void setRenderPass(RenderPass pass) { m_renderPass = pass; } RenderPass renderPass() const { return m_renderPass; } /** @} */ /** * @brief Render the contents of the drawable. * @param camera The current Camera. */ virtual void render(const Camera& camera); /** * Get the identifier for the object, this stores the parent Molecule and * the type represented by the geometry. */ Identifier& identifier() { return m_identifier; } const Identifier& identifier() const { return m_identifier; } /** * Return the primitives that are hit by the ray. * @param rayOrigin Origin of the ray. * @param rayEnd End point of the ray. * @param rayDirection Normalized direction of the ray. * @return Sorted collection of primitives that were hit. */ virtual std::multimap hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const; /** * Return the primitives within the supplied area. * @param f The frustrum defining the area highlighted. * @return Collection of primitives in the area. */ virtual Core::Array areaHits(const Frustrum& f) const; /** * Clear the contents of the node. */ virtual void clear(); protected: friend class GeometryNode; /** * @brief Set the parent node for the node. * @param parent The parent, a value of nullptr denotes no parent node. */ void setParent(GeometryNode* parent); GeometryNode* m_parent; bool m_visible; RenderPass m_renderPass; Identifier m_identifier; }; inline Drawable& Drawable::operator=(Drawable rhs) { using std::swap; swap(*this, rhs); return *this; } inline void swap(Drawable& lhs, Drawable& rhs) { using std::swap; swap(lhs.m_parent, rhs.m_parent); swap(lhs.m_visible, rhs.m_visible); swap(lhs.m_renderPass, rhs.m_renderPass); swap(lhs.m_identifier, rhs.m_identifier); } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_SPHERENODE_H avogadrolibs-1.101.0/avogadro/rendering/geometrynode.cpp000066400000000000000000000047541506155467400233410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "geometrynode.h" #include "drawable.h" #include "visitor.h" #include namespace Avogadro::Rendering { using Core::Array; GeometryNode::GeometryNode() {} GeometryNode::~GeometryNode() { clearDrawables(); } void GeometryNode::accept(Visitor& visitor) { visitor.visit(*this); for (auto& m_drawable : m_drawables) { m_drawable->accept(visitor); } } void GeometryNode::addDrawable(Drawable* object) { for (auto& m_drawable : m_drawables) { if (m_drawable == object) return; } object->setParent(this); m_drawables.push_back(object); } bool GeometryNode::removeDrawable(Drawable* object) { if (!object) return false; for (auto it = m_drawables.begin(); it != m_drawables.end(); ++it) { if (*it == object) { (*it)->setParent(nullptr); m_drawables.erase(it); return true; } } return false; } Drawable* GeometryNode::drawable(size_t index) { if (index >= m_drawables.size()) return nullptr; else return m_drawables[index]; } void GeometryNode::clearDrawables() { // Like all good parents, we destroy our children before we go... for (auto& m_drawable : m_drawables) { delete m_drawable; } m_drawables.clear(); } void GeometryNode::render(const Camera& camera) { for (auto& m_drawable : m_drawables) { if (m_drawable->isVisible()) m_drawable->render(camera); } } std::multimap GeometryNode::hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const { std::multimap result; for (auto m_drawable : m_drawables) { std::multimap drawableHits; if (m_drawable->isVisible()) drawableHits = m_drawable->hits(rayOrigin, rayEnd, rayDirection); result.insert(drawableHits.begin(), drawableHits.end()); } return result; } Array GeometryNode::areaHits(const Frustrum& f) const { Array result; for (auto m_drawable : m_drawables) { Array drawableHits; if (m_drawable->isVisible()) drawableHits = m_drawable->areaHits(f); result.insert(result.end(), drawableHits.begin(), drawableHits.end()); } return result; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/geometrynode.h000066400000000000000000000055231506155467400230010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_GEOMETRYNODE_H #define AVOGADRO_RENDERING_GEOMETRYNODE_H #include "node.h" #include "primitive.h" #include #include #include #include namespace Avogadro { namespace Rendering { class Camera; class Drawable; /** * @class GeometryNode geometrynode.h * @brief The GeometryNode class is the common base of all geometry nodes. * @author Marcus D. Hanwell * * The GeometryNode contains any Drawable objects, and is the only node type * that results in anything being rendered to the screen. */ class AVOGADRORENDERING_EXPORT GeometryNode : public Node { public: GeometryNode(); ~GeometryNode() override; /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Add a drawable object to the geometry node. * @param object Drawable object to be added. */ void addDrawable(Drawable* object); /** * @brief Remove child node, this node will no longer be deleted. * @param node Node to be removed. * @return True if the node was removed, false if it was not found. */ bool removeDrawable(Drawable* node); /** * @brief Get the child Node at the specified index. * @param index The index of the child. * @return A pointer to the child node, or nullptr if the index is out of * range. */ Drawable* drawable(size_t index); /** * @brief Get a reference to the child nodes list. */ std::vector& drawables() { return m_drawables; } const std::vector drawables() const { return m_drawables; } /** * @brief Remove all drawable objects. */ void clearDrawables(); /** * @brief Render the drawables in the geometry node. */ void render(const Camera& camera); /** * Return the primitives that are hit by the ray. * @param rayOrigin Origin of the ray. * @param rayEnd End point of the ray. * @param rayDirection Normalized direction of the ray. * @return Sorted collection of primitives that were hit. */ std::multimap hits(const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const; /** * Return the primitives within the supplied frustrum. */ Core::Array areaHits(const Frustrum& frustrum) const; protected: std::vector m_drawables; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_GEOMETRYNODE_H avogadrolibs-1.101.0/avogadro/rendering/geometryvisitor.cpp000066400000000000000000000113141506155467400241010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "geometryvisitor.h" #include "ambientocclusionspheregeometry.h" #include "curvegeometry.h" #include "linestripgeometry.h" #include "spheregeometry.h" namespace Avogadro::Rendering { GeometryVisitor::GeometryVisitor() : m_center(Vector3f::Zero()), m_radius(0.0f), m_dirty(false) { } GeometryVisitor::~GeometryVisitor() {} void GeometryVisitor::visit(Drawable&) {} void GeometryVisitor::visit(SphereGeometry& geometry) { const Core::Array& spheres = geometry.spheres(); if (!spheres.size()) return; m_dirty = true; Vector3f tmpCenter(Vector3f::Zero()); // First find the center of the sphere geometry. auto it = spheres.begin(); for (; it != spheres.end(); ++it) tmpCenter += it->center; tmpCenter /= static_cast(spheres.size()); // Now find its radius. float tmpRadius(0.0f); if (spheres.size() > 1) { for (it = spheres.begin(); it != spheres.end(); ++it) { float distance = (it->center - tmpCenter).squaredNorm(); if (distance > tmpRadius) tmpRadius = distance; } } tmpRadius = std::sqrt(tmpRadius); m_centers.push_back(tmpCenter); m_radii.push_back(tmpRadius); } void GeometryVisitor::visit(AmbientOcclusionSphereGeometry& geometry) { const Core::Array& spheres = geometry.spheres(); if (!spheres.size()) return; m_dirty = true; Vector3f tmpCenter(Vector3f::Zero()); // First find the center of the sphere geometry. auto it = spheres.begin(); for (; it != spheres.end(); ++it) tmpCenter += it->center; tmpCenter /= static_cast(spheres.size()); // Now find its radius. float tmpRadius(0.0f); if (spheres.size() > 1) { for (it = spheres.begin(); it != spheres.end(); ++it) { float distance = (it->center - tmpCenter).squaredNorm(); if (distance > tmpRadius) tmpRadius = distance; } } tmpRadius = std::sqrt(tmpRadius); m_centers.push_back(tmpCenter); m_radii.push_back(tmpRadius); } void GeometryVisitor::visit(CurveGeometry& cg) { const auto& lines = cg.lines(); if (lines.size() == 0) { return; } m_dirty = true; float qtty = 0.0f; Vector3f tmpCenter(Vector3f::Zero()); for (const auto& line : lines) { for (const auto& point : line->points) { tmpCenter += point->pos; } qtty += line->points.size(); } tmpCenter /= qtty; float tmpRadius = 0.0f; for (const auto& line : lines) { for (const auto& point : line->points) { float distance = (point->pos - tmpCenter).squaredNorm(); if (distance > tmpRadius) tmpRadius = distance; } } m_centers.push_back(tmpCenter); m_radii.push_back(std::sqrt(tmpRadius)); } void GeometryVisitor::visit(LineStripGeometry& lsg) { typedef Core::Array VertexArray; const VertexArray verts(lsg.vertices()); if (!verts.size()) return; m_dirty = true; Vector3f tmpCenter(Vector3f::Zero()); for (const auto& vert : verts) { tmpCenter += vert.vertex; } tmpCenter /= static_cast(verts.size()); float tmpRadius(0.f); for (const auto& vert : verts) { float distance = (vert.vertex - tmpCenter).squaredNorm(); if (distance > tmpRadius) tmpRadius = distance; } m_centers.push_back(tmpCenter); m_radii.push_back(std::sqrt(tmpRadius)); } void GeometryVisitor::clear() { m_center = Vector3f::Zero(); m_radius = 0.0f; m_dirty = false; m_centers.clear(); m_radii.clear(); } Vector3f GeometryVisitor::center() { average(); return m_center; } float GeometryVisitor::radius() { average(); return m_radius; } void GeometryVisitor::average() { if (!m_dirty) return; // Find the average position of the center, then the minimal enclosing radius. m_dirty = false; if (m_centers.size() == 1) { m_center = m_centers[0]; m_radius = m_radii[0]; } else { m_center = Vector3f::Zero(); std::vector::const_iterator cit; for (cit = m_centers.begin(); cit != m_centers.end(); ++cit) m_center += *cit; m_center /= static_cast(m_centers.size()); // Now find the smallest enclosing radius for the new center. m_radius = 0.0f; std::vector::const_iterator rit; for (cit = m_centers.begin(), rit = m_radii.begin(); cit != m_centers.end() && rit != m_radii.end(); ++cit, ++rit) { float distance = (m_center - (*cit)).norm() + (*rit); if (distance > m_radius) m_radius = distance; } } } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/geometryvisitor.h000066400000000000000000000041131506155467400235450ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_GEOMETRYVISITOR_H #define AVOGADRO_RENDERING_GEOMETRYVISITOR_H #include "visitor.h" #include #include namespace Avogadro { namespace Rendering { /** * @class GeometryVisitor geometryvisitor.h * * @brief Visitor that determines the geometry of the scene. * @author Marcus D. Hanwell * * This visitor will attempt to determine the geometry of the scene, most * notably the center and radius of the bounding sphere. */ class GeometryVisitor : public Visitor { public: GeometryVisitor(); ~GeometryVisitor() override; /** * The overloaded visit functions, the base versions of which do nothing. */ void visit(Node&) override { return; } void visit(GroupNode&) override { return; } void visit(GeometryNode&) override { return; } void visit(Drawable&) override; void visit(SphereGeometry&) override; void visit(AmbientOcclusionSphereGeometry&) override; void visit(CurveGeometry&) override; void visit(CylinderGeometry&) override { return; } void visit(MeshGeometry&) override { return; } void visit(TextLabel2D&) override { return; } void visit(TextLabel3D&) override { return; } void visit(LineStripGeometry&) override; /** * Clear the state of the visitor. */ void clear(); /** * Get the position of the center of the scene. */ Vector3f center(); /** * Get the radius of the scene. */ float radius(); private: /** * Get the average of the accumulated spherical centers and minimal radius. */ void average(); Vector3f m_center; float m_radius; bool m_dirty; std::vector m_centers; std::vector m_radii; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_GEOMETRYVISITOR_H avogadrolibs-1.101.0/avogadro/rendering/glrenderer.cpp000066400000000000000000000265521506155467400227710ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "glrenderer.h" #include "avogadrogl.h" #include "geometrynode.h" #include "glrendervisitor.h" #include "shader.h" #include "shaderprogram.h" #include "textlabel2d.h" #include "textlabel3d.h" #include "textrenderstrategy.h" #include "visitor.h" #include #include namespace Avogadro::Rendering { using Core::Array; GLRenderer::GLRenderer() : m_valid(false), m_textRenderStrategy(nullptr), m_center(Vector3f::Zero()), m_radius(20.0) #ifdef _3DCONNEXION , m_drawIcon(false), m_iconData(nullptr), m_iconWidth(0u), m_iconHeight(0u), m_iconPosition(Eigen::Vector3f::Zero()) #endif { m_overlayCamera.setIdentity(); float aspectRatio = static_cast(m_camera.width()) / static_cast(m_camera.height()); float distance = m_camera.distance(m_center); float offset = distance + m_radius; m_perspectiveFrustum = { -aspectRatio, aspectRatio, -1.0f, 1.0f, 2.0f, offset }; m_orthographicFrustum = { -5.0f * aspectRatio, 5.0f * aspectRatio, -5.0f, 5.0f, -offset, offset }; } GLRenderer::~GLRenderer() { delete m_textRenderStrategy; } void GLRenderer::initialize() { GLenum result = glewInit(); m_valid = (result == GLEW_OK || result == GLEW_ERROR_NO_GLX_DISPLAY); if (!m_valid) { m_error += "GLEW could not be initialized.\n"; return; } if (!GLEW_VERSION_2_0) { m_error += "GL version 2.0 is not supported by your graphics driver.\n"; m_valid = false; return; } m_solidPipeline.initialize(); } void GLRenderer::resize(int width, int height) { if (!m_valid) return; glViewport(0, 0, static_cast(width), static_cast(height)); m_camera.setViewport(width, height); m_overlayCamera.setViewport(width, height); m_solidPipeline.resize(width, height); } void GLRenderer::setPixelRatio(float ratio) { m_solidPipeline.setPixelRatio(ratio); } void GLRenderer::render() { if (!m_valid) return; Vector4ub c = m_scene.backgroundColor(); glClearColor(c[0] / 255.0f, c[1] / 255.0f, c[2] / 255.0f, c[3] / 255.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); applyProjection(); GLRenderVisitor visitor(m_camera, m_textRenderStrategy); // Setup for solid geometry m_solidPipeline.begin(); visitor.setRenderPass(SolidPass); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); m_scene.rootNode().accept(visitor); m_solidPipeline.end(); m_solidPipeline.adjustOffset(m_camera); // Setup for opaque geometry visitor.setRenderPass(OpaquePass); m_scene.rootNode().accept(visitor); // Setup for transparent geometry visitor.setRenderPass(TranslucentPass); glEnable(GL_BLEND); // nvidia drivers have a bug where they don't like blending // so on Mac we can use this (they don't use nvidia) #ifdef __APPLE__ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #else // Thanks to Giuseppe D'Angelo for a related comment: // https://bugreports.qt.io/browse/QTBUG-36739 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); #endif m_scene.rootNode().accept(visitor); // Setup for 3d overlay rendering visitor.setRenderPass(Overlay3DPass); glClear(GL_DEPTH_BUFFER_BIT); m_scene.rootNode().accept(visitor); // Setup for 2d overlay rendering visitor.setRenderPass(Overlay2DPass); visitor.setCamera(m_overlayCamera); glDisable(GL_DEPTH_TEST); m_scene.rootNode().accept(visitor); #ifdef _3DCONNEXION if (m_drawIcon && (m_iconData != nullptr)) { glPushMatrix(); Eigen::Vector4f pivotPosition = m_camera.projection().matrix() * m_camera.modelView().matrix() * Eigen::Vector4f(m_iconPosition.x(), m_iconPosition.y(), m_iconPosition.z(), 1.0); pivotPosition /= pivotPosition.w(); glRasterPos3d(pivotPosition.x(), pivotPosition.y(), pivotPosition.z()); glPixelZoom(1.0f, -1.0f); glBitmap(0.0f, 0.0f, 0.0f, 0.0f, -static_cast(m_iconWidth >> 1), static_cast(m_iconHeight >> 1), NULL); glDrawPixels(m_iconWidth, m_iconHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_iconData); glPopMatrix(); } #endif } void GLRenderer::resetCamera() { resetGeometry(); m_camera.setFocus(m_center); m_camera.setIdentity(); m_camera.translate(-m_center); m_camera.preTranslate(-2.22f * m_radius * Vector3f::UnitZ()); } void GLRenderer::resetGeometry() { m_scene.setDirty(true); if (m_camera.focus()(0) != m_camera.focus()(0) || m_camera.focus() == m_center) m_camera.setFocus(m_scene.center()); m_center = m_scene.center(); m_radius = m_scene.radius(); } void GLRenderer::setTextRenderStrategy(TextRenderStrategy* tren) { if (tren != m_textRenderStrategy) { // Force all labels to be regenerated on the next render: class ResetTextLabelVisitor : public Visitor { public: void visit(Node&) override { return; } void visit(GroupNode&) override { return; } void visit(GeometryNode&) override { return; } void visit(Drawable&) override { return; } void visit(SphereGeometry&) override { return; } void visit(AmbientOcclusionSphereGeometry&) override { return; } void visit(CurveGeometry&) override { return; } void visit(CylinderGeometry&) override { return; } void visit(MeshGeometry&) override { return; } void visit(Texture2D&) { return; } void visit(TextLabel2D& l) override { l.resetTexture(); } void visit(TextLabel3D& l) override { l.resetTexture(); } void visit(LineStripGeometry&) override { return; } } labelResetter; m_scene.rootNode().accept(labelResetter); delete m_textRenderStrategy; m_textRenderStrategy = tren; } } void GLRenderer::applyProjection() { float distance = m_camera.distance(m_center); float aspectRatio = static_cast(m_camera.width()) / static_cast(m_camera.height()); if (m_camera.projectionType() == Perspective) { m_perspectiveFrustum[0] = m_perspectiveFrustum[2] * aspectRatio; m_perspectiveFrustum[1] = m_perspectiveFrustum[3] * aspectRatio; m_perspectiveFrustum[5] = distance + m_radius; m_camera.calculatePerspective( m_perspectiveFrustum[0], m_perspectiveFrustum[1], m_perspectiveFrustum[2], m_perspectiveFrustum[3], m_perspectiveFrustum[4], m_perspectiveFrustum[5]); } else { // Renders the orthographic projection of the molecule m_orthographicFrustum[0] = m_orthographicFrustum[2] * aspectRatio; m_orthographicFrustum[1] = m_orthographicFrustum[3] * aspectRatio; m_orthographicFrustum[5] = distance + m_radius; m_orthographicFrustum[4] = -m_orthographicFrustum[5]; m_camera.calculateOrthographic(m_orthographicFrustum[0], // L m_orthographicFrustum[1], // R m_orthographicFrustum[2], // B m_orthographicFrustum[3], // T m_orthographicFrustum[4], // N m_orthographicFrustum[5]); // F } m_overlayCamera.calculateOrthographic( 0.f, static_cast(m_overlayCamera.width()), 0.f, static_cast(m_overlayCamera.height()), -1.f, 1.f); } std::multimap GLRenderer::hits( const GroupNode* group, const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const { std::multimap result; if (!group) return result; for (auto it : group->children()) { std::multimap loopHits; const Node* itNode = it.node; const auto* childGroup = dynamic_cast(itNode); if (childGroup) { loopHits = hits(childGroup, rayOrigin, rayEnd, rayDirection); result.insert(loopHits.begin(), loopHits.end()); continue; } const auto* childGeometry = itNode->cast(); if (childGeometry) { loopHits = hits(childGeometry, rayOrigin, rayEnd, rayDirection); result.insert(loopHits.begin(), loopHits.end()); continue; } } return result; } std::multimap GLRenderer::hits( const GeometryNode* geometry, const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const { if (!geometry) return std::multimap(); return geometry->hits(rayOrigin, rayEnd, rayDirection); } std::multimap GLRenderer::hits(int x, int y) const { // Our ray: const Vector3f origin(m_camera.unProject( Vector3f(static_cast(x), static_cast(y), 0.f))); const Vector3f end(m_camera.unProject( Vector3f(static_cast(x), static_cast(y), 1.f))); const Vector3f direction((end - origin).normalized()); return hits(&m_scene.rootNode(), origin, end, direction); } Array GLRenderer::hits(const GroupNode* group, const Frustrum& f) const { Array result; for (auto it : group->children()) { Array loopHits; const Node* itNode = it.node; const auto* childGroup = dynamic_cast(itNode); if (childGroup) { loopHits = hits(childGroup, f); result.insert(result.end(), loopHits.begin(), loopHits.end()); continue; } const auto childGeometry = itNode->cast(); if (childGeometry) { loopHits = childGeometry->areaHits(f); result.insert(result.end(), loopHits.begin(), loopHits.end()); continue; } } return result; } float GLRenderer::hit(const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const { std::multimap results = hits(&m_scene.rootNode(), rayOrigin, rayEnd, rayDirection); if (results.size()) return results.begin()->first; return std::numeric_limits::max(); } Array GLRenderer::hits(int x1, int y1, int x2, int y2) const { // Figure out where the corners of our rectangle are. Frustrum f; f.points[0] = m_camera.unProject( Vector3f(static_cast(x1), static_cast(y1), 0.f)); f.points[1] = m_camera.unProject( Vector3f(static_cast(x1), static_cast(y1), 1.f)); f.points[2] = m_camera.unProject( Vector3f(static_cast(x1), static_cast(y2), 0.f)); f.points[3] = m_camera.unProject( Vector3f(static_cast(x1), static_cast(y2), 1.f)); f.points[4] = m_camera.unProject( Vector3f(static_cast(x2), static_cast(y2), 0.f)); f.points[5] = m_camera.unProject( Vector3f(static_cast(x2), static_cast(y2), 1.f)); f.points[6] = m_camera.unProject( Vector3f(static_cast(x2), static_cast(y1), 0.f)); f.points[7] = m_camera.unProject( Vector3f(static_cast(x2), static_cast(y1), 1.f)); // Define a frustrum for testing if things are within it. f.planes[0] = (f.points[0] - f.points[1]).cross(f.points[2] - f.points[3]); f.planes[1] = (f.points[2] - f.points[3]).cross(f.points[4] - f.points[5]); f.planes[2] = (f.points[4] - f.points[5]).cross(f.points[6] - f.points[7]); f.planes[3] = (f.points[6] - f.points[7]).cross(f.points[0] - f.points[1]); return hits(&m_scene.rootNode(), f); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/glrenderer.h000066400000000000000000000125761506155467400224370ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_GLRENDERER_H #define AVOGADRO_RENDERING_GLRENDERER_H #include "avogadrorenderingexport.h" #include "bufferobject.h" #include "camera.h" #include "primitive.h" #include "scene.h" #include "shader.h" #include "shaderprogram.h" #include "solidpipeline.h" #include #include #include // For member variables. #include namespace Avogadro { namespace Rendering { class GeometryNode; class TextRenderStrategy; /** * @class GLRenderer glrenderer.h * @brief Render the scene using OpenGL calls. * @author Marcus D. Hanwell */ class AVOGADRORENDERING_EXPORT GLRenderer { public: GLRenderer(); ~GLRenderer(); /** Initialize the OpenGL context for rendering. */ void initialize(); /** Resize the context in response to window management events. */ void resize(int width, int height); /** Set the ratio of physical to logical pixels. */ void setPixelRatio(float ratio); /** Take care of rendering the scene, requires that the context is current. */ void render(); /** Reset the view to fit the entire scene. */ void resetCamera(); /** * Reset the scene geometry, this should be done when the scene geometry has * changed in order to ensure correct clipping. */ void resetGeometry(); /** Return the primitives under the display coordinate (x,y), mapped by depth. */ std::multimap hits(int x, int y) const; /** Return the top primitive under the display coordinate (x,y). */ Identifier hit(int x, int y) const; /** Return the depth of provided ray - geometry hit test. */ float hit(const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const; /** Return the primitives in the rectangular area provided. */ Core::Array hits(int x1, int y1, int x2, int y2) const; /** Check whether the GL context is valid and supports required features. * \sa error() to get more information if the context is not valid. */ bool isValid() const { return m_valid; } /** Get the error message if the context is not valid. Empty if valid. */ std::string error() const { return m_error; } /** Get the camera for this renderer. */ const Camera& camera() const; Camera& camera(); /** Get the overlay camera. */ const Camera& overlayCamera() const; Camera& overlayCamera(); /** Get the scene for this renderer. */ const Scene& scene() const { return m_scene; } Scene& scene() { return m_scene; } /** Get the solid pipeline for this renderer. */ const SolidPipeline& solidPipeline() const { return m_solidPipeline; } SolidPipeline& solidPipeline() { return m_solidPipeline; } /** * Get/set the text rendering strategy for this object. The renderer takes * ownership of the strategy object. @{ */ const TextRenderStrategy* textRenderStrategy() const; TextRenderStrategy* textRenderStrategy(); void setTextRenderStrategy(TextRenderStrategy* tren); /** @} */ std::array m_perspectiveFrustum; // L, R, B, T, N, F (planes order) std::array m_orthographicFrustum; // L, R, B, T, N, F (planes order) #ifdef _3DCONNEXION bool m_drawIcon; void* m_iconData; uint32_t m_iconWidth; uint32_t m_iconHeight; Eigen::Vector3f m_iconPosition; #endif private: /** * Apply the projection matrix. */ void applyProjection(); /** * @brief Detect hits in a group node. */ std::multimap hits(const GroupNode* group, const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const; /** * @brief Detect hits in a geometry node. */ std::multimap hits(const GeometryNode* geometry, const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const; Core::Array hits(const GroupNode* group, const Frustrum& frustrum) const; bool m_valid; std::string m_error; Camera m_camera; Camera m_overlayCamera; Scene m_scene; TextRenderStrategy* m_textRenderStrategy; SolidPipeline m_solidPipeline; Vector3f m_center; float m_radius; }; inline const Camera& GLRenderer::camera() const { return m_camera; } inline Camera& GLRenderer::camera() { return m_camera; } inline const Camera& GLRenderer::overlayCamera() const { return m_camera; } inline Camera& GLRenderer::overlayCamera() { return m_camera; } inline const TextRenderStrategy* GLRenderer::textRenderStrategy() const { return m_textRenderStrategy; } inline TextRenderStrategy* GLRenderer::textRenderStrategy() { return m_textRenderStrategy; } inline Identifier GLRenderer::hit(int x, int y) const { std::multimap results = hits(x, y); if (results.size()) return results.begin()->second; return Identifier(); } } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_GLRENDERER_H avogadrolibs-1.101.0/avogadro/rendering/glrendervisitor.cpp000066400000000000000000000043331506155467400240530ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "glrendervisitor.h" #include "ambientocclusionspheregeometry.h" #include "curvegeometry.h" #include "cylindergeometry.h" #include "linestripgeometry.h" #include "meshgeometry.h" #include "spheregeometry.h" #include "textlabel2d.h" #include "textlabel3d.h" namespace Avogadro::Rendering { GLRenderVisitor::GLRenderVisitor(const Camera& camera_, const TextRenderStrategy* trs) : m_camera(camera_), m_textRenderStrategy(trs), m_renderPass(NotRendering) { } GLRenderVisitor::~GLRenderVisitor() {} void GLRenderVisitor::visit(Drawable& geometry) { if (geometry.renderPass() == m_renderPass) geometry.render(m_camera); } void GLRenderVisitor::visit(SphereGeometry& geometry) { if (geometry.renderPass() == m_renderPass) geometry.render(m_camera); } void GLRenderVisitor::visit(AmbientOcclusionSphereGeometry& geometry) { if (geometry.renderPass() == m_renderPass) geometry.render(m_camera); } void GLRenderVisitor::visit(CurveGeometry& geometry) { if (geometry.renderPass() == m_renderPass) geometry.render(m_camera); } void GLRenderVisitor::visit(CylinderGeometry& geometry) { if (geometry.renderPass() == m_renderPass) geometry.render(m_camera); } void GLRenderVisitor::visit(MeshGeometry& geometry) { if (geometry.renderPass() == m_renderPass) geometry.render(m_camera); } void GLRenderVisitor::visit(TextLabel2D& geometry) { if (geometry.renderPass() == m_renderPass) { if (m_textRenderStrategy) geometry.buildTexture(*m_textRenderStrategy); geometry.render(m_camera); } } void GLRenderVisitor::visit(TextLabel3D& geometry) { if (geometry.renderPass() == m_renderPass) { if (m_textRenderStrategy) geometry.buildTexture(*m_textRenderStrategy); geometry.render(m_camera); } } void GLRenderVisitor::visit(LineStripGeometry& geometry) { if (geometry.renderPass() == m_renderPass) geometry.render(m_camera); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/glrendervisitor.h000066400000000000000000000047261506155467400235260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_GLRENDERVISITOR_H #define AVOGADRO_RENDERING_GLRENDERVISITOR_H #include "visitor.h" #include "avogadrorendering.h" #include "camera.h" namespace Avogadro { namespace Rendering { class TextRenderStrategy; /** * @class GLRenderVisitor glrendervisitor.h * * @brief Visitor that takes care of rendering the scene. * @author Marcus D. Hanwell * * This visitor will render elements in the scene. */ class AVOGADRORENDERING_EXPORT GLRenderVisitor : public Visitor { public: explicit GLRenderVisitor(const Camera& camera = Camera(), const TextRenderStrategy* trs = nullptr); ~GLRenderVisitor() override; /** * The current stage of a multipass rendering. * @sa Rendering::RenderPass * @{ */ void setRenderPass(RenderPass pass) { m_renderPass = pass; } RenderPass renderPass() const { return m_renderPass; } /** @} */ /** * The overloaded visit functions, the base versions of which do nothing. */ void visit(Node&) override { return; } void visit(GroupNode&) override { return; } void visit(GeometryNode&) override { return; } void visit(Drawable&) override; void visit(SphereGeometry&) override; void visit(AmbientOcclusionSphereGeometry&) override; void visit(CurveGeometry&) override; void visit(CylinderGeometry&) override; void visit(MeshGeometry&) override; void visit(TextLabel2D& geometry) override; void visit(TextLabel3D& geometry) override; void visit(LineStripGeometry& geometry) override; void setCamera(const Camera& camera_) { m_camera = camera_; } Camera camera() const { return m_camera; } /** * A TextRenderStrategy implementation used to render text for annotations. * If nullptr, no text will be produced. * @{ */ void setTextRenderStrategy(TextRenderStrategy* trs) { m_textRenderStrategy = trs; } const TextRenderStrategy* textRenderStrategy() const { return m_textRenderStrategy; } /** @} */ private: Camera m_camera; const TextRenderStrategy* m_textRenderStrategy; RenderPass m_renderPass; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_GLRENDERVISITOR_H avogadrolibs-1.101.0/avogadro/rendering/groupnode.cpp000066400000000000000000000050171506155467400226330ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "groupnode.h" #include "visitor.h" namespace Avogadro::Rendering { GroupNode::GroupNode(GroupNode* parent_) { if (parent_) parent_->addChild(this); } GroupNode::~GroupNode() { // Like all good parents, we destroy our children before we go... clear(ALL); } void GroupNode::accept(Visitor& visitor) { visitor.visit(*this); for (auto& it : m_children) { it.node->accept(visitor); } } void GroupNode::addChild(Node* node, NodeType ui) { if (!node || node == this) return; if (!hasChild(node)) { node->setParent(this); m_children.emplace_back(node, ui); } } bool GroupNode::hasChild(Node* node) const { if (!node) return false; for (auto it : m_children) { if (it.node == node) { return true; } } return false; } bool GroupNode::removeChild(Node* node) { if (!node) return false; for (auto it = m_children.begin(); it != m_children.end(); ++it) { if (it->node == node) { it->node->setParent(nullptr); m_children.erase(it); return true; } } return false; } Node* GroupNode::child(size_t index) { if (index >= m_children.size()) return nullptr; else { // this is only used in test so we can us a O(n) function auto it = m_children.begin(); it = std::next(it, index); return it->node; } } void GroupNode::clear(NodeType ui) { // Like all good parents, we destroy our children before we go... for (auto it = m_children.begin(); it != m_children.end();) { auto itNext = std::next(it); if (it->ui == ui || ui == ALL) { auto groupNode = it->node->cast(); if (groupNode != nullptr && ui != ALL) { switch (ui) { case UI: groupNode->clearUI(); break; default: case GEOMETRY: groupNode->clear(); break; } // like a good parent, kill your son if you don't have nieces if (groupNode->childCount() == 0) { delete it->node; m_children.erase(it); } } else { delete it->node; m_children.erase(it); } } it = itNext; } } void GroupNode::clear() { clear(GEOMETRY); } void GroupNode::clearUI() { clear(UI); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/groupnode.h000066400000000000000000000056621506155467400223060ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_GROUPNODE_H #define AVOGADRO_RENDERING_GROUPNODE_H #include "node.h" #include namespace Avogadro { namespace Rendering { /** * @class GroupNode groupnode.h * @brief The GroupNode class provides common API for grouping child nodes. * @author Marcus D. Hanwell * * The GroupNode may be used to group items together, but for most other * purposes a more derived Node type would be the correct choice. */ class AVOGADRORENDERING_EXPORT GroupNode : public Node { public: enum NodeType { ALL = 0, NONE = -1, UI = 1, GEOMETRY = 2, }; struct NodeInfo { NodeInfo() : ui(NONE), node(nullptr) {} NodeInfo(Node* n, NodeType u) : ui(u), node(n) {} NodeType ui; Node* node; }; explicit GroupNode(GroupNode* parent = nullptr); ~GroupNode() override; /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Add a child node, this node will have its parent set and will be * deleted by this node upon destruction. * @param node Node to be added. */ void addChild(Node* node, NodeType ui = NodeType::GEOMETRY); /** * @brief Add a child node, this node will have its parent set and will be * deleted by this node upon destruction. * @param node Node to be added. */ void addUIChild(Node* node); /** * @brief Remove child node, this node will no longer be deleted. * @param node Node to be removed. * @return True if the node was removed, false if it was not found. */ bool removeChild(Node* node); /** * @brief Get the child Node at the specified index. * @param index The index of the child. * @return A pointer to the child node, or nullptr if the index is out of * range. * time complexity: O(n) */ Node* child(size_t index); /** * @brief check if the Node exists in this GroupNode. * @param node Node to search. * @return True if the node was found, false otherwise. */ bool hasChild(Node* node) const; /** * @return The number of child nodes contained by the GroupNode. */ size_t childCount() const { return m_children.size(); } /** * @brief Get a reference to the child nodes list. */ std::list& children() { return m_children; } const std::list& children() const { return m_children; } /** * @brief Remove all non UI-children. */ void clear(); /** * @brief Remove all UI-children. */ void clearUI(); protected: void clear(NodeType type); std::list m_children; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_GROUPNODE_H avogadrolibs-1.101.0/avogadro/rendering/linestrip_fs.glsl000066400000000000000000000000531506155467400235040ustar00rootroot00000000000000void main() { gl_FragColor = gl_Color; } avogadrolibs-1.101.0/avogadro/rendering/linestrip_vs.glsl000066400000000000000000000002721506155467400235270ustar00rootroot00000000000000attribute vec4 vertex; attribute vec4 color; uniform mat4 modelView; uniform mat4 projection; void main() { gl_FrontColor = color; gl_Position = projection * modelView * vertex; } avogadrolibs-1.101.0/avogadro/rendering/linestripgeometry.cpp000066400000000000000000000176251506155467400244260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "linestripgeometry.h" #include "avogadrogl.h" #include "bufferobject.h" #include "camera.h" #include "scene.h" #include "shader.h" #include "shaderprogram.h" #include "visitor.h" #include #include #include #include namespace { #include "linestrip_fs.h" #include "linestrip_vs.h" } // namespace using Avogadro::Vector3f; using Avogadro::Vector3ub; using Avogadro::Vector4ub; using Avogadro::Core::Array; using std::cout; using std::endl; namespace Avogadro::Rendering { const size_t LineStripGeometry::InvalidIndex = std::numeric_limits::max(); class LineStripGeometry::Private { public: Private() {} BufferObject vbo; Shader vertexShader; Shader fragmentShader; ShaderProgram program; }; LineStripGeometry::LineStripGeometry() : m_color(255, 0, 0), m_opacity(255), m_dirty(false), d(new Private) { } LineStripGeometry::LineStripGeometry(const LineStripGeometry& other) : Drawable(other), m_vertices(other.m_vertices), m_lineStarts(other.m_lineStarts), m_lineWidths(other.m_lineWidths), m_color(other.m_color), m_opacity(other.m_opacity), m_dirty(true), d(new Private) { } LineStripGeometry::~LineStripGeometry() { delete d; } void LineStripGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void LineStripGeometry::update() { if (m_vertices.empty()) return; // Check if the VBOs are ready, if not get them ready. if (!d->vbo.ready() || m_dirty) { d->vbo.upload(m_vertices, BufferObject::ArrayBuffer); m_dirty = false; } // Build and link the shader if it has not been used yet. if (d->vertexShader.type() == Shader::Unknown) { d->vertexShader.setType(Shader::Vertex); d->vertexShader.setSource(linestrip_vs); d->fragmentShader.setType(Shader::Fragment); d->fragmentShader.setSource(linestrip_fs); if (!d->vertexShader.compile()) cout << d->vertexShader.error() << endl; if (!d->fragmentShader.compile()) cout << d->fragmentShader.error() << endl; d->program.attachShader(d->vertexShader); d->program.attachShader(d->fragmentShader); if (!d->program.link()) cout << d->program.error() << endl; d->program.detachShader(d->vertexShader); d->program.detachShader(d->fragmentShader); d->vertexShader.cleanup(); d->fragmentShader.cleanup(); } } void LineStripGeometry::render(const Camera& camera) { if (m_vertices.empty() || m_lineStarts.empty() || m_lineWidths.size() != m_lineStarts.size()) return; // Prepare the VBO and shader program if necessary. update(); if (!d->program.bind()) cout << d->program.error() << endl; d->vbo.bind(); // Set up our attribute arrays. if (!d->program.enableAttributeArray("vertex")) cout << d->program.error() << endl; if (!d->program.useAttributeArray("vertex", PackedVertex::vertexOffset(), sizeof(PackedVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program.error() << endl; } if (!d->program.enableAttributeArray("color")) cout << d->program.error() << endl; if (!d->program.useAttributeArray("color", PackedVertex::colorOffset(), sizeof(PackedVertex), UCharType, 4, ShaderProgram::Normalize)) { cout << d->program.error() << endl; } // Set up our uniforms (model-view and projection matrices right now). if (!d->program.setUniformValue("modelView", camera.modelView().matrix())) { cout << d->program.error() << endl; } if (!d->program.setUniformValue("projection", camera.projection().matrix())) { cout << d->program.error() << endl; } // Render the linestrips using the shader and bound VBO. auto startIter = m_lineStarts.begin(); auto startEnd = m_lineStarts.end(); auto widthIter = m_lineWidths.begin(); unsigned int startIndex; unsigned int endIndex; while (startIter + 1 != startEnd) { startIndex = *startIter; endIndex = *(startIter + 1); glLineWidth(*widthIter); glDrawArrays(GL_LINE_STRIP, static_cast(startIndex), static_cast(endIndex - startIndex)); ++startIter; ++widthIter; } // There is an implicit ending index of m_vertices.size(): startIndex = *startIter; endIndex = static_cast(m_vertices.size()); glLineWidth(*widthIter); glDrawArrays(GL_LINE_STRIP, static_cast(startIndex), static_cast(endIndex - startIndex)); d->vbo.release(); d->program.disableAttributeArray("vector"); d->program.disableAttributeArray("color"); d->program.release(); } void LineStripGeometry::clear() { m_vertices.clear(); m_lineStarts.clear(); m_lineWidths.clear(); m_dirty = true; } size_t LineStripGeometry::addLineStrip(const Core::Array& vertices, const Core::Array& rgba, float lineWidth) { if (vertices.empty() || vertices.size() != rgba.size()) return InvalidIndex; size_t result = m_lineStarts.size(); m_lineStarts.push_back(static_cast(m_vertices.size())); m_lineWidths.push_back(lineWidth); auto colorIter(rgba.begin()); auto vertIter(vertices.begin()); auto vertEnd(vertices.end()); m_vertices.reserve(m_vertices.size() + vertices.size()); while (vertIter != vertEnd) m_vertices.push_back(PackedVertex(*(vertIter++), *(colorIter++))); m_dirty = true; return result; } size_t LineStripGeometry::addLineStrip(const Core::Array& vertices, const Core::Array& rgb, float lineWidth) { if (vertices.empty() || vertices.size() != rgb.size()) return InvalidIndex; size_t result = m_lineStarts.size(); m_lineStarts.push_back(static_cast(m_vertices.size())); m_lineWidths.push_back(lineWidth); auto colorIter(rgb.begin()); auto vertIter(vertices.begin()); auto vertEnd(vertices.end()); m_vertices.reserve(m_vertices.size() + vertices.size()); Vector4ub tmpColor(0, 0, 0, m_opacity); while (vertIter != vertEnd) { tmpColor.head<3>() = *(colorIter++); m_vertices.push_back(PackedVertex(*(vertIter++), tmpColor)); } m_dirty = true; return result; } size_t LineStripGeometry::addLineStrip(const Core::Array& vertices, const Vector3ub& rgb, float lineWidth) { if (vertices.empty()) return InvalidIndex; size_t result = m_lineStarts.size(); m_lineStarts.push_back(static_cast(m_vertices.size())); m_lineWidths.push_back(lineWidth); auto vertIter(vertices.begin()); auto vertEnd(vertices.end()); m_vertices.reserve(m_vertices.size() + vertices.size()); Vector4ub tmpColor(rgb[0], rgb[1], rgb[2], m_opacity); while (vertIter != vertEnd) { m_vertices.push_back(PackedVertex(*(vertIter++), tmpColor)); } m_dirty = true; return result; } size_t LineStripGeometry::addLineStrip(const Core::Array& vertices, float lineWidth) { if (vertices.empty()) return InvalidIndex; size_t result = m_lineStarts.size(); m_lineStarts.push_back(static_cast(m_vertices.size())); m_lineWidths.push_back(lineWidth); auto vertIter(vertices.begin()); auto vertEnd(vertices.end()); m_vertices.reserve(m_vertices.size() + vertices.size()); Vector4ub tmpColor(m_color[0], m_color[1], m_color[2], m_opacity); while (vertIter != vertEnd) m_vertices.push_back(PackedVertex(*(vertIter++), tmpColor)); m_dirty = true; return result; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/linestripgeometry.h000066400000000000000000000103401506155467400240560ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_LINESTRIPGEOMETRY_H #define AVOGADRO_RENDERING_LINESTRIPGEOMETRY_H #include "drawable.h" #include namespace Avogadro { namespace Rendering { /** * @class LineStripGeometry linestripgeometry.h * * @brief The LineStripGeometry class is used to store sets of line strips. */ class AVOGADRORENDERING_EXPORT LineStripGeometry : public Drawable { public: struct PackedVertex { // 16 bytes total: Vector3f vertex; // 12 bytes Vector4ub color; // 4 bytes PackedVertex(const Vector3f& v, const Vector4ub& c) : vertex(v), color(c) {} static int vertexOffset() { return 0; } static int colorOffset() { return static_cast(sizeof(Vector3f)); } }; static const size_t InvalidIndex; LineStripGeometry(); LineStripGeometry(const LineStripGeometry& other); ~LineStripGeometry() override; LineStripGeometry& operator=(LineStripGeometry); friend void swap(LineStripGeometry& lhs, LineStripGeometry& rhs); /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Render the line strips. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; /** * Clear the contents of the node. */ void clear() override; /** * Add a complete line strip to the object. * @param vertices The 3D vertices which will be connected to form the line * strip. * @param color Vertex color. If not specified, use the current color() and * opacity(). If the 3 component color is set, the current opacity() is used. * @param lineWidth The width of the line strip. * @note All arrays must be the same length, or this function call will fail, * returning InvalidIndex. * @return The index of the first vertex added by this call. * @{ */ size_t addLineStrip(const Core::Array& vertices, const Core::Array& color, float lineWidth); size_t addLineStrip(const Core::Array& vertices, const Core::Array& color, float lineWidth); size_t addLineStrip(const Core::Array& vertices, const Vector3ub& color, float lineWidth); size_t addLineStrip(const Core::Array& vertices, float lineWidth); /** @} */ /** * The default color of the lines. This is used to set the color of new * vertices when no explicit vertex color is specified. * @{ */ void setColor(const Vector3ub& c) { m_color = c; } Vector3ub color() const { return m_color; } /** @} */ /** * The default opacity of the lines. This is used when either no explicit * vertex color is specified, or a three component color is used. * @{ */ void setOpacity(unsigned char opacity_) { m_opacity = opacity_; } unsigned char opacity() const { return m_opacity; } /** @} */ /** The vertex array. */ Core::Array vertices() const { return m_vertices; } private: /** * @brief Update the VBOs, IBOs etc ready for rendering. */ void update(); Core::Array m_vertices; Core::Array m_lineStarts; Core::Array m_lineWidths; Vector3ub m_color; unsigned char m_opacity; bool m_dirty; class Private; Private* d; }; inline LineStripGeometry& LineStripGeometry::operator=(LineStripGeometry other) { using std::swap; swap(*this, other); return *this; } inline void swap(LineStripGeometry& lhs, LineStripGeometry& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_vertices, rhs.m_vertices); swap(lhs.m_lineStarts, rhs.m_lineStarts); swap(lhs.m_lineWidths, rhs.m_lineWidths); swap(lhs.m_color, rhs.m_color); swap(lhs.m_opacity, rhs.m_opacity); lhs.m_dirty = rhs.m_dirty = true; } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_LINESTRIPGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/mesh_fs.glsl000066400000000000000000000006621506155467400224350ustar00rootroot00000000000000varying vec3 fnormal; void main() { vec3 N = normalize(fnormal); vec3 L = normalize(vec3(0, 1, 1)); vec3 E = vec3(0, 0, 1); vec3 H = normalize(L + E); float df = max(0.0, dot(N, L)); float sf = max(0.0, dot(N, H)); sf = pow(sf, 20.0); vec4 ambient = gl_Color / 3.0; vec4 diffuse = gl_Color; vec4 specular = gl_Color * 3.0; gl_FragColor = ambient + df * diffuse + sf * specular; gl_FragColor.a = gl_Color.a; } avogadrolibs-1.101.0/avogadro/rendering/mesh_opaque_fs.glsl000066400000000000000000000006721506155467400240100ustar00rootroot00000000000000varying vec3 fnormal; void main() { vec3 N = normalize(fnormal); vec3 L = normalize(vec3(0, 1, 1)); vec3 E = vec3(0, 0, 1); vec3 H = normalize(L + E); float df = max(0.0, dot(N, -L)); float sf = max(0.0, dot(N, -H)); sf = pow(sf, 32.0); vec4 ambient = gl_Color / 2.2; vec4 diffuse = gl_Color * 1.1; vec4 specular = gl_Color * 5.0; gl_FragColor = ambient + df * diffuse + sf * specular; gl_FragColor.a = gl_Color.a; } avogadrolibs-1.101.0/avogadro/rendering/mesh_vs.glsl000066400000000000000000000004611506155467400224520ustar00rootroot00000000000000attribute vec4 vertex; attribute vec4 color; attribute vec3 normal; uniform mat4 modelView; uniform mat4 projection; uniform mat3 normalMatrix; varying vec3 fnormal; void main() { gl_FrontColor = color; gl_Position = projection * modelView * vertex; fnormal = normalize(normalMatrix * normal); } avogadrolibs-1.101.0/avogadro/rendering/meshgeometry.cpp000066400000000000000000000204631506155467400233430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "meshgeometry.h" #include "avogadrogl.h" #include "bufferobject.h" #include "camera.h" #include "scene.h" #include "shader.h" #include "shaderprogram.h" #include "visitor.h" #include #include #include #include #include namespace { #include "mesh_fs.h" #include "mesh_opaque_fs.h" #include "mesh_vs.h" } // namespace using Avogadro::Vector3f; using Avogadro::Vector3ub; using Avogadro::Vector4ub; using std::cout; using std::endl; namespace Avogadro::Rendering { const unsigned int MeshGeometry::InvalidIndex = std::numeric_limits::max(); class MeshGeometry::Private { public: Private() {} BufferObject vbo; BufferObject ibo; inline static Shader* vertexShader = nullptr; inline static Shader* fragmentShader = nullptr; inline static Shader* fragmentShaderOpaque = nullptr; inline static ShaderProgram* program = nullptr; inline static ShaderProgram* programOpaque = nullptr; size_t numberOfVertices; size_t numberOfIndices; }; MeshGeometry::MeshGeometry() : m_color(255, 0, 0), m_opacity(255), m_dirty(false), d(new Private) { } MeshGeometry::MeshGeometry(const MeshGeometry& other) : Drawable(other), m_vertices(other.m_vertices), m_indices(other.m_indices), m_color(other.m_color), m_opacity(other.m_opacity), m_dirty(true), // Force rendering internals to be rebuilt d(new Private) { } MeshGeometry::~MeshGeometry() { delete d; } void MeshGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void MeshGeometry::update() { if (m_vertices.empty() || m_indices.empty()) return; // Check if the VBOs are ready, if not get them ready. if (!d->vbo.ready() || m_dirty) { d->vbo.upload(m_vertices, BufferObject::ArrayBuffer); d->ibo.upload(m_indices, BufferObject::ElementArrayBuffer); d->numberOfVertices = m_vertices.size(); d->numberOfIndices = m_indices.size(); m_dirty = false; } // Build and link the shader if it has not been used yet. if (d->vertexShader == nullptr) { d->vertexShader = new Shader; d->vertexShader->setType(Shader::Vertex); d->vertexShader->setSource(mesh_vs); d->fragmentShader = new Shader; d->fragmentShader->setType(Shader::Fragment); d->fragmentShader->setSource(mesh_fs); d->fragmentShaderOpaque = new Shader; d->fragmentShaderOpaque->setType(Shader::Fragment); d->fragmentShaderOpaque->setSource(mesh_opaque_fs); if (!d->vertexShader->compile()) cout << d->vertexShader->error() << endl; if (!d->fragmentShader->compile()) cout << d->fragmentShader->error() << endl; if (!d->fragmentShaderOpaque->compile()) cout << d->fragmentShaderOpaque->error() << endl; if (d->program == nullptr) d->program = new ShaderProgram; d->program->attachShader(*d->vertexShader); d->program->attachShader(*d->fragmentShader); if (!d->program->link()) cout << d->program->error() << endl; if (d->programOpaque == nullptr) d->programOpaque = new ShaderProgram; d->programOpaque->attachShader(*d->vertexShader); d->programOpaque->attachShader(*d->fragmentShaderOpaque); if (!d->programOpaque->link()) cout << d->programOpaque->error() << endl; } } void MeshGeometry::render(const Camera& camera) { if (m_indices.empty() || m_vertices.empty()) return; // Prepare the VBOs, IBOs and shader program if necessary. update(); ShaderProgram* program; // If the mesh is opaque, use the opaque shader if (m_opacity != 255) program = d->program; else program = d->programOpaque; if (!program->bind()) cout << program->error() << endl; d->vbo.bind(); d->ibo.bind(); // Set up our attribute arrays. if (!program->enableAttributeArray("vertex")) cout << program->error() << endl; if (!program->useAttributeArray("vertex", PackedVertex::vertexOffset(), sizeof(PackedVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << program->error() << endl; } if (!program->enableAttributeArray("color")) cout << program->error() << endl; if (!program->useAttributeArray("color", PackedVertex::colorOffset(), sizeof(PackedVertex), UCharType, 4, ShaderProgram::Normalize)) { cout << program->error() << endl; } if (!program->enableAttributeArray("normal")) cout << program->error() << endl; if (!program->useAttributeArray("normal", PackedVertex::normalOffset(), sizeof(PackedVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << program->error() << endl; } // Set up our uniforms (model-view and projection matrices right now). if (!program->setUniformValue("modelView", camera.modelView().matrix())) { cout << program->error() << endl; } if (!program->setUniformValue("projection", camera.projection().matrix())) { cout << program->error() << endl; } Matrix3f normalMatrix = camera.modelView().linear().inverse().transpose(); if (!program->setUniformValue("normalMatrix", normalMatrix)) std::cout << program->error() << std::endl; // Render the loaded spheres using the shader and bound VBO. glDrawRangeElements(GL_TRIANGLES, 0, static_cast(d->numberOfVertices - 1), static_cast(d->numberOfIndices), GL_UNSIGNED_INT, reinterpret_cast(0)); d->vbo.release(); d->ibo.release(); program->disableAttributeArray("vector"); program->disableAttributeArray("color"); program->disableAttributeArray("normal"); program->release(); } unsigned int MeshGeometry::addVertices(const Core::Array& v, const Core::Array& n, const Core::Array& c) { if (v.size() != n.size() || n.size() != c.size()) return InvalidIndex; size_t result = m_vertices.size(); auto vIter = v.begin(); auto vEnd = v.end(); auto nIter = n.begin(); auto cIter = c.begin(); while (vIter != vEnd) m_vertices.push_back(PackedVertex(*(cIter++), *(nIter++), *(vIter++))); m_dirty = true; return static_cast(result); } unsigned int MeshGeometry::addVertices(const Core::Array& v, const Core::Array& n, const Core::Array& c) { if (v.size() != n.size() || n.size() != c.size()) return InvalidIndex; size_t result = m_vertices.size(); auto vIter = v.begin(); auto vEnd = v.end(); auto nIter = n.begin(); auto cIter = c.begin(); Vector4ub tmpColor(0, 0, 0, m_opacity); while (vIter != vEnd) { tmpColor.head<3>() = *(cIter++); m_vertices.push_back(PackedVertex(tmpColor, *(nIter++), *(vIter++))); } m_dirty = true; return static_cast(result); } unsigned int MeshGeometry::addVertices(const Core::Array& v, const Core::Array& n) { if (v.size() != n.size()) return InvalidIndex; size_t result = m_vertices.size(); auto vIter = v.begin(); auto vEnd = v.end(); auto nIter = n.begin(); const Vector4ub tmpColor(m_color[0], m_color[1], m_color[2], m_opacity); while (vIter != vEnd) m_vertices.push_back(PackedVertex(tmpColor, *(nIter++), *(vIter++))); m_dirty = true; return static_cast(result); } void MeshGeometry::addTriangle(size_t index1, size_t index2, size_t index3) { m_indices.push_back(index1); m_indices.push_back(index2); m_indices.push_back(index3); m_dirty = true; } void MeshGeometry::addTriangles(const Core::Array& indiceArray) { m_indices.reserve(m_indices.size() + indiceArray.size()); std::copy(indiceArray.begin(), indiceArray.end(), std::back_inserter(m_indices)); m_dirty = true; } void MeshGeometry::clear() { m_vertices.clear(); m_indices.clear(); m_dirty = true; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/meshgeometry.h000066400000000000000000000123021506155467400230010ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_MESHGEOMETRY_H #define AVOGADRO_RENDERING_MESHGEOMETRY_H #include "drawable.h" #include namespace Avogadro { namespace Rendering { /** * @class MeshGeometry meshgeometry.h * @brief The MeshGeometry is used for triangle mesh geometry. * @author Marcus D. Hanwell */ class AVOGADRORENDERING_EXPORT MeshGeometry : public Drawable { public: struct PackedVertex { Vector4ub color; // 4 bytes Vector3f normal; // 12 bytes Vector3f vertex; // 12 bytes unsigned char padding[4]; // 4 bytes PackedVertex(const Vector4ub& c, const Vector3f& n, const Vector3f& v) : color(c), normal(n), vertex(v) { } static int colorOffset() { return 0; } static int normalOffset() { return static_cast(sizeof(Vector4ub)); } static int vertexOffset() { return normalOffset() + static_cast(sizeof(Vector3f)); } }; // 32 bytes total size - 16/32/64 are ideal for alignment. static const unsigned int InvalidIndex; MeshGeometry(); MeshGeometry(const MeshGeometry& other); ~MeshGeometry() override; MeshGeometry& operator=(MeshGeometry); friend void swap(MeshGeometry& lhs, MeshGeometry& rhs); /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Render the mesh geometry. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; /** * Add vertices to the object. Note that this just adds vertices to the * object. Use addTriangles with size_t indices to actually draw them. * @param vertices The 3D vertex points to add to the drawable. * @param normals The normal direction at the vertex. * @param colors Vertex color. If not specified, use the current color() and * opacity(). If the 3 component color is set, the current opacity() is used. * @note All arrays must be the same length, or this function call will fail, * returning InvalidIndex. * @return The index of the first vertex added by this call, used to specify * element arrays for the actual triangles. * @{ */ unsigned int addVertices(const Core::Array& vertices, const Core::Array& normals, const Core::Array& colors); unsigned int addVertices(const Core::Array& vertices, const Core::Array& normals, const Core::Array& colors); unsigned int addVertices(const Core::Array& vertices, const Core::Array& normals); /** @} */ /** * Add triangles to the mesh. Triangles are specified as 3-tuples of vertex * indices. Must call addVertices first, and use the return value to obtain * the valid index range. * @{ */ void addTriangle(size_t index1, size_t index2, size_t index3); void addTriangles(const Core::Array& indices); /** @} */ /** * Clear the contents of the node. */ void clear() override; /** * Get the number of vertices. */ size_t vertexCount() const { return m_vertices.size(); } /** * Get the number of vertices. */ size_t indexCount() const { return m_indices.size(); } /** * Get the number of triangles. */ size_t triangleCount() const { return m_indices.size() / 3; } /** * The default color of the mesh. This is used to set the color of new * vertices when no explicit vertex color is specified. * @{ */ void setColor(const Vector3ub& c) { m_color = c; } Vector3ub color() const { return m_color; } /** @} */ /** * The default opacity of the mesh. This is used when either no explicit * vertex color is specified, or a three component color is used. * @{ */ void setOpacity(unsigned char opacity_) { m_opacity = opacity_; } unsigned char opacity() const { return m_opacity; } /** @} */ Core::Array vertices() { return m_vertices; } Core::Array triangles() { return m_indices; } private: /** * @brief Update the VBOs, IBOs etc ready for rendering. */ void update(); Core::Array m_vertices; Core::Array m_indices; Vector3ub m_color; unsigned char m_opacity; bool m_dirty; class Private; Private* d; }; inline MeshGeometry& MeshGeometry::operator=(MeshGeometry other) { using std::swap; swap(*this, other); return *this; } inline void swap(MeshGeometry& lhs, MeshGeometry& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_vertices, rhs.m_vertices); swap(lhs.m_indices, rhs.m_indices); swap(lhs.m_color, rhs.m_color); swap(lhs.m_opacity, rhs.m_opacity); lhs.m_dirty = rhs.m_dirty = true; } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_MESHGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/node.cpp000066400000000000000000000010101506155467400215430ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "node.h" namespace Avogadro::Rendering { Node::Node() : m_parent(nullptr), m_visible(true) {} Node::~Node() {} void Node::setParent(GroupNode* parent_) { m_parent = parent_; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/node.h000066400000000000000000000043111506155467400212170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_NODE_H #define AVOGADRO_RENDERING_NODE_H #include "avogadrorenderingexport.h" #include namespace Avogadro { namespace Rendering { class GroupNode; class Visitor; /** * @class Node node.h * @brief The Node class is the base class for all items in the scene. * @author Marcus D. Hanwell * * The Node class is the base class for nodes in the Scene, providing common * API and functionality. */ class AVOGADRORENDERING_EXPORT Node { public: Node(); virtual ~Node(); /** * Accept a visit from our friendly visitor. */ virtual void accept(Visitor&) { return; } /** * @brief Get a pointer to the node's parent. * @return Pointer to the parent node, nullptr if no parent. */ const GroupNode* parent() const { return m_parent; } GroupNode* parent() { return m_parent; } /** * @brief Set the visibility of the node. * @param visibility True if the node is visible, false if invisible. */ void setVisible(bool visibility) { m_visible = visibility; } /** * @brief Get the current visibility of the node. * @return True if visible. */ bool isVisible() const { return m_visible; } /** * @brief Attempt to dynamic_cast to specified node type. * @return Valid pointer to specified type, or null. */ template T* cast(); template const T* cast() const; protected: friend class GroupNode; /** * @brief Set the parent node for the node. * @param parent The parent, a value of nullptr denotes no parent node. */ void setParent(GroupNode* parent); GroupNode* m_parent; bool m_visible; }; template T* Node::cast() { return dynamic_cast(this); } template const T* Node::cast() const { return dynamic_cast(this); } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_NODE_H avogadrolibs-1.101.0/avogadro/rendering/plyvisitor.cpp000066400000000000000000000337371506155467400230670ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "plyvisitor.h" namespace Avogadro::Rendering { using std::ofstream; using std::ostream; using std::ostringstream; using std::string; using std::vector; namespace { ostream& operator<<(ostream& os, const Vector3f& v) { os << v[0] << " " << v[1] << " " << v[2]; return os; } ostream& operator<<(ostream& os, const Vector3ub& color) { // PLY expects same number of parameters every time, so if no alpha given use // 1 os << color[0] / 255.0f << " " << color[1] / 255.0f << " " << color[2] / 255.0f << " " << 1; return os; } ostream& operator<<(ostream& os, const Vector4ub& color) { os << color[0] / 255.0f << " " << color[1] / 255.0f << " " << color[2] / 255.0f << " " << color[3] / 255.0f; return os; } } // namespace PLYVisitor::PLYVisitor(const Camera& c) : m_camera(c), m_backgroundColor(255, 255, 255), m_ambientColor(100, 100, 100), m_aspectRatio(800.0f / 600.0f) { } PLYVisitor::~PLYVisitor() {} void PLYVisitor::begin() {} string PLYVisitor::end() { // Adds the PLY header after the final counts are known ostringstream header; // Header format header << "ply" << '\n' << "format ascii 1.0" << '\n' << "element vertex " << m_vertexCount << '\n' << "property float x" << '\n' << "property float y" << '\n' << "property float z" << '\n' << "property float red" << '\n' << "property float green" << '\n' << "property float blue" << '\n' << "property float alpha" << '\n' << "element face " << m_faceCount << '\n' << "property list uchar uint vertex_index" << '\n' << "end_header" << '\n'; // Add the vertices and faces return header.str() + m_sceneVertices + m_sceneFaces; } void PLYVisitor::visit(Drawable&) {} void PLYVisitor::visit(SphereGeometry& geometry) { for (const auto& s : geometry.spheres()) { // Uses an Icosphere method (logic and new functions could be added here to // pick different methods) visitSphereIcosphereRecursionMethod(s, 5); } } void PLYVisitor::visitSphereIcosphereRecursionMethod(const SphereColor& sphere, unsigned int subdivisions) { Vector3f center = sphere.center; float radius = sphere.radius; // Defines an Icosahedron Vertices and Faces float phi = (1.0f + sqrt(5.0f)) / 2.0f; float a = 1.0f; float b = 1.0f / phi; vector vertices = { Vector3f(0, b, -a), Vector3f(b, a, 0), Vector3f(-b, a, 0), Vector3f(0, b, a), Vector3f(0, -b, a), Vector3f(-a, 0, b), Vector3f(0, -b, -a), Vector3f(a, 0, -b), Vector3f(a, 0, b), Vector3f(-a, 0, -b), Vector3f(b, -a, 0), Vector3f(-b, -a, 0) }; // Local Indexes for the faces vector> faces = { vector{ 2, 1, 0 }, vector{ 1, 2, 3 }, vector{ 5, 4, 3 }, vector{ 4, 8, 3 }, vector{ 7, 6, 0 }, vector{ 6, 9, 0 }, vector{ 11, 10, 4 }, vector{ 10, 11, 6 }, vector{ 9, 5, 2 }, vector{ 5, 9, 11 }, vector{ 8, 7, 1 }, vector{ 7, 8, 10 }, vector{ 2, 5, 3 }, vector{ 8, 1, 3 }, vector{ 9, 2, 0 }, vector{ 1, 7, 0 }, vector{ 11, 9, 6 }, vector{ 7, 10, 6 }, vector{ 5, 11, 4 }, vector{ 10, 8, 4 } }; // For every subdivision for (unsigned int i = 0; i < subdivisions; ++i) { // Prerecord face size so doesn't change mid loop int facesSize = faces.size(); // For every face for (int j = 0; j < facesSize; ++j) { // Face vertices Vector3f faceVertexOne(vertices.at(faces.at(j).at(0))); Vector3f faceVertexTwo(vertices.at(faces.at(j).at(1))); Vector3f faceVertexThree(vertices.at(faces.at(j).at(2))); unsigned int faceIndexOne = faces.at(j).at(0); unsigned int faceIndexTwo = faces.at(j).at(1); unsigned int faceIndexThree = faces.at(j).at(2); // Get the vertex at the midpoint between One and Two and its Index Vector3f vertexOneTwo((faceVertexOne[0] + faceVertexTwo[0]) / 2.0f, (faceVertexOne[1] + faceVertexTwo[1]) / 2.0f, (faceVertexOne[2] + faceVertexTwo[2]) / 2.0f); unsigned int indexOneTwo = vertices.size(); vertices.push_back(vertexOneTwo); // Get the vertex at the midpoint between Two and Three and its Index Vector3f vertexTwoThree((faceVertexTwo[0] + faceVertexThree[0]) / 2.0f, (faceVertexTwo[1] + faceVertexThree[1]) / 2.0f, (faceVertexTwo[2] + faceVertexThree[2]) / 2.0f); unsigned int indexTwoThree = vertices.size(); vertices.push_back(vertexTwoThree); // Get the vertex at the midpoint between One and Three and its Index Vector3f vertexOneThree((faceVertexOne[0] + faceVertexThree[0]) / 2.0f, (faceVertexOne[1] + faceVertexThree[1]) / 2.0f, (faceVertexOne[2] + faceVertexThree[2]) / 2.0f); unsigned int indexOneThree = vertices.size(); vertices.push_back(vertexOneThree); // Replace the original face with one new face and push the others to the // back vector subdividedFaceOne = { faceIndexOne, indexOneTwo, indexOneThree }; vector subdividedFaceTwo = { faceIndexTwo, indexTwoThree, indexOneTwo }; vector subdividedFaceThree = { faceIndexThree, indexOneThree, indexTwoThree }; vector subdividedFaceFour = { indexOneTwo, indexTwoThree, indexOneThree }; faces.at(j) = subdividedFaceOne; faces.push_back(subdividedFaceTwo); faces.push_back(subdividedFaceThree); faces.push_back(subdividedFaceFour); } } ostringstream vertexStr; ostringstream faceStr; // Project every vertex onto the sphere and record it for (unsigned int i = 0; i < vertices.size(); ++i) { // Normalize the vertex and then project it float x = vertices.at(i)[0]; float y = vertices.at(i)[1]; float z = vertices.at(i)[2]; float distance = std::hypot(x, y, z); vertices.at(i)[0] = (x / distance) * radius; vertices.at(i)[1] = (y / distance) * radius; vertices.at(i)[2] = (z / distance) * radius; // Adjust to be around sphere's radius vertices.at(i)[0] += center[0]; vertices.at(i)[1] += center[1]; vertices.at(i)[2] += center[2]; // Add the vertex vertexStr << vertices.at(i) << " " << sphere.color << '\n'; } // Adjust every face to have indices for the PLY file and add it for (unsigned int i = 0; i < faces.size(); ++i) { faceStr << 3 << " " << faces.at(i)[0] + m_vertexCount << " " << faces.at(i)[1] + m_vertexCount << " " << faces.at(i)[2] + m_vertexCount << '\n'; } // Adjust the counts and add the new vertices and faces m_vertexCount += vertices.size(); m_faceCount += faces.size(); m_sceneVertices += vertexStr.str(); m_sceneFaces += faceStr.str(); } void PLYVisitor::visit(AmbientOcclusionSphereGeometry&) {} void PLYVisitor::visit(CylinderGeometry& geometry) { for (const auto& c : geometry.cylinders()) { // Uses an Icosphere method (logic and new functions could be added here to // pick different methods) visitCylinderLateralMethod(c, 20); } } void PLYVisitor::visitCylinderLateralMethod(const CylinderColor& geometry, unsigned int lateralFaces) { ostringstream vertexStr; ostringstream faceStr; // Add each end of the cylinder to the vertices Vector3f end1 = geometry.end1; Vector3f end2 = geometry.end2; vertexStr << end1 << " " << geometry.color << '\n'; vertexStr << end2 << " " << geometry.color << '\n'; m_vertexCount += 2; // Radius and the length vector of cylinder float radius = geometry.radius; float length = std::hypot(end1[0] - end2[0], end1[1] - end2[1], end1[2] - end2[2]); // Normalize the plane vector of the cylinder Vector3f normalVector((end1[0] - end2[0]) / length, (end1[1] - end2[1]) / length, (end1[2] - end2[2]) / length); // Find a basis vector orthogonal to the plane vector Vector3f u; // If the Plane Vector doesn't point entirely in the y direction if (normalVector[1] != 1.0f) { // Choose a orthogonal vector for the y-axis (if plane vector points // entirely in y, this will be 0-vector) u[0] = normalVector[2]; u[1] = 0; u[2] = -1 * normalVector[0]; } // Otherwise, must use different orthogonal vector since y-axis one would be // 0-vector else { u[0] = 0; u[1] = normalVector[2]; u[2] = -1 * normalVector[1]; } // Length of u vector float uLength = std::hypot(u[0], u[1], u[2]); // Normalize the vector u[0] /= uLength; u[1] /= uLength; u[2] /= uLength; // Find the other basis vector orthogonal to the plane vector via u x v = // normalVector Vector3f v; v[0] = normalVector[1] * u[2] - normalVector[2] * u[1]; v[1] = normalVector[2] * u[0] - normalVector[0] * u[2]; v[2] = normalVector[0] * u[1] - normalVector[1] * u[0]; // Angle between each lateral face float baseAngle = 2 * 3.14159265359 / lateralFaces; // Find the vertices and faces for each lateral face for (unsigned int i = 0; i < lateralFaces; ++i) { // Current angle float angle = baseAngle * i; // Code works by assuming there are 2 * lateralFaces vertices in the // cylinder (not including the ends). i corresponds to the lateral face // number, to which there are two corresponding vertices (top and bottom). // Place the top and bottom lateral face vertex into the file (p = center + // R*(u*cos(a) + v*sin(a))) vertexStr << Vector3f(end1[0] + radius * u[0] * cos(angle) + radius * v[0] * sin(angle), end1[1] + radius * u[1] * cos(angle) + radius * v[1] * sin(angle), end1[2] + radius * u[2] * cos(angle) + radius * v[2] * sin(angle)) << " " << geometry.color << '\n'; vertexStr << Vector3f(end2[0] + radius * u[0] * cos(angle) + radius * v[0] * sin(angle), end2[1] + radius * u[1] * cos(angle) + radius * v[1] * sin(angle), end2[2] + radius * u[2] * cos(angle) + radius * v[2] * sin(angle)) << " " << geometry.color << '\n'; // Code then needs to create the two lateral faces these vertices create // with the next vertices 2 * i gives the index of the first vertex, plus 1 // for second vertex, plus 2 or 3 for next faces vertex Modulo included for // +2 or +3 to prevent going out of bounds // Lateral face made up of the next face's first vertex, first vertex, and // second vertex faceStr << 3 << " " << (2 * i + 2) % (2 * lateralFaces) + m_vertexCount << " " << 2 * i + m_vertexCount << " " << 2 * i + 1 + m_vertexCount << '\n'; // Lateral face made up of next face's first vertex, second vertex, and next // face's second vertex faceStr << 3 << " " << (2 * i + 2) % (2 * lateralFaces) + m_vertexCount << " " << 2 * i + 1 + m_vertexCount << " " << (2 * i + 3) % (2 * lateralFaces) + m_vertexCount << '\n'; // Code needs to deal with faces on top and bottom of cylinder // To turn local indices to the indices of the PLY file, -2 is used for end1 // and -1 is used for end2 // Top face made up of end1, first vertex, and next face's first vertex faceStr << 3 << " " << -2 + m_vertexCount << " " << 2 * i + m_vertexCount << " " << (2 * i + 2) % (2 * lateralFaces) + m_vertexCount << '\n'; // Bottom face made up of next face's second vertex, second vertex, and end2 faceStr << 3 << " " << (2 * i + 3) % (2 * lateralFaces) + m_vertexCount << " " << 2 * i + 1 + m_vertexCount << " " << -1 + m_vertexCount << '\n'; } // Adjust the counts and add the new vertices and faces m_vertexCount += 2 * lateralFaces; m_faceCount += 4 * lateralFaces; m_sceneVertices += vertexStr.str(); m_sceneFaces += faceStr.str(); } void PLYVisitor::visit(MeshGeometry& geometry) { Core::Array v = geometry.vertices(); Core::Array tris = geometry.triangles(); ostringstream vertexStr; ostringstream faceStr; // Record every vertex in the mesh for (size_t i = 0; i < v.size(); ++i) { vertexStr << v[i].vertex << " " << v[i].color << '\n'; } // Record every face and adjust the indices for (size_t i = 0; i < tris.size(); i += 3) { faceStr << 3 << " " << tris[i] + m_vertexCount << " " << tris[i + 1] + m_vertexCount << " " << tris[i + 2] + m_vertexCount << '\n'; } // Adjust the counts and add the vertices and faces // I think the vertex order on the meshes are messed up for the Coordinate // System Mesh resulting in mismatched normals, since my code shouldn't be // doing anything to it. m_vertexCount += v.size(); m_faceCount += tris.size() / 3; m_sceneVertices += vertexStr.str(); m_sceneFaces += faceStr.str(); } void PLYVisitor::visit(LineStripGeometry&) {} } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/plyvisitor.h000066400000000000000000000052361506155467400225250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_PLYVISITOR_H #define AVOGADRO_RENDERING_PLYVISITOR_H #include "visitor.h" #include "avogadrorendering.h" #include "spheregeometry.h" #include "cylindergeometry.h" #include "ambientocclusionspheregeometry.h" #include "linestripgeometry.h" #include "meshgeometry.h" #include "camera.h" #include #include #include #include namespace Avogadro { namespace Rendering { /** * @class PLYVisitor plyvisitor.h * @brief Visitor that visits scene elements and creates a PLY input file. * * This visitor will render elements in the scene to a text file that contains * elements that can be rendered as PLY. */ class AVOGADRORENDERING_EXPORT PLYVisitor : public Visitor { public: explicit PLYVisitor(const Camera& camera); ~PLYVisitor() override; void begin(); std::string end(); /** * The overloaded visit functions, the base versions of which do nothing. */ void visit(Node&) override { return; } void visit(GroupNode&) override { return; } void visit(GeometryNode&) override { return; } void visit(Drawable&) override; void visit(SphereGeometry&) override; void visit(AmbientOcclusionSphereGeometry&) override; void visit(CurveGeometry&) override { return; } void visit(CylinderGeometry&) override; void visit(MeshGeometry&) override; void visit(TextLabel2D&) override { return; } void visit(TextLabel3D&) override { return; } void visit(LineStripGeometry& geometry) override; void setCamera(const Camera& c) { m_camera = c; } Camera camera() const { return m_camera; } void setBackgroundColor(const Vector3ub& c) { m_backgroundColor = c; } void setAmbientColor(const Vector3ub& c) { m_ambientColor = c; } void setAspectRatio(float ratio) { m_aspectRatio = ratio; } private: Camera m_camera; Vector3ub m_backgroundColor; Vector3ub m_ambientColor; float m_aspectRatio; long m_vertexCount = 0; long m_faceCount = 0; std::string m_sceneVertices = ""; std::string m_sceneFaces = ""; void visitSphereIcosphereRecursionMethod(const SphereColor& geometry, unsigned int subdivisions); void visitCylinderLateralMethod(const CylinderColor& geometry, unsigned int lateralFaces); }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_PLYVISITOR_H avogadrolibs-1.101.0/avogadro/rendering/povrayvisitor.cpp000066400000000000000000000127241506155467400235740ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "povrayvisitor.h" #include "ambientocclusionspheregeometry.h" #include "cylindergeometry.h" #include "linestripgeometry.h" #include "meshgeometry.h" #include "spheregeometry.h" #include #include namespace Avogadro::Rendering { using std::ofstream; using std::ostream; using std::ostringstream; using std::string; namespace { ostream& operator<<(ostream& os, const Vector3f& v) { os << v[0] << ", " << v[1] << ", " << v[2]; return os; } ostream& operator<<(ostream& os, const Vector3ub& color) { os << color[0] / 255.0f << ", " << color[1] / 255.0f << ", " << color[2] / 255.0f; return os; } } // namespace POVRayVisitor::POVRayVisitor(const Camera& c) : m_camera(c), m_backgroundColor(255, 255, 255), m_ambientColor(100, 100, 100), m_aspectRatio(800.0f / 600.0f) { } POVRayVisitor::~POVRayVisitor() {} void POVRayVisitor::begin() { // Initialise our POV-Ray scene // The POV-Ray camera basically has the same matrix elements - we just need to // translate // FIXME Still working on getting the translation to POV-Ray right... Vector3f cameraT = -(m_camera.modelView().linear().adjoint() * m_camera.modelView().translation()); Vector3f cameraX = m_camera.modelView().linear().row(0).transpose().normalized(); Vector3f cameraY = m_camera.modelView().linear().row(1).transpose().normalized(); Vector3f cameraZ = -m_camera.modelView().linear().row(2).transpose().normalized(); double huge = 100; Vector3f light0pos = huge * (m_camera.modelView().linear().adjoint() * Vector3f(0, 1, 0)); // Output the POV-Ray initialisation code ostringstream str; str << "global_settings {\n" << "\tambient_light rgb <" << m_ambientColor << ">\n" << "\tmax_trace_level 15\n}\n\n" << "background { color rgb <" << m_backgroundColor << "> }\n\n" << "camera {\n" << "\tperspective\n" << "\tlocation <" << cameraT.x() << ", " << cameraT.y() << ", " << cameraT.z() << ">\n" << "\tangle 70\n" << "\tup <" << cameraY.x() << ", " << cameraY.y() << ", " << cameraY.z() << ">\n" << "\tright <" << cameraX.x() << ", " << cameraX.y() << ", " << cameraX.z() << "> * " << m_aspectRatio << '\n' << "\tdirection <" << cameraZ.x() << ", " << cameraZ.y() << ", " << cameraZ.z() << "> }\n\n" << "light_source {\n" << "\t<" << light0pos[0] << ", " << light0pos[1] << ", " << light0pos[2] << ">\n" << "\tcolor rgb <1.0, 1.0, 1.0>\n" << "\tfade_distance " << 2 * huge << '\n' << "\tfade_power 0\n" << "\tparallel\n" << "\tpoint_at <" << -light0pos[0] << ", " << -light0pos[1] << ", " << -light0pos[2] << ">\n" << "}\n\n" << "#default {\n\tfinish {ambient .8 diffuse 1 specular 1 roughness .005 " "metallic 0.5}\n}\n\n"; m_sceneData = str.str(); } string POVRayVisitor::end() { return m_sceneData; } void POVRayVisitor::visit(Drawable&) { return; } void POVRayVisitor::visit(SphereGeometry& geometry) { ostringstream str; for (const auto& s : geometry.spheres()) { str << "sphere {\n\t<" << s.center << ">, " << s.radius << "\n\tpigment { rgbt <" << s.color << ", 0.0> }\n}\n"; } m_sceneData += str.str(); } void POVRayVisitor::visit(AmbientOcclusionSphereGeometry&) {} void POVRayVisitor::visit(CylinderGeometry& geometry) { ostringstream str; for (const auto& c : geometry.cylinders()) { str << "cylinder {\n" << "\t<" << c.end1 << ">,\n" << "\t<" << c.end2 << ">, " << c.radius << "\n\tpigment { rgbt <" << c.color << ", 0.0> }\n}\n"; } m_sceneData += str.str(); } void POVRayVisitor::visit(MeshGeometry& geometry) { ostringstream str; str << "mesh2 {\n"; Core::Array v = geometry.vertices(); Core::Array tris = geometry.triangles(); str << "vertex_vectors{" << v.size() << ",\n"; for (size_t i = 0; i < v.size(); ++i) { str << "<" << v[i].vertex << ">,"; if (i != 0 && i % 3) str << "\n"; } str << "\n}\n"; str << "normal_vectors{" << v.size() << ",\n"; for (size_t i = 0; i < v.size(); ++i) { str << "<" << v[i].normal << ">,"; if (i != 0 && i % 3) str << "\n"; } str << "\n}\n"; str << "texture_list{" << v.size() << ",\n"; float r = 0.0; float g = 0.0; float b = 0.0; float t = 1.0 - geometry.opacity() / 255.0; for (auto& i : v) { r = i.color[0] / 255.0; g = i.color[1] / 255.0; b = i.color[2] / 255.0; str << "texture{pigment{rgbt<" << r << ", " << g << ", " << b << "," << t << ">}}\n"; } str << "\n}\n"; str << "face_indices{" << tris.size() / 3 << ",\n"; for (size_t i = 0; i < tris.size(); i += 3) { str << "<" << tris[i] << "," << tris[i + 1] << "," << tris[i + 2] << ">"; // this represents the texture color from the vertex str << ", " << (i / 3); if (i != tris.size() - 3) str << ", "; if (i != 0 && ((i + 1) / 3) % 3 == 0) str << '\n'; } str << "\n}\n"; str << "\tpigment { rgbt <" << r << ", " << g << "," << b << "," << t << "> }\n" << "}\n\n"; m_sceneData += str.str(); } void POVRayVisitor::visit(LineStripGeometry&) {} } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/povrayvisitor.h000066400000000000000000000041651506155467400232410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_POVRAYVISITOR_H #define AVOGADRO_RENDERING_POVRAYVISITOR_H #include "visitor.h" #include "avogadrorendering.h" #include "camera.h" #include namespace Avogadro { namespace Rendering { /** * @class POVRayVisitor povrayvisitor.h * @brief Visitor that visits scene elements and creates a POV-Ray input file. * * This visitor will render elements in the scene to a text file that contains * elements that can be rendered by POV-Ray. */ class AVOGADRORENDERING_EXPORT POVRayVisitor : public Visitor { public: POVRayVisitor(const Camera& camera); ~POVRayVisitor() override; void begin(); std::string end(); /** * The overloaded visit functions, the base versions of which do nothing. */ void visit(Node&) override { return; } void visit(GroupNode&) override { return; } void visit(GeometryNode&) override { return; } void visit(Drawable&) override; void visit(SphereGeometry&) override; void visit(AmbientOcclusionSphereGeometry&) override; void visit(CurveGeometry&) override { return; } void visit(CylinderGeometry&) override; void visit(MeshGeometry&) override; void visit(TextLabel2D&) override { return; } void visit(TextLabel3D&) override { return; } void visit(LineStripGeometry& geometry) override; void setCamera(const Camera& c) { m_camera = c; } Camera camera() const { return m_camera; } void setBackgroundColor(const Vector3ub& c) { m_backgroundColor = c; } void setAmbientColor(const Vector3ub& c) { m_ambientColor = c; } void setAspectRatio(float ratio) { m_aspectRatio = ratio; } private: Camera m_camera; Vector3ub m_backgroundColor; Vector3ub m_ambientColor; float m_aspectRatio; std::string m_sceneData; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_POVRAYVISITOR_H avogadrolibs-1.101.0/avogadro/rendering/primitive.h000066400000000000000000000070411506155467400223050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_PRIMITIVE_H #define AVOGADRO_RENDERING_PRIMITIVE_H #include namespace Avogadro { namespace Rendering { /** Identifies the type of object a primitive represents. */ enum Type { InvalidType = -1, AtomType, BondType }; /** Used to identify the primitive during picking. */ struct Identifier { Identifier() : molecule(0), type(InvalidType), index(MaxIndex) {} bool operator==(const Identifier& other) const { return molecule == other.molecule && type == other.type && index == other.index; } bool operator!=(const Identifier& other) const { return !operator==(other); } bool isValid() const { return type != InvalidType && molecule != nullptr; } const void* molecule; Type type; Index index; }; class Primitive { public: /** Identifies the type of object a primitive represents. */ enum Type { Invalid = -1, Atom, Bond }; /** Used to identify the primitive during picking. */ struct Identifier { Identifier() : molecule(0), type(Invalid), index(MaxIndex) {} bool operator==(const Identifier& other) const { return molecule == other.molecule && type == other.type && index == other.index; } bool operator!=(const Identifier& other) const { return !operator==(other); } bool isValid() const { return type != Invalid; } const void* molecule; Type type; Index index; }; Primitive(Identifier id, const Vector3ub& color_) : m_identifier(id), m_color(color_) { } Identifier identifier() const { return m_identifier; } void setIdentifier(Identifier id) { m_identifier = id; } const Vector3ub& color() const { return m_color; } void setColor(const Vector3ub& c) { m_color = c; } private: Identifier m_identifier; Vector3ub m_color; }; class Sphere : public Primitive { public: Sphere(const Vector3f& position_, float radius_, Primitive::Identifier id, const Vector3ub& color_) : Primitive(id, color_), m_position(position_), m_radius(radius_) { } const Vector3f& position() const { return m_position; } void setPosition(const Vector3f& pos) { m_position = pos; } float radius() const { return m_radius; } void setRadius(float r) { m_radius = r; } private: Vector3f m_position; float m_radius; }; class Cylinder : public Primitive { public: /// Direction must be normalized Cylinder(const Vector3f& position_, const Vector3f& direction_, float length_, float radius_, Primitive::Identifier id, const Vector3ub& color_) : Primitive(id, color_), m_position(position_), m_direction(direction_), m_length(length_), m_radius(radius_) { } const Vector3f& position() const { return m_position; } void setPosition(const Vector3f& pos) { m_position = pos; } const Vector3f& direction() const { return m_direction; } void setDirection(const Vector3f& dir) { m_direction = dir; } float length() const { return m_length; } void setLength(float l) { m_length = l; } float radius() const { return m_radius; } void setRadius(float r) { m_radius = r; } private: Vector3f m_position; Vector3f m_direction; float m_length; float m_radius; }; } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_PRIMITIVE_H avogadrolibs-1.101.0/avogadro/rendering/quad.cpp000066400000000000000000000021041506155467400215550ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "quad.h" using Avogadro::Core::Array; namespace Avogadro::Rendering { void Quad::setQuad(const Vector3f& topLeft, const Vector3f& topRight, const Vector3f& bottomLeft, const Vector3f& bottomRight) { const Vector3f bottom = bottomRight - bottomLeft; const Vector3f left = topLeft - bottomLeft; const Vector3f normal = bottom.cross(left).normalized(); Array norms(4, normal); Array verts(4); verts[0] = topLeft; verts[1] = topRight; verts[2] = bottomLeft; verts[3] = bottomRight; Array indices(6); indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 2; indices[4] = 1; indices[5] = 3; clear(); addVertices(verts, norms); addTriangles(indices); } } // End namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/quad.h000066400000000000000000000017741506155467400212360ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_QUAD_H #define AVOGADRO_RENDERING_QUAD_H #include "meshgeometry.h" #include namespace Avogadro { namespace Rendering { /** * @class Quad quad.h * * @brief The Quad class is a convenience class for creating a quadrilateral * mesh. */ class AVOGADRORENDERING_EXPORT Quad : public MeshGeometry { public: Quad() {} ~Quad() override {} /** * @brief setQuad Set the four corners of the quad. */ void setQuad(const Vector3f& topLeft, const Vector3f& topRight, const Vector3f& bottomLeft, const Vector3f& bottomRight); }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_QUAD_H avogadrolibs-1.101.0/avogadro/rendering/quadoutline.cpp000066400000000000000000000014651506155467400231660ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "quadoutline.h" using Avogadro::Core::Array; namespace Avogadro::Rendering { void QuadOutline::setQuad(const Vector3f& topLeft, const Vector3f& topRight, const Vector3f& bottomLeft, const Vector3f& bottomRight, float lineWidth) { Array verts(5); verts[0] = topLeft; verts[1] = topRight; verts[2] = bottomRight; verts[3] = bottomLeft; verts[4] = topLeft; clear(); addLineStrip(verts, lineWidth); } } // End namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/quadoutline.h000066400000000000000000000022061506155467400226250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_QUADOUTLINE_H #define AVOGADRO_RENDERING_QUADOUTLINE_H #include "linestripgeometry.h" #include namespace Avogadro { namespace Rendering { /** * @class QuadOutline quadoutline.h * * @brief The QuadOutline class is a convenience class for creating a * quadrilateral outline as a LineStripGeometry. */ class AVOGADRORENDERING_EXPORT QuadOutline : public LineStripGeometry { public: QuadOutline() {} ~QuadOutline() override {} /** * @brief setQuad Set the four corners of the quad. */ void setQuad(const Vector3f& topLeft, const Vector3f& topRight, const Vector3f& bottomLeft, const Vector3f& bottomRight, float lineWidth); }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_QUADOUTLINE_H avogadrolibs-1.101.0/avogadro/rendering/scene.cpp000066400000000000000000000021401506155467400217200ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "scene.h" #include "geometryvisitor.h" #include namespace Avogadro::Rendering { Scene::Scene() : m_backgroundColor(0, 0, 0, 0), m_dirty(true), m_center(Vector3f::Zero()), m_radius(4.0f) { } Scene::~Scene() {} Vector3f Scene::center() { if (!m_dirty) return m_center; GeometryVisitor visitor; m_rootNode.accept(visitor); // For an empty scene ensure that a minimum radius of 4.0 (gives space). m_center = visitor.center(); m_radius = std::max(4.0f, visitor.radius()) + 2.0f; m_dirty = false; return m_center; } float Scene::radius() { if (!m_dirty) return m_radius; // We need to know where the center is to get the radius center(); return m_radius; } void Scene::clear() { m_rootNode.clear(); m_dirty = true; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/scene.h000066400000000000000000000076771506155467400214110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_SCENE_H #define AVOGADRO_RENDERING_SCENE_H #include "avogadrorenderingexport.h" #include "groupnode.h" #include "primitive.h" #include #include #include // For member variables. #include // For member variables. #include // For member variables. namespace Avogadro { namespace Core { class Molecule; } namespace Rendering { /** * @class Scene scene.h * @brief The Scene contains data ready to be rendered. * @author Marcus D. Hanwell * * A collection of geometric primitives ready for rendering. This is highly * targeted for fast rendering on modern OpenGL 2.1/ES 2.0 GPUs, but can also * be adapted and rendered using other approaches. */ /// Pack the vertex data into a contiguous array. struct ColorTextureVertex { Vector3f vertex; // 12 bytes Vector3ub color; // 3 bytes unsigned char unusedAlign; // 1 byte Vector2f textureCoord; // 8 bytes Vector2f textureCoord2; // 8 bytes ColorTextureVertex(const Vector3f& p, const Vector3ub& c, const Vector2f& t, const Vector2f& t2 = Vector2f::Zero()) : vertex(p), color(c), textureCoord(t), textureCoord2(t2) { } static int vertexOffset() { return 0; } static int colorOffset() { return static_cast(sizeof(Vector3f)); } static int textureCoordOffset() { return colorOffset() + static_cast(sizeof(Vector3ub) + sizeof(unsigned char)); } static int textureCoord2Offset() { return textureCoordOffset() + static_cast(sizeof(Vector2f)); } }; // 32 bytes total size - 16/32/64 are ideal for alignment. /// Pack the vertex data into a contiguous array. struct ColorNormalVertex { Vector3ub color; // 3 bytes unsigned char unusedAlign; // 1 byte Vector3f normal; // 12 bytes Vector3f vertex; // 12 bytes unsigned char padding[4]; // 4 bytes ColorNormalVertex() {} ColorNormalVertex(const Vector3ub& c, const Vector3f& n, const Vector3f& v) : color(c), normal(n), vertex(v) { } static int colorOffset() { return 0; } static int normalOffset() { return static_cast(sizeof(Vector3ub) + sizeof(unsigned char)); } static int vertexOffset() { return normalOffset() + static_cast(sizeof(Vector3f)); } }; // 32 bytes total size - 16/32/64 are ideal for alignment. class AVOGADRORENDERING_EXPORT Scene { public: Scene(); ~Scene(); /** Get the center of the points contained in this Scene. */ Vector3f center(); /** Get the radius, which is currently just the largest of the axis-aligned * components of the positions. */ float radius(); /** * Get the root node of the scene. */ GroupNode& rootNode() { return m_rootNode; } const GroupNode& rootNode() const { return m_rootNode; } /** * Set the background color of the scene (default is black). */ void setBackgroundColor(const Vector4ub& color) { m_backgroundColor = color; } /** * Get the background color of the scene. */ Vector4ub backgroundColor() const { return m_backgroundColor; } /** * Mark the scene as dirty, primarily to ensure radius/center will be * recalculated. */ void setDirty(bool dirty) { m_dirty = dirty; } /** * Has the scene been marked as dirty? */ bool isDirty() const { return m_dirty; } /** Clear the scene of all elements. */ void clear(); private: GroupNode m_rootNode; Vector4ub m_backgroundColor; mutable bool m_dirty; mutable Vector3f m_center; mutable float m_radius; }; } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_SCENE_H avogadrolibs-1.101.0/avogadro/rendering/shader.cpp000066400000000000000000000037351506155467400221040ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "shader.h" #include "avogadrogl.h" namespace Avogadro::Rendering { Shader::Shader(Type type_, const std::string& source_) : m_type(type_), m_handle(0), m_dirty(true), m_source(source_) { } Shader::~Shader() {} void Shader::setType(Type type_) { m_type = type_; m_dirty = true; } void Shader::setSource(const std::string& source_) { m_source = source_; m_dirty = true; } bool Shader::compile() { if (m_source.empty() || m_type == Unknown || !m_dirty) return false; // Ensure we delete the previous shader if necessary. if (m_handle != 0) { glDeleteShader(static_cast(m_handle)); m_handle = 0; } GLenum type_ = m_type == Vertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER; GLuint handle_ = glCreateShader(type_); const auto* source_ = static_cast(m_source.c_str()); glShaderSource(handle_, 1, &source_, nullptr); glCompileShader(handle_); GLint isCompiled; glGetShaderiv(handle_, GL_COMPILE_STATUS, &isCompiled); // Handle shader compilation failures. if (!isCompiled) { GLint length(0); glGetShaderiv(handle_, GL_INFO_LOG_LENGTH, &length); if (length > 1) { char* logMessage = new char[length]; glGetShaderInfoLog(handle_, length, nullptr, logMessage); m_error = logMessage; delete[] logMessage; } glDeleteShader(handle_); return false; } // The shader compiled, store its handle and return success. m_handle = static_cast(handle_); m_dirty = false; return true; } void Shader::cleanup() { if (m_type == Unknown || m_handle == 0) return; glDeleteShader(static_cast(m_handle)); m_handle = 0; m_dirty = false; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/shader.h000066400000000000000000000041321506155467400215410ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_SHADER_H #define AVOGADRO_RENDERING_SHADER_H #include "avogadrorenderingexport.h" #include #include // For member variables. namespace Avogadro { namespace Rendering { /** * @class Shader shader.h * @brief Vertex or Fragment shader, combined into a ShaderProgram. * @author Marcus D. Hanwell * * This class creates a Vertex or Fragment shader, that can be attached to a * ShaderProgram in order to render geometry etc. */ class AVOGADRORENDERING_EXPORT Shader { public: /** Available shader types. */ enum Type { Vertex, /**< Vertex shader */ Fragment, /**< Fragment shader */ Unknown /**< Unknown (default) */ }; explicit Shader(Type type = Unknown, const std::string& source = ""); ~Shader(); /** Set the shader type. */ void setType(Type type); /** Get the shader type, typically Vertex or Fragment. */ Type type() const { return m_type; } /** Set the shader source to the supplied string. */ void setSource(const std::string& source); /** Get the source for the shader. */ std::string source() const { return m_source; } /** Get the error message (empty if none) for the shader. */ std::string error() const { return m_error; } /** Get the handle of the shader. */ Index handle() const { return m_handle; } /** Compile the shader. * @note A valid context must to current in order to compile the shader. */ bool compile(); /** Delete the shader. * @note This should only be done once the ShaderProgram is done with the * Shader. */ void cleanup(); protected: Type m_type; Index m_handle; bool m_dirty; std::string m_source; std::string m_error; }; } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_SHADER_H avogadrolibs-1.101.0/avogadro/rendering/shaderprogram.cpp000066400000000000000000000331611506155467400234700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "shaderprogram.h" #include "avogadrogl.h" #include "shader.h" #include "texture2d.h" #include #include namespace Avogadro::Rendering { namespace { inline GLenum convertType(Type type) { switch (type) { default: case UCharType: return GL_UNSIGNED_BYTE; case CharType: return GL_BYTE; case ShortType: return GL_SHORT; case UShortType: return GL_UNSIGNED_SHORT; case IntType: return GL_INT; case UIntType: return GL_UNSIGNED_INT; case FloatType: return GL_FLOAT; case DoubleType: return GL_DOUBLE; } } inline GLenum lookupTextureUnit(GLint index) { #define MAKE_TEXTURE_UNIT_CASE(i) \ case i: \ return GL_TEXTURE##i; switch (index) { MAKE_TEXTURE_UNIT_CASE(0) MAKE_TEXTURE_UNIT_CASE(1) MAKE_TEXTURE_UNIT_CASE(2) MAKE_TEXTURE_UNIT_CASE(3) MAKE_TEXTURE_UNIT_CASE(4) MAKE_TEXTURE_UNIT_CASE(5) MAKE_TEXTURE_UNIT_CASE(6) MAKE_TEXTURE_UNIT_CASE(7) MAKE_TEXTURE_UNIT_CASE(8) MAKE_TEXTURE_UNIT_CASE(9) MAKE_TEXTURE_UNIT_CASE(10) MAKE_TEXTURE_UNIT_CASE(11) MAKE_TEXTURE_UNIT_CASE(12) MAKE_TEXTURE_UNIT_CASE(13) MAKE_TEXTURE_UNIT_CASE(14) MAKE_TEXTURE_UNIT_CASE(15) MAKE_TEXTURE_UNIT_CASE(16) MAKE_TEXTURE_UNIT_CASE(17) MAKE_TEXTURE_UNIT_CASE(18) MAKE_TEXTURE_UNIT_CASE(19) MAKE_TEXTURE_UNIT_CASE(20) MAKE_TEXTURE_UNIT_CASE(21) MAKE_TEXTURE_UNIT_CASE(22) MAKE_TEXTURE_UNIT_CASE(23) MAKE_TEXTURE_UNIT_CASE(24) MAKE_TEXTURE_UNIT_CASE(25) MAKE_TEXTURE_UNIT_CASE(26) MAKE_TEXTURE_UNIT_CASE(27) MAKE_TEXTURE_UNIT_CASE(28) MAKE_TEXTURE_UNIT_CASE(29) MAKE_TEXTURE_UNIT_CASE(30) MAKE_TEXTURE_UNIT_CASE(31) default: return 0; } } } // namespace ShaderProgram::ShaderProgram() : m_handle(0), m_vertexShader(0), m_fragmentShader(0), m_linked(false) { initializeTextureUnits(); } ShaderProgram::~ShaderProgram() { if (m_handle != 0) { glDeleteProgram(static_cast(m_handle)); } } bool ShaderProgram::attachShader(const Shader& shader) { if (shader.handle() == 0) { m_error = "Shader object was not initialized, cannot attach it."; return false; } if (shader.type() == Shader::Unknown) { m_error = "Shader object is of type Unknown and cannot be used."; return false; } if (m_handle == 0) { GLuint handle_ = glCreateProgram(); if (handle_ == 0) { m_error = "Could not create shader program."; return false; } m_handle = static_cast(handle_); m_linked = false; } if (shader.type() == Shader::Vertex) { if (m_vertexShader != 0) { glDetachShader(static_cast(m_handle), static_cast(m_vertexShader)); } m_vertexShader = shader.handle(); } else if (shader.type() == Shader::Fragment) { if (m_fragmentShader != 0) { glDetachShader(static_cast(m_handle), static_cast(m_fragmentShader)); } m_fragmentShader = shader.handle(); } else { m_error = "Unknown shader type encountered - this should not happen."; return false; } glAttachShader(static_cast(m_handle), static_cast(shader.handle())); m_linked = false; return true; } bool ShaderProgram::detachShader(const Shader& shader) { if (shader.handle() == 0) { m_error = "Shader object was not initialized, cannot attach it."; return false; } if (shader.type() == Shader::Unknown) { m_error = "Shader object is of type Unknown and cannot be used."; return false; } if (m_handle == 0) { m_error = "This shader prorgram has not been initialized yet."; } switch (shader.type()) { case Shader::Vertex: if (m_vertexShader != shader.handle()) { m_error = "The supplied shader was not attached to this program."; return false; } else { glDetachShader(static_cast(m_handle), static_cast(shader.handle())); m_vertexShader = 0; return true; } case Shader::Fragment: if (m_fragmentShader != shader.handle()) { m_error = "The supplied shader was not attached to this program."; return false; } else { glDetachShader(static_cast(m_handle), static_cast(shader.handle())); m_fragmentShader = 0; return true; } default: return false; } } bool ShaderProgram::link() { if (m_linked) return true; if (m_handle == 0) { m_error = "Program has not been initialized, and/or does not have shaders."; return false; } GLint isCompiled; glLinkProgram(static_cast(m_handle)); glGetProgramiv(static_cast(m_handle), GL_LINK_STATUS, &isCompiled); if (isCompiled == 0) { GLint length(0); glGetShaderiv(static_cast(m_handle), GL_INFO_LOG_LENGTH, &length); if (length > 1) { char* logMessage = new char[length]; glGetShaderInfoLog(static_cast(m_handle), length, nullptr, logMessage); m_error = logMessage; delete[] logMessage; } return false; } m_linked = true; m_attributes.clear(); return true; } bool ShaderProgram::bind() { if (!m_linked && !link()) return false; glUseProgram(static_cast(m_handle)); return true; } void ShaderProgram::release() { glUseProgram(0); releaseAllTextureUnits(); } bool ShaderProgram::enableAttributeArray(const std::string& name) { auto location = static_cast(findAttributeArray(name)); if (location == -1) { m_error = "Could not enable attribute " + name + ". No such attribute."; return false; } glEnableVertexAttribArray(location); return true; } bool ShaderProgram::disableAttributeArray(const std::string& name) { auto location = static_cast(findAttributeArray(name)); if (location == -1) { m_error = "Could not disable attribute " + name + ". No such attribute."; return false; } glDisableVertexAttribArray(location); return true; } #define BUFFER_OFFSET(i) ((char*)nullptr + (i)) bool ShaderProgram::useAttributeArray(const std::string& name, int offset, size_t stride, Type elementType, int elementTupleSize, NormalizeOption normalize) { auto location = static_cast(findAttributeArray(name)); if (location == -1) { m_error = "Could not use attribute " + name + ". No such attribute."; return false; } glVertexAttribPointer(location, elementTupleSize, convertType(elementType), normalize == Normalize ? GL_TRUE : GL_FALSE, static_cast(stride), BUFFER_OFFSET(offset)); return true; } bool ShaderProgram::setTextureSampler(const std::string& name, const Texture2D& texture) { // Look up sampler location: auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set sampler " + name + ". No uniform with that name."; return false; } // Check if the texture is already bound: GLint textureUnitId = 0; auto result = m_textureUnitBindings.find(&texture); if (result == m_textureUnitBindings.end()) { // Not bound. Attempt to bind the texture to an available texture unit. // We'll leave GL_TEXTURE0 unbound, as it is used for manipulating // textures. auto begin = m_boundTextureUnits.begin() + 1; auto end = m_boundTextureUnits.end(); auto available = std::find(begin, end, false); if (available == end) { m_error = "Could not set sampler " + name + ". No remaining texture units available."; return false; } textureUnitId = static_cast(available - begin); GLenum textureUnit = lookupTextureUnit(textureUnitId); if (textureUnit == 0) { m_error = "Could not set sampler " + name + ". Texture unit lookup failed."; return false; } glActiveTexture(textureUnit); if (!texture.bind()) { m_error = "Could not set sampler " + name + ": Error while binding texture: '" + texture.error() + "'."; glActiveTexture(GL_TEXTURE0); return false; } glActiveTexture(GL_TEXTURE0); // Mark texture unit as in-use. m_textureUnitBindings.insert(std::make_pair(&texture, textureUnitId)); *available = true; } else { // Texture is already bound. textureUnitId = result->second; } // Set the texture unit uniform glUniform1i(location, textureUnitId); return true; } bool ShaderProgram::setUniformValue(const std::string& name, int i) { auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set uniform " + name + ". No such uniform."; return false; } glUniform1i(location, static_cast(i)); return true; } bool ShaderProgram::setUniformValue(const std::string& name, float f) { auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set uniform " + name + ". No such uniform."; return false; } glUniform1f(location, static_cast(f)); return true; } bool ShaderProgram::setUniformValue(const std::string& name, const Eigen::Matrix3f& matrix) { auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set uniform " + name + ". No such uniform."; return false; } glUniformMatrix3fv(location, 1, GL_FALSE, static_cast(matrix.data())); return true; } bool ShaderProgram::setUniformValue(const std::string& name, const Eigen::Matrix4f& matrix) { auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set uniform " + name + ". No such uniform."; return false; } glUniformMatrix4fv(location, 1, GL_FALSE, static_cast(matrix.data())); return true; } bool ShaderProgram::setUniformValue(const std::string& name, const Vector3f& v) { auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set uniform " + name + ". No such uniform."; return false; } glUniform3fv(location, 1, v.data()); return true; } bool ShaderProgram::setUniformValue(const std::string& name, const Vector2i& v) { auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set uniform " + name + ". No such uniform."; return false; } glUniform2iv(location, 1, v.data()); return true; } bool ShaderProgram::setUniformValue(const std::string& name, const Vector3ub& v) { auto location = static_cast(findUniform(name)); if (location == -1) { m_error = "Could not set uniform " + name + ". No such uniform."; return false; } Vector3f colorf(v.cast() * (1.0f / 255.0f)); glUniform3fv(location, 1, colorf.data()); return true; } bool ShaderProgram::setAttributeArrayInternal( const std::string& name, void* buffer, Avogadro::Type type, int tupleSize, ShaderProgram::NormalizeOption normalize) { if (type == Avogadro::UnknownType) { m_error = "Unrecognized data type for attribute " + name + "."; return false; } auto location = static_cast(findAttributeArray(name)); if (location == -1) { m_error = "Could not set attribute " + name + ". No such attribute."; return false; } const auto* data = static_cast(buffer); glVertexAttribPointer(location, tupleSize, convertType(type), normalize == Normalize ? GL_TRUE : GL_FALSE, 0, data); return true; } void ShaderProgram::initializeTextureUnits() { GLint numTextureUnits = 0; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numTextureUnits); // We'll impose a hard limit of 32 texture units for symbolic lookups. // This seems to be about the maximum available on current hardware. // If increasing this limit, modify the lookupTextureUnit method // appropriately. numTextureUnits = std::clamp(numTextureUnits, 0, 32); m_boundTextureUnits.clear(); m_boundTextureUnits.resize(numTextureUnits, false); m_textureUnitBindings.clear(); } void ShaderProgram::releaseAllTextureUnits() { std::fill(m_boundTextureUnits.begin(), m_boundTextureUnits.end(), false); m_textureUnitBindings.clear(); } inline int ShaderProgram::findAttributeArray(const std::string& name) { if (name.empty() || !m_linked) return -1; const auto* namePtr = static_cast(name.c_str()); auto location = static_cast( glGetAttribLocation(static_cast(m_handle), namePtr)); if (location == -1) { m_error = "Specified attribute not found in current shader program: "; m_error += name; } return location; } inline int ShaderProgram::findUniform(const std::string& name) { if (name.empty() || !m_linked) return -1; const auto* namePtr = static_cast(name.c_str()); auto location = static_cast( glGetUniformLocation(static_cast(m_handle), namePtr)); if (location == -1) m_error = "Uniform " + name + " not found in current shader program."; return location; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/shaderprogram.h000066400000000000000000000162001506155467400231300ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_SHADERPROGRAM_H #define AVOGADRO_RENDERING_SHADERPROGRAM_H #include "avogadrorenderingexport.h" #include #include #include #include // For member variables. #include // For member variables. #include // For member variables. namespace Avogadro { namespace Rendering { class Shader; class Texture2D; /** * @class ShaderProgram shaderprogram.h * @brief The ShaderProgram uses one or more Shader objects. * @author Marcus D. Hanwell * * This class creates a Vertex or Fragment shader, that can be attached to a * ShaderProgram in order to render geometry etc. */ class AVOGADRORENDERING_EXPORT ShaderProgram { public: /** Options for attribute normalization. */ enum NormalizeOption { /// The values range across the limits of the numeric type. /// This option instructs the rendering engine to normalize them to /// the range [0.0, 1.0] for unsigned types, and [-1.0, 1.0] for signed /// types. /// For example, unsigned char values will be mapped so that 0 = 0.0, /// and 255 = 1.0. /// The resulting floating point numbers will be passed into /// the shader program. Normalize, /// The values should be used as-is. Do not perform any normalization. NoNormalize }; ShaderProgram(); ~ShaderProgram(); /** Attach the supplied shader to this program. * @note A maximum of one Vertex shader and one Fragment shader can be * attached to a shader prorgram. * @return true on success. */ bool attachShader(const Shader& shader); /** Detach the supplied shader from this program. * @note A maximum of one Vertex shader and one Fragment shader can be * attached to a shader prorgram. * @return true on success. */ bool detachShader(const Shader& shader); /** Attempt to link the shader program. * @return false on failure. Query error to get the reason. * @note The shaders attached to the program must have been compiled. */ bool link(); /** Bind the program in order to use it. If the program has not been linked * then link() will be called. */ bool bind(); /** Releases the shader program from the current context. */ void release(); /** Get the error message (empty if none) for the shader program. */ std::string error() const { return m_error; } /** Enable the named attribute array. Return false if the attribute array is * not contained in the linked shader program. */ bool enableAttributeArray(const std::string& name); /** Disable the named attribute array. Return false if the attribute array is * not contained in the linked shader program. */ bool disableAttributeArray(const std::string& name); /** Use the named attribute array with the bound BufferObject. * @param name of the attribute (as seen in the shader program). * @param offset into the bound BufferObject. * @param stride The stride of the element access (i.e. the size of each * element in the currently bound BufferObject). 0 may be used to indicate * tightly packed data. * @param elementType Tag identifying the memory representation of the * element. * @param elementTupleSize The number of elements per vertex (e.g. a 3D * position attribute would be 3). * @param normalize Indicates the range used by the attribute data. * See NormalizeOption for more information. * @return false if the attribute array does not exist. */ bool useAttributeArray(const std::string& name, int offset, size_t stride, Avogadro::Type elementType, int elementTupleSize, NormalizeOption normalize); /** Upload the supplied array of tightly packed values to the named attribute. * BufferObject attributes should be preferred and this may be removed in * future. * * @param name Attribute name * @param array Container of data. See note. * @param tupleSize The number of elements per vertex, e.g. a 3D coordinate * array will have a tuple size of 3. * @param normalize Indicates the range used by the attribute data. * See NormalizeOption for more information. * * @note The ContainerT type must have tightly packed values of * ContainerT::value_type accessible by reference via ContainerT::operator[]. * Additionally, the standard size() and empty() methods must be implemented. * The std::vector and Avogadro::Core::Array classes are examples of such * supported containers. */ template bool setAttributeArray(const std::string& name, const ContainerT& array, int tupleSize, NormalizeOption normalize); /** Set the sampler @a samplerName to use the specified texture. */ bool setTextureSampler(const std::string& samplerName, const Texture2D& texture); /** Set the @p name uniform value to int @p i. */ bool setUniformValue(const std::string& name, int i); /** Set the @p name uniform value to float @p f. */ bool setUniformValue(const std::string& name, float f); /** Set the @p name uniform value to @p matrix. */ bool setUniformValue(const std::string& name, const Eigen::Matrix3f& matrix); bool setUniformValue(const std::string& name, const Eigen::Matrix4f& matrix); /** Set the @p name uniform value to the supplied value. @{ */ bool setUniformValue(const std::string& name, const Vector3f& v); bool setUniformValue(const std::string& name, const Vector2i& v); bool setUniformValue(const std::string& name, const Vector3ub& v); /** @} */ protected: bool setAttributeArrayInternal(const std::string& name, void* buffer, Avogadro::Type type, int tupleSize, NormalizeOption normalize); Index m_handle; Index m_vertexShader; Index m_fragmentShader; bool m_linked; std::string m_error; std::map m_attributes; std::map m_textureUnitBindings; std::vector m_boundTextureUnits; private: void initializeTextureUnits(); void releaseAllTextureUnits(); int findAttributeArray(const std::string& name); int findUniform(const std::string& name); }; template inline bool ShaderProgram::setAttributeArray(const std::string& name, const ContainerT& array, int tupleSize, NormalizeOption normalize) { if (array.empty()) { m_error = "Refusing to upload empty array for attribute " + name + "."; return false; } Type type = Avogadro::TypeTraits::EnumValue; return setAttributeArrayInternal(name, &array[0], type, tupleSize, normalize); } } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_SHADERPROGRAM_H avogadrolibs-1.101.0/avogadro/rendering/solid_first_fs.glsl000066400000000000000000000172771506155467400240340ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////// // // First-stage screen-space fragment shader for the solid pipeline // // It offers ambient occlusion and edge detection capabilities. // ////////////////////////////////////////////////////////////////////// #version 120 // // Input // // texture coordinates varying vec2 UV; // // Uniforms // // RGB rendered texture uniform sampler2D inRGBTex; // RGB color for applying fog uniform float fogR; uniform float fogG; uniform float fogB; uniform float offset; // Depth rendered texture uniform sampler2D inDepthTex; // 1.0 if enabled, 0.0 if disabled uniform float inAoEnabled; // 0.0 if disabled uniform float inFogStrength; // Shadow strength for SSAO uniform float inAoStrength; // 1.0 if enabled, 0.0 if disabled uniform float inEdStrength; // amount of offset when zoom-in or zoom-out. uniform float uoffset; // Dof strength uniform float inDofStrength; // Dof position uniform float inDofPosition; // position for other molecules. uniform float inFogPosition; // Rendering surface dimensions, in pixels uniform float width, height; vec3 getNormalAt(vec2 normalUV) { float xpos = texture2D(inDepthTex, normalUV + vec2(1.0 / width, 0.0)).x; float xneg = texture2D(inDepthTex, normalUV - vec2(1.0 / width, 0.0)).x; float ypos = texture2D(inDepthTex, normalUV + vec2(0.0, 1.0 / height)).x; float yneg = texture2D(inDepthTex, normalUV - vec2(0.0, 1.0 / height)).x; float xdelta = xpos - xneg; float ydelta = ypos - yneg; vec3 r = vec3(xdelta, ydelta, 1.0 / width + 1.0 / height); return normalize(r); } vec3 getNormalNear(vec2 normalUV) { float cent = texture2D(inDepthTex, normalUV).x; float xpos = texture2D(inDepthTex, normalUV + vec2(1.0 / width, 0.0)).x; float xneg = texture2D(inDepthTex, normalUV - vec2(1.0 / width, 0.0)).x; float ypos = texture2D(inDepthTex, normalUV + vec2(0.0, 1.0 / height)).x; float yneg = texture2D(inDepthTex, normalUV - vec2(0.0, 1.0 / height)).x; float xposdelta = xpos - cent; float xnegdelta = cent - xneg; float yposdelta = ypos - cent; float ynegdelta = cent - yneg; float xdelta = abs(xposdelta) > abs(xnegdelta) ? xnegdelta : xposdelta; float ydelta = abs(yposdelta) > abs(ynegdelta) ? ynegdelta : yposdelta; vec3 r = vec3(xdelta, ydelta, 0.5 / width + 0.5 / height); return normalize(r); } float lerp(float a, float b, float f) { return a + f * (b - a); } float rand(vec2 co) { return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); } float depthToZ(float depth) { float eyeZ = ((height * 0.57735) / 2.0); float near = 2.0; float far = 8000.0; float depthNormalized = 2.0 * depth - 1.0; return 2.0 * near * far / (far + near - depthNormalized * (far - near)); } float calcBlur(float z, float pixelScale) { return clamp(abs(z - 39.0), 0.0, 0.5 * pixelScale); } vec4 applyBlur(vec2 texCoord) { float pixelScale = max(width, height); float origZ = depthToZ(texture2D(inDepthTex, texCoord).x); float blurAmt = calcBlur(origZ, pixelScale); // Skip blurring if the original depth is less than the threshold if (origZ < uoffset * inDofPosition) { return texture2D(inRGBTex, texCoord); } float total = 1.0; vec4 color = texture2D(inRGBTex, texCoord); for (int i = 0; i < 32; i++) { float t = (float(i) / float(64)); float angle = (t * 4.0) * 6.28319; float radius = (t * 2. - 1.); angle += 1.0 * rand(gl_FragCoord.xy); vec2 offset = (vec2(cos(angle), sin(angle)) * radius * 0.05 * inDofStrength) / pixelScale; float z = depthToZ(texture2D(inDepthTex, texCoord + offset).x); float sampleBlur = calcBlur(z, pixelScale); float weight = 1.0 - smoothstep(0.0, 1.0, abs(z - origZ) / blurAmt); vec4 sample = texture2D(inRGBTex, texCoord+offset); color += weight * sample; total += weight; } return color / total; } vec4 applyFog(vec2 texCoord) { vec4 finalColor = mix( texture2D(inRGBTex, texCoord), vec4(vec3(fogR, fogG, fogB), 1.), pow(texture2D(inDepthTex, texCoord.xy).r, uoffset * inFogPosition / 10.0) ) + inFogStrength / 100.0; return finalColor; } const vec2 SSAOkernel[16] = vec2[16]( vec2(0.072170, 0.081556), vec2(-0.035126, 0.056701), vec2(-0.034186, -0.083598), vec2(-0.056102, -0.009235), vec2(0.017487, -0.099822), vec2(0.071065, 0.015921), vec2(0.040950, 0.079834), vec2(-0.087751, 0.065326), vec2(0.061108, -0.025829), vec2(0.081262, -0.025854), vec2(-0.063816, 0.083857), vec2(0.043747, -0.068586), vec2(-0.089848, 0.049046), vec2(-0.065370, 0.058761), vec2(0.099581, -0.089322), vec2(-0.032077, -0.042826) ); float computeSSAOLuminosity(vec3 normal) { float totalOcclusion = 0.0; float depth = texture2D(inDepthTex, UV).x; float A = (width * UV.x + 10 * height * UV.y) * 2.0 * 3.14159265358979 * 5.0 / 16.0; float S = sin(A); float C = cos(A); mat2 rotation = mat2( C, -S, S, C ); for (int i = 0; i < 16; i++) { vec2 samplePoint = rotation * SSAOkernel[i]; float occluderDepth = texture2D(inDepthTex, UV + samplePoint).x; vec3 occluder = vec3(samplePoint.xy, depth - occluderDepth); float d = length(occluder); float occlusion = max(0.0, dot(normal, occluder)) * (1.0 / (1.0 + d)); totalOcclusion += occlusion; } return max(0.0, 1.2 - inAoStrength * totalOcclusion); } float computeEdgeLuminosity(vec3 normal) { return max(0.0, pow(normal.z - 0.1, 1.0 / 3.0)); } void main() { float luminosity = 1.0; vec4 color = texture2D(inRGBTex, UV); vec4 finalColor = color; // Initialize finalColor with base color // Compute luminosity based on Ambient Occlusion (AO) and Edge Detection if (inAoEnabled != 0.0) { luminosity *= max(1.2 * (1.0 - inAoEnabled), computeSSAOLuminosity(getNormalNear(UV))); } if (inEdStrength != 0.0) { luminosity *= max(1.0 - inEdStrength, computeEdgeLuminosity(getNormalAt(UV))); } // Compute foggedColor if Fog is enabled vec4 foggedColor = color; if (inFogStrength != 0.0) { foggedColor = applyFog(UV); } // Compute blurredColor if DOF is enabled vec4 blurredColor = color; if (inDofStrength != 0.0) { blurredColor = applyBlur(UV); } // Determine finalColor based on enabled effects if (inAoEnabled != 0.0 || inEdStrength != 0.0 || inDofStrength != 0.0) { if (inFogStrength != 0.0 && inDofStrength != 0.0) { // Both Fog and DOF are enabled vec4 mixedColor = mix(foggedColor, blurredColor, 0.5); finalColor = vec4(mixedColor.rgb * luminosity, mixedColor.a); } else if (inFogStrength != 0.0) { // Only Fog is enabled with ao/edge-detection finalColor = vec4(foggedColor.rgb * luminosity, foggedColor.a); } else if (inDofStrength != 0.0) { // Only DOF is enabled with/without ao/edge finalColor = vec4(blurredColor.rgb * luminosity, blurredColor.a); } else { // Only AO and/or Edge Detection are enabled finalColor = vec4(color.rgb * luminosity, color.a); } } else { // Neither AO, DOF, nor Edge Detection is enabled if (inFogStrength != 0.0) { // Only Fog is enabled finalColor = foggedColor; } else { // No effects are enabled finalColor = color; } } // Set the final fragment color gl_FragColor = finalColor; // Set fragment depth gl_FragDepth = texture2D(inDepthTex, UV).x; } avogadrolibs-1.101.0/avogadro/rendering/solid_vs.glsl000066400000000000000000000006761506155467400226400ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////// // // No-op shader for rendering a fullscreen quad within the solid pipeline // ////////////////////////////////////////////////////////////////////// // // Input // // input coordinates attribute vec3 inXYZ; // // Output // // texture coordinates varying vec2 UV; void main() { gl_Position = vec4(inXYZ.xyz, 1.0); UV = inXYZ.xy * vec2(0.5, 0.5) + vec2(0.5, 0.5); } avogadrolibs-1.101.0/avogadro/rendering/solidpipeline.cpp000066400000000000000000000167301506155467400234750ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "solidpipeline.h" #include "avogadrogl.h" #include "camera.h" #include "shader.h" #include "shaderprogram.h" #include "solid_vs.h" #include "solid_first_fs.h" #include #include namespace Avogadro::Rendering { class SolidPipeline::Private { public: Private() {} void attachStage(ShaderProgram& prog, const GLchar* nameRGB, GLuint texRGB, const GLchar* nameDepth, GLuint texDepth, int w, int h) { prog.bind(); GLuint programID; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&programID); GLuint attrRGB = glGetUniformLocation(programID, nameRGB); glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, texRGB); glUniform1i(attrRGB, 1); GLuint attrDepth = glGetUniformLocation(programID, nameDepth); glActiveTexture(GL_TEXTURE0 + 2); glBindTexture(GL_TEXTURE_2D, texDepth); glUniform1i(attrDepth, 2); prog.setUniformValue("width", float(w)); prog.setUniformValue("height", float(h)); } GLuint defaultFBO; GLuint renderFBO; GLuint renderTexture; GLuint depthTexture; GLuint screenVBO; ShaderProgram firstStageShaders; Shader screenVertexShader; Shader firstFragmentShader; }; static const GLfloat s_fullscreenQuad[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, }; void initializeFramebuffer(GLuint* outFBO, GLuint* texRGB, GLuint* texDepth) { glGenFramebuffers(1, outFBO); glBindFramebuffer(GL_FRAMEBUFFER, *outFBO); glGenTextures(1, texRGB); glBindTexture(GL_TEXTURE_2D, *texRGB); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texRGB, 0); glGenTextures(1, texDepth); glBindTexture(GL_TEXTURE_2D, *texDepth); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, *texDepth, 0); } SolidPipeline::SolidPipeline() : m_pixelRatio(1.0f), m_aoEnabled(false), m_dofStrength(1.0f), m_dofPosition(1.0), m_dofEnabled(false), m_fogPosition(1.0), m_backgroundColor(0, 0, 0, 0), m_fogEnabled(true), m_aoStrength(1.0f), m_fogStrength(1.0f), m_edEnabled(false), m_edStrength(1.0f), m_width(0), m_height(0), d(new Private) { } SolidPipeline::~SolidPipeline() { delete d; } void SolidPipeline::initialize() { initializeFramebuffer(&d->renderFBO, &d->renderTexture, &d->depthTexture); glGenBuffers(1, &d->screenVBO); glBindBuffer(GL_ARRAY_BUFFER, d->screenVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(s_fullscreenQuad), s_fullscreenQuad, GL_STATIC_DRAW); d->screenVertexShader.setType(Shader::Vertex); d->screenVertexShader.setSource(solid_vs); if (!d->screenVertexShader.compile()) std::cout << d->screenVertexShader.error() << std::endl; d->firstFragmentShader.setType(Shader::Fragment); d->firstFragmentShader.setSource(solid_first_fs); if (!d->firstFragmentShader.compile()) std::cout << d->firstFragmentShader.error() << std::endl; d->firstStageShaders.attachShader(d->screenVertexShader); d->firstStageShaders.attachShader(d->firstFragmentShader); if (!d->firstStageShaders.link()) std::cout << d->firstStageShaders.error() << std::endl; } void SolidPipeline::begin() { glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&d->defaultFBO); glBindFramebuffer(GL_FRAMEBUFFER, d->renderFBO); GLenum drawBuffersList[1] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, drawBuffersList); GLfloat tmp[5]; glGetFloatv(GL_COLOR_CLEAR_VALUE, tmp); glGetFloatv(GL_DEPTH_CLEAR_VALUE, tmp + 4); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(tmp[0], tmp[1], tmp[2], tmp[3]); glClearDepth(tmp[4]); } void SolidPipeline::end() { // Draw fullscreen quad glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, d->screenVBO); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); // Draw to screen if (glIsFramebuffer(d->defaultFBO)) { glBindFramebuffer(GL_FRAMEBUFFER, d->defaultFBO); GLenum drawBuffersList[1] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, drawBuffersList); } else { glBindFramebuffer(GL_FRAMEBUFFER, 0); glDrawBuffer(GL_BACK); } d->attachStage(d->firstStageShaders, "inRGBTex", d->renderTexture, "inDepthTex", d->depthTexture, m_width, m_height); d->firstStageShaders.setUniformValue("inAoEnabled", m_aoEnabled ? 1.0f : 0.0f); d->firstStageShaders.setUniformValue("inDofEnabled", m_dofEnabled ? 1.0f : 0.0f); d->firstStageShaders.setUniformValue( "inDofStrength", m_dofEnabled ? (m_dofStrength * 100.0f) : 0.0f); d->firstStageShaders.setUniformValue("inDofPosition", ((m_dofPosition) / 10.0f)); d->firstStageShaders.setUniformValue("inAoStrength", m_aoStrength); d->firstStageShaders.setUniformValue("inEdStrength", m_edStrength); d->firstStageShaders.setUniformValue("inFogEnabled", m_fogEnabled ? 1.0f : 0.0f); d->firstStageShaders.setUniformValue("inFogStrength", m_fogEnabled ? m_fogStrength : 0.0f); d->firstStageShaders.setUniformValue("inFogPosition", m_fogPosition); d->firstStageShaders.setUniformValue("fogR", (m_backgroundColor[0]) / 255.0f); d->firstStageShaders.setUniformValue("fogG", (m_backgroundColor[1]) / 255.0f); d->firstStageShaders.setUniformValue("fogB", (m_backgroundColor[2]) / 255.0f); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableVertexAttribArray(0); } void SolidPipeline::adjustOffset(const Camera& cam) { // The numbers used in calculations are random. // They help define an offset with the projection-matrix // to make the fog dynamic as the molecule moves away // from the camera or come closer. Eigen::Matrix4f projectView = cam.projection().matrix(); float project = ((((5000 + projectView(2, 3) * 1000) / 6) + 55) * 100); float offSet = 0.000102337 * pow(project, 2) - 3.84689 * project + 36182.2; if (project >= 21018.106 && project < 21595.588) { offSet = 2.63129 * project - 54768.4; } else if (project >= 21595.588) { offSet = 9.952 * project - 212865; } d->firstStageShaders.setUniformValue("uoffset", offSet); } void SolidPipeline::resize(int width, int height) { m_width = width * m_pixelRatio; m_height = height * m_pixelRatio; glBindTexture(GL_TEXTURE_2D, d->renderTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, d->depthTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); } void SolidPipeline::setPixelRatio(float ratio) { m_pixelRatio = ratio; } } // End namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/solidpipeline.h000066400000000000000000000071271506155467400231420ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_SOLIDPIPELINE_H #define AVOGADRO_RENDERING_SOLIDPIPELINE_H #include "camera.h" namespace Avogadro { namespace Rendering { class SolidPipeline { public: SolidPipeline(); ~SolidPipeline(); /** * @brief Probably don't want to support copy/assignment. */ SolidPipeline(const SolidPipeline&) = delete; SolidPipeline& operator=(const SolidPipeline&) = delete; /** * @brief Initialize OpenGL objects. */ void initialize(); /** * @brief Begin solid geometry rendering. */ void begin(); void adjustOffset(const Camera& camera); /** * @brief End solid geometry rendering and apply screen-space shaders. */ void end(); /** * @brief Resize buffers for width x height viewport. */ void resize(int width, int height); /** * @brief Set pixel ratio (1.0 on standard displays, 2.0 on Retina, etc.). */ void setPixelRatio(float ratio); /** * @brief Get or set whether Ambient Occlusion is enabled. */ bool getAoEnabled() { return m_aoEnabled; } void setAoEnabled(bool enabled) { m_aoEnabled = enabled; } /** * @brief Get or set whether Depth-of-feild is enabled. */ bool getDofEnabled() { return m_dofEnabled; } void setDofEnabled(bool enabled) { m_dofEnabled = enabled; } /** * @brief Get or set whether Fog is enabled. */ bool getFogEnabled() { return m_fogEnabled; } void setFogEnabled(bool enabled) { m_fogEnabled = enabled; } /** * @brief Set Background Color to it's current value. */ Vector4ub backgroundColor() const { return m_backgroundColor; } void setBackgroundColor(const Vector4ub& c) { m_backgroundColor = c; } /** * @brief Get or set shadow strength for Ambient Occlusion. */ float getAoStrength() { return m_aoStrength; } void setAoStrength(float strength) { m_aoStrength = strength; } /** * @brief Get or set fog strength. */ float getFogStrength() { return m_fogStrength; } void setFogStrength(float strength) { m_fogStrength = strength; } /** * @brief Get or set fog position */ float getFogPosition() { return m_fogPosition; } void setFogPosition(float position) { m_fogPosition = position; } /** * @brief Get or set whether Edge Detection is enabled. */ bool getEdEnabled() { return m_edEnabled; } void setEdEnabled(bool enabled) { m_edEnabled = enabled; m_edStrength = (m_edEnabled) ? 1.0 : 0.0; } /** * @brief Get or set dof strength. */ float getDofStrength() { return m_dofStrength; } void setDofStrength(float strength) { m_dofStrength = strength; } /** * @brief Set positon of dof */ float getDofPosition() { return m_dofPosition; } void setDofPosition(float position) { m_dofPosition = position; } /** * @brief Get or set the strength of the edge effect */ bool getEdStrength() { return m_edStrength; } void setEdStrength(float strength) { m_edStrength = strength; } private: float m_pixelRatio; bool m_aoEnabled; float m_dofStrength; float m_dofPosition; bool m_dofEnabled; float m_fogPosition; Vector4ub m_backgroundColor; Eigen::Affine3f modelView; bool m_fogEnabled; float m_aoStrength; float m_fogStrength; bool m_edEnabled; float m_edStrength; int m_width; int m_height; class Private; Private* d; }; } // End namespace Rendering } // End namespace Avogadro #endifavogadrolibs-1.101.0/avogadro/rendering/sphere_ao_bake_fs.glsl000066400000000000000000000046651506155467400244370ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////// // // Ambient occlusion shader for sphere impostors // // This fragment shader is used for baking the ambient occlusion // maps. // ////////////////////////////////////////////////////////////////////// // // Input // // the sphere center position: eye coords varying vec3 v_pos; // the sphere radius varying float v_radius; // stretched corner: [-1.x, 1.x] (see below) varying vec2 v_corner; // // Uniforms // // the model-view matrix uniform mat4 u_modelView; // the orthographic projection matrix uniform mat4 u_projection; // depth texture sampler uniform sampler2D u_depthTex; // intensity = 1 / (number of light directions) uniform float u_intensity; /** * Inverse gnomonic projection over octahedron unfloded into a square. This * inverse projection goes from texture coordinates to the surface of the unit * sphere. Both the texture and unit sphere coordinates are in the range * [-1, 1]. * * In practice, this function returns the normal vector in model coordinate * space. The z is inverted since going from clip coords to NDC inverts the * z axis. * * reference: Tarini et al. page 3, eq. (5) */ vec3 textureToSphereSurfaceCoord(in vec2 coord) { vec2 absCoord = abs(coord); float h = 1.0 - absCoord.s - absCoord.t; return (h >= 0.0) ? vec3(coord.st, -h) : vec3(sign(coord.st) * (1.0 - absCoord.ts), -h); } void main() { // map texture coords to normal in model coords vec3 N = textureToSphereSurfaceCoord(clamp(v_corner, -1.0, 1.0)); // model coords -> eye coords N = normalize(vec3(u_modelView * vec4(N, 0.0))); // add the normal xy components to the sphere eye coords vec4 pos = vec4(v_pos, 1.0); pos.xy += N.xy * v_radius; // eye coord -> clip coords [-1, 1] pos = u_projection * pos; // clip coords -> [0, 1] for xy and [near, far] for z pos.xy = (pos.xy + vec2(1.0, 1.0)) / 2.0; pos.z = ((gl_DepthRange.diff * pos.z) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; // compute angle between sphere surface and light direction float cos_alpha = dot(N, vec3(0, 0, 1)); // since we are using flat impostors in the depth texture, cos_alpha needs to be positive if (cos_alpha > 0.0 && texture2D(u_depthTex, pos.xy).r > pos.z) { // the texel is visible from the light source gl_FragColor = vec4(vec3(1.0, 1.0, 1.0) * cos_alpha * u_intensity, 1.0); } else { // texel not visible gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); } } avogadrolibs-1.101.0/avogadro/rendering/sphere_ao_bake_vs.glsl000066400000000000000000000030221506155467400244410ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////// // // Ambient occlusion shader for sphere impostors // // This fragment shader is used for baking the ambient occlusion // maps. // ////////////////////////////////////////////////////////////////////// // // Input // // sphere center position: model coords attribute vec3 a_pos; // corner: [-radius, radius] attribute vec2 a_corner; // offset for the center of the sphere's AO map texture tile attribute vec2 a_tileOffset; // // Output // // the sphere center position: eye coords varying vec3 v_pos; // the sphere radius varying float v_radius; // stretched corner: [-1.x, 1.x] (see below) varying vec2 v_corner; // // Uniforms // // the model-view matrix uniform mat4 u_modelView; // the size of the AO texture [pixels] (e.g. 1024) uniform float u_textureSize; // the size of a single tile in texture coords [0, 1] uniform float u_tileSize; void main() { // pass through radius v_radius = abs(a_corner.s); // position: model coords -> eye coords v_pos = vec3(u_modelView * vec4(a_pos, 1.0)); // normalize corner: [-radius, radius] -> [-1, 1] vec2 corner = a_corner / v_radius; // enlarge texture space to trim half a texel from the tile // note: v_corner is in range [-1, 1] so we add 2 / (tile size in pixels) v_corner = corner * (1.0 + 2.0 / (u_textureSize * u_tileSize)); // NDC are in range [-1, 1], the * 2 - 1 translates and scales the position to [0, 1] gl_Position = vec4(a_tileOffset * 2.0 - vec2(1.0) + corner * u_tileSize, 0.0, 1.0); } avogadrolibs-1.101.0/avogadro/rendering/sphere_ao_depth_fs.glsl000066400000000000000000000011571506155467400246320ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////// // // Ambient occlusion shader for sphere impostors // // This fragment shader is used for rendering the depth texture from // the light source's view. // ////////////////////////////////////////////////////////////////////// // // Input // // normalized corner: [-1, 1] varying vec2 v_corner; void main() { // figure out if we are inside our sphere float zz = 1.0 - v_corner.x * v_corner.x - v_corner.y * v_corner.y; if (zz <= 0.0) discard; // draw buffer is not attached, output any color gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } avogadrolibs-1.101.0/avogadro/rendering/sphere_ao_depth_vs.glsl000066400000000000000000000020661506155467400246520ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////// // // Ambient occlusion shader for sphere impostors // // This vertex shader is used for rendering the depth texture from // the light source's view. // ////////////////////////////////////////////////////////////////////// // // Input // // sphere position: model coordinates attribute vec3 a_pos; // sphere corner: [-r, -r], [r, -r], [r, r], [-r, r] attribute vec2 a_corner; // // Output // // normalized corner: [-1, 1] varying vec2 v_corner; // // Uniforms // // model-view matrix of the current light direction uniform mat4 u_modelView; // projection matrix uniform mat4 u_projection; void main() { // extract radius from unnormalized corner attribute float radius = abs(a_corner.s); // normalize corner to be in [-1, 1] range v_corner = a_corner / radius; // model coords -> eye coords vec4 pos = u_modelView * vec4(a_pos, 1.0); // translate position to corner taking radius into account pos.xy += a_corner; // eye coords -> clip coords gl_Position = u_projection * pos; } avogadrolibs-1.101.0/avogadro/rendering/sphere_ao_render_fs.glsl000066400000000000000000000060571506155467400250110ustar00rootroot00000000000000//#define CONTOUR_LINES //#define TOON_SHADING // normalized corner varying vec2 v_corner; // color varying vec3 v_color; // position in eye-coordinate space varying vec4 v_eyePos; // sphere radius varying float v_radius; // AO tile offset varying vec2 v_tileOffset; // inverse model-view matrix uniform mat4 u_modelView; uniform mat3 u_invModelView; // the projection matrix uniform mat4 u_projection; // the texture sampler uniform sampler2D u_tex; uniform float u_texScale; #ifdef CONTOUR_LINES const float contourWidth = 0.3; #endif #ifdef TOON_SHADING const float levels = 4.0; #endif vec2 sphereSurfaceToTextureCoord(in vec3 coord) { vec3 absCoord = abs(coord); float d = absCoord.x + absCoord.y + absCoord.z; return (coord.z <= 0.0) ? coord.xy / d : sign(coord.xy) * (1.0 - absCoord.yx / d); } float cosine(in vec3 a, in vec3 b) { float cos_alpha = max(0.0, dot(a, b)); #ifdef TOON_SHADING cos_alpha = floor(cos_alpha * levels) / levels; #endif return cos_alpha; } void main() { // figure out if we are inside our sphere float zz = 1.0 - v_corner.x * v_corner.x - v_corner.y * v_corner.y; #ifdef CONTOUR_LINES if (zz <= -sqrt(contourWidth)) { // fragment outside sphere + contour radius discard; } if (zz <= 0.0) { // fragment is part of the contour float xi = abs(zz); // [0, contourWidth] // eta determines how much the contours are pushed back float eta = 0.0004; gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // black gl_FragDepth = gl_FragCoord.z - 10e-10 + eta * xi; return; } #else if (zz <= 0.0) discard; #endif // compute normal in eye coods vec3 N = normalize(vec3(v_corner, sqrt(zz))); // compute the pixel's depth vec4 pos = v_eyePos; pos.z += N.z * v_radius; // radius is 1.0 pos = u_projection * pos; gl_FragDepth = (pos.z / pos.w + 1.0) / 2.0; // transform to normal to model-space vec3 modelN = N; modelN = normalize(u_invModelView * modelN); // determine (u, v) texture coordinates using gnomonic projection vec2 uv = sphereSurfaceToTextureCoord(modelN); // [-1, 1] uv = v_tileOffset + uv * u_texScale; //gl_FragColor = vec4(v_color, 1.0) * texture2D(u_tex, uv); // direction of light source vec3 L = normalize(vec3(0, 1, 1)); // eye direction vec3 E = vec3(0, 0, 1); // angle between normal and light direction float cos_alpha = cosine(N, L); // compute ambient color vec3 ambient = 0.4 * v_color; // compute diffuse color vec3 diffuse = 0.55 * v_color * cos_alpha; // compute specular color (approximate Fresnel reflection) vec3 H = normalize(L + E); // halfway vector between N and E float cos_beta = cosine(N, H); vec3 specular = 0.5 * (vec3(1, 1, 1) - v_color)* pow(cos_beta, 20.0); // final color vec3 color = ambient + diffuse + specular; gl_FragColor = 1.2 * vec4(color, 1.0) * texture2D(u_tex, uv); // AO + Phong reflection [+ contours] //gl_FragColor = vec4(color, 1.0); // Phong reflection [+ contours] //gl_FragColor = 1.2 * texture2D(u_tex, uv); // AO [+ contours] //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); // contours + white atoms } avogadrolibs-1.101.0/avogadro/rendering/sphere_ao_render_vs.glsl000066400000000000000000000036421506155467400250260ustar00rootroot00000000000000// // Input // // sphere position: model coordinates attribute vec3 a_pos; // sphere corner: [-r, -r], [r, -r], [r, r], [-r, r] attribute vec2 a_corner; // offset for the center of the sphere's AO map texture tile attribute vec2 a_tileOffset; // color: RGB attribute vec3 a_color; // // Output // // normalized corner varying vec2 v_corner; // color varying vec3 v_color; // position in eye-coordinate space varying vec4 v_eyePos; // sphere radius varying float v_radius; // AO tile offset varying vec2 v_tileOffset; // // Uniforms // // model-view matrix uniform mat4 u_modelView; // projection matrix uniform mat4 u_projection; #define CONTOUR_LINES #ifdef CONTOUR_LINES const float contourWidth = 0.3; #endif void main() { // pass through AO tile offset & color v_tileOffset = a_tileOffset; v_color = a_color; // extract radius from unnormalized corner attribute v_radius = abs(a_corner.x); // normalize corner to be in [-1, 1] range #ifdef CONTOUR_LINES v_corner = a_corner / v_radius + sign(a_corner) * vec2(contourWidth, contourWidth); #else v_corner = a_corner / v_radius; #endif // compute eye-coordinate sphere position v_eyePos = u_modelView * vec4(a_pos, 1.0); // test if the closest point on the sphere would be clipped vec4 clipTestNear = v_eyePos; // z-axis points to eye (camera), add radius to sphere position clipTestNear.z += v_radius; // project the eye-coordinate sphere position clipTestNear = u_projection * clipTestNear; if (clipTestNear.z > -clipTestNear.w) { // not clipped, calculate clip coordinate gl_Position = v_eyePos; #ifdef CONTOUR_LINES gl_Position.xy += a_corner + sign(a_corner) * vec2(contourWidth, contourWidth); //gl_Position.xy += a_corner; #else gl_Position.xy += a_corner; #endif gl_Position = u_projection * gl_Position; } else { // clipped, invalidate the clip coordinate to ensure that it will be clipped gl_Position.w = 0.0; } } avogadrolibs-1.101.0/avogadro/rendering/spheregeometry.cpp000066400000000000000000000207311506155467400236730ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "spheregeometry.h" #include "camera.h" #include "scene.h" #include "bufferobject.h" #include "shader.h" #include "shaderprogram.h" #include "visitor.h" namespace { #include "spheres_fs.h" #include "spheres_vs.h" } // namespace #include "avogadrogl.h" #include using std::cout; using std::endl; namespace Avogadro::Rendering { using Core::Array; class SphereGeometry::Private { public: Private() {} BufferObject vbo; BufferObject ibo; inline static Shader* vertexShader = nullptr; inline static Shader* fragmentShader = nullptr; inline static ShaderProgram* program = nullptr; size_t numberOfVertices; size_t numberOfIndices; }; SphereGeometry::SphereGeometry() : m_dirty(false), d(new Private) { setRenderPass(SolidPass); } SphereGeometry::SphereGeometry(const SphereGeometry& other) : Drawable(other), m_spheres(other.m_spheres), m_indices(other.m_indices), m_dirty(true), d(new Private) { setRenderPass(SolidPass); } SphereGeometry::~SphereGeometry() { delete d; } void SphereGeometry::accept(Visitor& visitor) { visitor.visit(*this); } void SphereGeometry::update() { if (m_indices.empty() || m_spheres.empty()) return; // Check if the VBOs are ready, if not get them ready. if (!d->vbo.ready() || m_dirty) { std::vector sphereIndices; std::vector sphereVertices; sphereIndices.reserve(m_indices.size() * 4); sphereVertices.reserve(m_spheres.size() * 4); auto itIndex = m_indices.begin(); auto itSphere = m_spheres.begin(); for (size_t i = 0; itIndex != m_indices.end() && itSphere != m_spheres.end(); ++i, ++itIndex, ++itSphere) { // Use our packed data structure... float r = itSphere->radius; unsigned int index = 4 * static_cast(i); ColorTextureVertex vert(itSphere->center, itSphere->color, Vector2f(-r, -r)); sphereVertices.push_back(vert); vert.textureCoord = Vector2f(-r, r); sphereVertices.push_back(vert); vert.textureCoord = Vector2f(r, -r); sphereVertices.push_back(vert); vert.textureCoord = Vector2f(r, r); sphereVertices.push_back(vert); // 6 indexed vertices to draw a quad... sphereIndices.push_back(index + 0); sphereIndices.push_back(index + 1); sphereIndices.push_back(index + 2); sphereIndices.push_back(index + 3); sphereIndices.push_back(index + 2); sphereIndices.push_back(index + 1); // m_spheres.push_back(Sphere(position, r, id, color)); } if (!d->vbo.upload(sphereVertices, BufferObject::ArrayBuffer)) cout << d->vbo.error() << endl; if (!d->ibo.upload(sphereIndices, BufferObject::ElementArrayBuffer)) cout << d->ibo.error() << endl; d->numberOfVertices = sphereVertices.size(); d->numberOfIndices = sphereIndices.size(); m_dirty = false; } // Build and link the shader if it has not been used yet. if (d->vertexShader == nullptr) { d->vertexShader = new Shader; d->vertexShader->setType(Shader::Vertex); d->vertexShader->setSource(spheres_vs); d->fragmentShader = new Shader; d->fragmentShader->setType(Shader::Fragment); d->fragmentShader->setSource(spheres_fs); if (!d->vertexShader->compile()) cout << d->vertexShader->error() << endl; if (!d->fragmentShader->compile()) cout << d->fragmentShader->error() << endl; if (d->program == nullptr) d->program = new ShaderProgram; d->program->attachShader(*d->vertexShader); d->program->attachShader(*d->fragmentShader); if (!d->program->link()) cout << d->program->error() << endl; /* d->program.detachShader(d->vertexShader); d->program.detachShader(d->fragmentShader); d->vertexShader.cleanup(); d->fragmentShader.cleanup(); */ } } void SphereGeometry::render(const Camera& camera) { if (m_indices.empty() || m_spheres.empty()) return; // Prepare the VBOs, IBOs and shader program if necessary. update(); if (!d->program->bind()) cout << d->program->error() << endl; d->vbo.bind(); d->ibo.bind(); // Set up our attribute arrays. if (!d->program->enableAttributeArray("vertex")) cout << d->program->error() << endl; if (!d->program->useAttributeArray( "vertex", ColorTextureVertex::vertexOffset(), sizeof(ColorTextureVertex), FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program->error() << endl; } if (!d->program->enableAttributeArray("color")) cout << d->program->error() << endl; if (!d->program->useAttributeArray("color", ColorTextureVertex::colorOffset(), sizeof(ColorTextureVertex), UCharType, 3, ShaderProgram::Normalize)) { cout << d->program->error() << endl; } if (!d->program->enableAttributeArray("texCoordinate")) cout << d->program->error() << endl; if (!d->program->useAttributeArray( "texCoordinate", ColorTextureVertex::textureCoordOffset(), sizeof(ColorTextureVertex), FloatType, 2, ShaderProgram::NoNormalize)) { cout << d->program->error() << endl; } // Set up our uniforms (model-view and projection matrices right now). if (!d->program->setUniformValue("modelView", camera.modelView().matrix())) { cout << d->program->error() << endl; } if (!d->program->setUniformValue("projection", camera.projection().matrix())) { cout << d->program->error() << endl; } if (!d->program->setUniformValue("opacity", m_opacity)) { cout << d->program->error() << endl; } // Render the loaded spheres using the shader and bound VBO. glDrawRangeElements(GL_TRIANGLES, 0, static_cast(d->numberOfVertices), static_cast(d->numberOfIndices), GL_UNSIGNED_INT, (const GLvoid*)nullptr); d->vbo.release(); d->ibo.release(); d->program->disableAttributeArray("vector"); d->program->disableAttributeArray("color"); d->program->disableAttributeArray("texCoordinates"); d->program->release(); } std::multimap SphereGeometry::hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const { std::multimap result; // Check for intersection. for (size_t i = 0; i < m_spheres.size(); ++i) { const SphereColor& sphere = m_spheres[i]; Vector3f distance = sphere.center - rayOrigin; float B = distance.dot(rayDirection); float C = distance.dot(distance) - (sphere.radius * sphere.radius); float D = B * B - C; // Test for intersection if (D < 0.0f) continue; // Test for clipping if (B < 0.0f || (sphere.center - rayEnd).dot(rayDirection) > 0.0f) continue; Identifier id; id.molecule = m_identifier.molecule; id.type = m_identifier.type; id.index = m_indices[i]; if (id.type != InvalidType) { auto rootD = static_cast(sqrt(D)); float depth = std::min(std::abs(B + rootD), std::abs(B - rootD)); result.insert(std::pair(depth, id)); } } return result; } Array SphereGeometry::areaHits(const Frustrum& f) const { Array result; // Check for intersection. for (size_t i = 0; i < m_spheres.size(); ++i) { const SphereColor& sphere = m_spheres[i]; int in = 0; for (in = 0; in < 4; ++in) { float dist = (sphere.center - f.points[2 * in]).dot(f.planes[in]); if (dist > 0.0f) { // Outside of our frustrum, break. break; } } if (in == 4) { // The center is within the four planes that make our frustrum - hit. Identifier id; id.molecule = m_identifier.molecule; id.type = m_identifier.type; id.index = m_indices[i]; result.push_back(id); } } return result; } void SphereGeometry::addSphere(const Vector3f& position, const Vector3ub& color, float radius, size_t index) { m_dirty = true; m_spheres.push_back(SphereColor(position, radius, color)); m_indices.push_back(index == MaxIndex ? m_indices.size() : index); } void SphereGeometry::clear() { m_spheres.clear(); m_indices.clear(); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/spheregeometry.h000066400000000000000000000073121506155467400233400ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_SPHEREGEOMETRY_H #define AVOGADRO_RENDERING_SPHEREGEOMETRY_H #include "drawable.h" #include #include namespace Avogadro { namespace Rendering { struct SphereColor { SphereColor(Vector3f centre, float r, Vector3ub c) : center(centre), radius(r), color(c) { } Vector3f center; float radius; Vector3ub color; }; /** * @class SphereGeometry spheregeometry.h * @brief The SphereGeometry class contains one or more spheres. * @author Marcus D. Hanwell * * This Drawable is capable of storing the geometry for one or more spheres. * A sphere is defined by a center point, a radius and a color. If the * spheres are not a densely packed one-to-one mapping with the objects indices * they can also optionally use an identifier that will point to some numeric * ID for the purposes of picking. */ class AVOGADRORENDERING_EXPORT SphereGeometry : public Drawable { public: SphereGeometry(); SphereGeometry(const SphereGeometry& other); ~SphereGeometry() override; SphereGeometry& operator=(SphereGeometry); friend void swap(SphereGeometry& lhs, SphereGeometry& rhs); /** * Accept a visit from our friendly visitor. */ void accept(Visitor&) override; /** * @brief Update the VBOs, IBOs etc ready for rendering. */ void update(); /** * @brief Render the sphere geometry. * @param camera The current camera to be used for rendering. */ void render(const Camera& camera) override; /** * Return the primitives that are hit by the ray. * @param rayOrigin Origin of the ray. * @param rayEnd End point of the ray. * @param rayDirection Normalized direction of the ray. * @return Sorted collection of primitives that were hit. */ std::multimap hits( const Vector3f& rayOrigin, const Vector3f& rayEnd, const Vector3f& rayDirection) const override; /** * Return the primitives within the supplied frustrum. */ Core::Array areaHits(const Frustrum& f) const override; /** * Set the opacity of the spheres in this group. */ void setOpacity(float o) { m_opacity = o; if (o < 1.0f) setRenderPass(TranslucentPass); } /** * Add a sphere to the geometry object. */ void addSphere(const Vector3f& position, const Vector3ub& color, float radius, size_t index = MaxIndex); /** * Get a reference to the spheres. */ Core::Array& spheres() { return m_spheres; } const Core::Array& spheres() const { return m_spheres; } /** * Clear the contents of the node. */ void clear() override; /** * Get the number of spheres in the node object. */ size_t size() const { return m_spheres.size(); } private: Core::Array m_spheres; Core::Array m_indices; bool m_dirty; float m_opacity = 1.0f; class Private; Private* d; }; inline SphereGeometry& SphereGeometry::operator=(SphereGeometry other) { using std::swap; swap(*this, other); return *this; } inline void swap(SphereGeometry& lhs, SphereGeometry& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_spheres, rhs.m_spheres); swap(lhs.m_indices, rhs.m_indices); lhs.m_dirty = rhs.m_dirty = true; } } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_SPHEREGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/spheres_fs.glsl000066400000000000000000000016621506155467400231530ustar00rootroot00000000000000varying vec2 v_texCoord; varying vec4 eyePosition; varying vec3 fColor; uniform mat3 normal; varying float radius; uniform float opacity; uniform mat4 projection; void main() { // Figure out if we are inside our sphere. float zz = 1.0 - v_texCoord.x*v_texCoord.x - v_texCoord.y*v_texCoord.y; if (zz <= 0.0) discard; vec3 N = vec3(v_texCoord, sqrt(zz)); vec3 L = normalize(vec3(0, 1, 1)); vec3 E = vec3(0, 0, 1); vec3 H = normalize(L + E); float df = max(0.0, dot(N, L)); // cos_alpha float sf = max(0.0, dot(N, H)); // cos_beta vec3 ambient = 0.4 * fColor; vec3 diffuse = 0.55 * fColor; vec3 specular = 0.5 * (vec3(1, 1, 1) - fColor); vec3 color = ambient + df * diffuse + pow(sf, 20.0) * specular; gl_FragColor = vec4(color, opacity); // determine fragment depth vec4 pos = eyePosition; pos.z += N.z * radius;//The radius is 1.0 pos = projection * pos; gl_FragDepth = (pos.z / pos.w + 1.0) / 2.0; } avogadrolibs-1.101.0/avogadro/rendering/spheres_vs.glsl000066400000000000000000000015201506155467400231640ustar00rootroot00000000000000attribute vec4 vertex; attribute vec3 color; attribute vec2 texCoordinate; varying vec2 v_texCoord; varying vec3 fColor; varying vec4 eyePosition; varying float radius; uniform mat4 modelView; uniform mat4 projection; void main() { radius = abs(texCoordinate.x); fColor = color; v_texCoord = texCoordinate / radius; gl_Position = modelView * vertex; eyePosition = gl_Position; // Test if the closest point on the sphere would be clipped. vec4 clipTestNear = eyePosition; clipTestNear.z += radius; clipTestNear = projection * clipTestNear; if (clipTestNear.z > -clipTestNear.w) { // If not, calculate clip coordinate gl_Position.xy += texCoordinate; gl_Position = projection * gl_Position; } else { // If so, invalidate the clip coordinate to ensure that it will be clipped. gl_Position.w = 0.0; } } avogadrolibs-1.101.0/avogadro/rendering/textlabel2d.cpp000066400000000000000000000015661506155467400230500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "textlabel2d.h" #include "visitor.h" namespace Avogadro::Rendering { TextLabel2D::TextLabel2D() { setRenderPass(Rendering::Overlay2DPass); } TextLabel2D::~TextLabel2D() {} void TextLabel2D::accept(Visitor& visitor) { visitor.visit(*this); } void TextLabel2D::setAnchor(const Vector2i& windowCoords) { setAnchorInternal(Vector3f(static_cast(windowCoords.x()), static_cast(windowCoords.y()), 0.f)); } Vector2i TextLabel2D::anchor() const { return getAnchorInternal().head<2>().cast(); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/textlabel2d.h000066400000000000000000000021521506155467400225050ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_TEXTLABEL2D_H #define AVOGADRO_RENDERING_TEXTLABEL2D_H #include "avogadrorenderingexport.h" #include "textlabelbase.h" namespace Avogadro { namespace Rendering { /** * @class TextLabel2D textlabel2d.h * @brief The TextLabel2D class renders text in an overlay plane, anchored to * a point in window coordinates. */ class AVOGADRORENDERING_EXPORT TextLabel2D : public TextLabelBase { public: TextLabel2D(); ~TextLabel2D() override; void accept(Visitor&) override; /** * The anchor point in window coordinates, taking the origin at the upper-left * corner. * @{ */ void setAnchor(const Vector2i& windowCoords); Vector2i anchor() const; /** @} */ }; } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_TEXTLABEL2D_H avogadrolibs-1.101.0/avogadro/rendering/textlabel3d.cpp000066400000000000000000000015541506155467400230460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "textlabel3d.h" #include "visitor.h" namespace Avogadro::Rendering { TextLabel3D::TextLabel3D() { setRenderPass(TranslucentPass); } TextLabel3D::~TextLabel3D() {} void TextLabel3D::accept(Visitor& visitor) { visitor.visit(*this); } void TextLabel3D::setAnchor(const Vector3f& position) { setAnchorInternal(position); } Vector3f TextLabel3D::anchor() const { return getAnchorInternal(); } void TextLabel3D::setRadius(float r) { setRadiusInternal(r); } float TextLabel3D::radius() const { return getRadiusInternal(); } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/textlabel3d.h000066400000000000000000000024511506155467400225100ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_TEXTLABEL3D_H #define AVOGADRO_RENDERING_TEXTLABEL3D_H #include "avogadrorenderingexport.h" #include "textlabelbase.h" namespace Avogadro { namespace Rendering { /** * @class TextLabel3D textlabel3d.h * @brief The TextLabel3D class renders billboarded text that is anchored to a * point in world coordinates. */ class AVOGADRORENDERING_EXPORT TextLabel3D : public TextLabelBase { public: TextLabel3D(); ~TextLabel3D() override; void accept(Visitor&) override; /** * The anchor position in world coordinates. * @{ */ void setAnchor(const Vector3f& position); Vector3f anchor() const; /** @} */ /** * The distance to project the label towards the camera from the anchor point. * Useful for moving the label on top of, e.g. atom spheres. 0.f by default. * @{ */ void setRadius(float r); float radius() const; /** @} */ }; } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_TEXTLABEL3D_H avogadrolibs-1.101.0/avogadro/rendering/textlabelbase.cpp000066400000000000000000000236021506155467400234500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "textlabelbase.h" #include "avogadrogl.h" #include "bufferobject.h" #include "camera.h" #include "shader.h" #include "shaderprogram.h" #include "textrenderstrategy.h" #include "texture2d.h" #include "visitor.h" #include #include #include namespace { #include "textlabelbase_fs.h" #include "textlabelbase_vs.h" } // namespace #include using Avogadro::Core::Array; namespace Avogadro::Rendering { class TextLabelBase::RenderImpl { public: struct PackedVertex { Vector2i offset; // 8 bytes (8) Vector2f tcoord; // 8 bytes (16) PackedVertex() : offset(0, 0), tcoord(0.f, 0.f) {} static int offsetOffset() { return 0; } static int tcoordOffset() { return static_cast(sizeof(Vector2i)); } }; // Actual vertex data Array vertices; BufferObject vbo; // Sentinels: bool shadersInvalid; bool textureInvalid; bool vboInvalid; // Uniforms: Vector3f anchor; float radius; Texture2D texture; // Shaders inline static Shader* vertexShader = nullptr; inline static Shader* fragmentShader = nullptr; inline static ShaderProgram* shaderProgram = nullptr; RenderImpl(); ~RenderImpl() {} void setTextureData(const Array& buffer, const Vector2i& dimensions); void setOffsets(const Vector2i& dimensions, TextProperties::HAlign hAlign, TextProperties::VAlign vAlign); void render(const Camera& cam); void compileShaders(); void uploadVbo(); }; TextLabelBase::RenderImpl::RenderImpl() : vertices(4), shadersInvalid(true), textureInvalid(true), vboInvalid(true), radius(0.0) { texture.setMinFilter(Texture2D::Linear); texture.setMagFilter(Texture2D::Linear); texture.setWrappingS(Texture2D::ClampToEdge); texture.setWrappingT(Texture2D::ClampToEdge); } void TextLabelBase::RenderImpl::setTextureData( const Array& buffer, const Vector2i& dimensions) { // Calculate texture coordinates. This centers each texel on a pixel: const Vector2f dimsF(dimensions.cast()); const Vector2f denoms(dimsF * 2.f); const float uMin = 1.f / denoms[0]; const float vMin = 1.f / denoms[1]; const float uMax = ((2.f * dimsF[0]) - 1.f) / denoms[0]; const float vMax = ((2.f * dimsF[1]) - 1.f) / denoms[1]; vertices[0].tcoord = Vector2f(uMin, vMin); vertices[1].tcoord = Vector2f(uMax, vMin); vertices[2].tcoord = Vector2f(uMin, vMax); vertices[3].tcoord = Vector2f(uMax, vMax); vboInvalid = true; // Upload texture texture.upload(buffer, dimensions, Texture2D::IncomingRGBA, Texture2D::InternalRGBA); textureInvalid = false; } void TextLabelBase::RenderImpl::setOffsets(const Vector2i& dimensions, TextProperties::HAlign hAlign, TextProperties::VAlign vAlign) { Vector2i& tl = vertices[0].offset; Vector2i& tr = vertices[1].offset; Vector2i& bl = vertices[2].offset; Vector2i& br = vertices[3].offset; switch (hAlign) { case TextProperties::HLeft: bl.x() = tl.x() = 0; br.x() = tr.x() = dimensions.x() - 1; break; case TextProperties::HCenter: bl.x() = tl.x() = -(dimensions.x() / 2); br.x() = tr.x() = dimensions.x() / 2 + (dimensions.x() % 2 == 0 ? 1 : 0); break; case TextProperties::HRight: bl.x() = tl.x() = -(dimensions.x() - 1); br.x() = tr.x() = 0; break; } switch (vAlign) { case TextProperties::VTop: bl.y() = br.y() = -(dimensions.y() - 1); tl.y() = tr.y() = 0; break; case TextProperties::VCenter: bl.y() = br.y() = -(dimensions.y() / 2); tl.y() = tr.y() = dimensions.y() / 2 - (dimensions.y() % 2 == 0 ? 1 : 0); break; case TextProperties::VBottom: bl.y() = br.y() = 0; tl.y() = tr.y() = dimensions.y() - 1; break; } vboInvalid = true; } void TextLabelBase::RenderImpl::render(const Camera& cam) { // The texture should be valid at this point. if (textureInvalid) { std::cerr << "Unable to render text label -- no texture set. " "This is a bug." << std::endl; return; } // Prepare GL if (shadersInvalid) compileShaders(); if (vboInvalid) uploadVbo(); const Matrix4f mv(cam.modelView().matrix()); const Matrix4f proj(cam.projection().matrix()); const Vector2i vpDims(cam.width(), cam.height()); // Bind vbo if (!vbo.bind()) { std::cerr << "Error while binding TextLabelBase VBO: " << vbo.error() << std::endl; return; } // Setup shaders if (!shaderProgram->bind() || !shaderProgram->setUniformValue("mv", mv) || !shaderProgram->setUniformValue("proj", proj) || !shaderProgram->setUniformValue("vpDims", vpDims) || !shaderProgram->setUniformValue("anchor", anchor) || !shaderProgram->setUniformValue("radius", radius) || !shaderProgram->setTextureSampler("texture", texture) || !shaderProgram->enableAttributeArray("offset") || !shaderProgram->useAttributeArray("offset", PackedVertex::offsetOffset(), sizeof(PackedVertex), IntType, 2, ShaderProgram::NoNormalize) || !shaderProgram->enableAttributeArray("texCoord") || !shaderProgram->useAttributeArray( "texCoord", PackedVertex::tcoordOffset(), sizeof(PackedVertex), FloatType, 2, ShaderProgram::NoNormalize)) { std::cerr << "Error setting up TextLabelBase shader program: " << shaderProgram->error() << std::endl; vbo.release(); shaderProgram->release(); return; } // Draw texture glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Release resources: shaderProgram->disableAttributeArray("texCoords"); shaderProgram->disableAttributeArray("offset"); shaderProgram->release(); vbo.release(); } void TextLabelBase::RenderImpl::compileShaders() { if (vertexShader != nullptr && fragmentShader != nullptr && shaderProgram != nullptr) return; if (vertexShader == nullptr) vertexShader = new Shader; vertexShader->setType(Shader::Vertex); vertexShader->setSource(textlabelbase_vs); if (!vertexShader->compile()) { std::cerr << vertexShader->error() << std::endl; return; } if (fragmentShader == nullptr) fragmentShader = new Shader; fragmentShader->setType(Shader::Fragment); fragmentShader->setSource(textlabelbase_fs); if (!fragmentShader->compile()) { std::cerr << fragmentShader->error() << std::endl; return; } if (shaderProgram == nullptr) shaderProgram = new ShaderProgram; shaderProgram->attachShader(*vertexShader); shaderProgram->attachShader(*fragmentShader); if (!shaderProgram->link()) { std::cerr << shaderProgram->error() << std::endl; return; } /* shaderProgram->detachShader(vertexShader); shaderProgram->detachShader(fragmentShader); vertexShader->cleanup(); fragmentShader->cleanup(); */ shadersInvalid = false; } void TextLabelBase::RenderImpl::uploadVbo() { if (!vbo.upload(vertices, BufferObject::ArrayBuffer)) std::cerr << "TextLabelBase VBO error: " << vbo.error() << std::endl; else vboInvalid = false; } TextLabelBase::TextLabelBase() : m_render(new RenderImpl) {} TextLabelBase::TextLabelBase(const TextLabelBase& other) : Drawable(other), m_text(other.m_text), m_textProperties(other.m_textProperties), m_imageDimensions(other.m_imageDimensions), m_imageRgba(other.m_imageRgba), m_render(new RenderImpl) { } TextLabelBase::~TextLabelBase() { delete m_render; } void TextLabelBase::render(const Camera& camera) { m_render->render(camera); } void TextLabelBase::buildTexture(const TextRenderStrategy& tren) { if (!m_render->textureInvalid) return; // Determine texture size and allocate buffer int bbox[4]; tren.boundingBox(m_text, m_textProperties, bbox); const Vector2i newDims(bbox[1] - bbox[0] + 1, bbox[3] - bbox[2] + 1); if (newDims != m_imageDimensions) { m_imageDimensions = newDims; m_render->setOffsets(m_imageDimensions, m_textProperties.hAlign(), m_textProperties.vAlign()); } const size_t bytesPerPixel = 4; // RGBA m_imageRgba.resize( static_cast(m_imageDimensions[0] * m_imageDimensions[1]) * bytesPerPixel); // Render the text to the buffer if (m_imageRgba.size() > 0) { tren.render(m_text, m_textProperties, m_imageRgba.data(), m_imageDimensions); } m_render->setTextureData(m_imageRgba, m_imageDimensions); } void TextLabelBase::setText(const std::string& str) { if (str != m_text) { m_text = str; m_render->textureInvalid = true; } } const std::string& TextLabelBase::text() const { return m_text; } void TextLabelBase::setTextProperties(const TextProperties& tprop) { if (tprop != m_textProperties) { m_textProperties = tprop; m_render->textureInvalid = true; m_render->setOffsets(m_imageDimensions, tprop.hAlign(), tprop.vAlign()); } } const TextProperties& TextLabelBase::textProperties() const { return m_textProperties; } void TextLabelBase::resetTexture() { m_render->textureInvalid = true; } void TextLabelBase::setAnchorInternal(const Vector3f& a) { m_render->anchor = a; } Vector3f TextLabelBase::getAnchorInternal() const { return m_render->anchor; } void TextLabelBase::setRadiusInternal(float radius) { m_render->radius = radius; } float TextLabelBase::getRadiusInternal() const { return m_render->radius; } void TextLabelBase::markDirty() { m_render->shadersInvalid = true; m_render->textureInvalid = true; m_render->vboInvalid = true; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/textlabelbase.h000066400000000000000000000053731506155467400231220ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_TEXTLABELBASE_H #define AVOGADRO_RENDERING_TEXTLABELBASE_H #include "avogadrorenderingexport.h" #include "drawable.h" #include #include #include namespace Avogadro { namespace Rendering { class TextRenderStrategy; /** * @class TextLabelBase textlabelbase.h * @brief The TextLabelBase class provides a generic implementation of a text * drawable. */ class AVOGADRORENDERING_EXPORT TextLabelBase : public Drawable { public: TextLabelBase(); TextLabelBase(const TextLabelBase& other); ~TextLabelBase() override; TextLabelBase& operator=(TextLabelBase other); friend void swap(TextLabelBase& lhs, TextLabelBase& rhs); void render(const Camera& camera) override; /** * Render the string to the internal texture buffer. * @param tren The text rendering strategy to use. */ void buildTexture(const TextRenderStrategy& tren); /** * The text that will be rendered. * @{ */ void setText(const std::string& str); const std::string& text() const; /** @} */ /** * The properties of the rendered text. * @{ */ void setTextProperties(const TextProperties& tprop); const TextProperties& textProperties() const; /** @} */ /** * Clear the texture, forcing it to be regenerated on the next render. */ void resetTexture(); protected: std::string m_text; TextProperties m_textProperties; Vector2i m_imageDimensions; Core::Array m_imageRgba; // Subclasses use this to update the text position: void setAnchorInternal(const Vector3f& anchor); Vector3f getAnchorInternal() const; // ...and the radius. void setRadiusInternal(float radius); float getRadiusInternal() const; void markDirty(); private: // Container for rendering cache: class RenderImpl; RenderImpl* const m_render; }; inline TextLabelBase& TextLabelBase::operator=(TextLabelBase other) { using std::swap; swap(*this, other); return *this; } inline void swap(TextLabelBase& lhs, TextLabelBase& rhs) { using std::swap; swap(static_cast(lhs), static_cast(rhs)); swap(lhs.m_text, rhs.m_text); swap(lhs.m_textProperties, rhs.m_textProperties); swap(lhs.m_imageDimensions, rhs.m_imageDimensions); swap(lhs.m_imageRgba, rhs.m_imageRgba); lhs.markDirty(); rhs.markDirty(); } } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_TEXTLABELBASE_H avogadrolibs-1.101.0/avogadro/rendering/textlabelbase_fs.glsl000066400000000000000000000002271506155467400243150ustar00rootroot00000000000000uniform sampler2D texture; varying vec2 texc; void main(void) { gl_FragColor = texture2D(texture, texc); if (gl_FragColor.a == 0.) discard; } avogadrolibs-1.101.0/avogadro/rendering/textlabelbase_vs.glsl000066400000000000000000000034271506155467400243420ustar00rootroot00000000000000// Modelview/projection matrix uniform mat4 mv; uniform mat4 proj; // anchor position uniform vec3 anchor; // Distance to project the label towards the camera uniform float radius; // Vertex attributes. attribute vec2 offset; attribute vec2 texCoord; // Viewport dimensions: uniform ivec2 vpDims; // Texture coordinate. varying vec2 texc; // Given a clip coordinate, align the vertex to the nearest pixel center. void alignToPixelCenter(inout vec4 clipCoord) { // Half pixel increments (clip coord span / [2*numPixels] = [2*w] / [2*l]): vec2 inc = abs(clipCoord.w) / vec2(vpDims); // Fix up coordinates -- pixel centers are at xy = (-w + (2*i + 1) * inc) // for the i'th pixel. First find i and floor it. Just solve the above for i: ivec2 pixels = ivec2(floor((clipCoord.xy + abs(clipCoord.ww) - inc) / (2. * inc))); // Now reapply the equation to obtain a pixel centered offset. clipCoord.xy = -abs(clipCoord.ww) + (2. * vec2(pixels) + vec2(1., 1.)) * inc; } void main(void) { // Transform to eye coordinates: vec4 eyeAnchor = mv * vec4(anchor, 1.0); // Apply radius; eyeAnchor += vec4(0., 0., radius, 0.); // Transform to clip coordinates vec4 clipAnchor = proj * eyeAnchor; // Move the anchor to a pixel center: alignToPixelCenter(clipAnchor); // Align offset to cell centers using the w coordinate: // Since w determines whether or not the vertex is clipped, (-w, w) spans // the width/height of the display. Using the viewport width/height in pixels, // we can properly convert the offset into pixel units. vec2 conv = (2. * abs(clipAnchor.w)) / vec2(vpDims); // Apply the offset: gl_Position = clipAnchor + vec4(offset.x * conv.x, offset.y * conv.y, 0., 0.); // Pass through the texture coordinate texc = texCoord; } avogadrolibs-1.101.0/avogadro/rendering/textproperties.cpp000066400000000000000000000041131506155467400237260ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "textproperties.h" #include namespace Avogadro::Rendering { TextProperties::TextProperties() : m_pixelHeight(24), m_hAlign(HLeft), m_vAlign(VTop), m_rotationDegreesCW(0.f), m_fontFamily(SansSerif), m_fontStyles(NoFontStyle) { setColorRgba(255, 255, 255, 255); } TextProperties::TextProperties(const TextProperties& other) : m_pixelHeight(other.m_pixelHeight), m_hAlign(other.m_hAlign), m_vAlign(other.m_vAlign), m_rotationDegreesCW(other.m_rotationDegreesCW), m_fontFamily(other.m_fontFamily), m_fontStyles(other.m_fontStyles) { m_rgba[0] = other.m_rgba[0]; m_rgba[1] = other.m_rgba[1]; m_rgba[2] = other.m_rgba[2]; m_rgba[3] = other.m_rgba[3]; } TextProperties::~TextProperties() {} TextProperties& TextProperties::operator=(TextProperties other) { swap(other); return *this; } void TextProperties::swap(TextProperties& other) { using std::swap; swap(m_pixelHeight, other.m_pixelHeight); swap(m_hAlign, other.m_hAlign); swap(m_vAlign, other.m_vAlign); swap(m_rotationDegreesCW, other.m_rotationDegreesCW); swap(m_fontFamily, other.m_fontFamily); swap(m_fontStyles, other.m_fontStyles); swap(m_rgba[0], other.m_rgba[0]); swap(m_rgba[1], other.m_rgba[1]); swap(m_rgba[2], other.m_rgba[2]); swap(m_rgba[3], other.m_rgba[3]); } bool TextProperties::operator==(const TextProperties& other) const { return m_pixelHeight == other.m_pixelHeight && m_hAlign == other.m_hAlign && m_vAlign == other.m_vAlign && m_rotationDegreesCW == other.m_rotationDegreesCW && m_fontFamily == other.m_fontFamily && m_fontStyles == other.m_fontStyles && m_rgba[0] == other.m_rgba[0] && m_rgba[1] == other.m_rgba[1] && m_rgba[2] == other.m_rgba[2] && m_rgba[3] == other.m_rgba[3]; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/textproperties.h000066400000000000000000000147671506155467400234130ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_TEXTPROPERTIES_H #define AVOGADRO_RENDERING_TEXTPROPERTIES_H #include "avogadrorenderingexport.h" #include namespace Avogadro { namespace Rendering { /** * @class TextProperties textproperties.h * @brief The TextProperties class controls formatting options for text. */ class AVOGADRORENDERING_EXPORT TextProperties { public: /** Enum defining a minimal set of font families. */ enum FontFamily { SansSerif, Serif, Mono }; /** Horizontal alignment options. */ enum HAlign { HLeft, HCenter, HRight }; /** Vertical alignment options. */ enum VAlign { VTop, VCenter, VBottom }; /** Flags for style options (bold, italic, ...) */ enum FontStyle { NoFontStyle = 0x0, Bold = 0x1, Italic = 0x2, Underline = 0x4 }; /** Used for bitwise combinations of FontStyle values. */ typedef int FontStyles; TextProperties(); TextProperties(const TextProperties& other); ~TextProperties(); TextProperties& operator=(TextProperties other); void swap(TextProperties& other); bool operator==(const TextProperties& other) const; bool operator!=(const TextProperties& other) const { return !operator==(other); } /** * The height of the text in pixels. */ void setPixelHeight(size_t height) { m_pixelHeight = height; } size_t pixelHeight() const { return m_pixelHeight; } /** @} */ /** * Horizontal alignment of the text. */ void setHAlign(HAlign align) { m_hAlign = align; } HAlign hAlign() const { return m_hAlign; } /** @} */ /** * Vertical alignment of the text. */ void setVAlign(VAlign align) { m_vAlign = align; } VAlign vAlign() const { return m_vAlign; } /** @} */ /** * Set the horizontal and vertical alignment of the quad to the anchor point. */ void setAlign(HAlign hAlign, VAlign vAlign); /** * Rotates the text clockwise. */ void setRotationDegreesCW(float rot) { m_rotationDegreesCW = rot; } float rotationDegreesCW() const { return m_rotationDegreesCW; } /** @} */ /** * The font family. */ void setFontFamily(FontFamily family) { m_fontFamily = family; } FontFamily fontFamily() const { return m_fontFamily; } /** @} */ /** * Font style flags. */ void setFontStyles(FontStyles styles) { m_fontStyles = styles; } FontStyles fontStyles() const { return m_fontStyles; } /** @} */ /** * Toggle bold text. */ void setBold(bool b); bool bold() const; /** @} */ /** * Toggle italic text. */ void setItalic(bool b); bool italic() const; /** @} */ /** * Toggle underlined text. */ void setUnderline(bool b); bool underline() const; /** @} */ /** * Set the color of the text. Components are in the range [0, 255] * @{ */ void setColorRgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a); void setColorRgba(const unsigned char rgba[4]); void setColorRgba(const Vector4ub& rgba); void colorRgba(unsigned char rgba[4]) const; Vector4ub colorRgba() const; void setColorRgb(unsigned char r, unsigned char g, unsigned char b); void setColorRgb(const unsigned char rgb[3]); void setColorRgb(const Vector3ub& rgb); void colorRgb(unsigned char rgb[3]) const; Vector3ub colorRgb() const; void setRed(unsigned char r) { m_rgba[0] = r; } unsigned char red() const { return m_rgba[0]; } void setGreen(unsigned char g) { m_rgba[1] = g; } unsigned char green() const { return m_rgba[1]; } void setBlue(unsigned char b) { m_rgba[2] = b; } unsigned char blue() const { return m_rgba[2]; } void setAlpha(unsigned char a) { m_rgba[3] = a; } unsigned char alpha() const { return m_rgba[3]; } /** @} */ private: size_t m_pixelHeight; HAlign m_hAlign; VAlign m_vAlign; float m_rotationDegreesCW; FontFamily m_fontFamily; FontStyles m_fontStyles; unsigned char m_rgba[4]; }; inline void TextProperties::setAlign(TextProperties::HAlign h, TextProperties::VAlign v) { setHAlign(h); setVAlign(v); } inline void TextProperties::setBold(bool b) { if (b) m_fontStyles |= Bold; else m_fontStyles &= ~Bold; } inline bool TextProperties::bold() const { return (m_fontStyles & Bold) != 0; } inline void TextProperties::setItalic(bool b) { if (b) m_fontStyles |= Italic; else m_fontStyles &= ~Italic; } inline bool TextProperties::italic() const { return (m_fontStyles & Italic) != 0; } inline void TextProperties::setUnderline(bool b) { if (b) m_fontStyles |= Underline; else m_fontStyles &= ~Underline; } inline bool TextProperties::underline() const { return (m_fontStyles & Underline) != 0; } inline void TextProperties::setColorRgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { m_rgba[0] = r; m_rgba[1] = g; m_rgba[2] = b; m_rgba[3] = a; } inline void TextProperties::setColorRgba(const unsigned char rgba[4]) { m_rgba[0] = rgba[0]; m_rgba[1] = rgba[1]; m_rgba[2] = rgba[2]; m_rgba[3] = rgba[3]; } inline void TextProperties::setColorRgba(const Vector4ub& rgba) { setColorRgba(rgba.data()); } inline void TextProperties::colorRgba(unsigned char rgba[4]) const { rgba[0] = m_rgba[0]; rgba[1] = m_rgba[1]; rgba[2] = m_rgba[2]; rgba[3] = m_rgba[3]; } inline Vector4ub TextProperties::colorRgba() const { return Vector4ub(m_rgba); } inline void TextProperties::setColorRgb(unsigned char r, unsigned char g, unsigned char b) { m_rgba[0] = r; m_rgba[1] = g; m_rgba[2] = b; } inline void TextProperties::setColorRgb(const unsigned char rgb[3]) { m_rgba[0] = rgb[0]; m_rgba[1] = rgb[1]; m_rgba[2] = rgb[2]; } inline void TextProperties::setColorRgb(const Vector3ub& rgb) { setColorRgb(rgb.data()); } inline void TextProperties::colorRgb(unsigned char rgb[3]) const { rgb[0] = m_rgba[0]; rgb[1] = m_rgba[1]; rgb[2] = m_rgba[2]; } inline Vector3ub TextProperties::colorRgb() const { return Vector3ub(m_rgba); } inline void swap(TextProperties& lhs, TextProperties& rhs) { lhs.swap(rhs); } } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_TEXTPROPERTIES_H avogadrolibs-1.101.0/avogadro/rendering/textrenderstrategy.cpp000066400000000000000000000007451506155467400246030ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "textrenderstrategy.h" namespace Avogadro::Rendering { TextRenderStrategy::TextRenderStrategy() {} TextRenderStrategy::~TextRenderStrategy() {} } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/textrenderstrategy.h000066400000000000000000000035711506155467400242500ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_TEXTRENDERSTRATEGY_H #define AVOGADRO_RENDERING_TEXTRENDERSTRATEGY_H #include "avogadrorenderingexport.h" #include #include namespace Avogadro { namespace Rendering { class TextProperties; /** * @class TextRenderStrategy textrenderstrategy.h * * @brief The TextRenderStrategy class defines an interface for generating * text images. */ class AVOGADRORENDERING_EXPORT TextRenderStrategy { public: TextRenderStrategy(); virtual ~TextRenderStrategy(); /** * Copy the current TextRenderStrategy implementation into an new object. */ virtual TextRenderStrategy* newInstance() const = 0; /** * @brief Calculate a bounding box. * @param string The text. * @param tprop The properties. * @param bbox The result in pixels (left, right, top, bottom). */ virtual void boundingBox(const std::string& string, const TextProperties& tprop, int bbox[4]) const = 0; /** * @brief render Render the string. * @param string The text. * @param tprop The properties. * @param buffer The target. Must be dims[0]*dims[1]*4 * bytes. The buffer will be filled with RGBA image data, with the top * scan row at the beginning. * @param dims Dimensions in pixels of the target buffer. */ virtual void render(const std::string& string, const TextProperties& tprop, unsigned char* buffer, const Vector2i& dims) const = 0; }; } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_TEXTRENDERSTRATEGY_H avogadrolibs-1.101.0/avogadro/rendering/texture2d.cpp000066400000000000000000000153421506155467400225610ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "texture2d.h" #include "avogadrogl.h" namespace Avogadro::Rendering { namespace { GLint convertFilterOptionToGL(Texture2D::FilterOption opt) { switch (opt) { case Texture2D::Nearest: return GL_NEAREST; case Texture2D::Linear: return GL_LINEAR; default: return -1; } } Texture2D::FilterOption convertFilterOptionFromGL(GLint opt) { switch (opt) { case GL_NEAREST: return Texture2D::Nearest; case GL_LINEAR: return Texture2D::Linear; default: return Texture2D::InvalidFilter; } } GLint convertWrappingOptionToGL(Texture2D::WrappingOption opt) { switch (opt) { case Texture2D::ClampToEdge: return GL_CLAMP_TO_EDGE; case Texture2D::MirroredRepeat: return GL_MIRRORED_REPEAT; case Texture2D::Repeat: return GL_REPEAT; default: return -1; } } Texture2D::WrappingOption convertWrappingOptionFromGL(GLint opt) { switch (opt) { case GL_CLAMP_TO_EDGE: return Texture2D::ClampToEdge; case GL_MIRRORED_REPEAT: return Texture2D::MirroredRepeat; case GL_REPEAT: return Texture2D::Repeat; default: return Texture2D::InvalidWrapping; } } GLint convertInternalFormatToGL(Texture2D::InternalFormat fmt) { switch (fmt) { case Texture2D::InternalDepth: return GL_DEPTH_COMPONENT; case Texture2D::InternalDepthStencil: return GL_DEPTH_STENCIL; case Texture2D::InternalR: return GL_RED; case Texture2D::InternalRG: return GL_RG; case Texture2D::InternalRGB: return GL_RGB; case Texture2D::InternalRGBA: return GL_RGBA; default: return -1; } } GLint convertIncomingFormatToGL(Texture2D::IncomingFormat fmt) { switch (fmt) { case Texture2D::IncomingR: return GL_RED; case Texture2D::IncomingRG: return GL_RG; case Texture2D::IncomingRGB: return GL_RGB; case Texture2D::IncomingBGR: return GL_BGR; case Texture2D::IncomingRGBA: return GL_RGBA; case Texture2D::IncomingBGRA: return GL_BGRA; case Texture2D::IncomingDepth: return GL_DEPTH_COMPONENT; case Texture2D::IncomingDepthStencil: return GL_DEPTH_STENCIL; default: return -1; } } GLenum convertTypeToGL(Type type) { switch (type) { case CharType: return GL_BYTE; case UCharType: return GL_UNSIGNED_BYTE; case ShortType: return GL_SHORT; case UShortType: return GL_UNSIGNED_SHORT; case IntType: return GL_INT; case UIntType: return GL_UNSIGNED_INT; case FloatType: return GL_FLOAT; case DoubleType: return GL_DOUBLE; default: return 0; } } } // namespace class Texture2D::Private { public: Private() : textureId(0) {} ~Private() { if (textureId > 0) glDeleteTextures(1, &textureId); } mutable GLuint textureId; }; Texture2D::Texture2D() : d(new Private) {} Texture2D::~Texture2D() { delete d; } Index Texture2D::handle() const { return static_cast(d->textureId); } void Texture2D::setMinFilter(Texture2D::FilterOption opt) { Index old = pushTexture(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, convertFilterOptionToGL(opt)); popTexture(old); } Texture2D::FilterOption Texture2D::minFilter() const { Index old = pushTexture(); GLint result; glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &result); popTexture(old); return convertFilterOptionFromGL(result); } void Texture2D::setMagFilter(Texture2D::FilterOption opt) { Index old = pushTexture(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, convertFilterOptionToGL(opt)); popTexture(old); } Texture2D::FilterOption Texture2D::magFilter() const { Index old = pushTexture(); GLint result; glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &result); popTexture(old); return convertFilterOptionFromGL(result); } void Texture2D::setWrappingS(Texture2D::WrappingOption opt) { Index old = pushTexture(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, convertWrappingOptionToGL(opt)); popTexture(old); } Texture2D::WrappingOption Texture2D::wrappingS() const { Index old = pushTexture(); GLint result; glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &result); popTexture(old); return convertWrappingOptionFromGL(result); } void Texture2D::setWrappingT(Texture2D::WrappingOption opt) { Index old = pushTexture(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, convertWrappingOptionToGL(opt)); popTexture(old); } Texture2D::WrappingOption Texture2D::wrappingT() const { Index old = pushTexture(); GLint result; glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &result); popTexture(old); return convertWrappingOptionFromGL(result); } bool Texture2D::bind() const { return pushTexture() != MaxIndex; } bool Texture2D::release() const { popTexture(0); return true; } bool Texture2D::uploadInternal(const void* buffer, const Vector2i& dims, Texture2D::IncomingFormat dataFormat, Avogadro::Type dataType, Texture2D::InternalFormat internalFormat) { // The dataType has already been validated. Index old = pushTexture(); glTexImage2D(GL_TEXTURE_2D, 0, convertInternalFormatToGL(internalFormat), dims[0], dims[1], 0, convertIncomingFormatToGL(dataFormat), convertTypeToGL(dataType), static_cast(const_cast(buffer))); popTexture(old); return true; } Index Texture2D::pushTexture() const { GLint currentHandle; glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤tHandle); if (d->textureId == 0) { if (!const_cast(this)->generateTextureHandle()) return MaxIndex; } glBindTexture(GL_TEXTURE_2D, d->textureId); return static_cast(currentHandle); } void Texture2D::popTexture(Index id) const { glBindTexture(GL_TEXTURE_2D, static_cast(id)); } bool Texture2D::generateTextureHandle() { if (d->textureId > 0) { m_error = "Refusing to overwrite existing texture handle."; return false; } glGenTextures(1, &d->textureId); if (d->textureId == 0) { m_error = "Error generating texture handle."; return false; } // Set up defaults to match the documentation: setMinFilter(Linear); setMagFilter(Linear); setWrappingS(Repeat); setWrappingT(Repeat); return true; } } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/texture2d.h000066400000000000000000000160431506155467400222250ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_TEXTURE2D_H #define AVOGADRO_RENDERING_TEXTURE2D_H #include "avogadrorenderingexport.h" #include #include #include namespace Avogadro { namespace Rendering { /** * @class Texture2D texture2d.h * @brief The Texture2D class manages texture resources in graphics libraries. */ class AVOGADRORENDERING_EXPORT Texture2D { public: /** * @brief The FilterOption enum defines options for interpolating * texels onto pixels. */ enum FilterOption { InvalidFilter = -1, /// Use the nearest texel for the pixel value. Nearest = 0, /// Interpolate the four neighboring texels for the pixel value. Linear }; /** * @brief The WrappingOption enum defines options for handling texture * coordinates outside of the range [0, 1]. Note that these are specified * separately for each dimension of the texture. */ enum WrappingOption { InvalidWrapping = -1, /// Use the texel at the nearest edge. ClampToEdge = 0, /// Repeat the texels such that texture coordinates in the range [1, 2] map /// to [1, 0]; [2, 3] map to [0, 1], and so on. MirroredRepeat, /// Repeat the texels such that integer components of the texture /// coordinates are ignored (e.g. 1.7 becomes 0.7, 2.3 becomes 0.3, etc). Repeat }; /** * @brief The InternalFormat enum defines how the texture data will be stored * by the graphics library implementation. */ enum InternalFormat { InvalidInternalFormat = -1, /// Each element is a single depth component. InternalDepth = 0, /// Each element contains a depth component and a stencil value. InternalDepthStencil, /// Each element contains a single color value. InternalR, /// Each element contains two color values. InternalRG, /// Each element contains three color values. InternalRGB, /// Each element contains four color values. InternalRGBA }; /** * @brief The IncomingFormat enum defines the supported formats for incoming * texture data. */ enum IncomingFormat { InvalidIncomingFormat = -1, /// Each element is a single red component. IncomingR = 0, /// Each element is a red then green component. IncomingRG, /// Each element is a red, green, then blue component. IncomingRGB, /// Each element is a blue, green, then red component. IncomingBGR, /// Each element is a red, green, blue, then alpha component. IncomingRGBA, /// Each element is a blue, green, red, then alpha component. IncomingBGRA, /// Each element is a single depth component. IncomingDepth, /// Each element is a depth component followed by a stencil value. IncomingDepthStencil }; Texture2D(); ~Texture2D(); /** A unique integer value identifying the texture. */ Index handle() const; /** True if the texture is ready to use. */ bool ready() const { return !m_dirty; } /** Filtering options for interpolating pixel values. Default is Linear. {@ */ void setMinFilter(FilterOption opt); FilterOption minFilter() const; void setMagFilter(FilterOption opt); FilterOption magFilter() const; /** @} */ /** Wrapping options in the s-direction. Default is Repeat. @{ */ void setWrappingS(WrappingOption opt); WrappingOption wrappingS() const; /** @} */ /** Wrapping options in the t-direction. Default is Repeat. @{ */ void setWrappingT(WrappingOption opt); WrappingOption wrappingT() const; /** @} */ /** * Upload a buffer of texture data to the graphics library. * * @param buffer The texture data (see below for requirements of ContainerT). * @param dims The width and height of the texture data. * @param dataFormat The ordering of components in the buffer data. * @param internalFormat The internal storage ordering of components in the * buffer data. * * The buffer must contain homogeneous elements of the follow types: unsigned * char, char, unsigned short, short, unsigned int, int, or float. * * The first element of @a buffer is the lower-left texel. Subsequent elements * are ordered such that rows are contiguous, moving right and up. * * The ContainerT must satisfy the following requirements (short version: * use std::vector or Avogadro::Core::Array): * - ContainerT must have tightly packed values of ContainerT::value_type * - elements must be accessible by reference via ContainerT::operator[]. * - ContainerT::size() must return the number of elements in the container * as integral type ContainerT::size_type. */ template bool upload(const ContainerT& buffer, const Vector2i& dims, IncomingFormat dataFormat, InternalFormat internalFormat); /** Bind the texture for rendering. */ bool bind() const; bool release() const; std::string error() const { return m_error; } private: bool uploadInternal(const void* buffer, const Vector2i& dims, IncomingFormat dataFormat, Avogadro::Type dataType, InternalFormat internalFormat); // Used to preserve GL state when setting/querying texture properties: Index pushTexture() const; // -1 indicates error void popTexture(Index) const; // Create a texture handle and set the defaults. bool generateTextureHandle(); class Private; Private* const d; bool m_dirty; mutable std::string m_error; }; template inline bool Texture2D::upload(const ContainerT& buffer, const Vector2i& dims, IncomingFormat incomingFormat, InternalFormat internalFormat) { if (buffer.size() == 0 || dims[0] == 0 || dims[1] == 0) { m_error = "Refusing to upload empty array."; return false; } if (buffer.size() < static_cast(dims[0] * dims[1])) { m_error = "Buffer data is smaller than specified dimensions."; return false; } using Avogadro::Type; // Not to be confused with Avogadro::Rendering::Type... Type incomingType = static_cast(TypeTraits::EnumValue); switch (incomingType) { case Avogadro::CharType: case Avogadro::UCharType: case Avogadro::ShortType: case Avogadro::UShortType: case Avogadro::IntType: case Avogadro::UIntType: case Avogadro::FloatType: break; default: m_error = "Unsupported type for texture data: '"; m_error += TypeTraits::name(); m_error += "'."; return false; } return uploadInternal(&buffer[0], dims, incomingFormat, incomingType, internalFormat); } } // namespace Rendering } // namespace Avogadro #endif // AVOGADRO_RENDERING_TEXTURE2D_H avogadrolibs-1.101.0/avogadro/rendering/transformnode.cpp000066400000000000000000000007471506155467400235170ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "transformnode.h" namespace Avogadro::Rendering { TransformNode::TransformNode(GroupNode* p) : GroupNode(p) {} TransformNode::~TransformNode() {} } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/transformnode.h000066400000000000000000000016751506155467400231650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_TRANSFORMNODE_H #define AVOGADRO_RENDERING_TRANSFORMNODE_H #include "groupnode.h" namespace Avogadro { namespace Rendering { /** * @class TransformNode transformnode.h * @brief The TransformNode class applies a transform to all child nodes. * @author Marcus D. Hanwell * * @todo This is currently a stub and does nothing. */ class AVOGADRORENDERING_EXPORT TransformNode : public GroupNode { public: explicit TransformNode(GroupNode* parent = nullptr); ~TransformNode() override; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_TRANSFORMNODE_H avogadrolibs-1.101.0/avogadro/rendering/visitor.cpp000066400000000000000000000006561506155467400223340ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "visitor.h" namespace Avogadro::Rendering { Visitor::Visitor() {} Visitor::~Visitor() {} } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/visitor.h000066400000000000000000000035551506155467400220020ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_VISITOR_H #define AVOGADRO_RENDERING_VISITOR_H #include "avogadrorenderingexport.h" #include namespace Avogadro { namespace Rendering { class CylinderGeometry; class Drawable; class GeometryNode; class GroupNode; class LineStripGeometry; class MeshGeometry; class Node; class SphereGeometry; class TextLabel2D; class TextLabel3D; class AmbientOcclusionSphereGeometry; class CurveGeometry; /** * @class Visitor visitor.h * @brief The Visitor class is the base class for all Node visitors. * @author Marcus D. Hanwell * * The Visitor class is the base class for visitors to Node objects in the * Scene, providing common API and functionality. */ class AVOGADRORENDERING_EXPORT Visitor { public: Visitor(); virtual ~Visitor(); /** * The overloaded visit functions, the base versions of which do nothing. */ virtual void visit(Node&) { return; } virtual void visit(GroupNode&) { return; } virtual void visit(GeometryNode&) { return; } virtual void visit(Drawable&) { return; } virtual void visit(SphereGeometry&) { return; } virtual void visit(AmbientOcclusionSphereGeometry&) { return; } virtual void visit(CurveGeometry&) { return; } virtual void visit(CylinderGeometry&) { return; } virtual void visit(MeshGeometry&) { return; } virtual void visit(TextLabel2D&) { return; } virtual void visit(TextLabel3D&) { return; } virtual void visit(LineStripGeometry&) { return; } }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_VISITOR_H avogadrolibs-1.101.0/avogadro/rendering/volumegeometry.cpp000066400000000000000000000007211506155467400237110ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "volumegeometry.h" namespace Avogadro::Rendering { VolumeGeometry::VolumeGeometry() {} VolumeGeometry::~VolumeGeometry() {} } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/volumegeometry.h000066400000000000000000000016551506155467400233650ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_VOLUMEGEOMETRY_H #define AVOGADRO_RENDERING_VOLUMEGEOMETRY_H #include "drawable.h" namespace Avogadro { namespace Rendering { /** * @class VolumeGeometry volumegeometry.h * @brief The VolumeGeometry class contains a regularly spaced volumetric data * set. * @author Marcus D. Hanwell * * @todo This is currently a stub and does nothing. */ class AVOGADRORENDERING_EXPORT VolumeGeometry : public Drawable { public: VolumeGeometry(); ~VolumeGeometry() override; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_VOLUMEGEOMETRY_H avogadrolibs-1.101.0/avogadro/rendering/vrmlvisitor.cpp000066400000000000000000000121401506155467400232240ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vrmlvisitor.h" #include "ambientocclusionspheregeometry.h" #include "cylindergeometry.h" #include "linestripgeometry.h" #include "meshgeometry.h" #include "spheregeometry.h" #include #include namespace Avogadro::Rendering { using std::ofstream; using std::ostream; using std::ostringstream; using std::string; namespace { ostream& operator<<(ostream& os, const Vector3f& v) { os << v[0] << " " << v[1] << " " << v[2]; return os; } ostream& operator<<(ostream& os, const Vector3ub& color) { os << color[0] / 255.0f << "\t" << color[1] / 255.0f << "\t" << color[2] / 255.0f; return os; } ostream& operator<<(ostream& os, const Vector4ub& color) { os << color[0] / 255.0f << " " << color[1] / 255.0f << " " << color[2] / 255.0f << color[3] / 255.0f; return os; } } // namespace VRMLVisitor::VRMLVisitor(const Camera& c) : m_camera(c), m_backgroundColor(255, 255, 255), m_ambientColor(100, 100, 100), m_aspectRatio(800.0f / 600.0f) { } VRMLVisitor::~VRMLVisitor() {} void VRMLVisitor::begin() { // Initialise the VRML scene Vector3f cameraT = -(m_camera.modelView().linear().adjoint() * m_camera.modelView().translation()); // Output the POV-Ray initialisation code // orientation should be set // http://cgvr.informatik.uni-bremen.de/teaching/vr_literatur/Calculating%20VRML%20Viewpoints.html ostringstream str; str << "#VRML V2.0 utf8\n" << "DEF DefaultView Viewpoint {\n" << "position " << cameraT << " \n" << "fieldOfView 0.785398\n}\n"; m_sceneData = str.str(); } string VRMLVisitor::end() { return m_sceneData; } void VRMLVisitor::visit(Drawable&) {} void VRMLVisitor::visit(SphereGeometry& geometry) { ostringstream str; for (auto s : geometry.spheres()) { str << "Transform {\n" << "\ttranslation\t" << s.center[0] << "\t" << s.center[1] << "\t" << s.center[2] << "\n\tchildren Shape {\n" << "\t\tgeometry Sphere {\n\t\t\tradius\t" << s.radius << "\n\t\t}\n" << "\t\tappearance Appearance {\n" << "\t\t\tmaterial Material {\n" << "\t\t\t\tdiffuseColor\t" << s.color << "\n\t\t\t}\n\t\t}\n\t}\n}\n"; } m_sceneData += str.str(); } void VRMLVisitor::visit(AmbientOcclusionSphereGeometry&) {} void VRMLVisitor::visit(CylinderGeometry& geometry) { ostringstream str; for (auto c : geometry.cylinders()) { // double scale = 1.0; double x1, x2, y1, y2, z1, z2; x1 = c.end1[0]; x2 = c.end2[0]; y1 = c.end1[1]; y2 = c.end2[1]; z1 = c.end1[2]; z2 = c.end2[2]; double dx = x2 - x1; double dy = y2 - y1; double dz = z2 - z1; double length = std::hypot(dx, dy, dz); double tx = dx / 2 + x1; double ty = dy / 2 + y1; double tz = dz / 2 + z1; dx = dx / length; dy = dy / length; dz = dz / length; double ax, ay, az, angle; if (dy > 0.999) { ax = 1.0; ay = 0.0; az = 0.0; angle = 0.0; } else if (dy < -0.999) { ax = 1.0; ay = 0.0; az = 0.0; angle = 3.14159265359; } else { ax = dz; ay = 0.0; az = dx * -1.0; angle = acos(dy); } length = length / 2.0; str << "Transform {\n" << "\ttranslation\t" << tx << "\t" << ty << "\t" << tz << "\n\tscale " << " 1 " << length << " 1" << "\n\trotation " << ax << " " << ay << " " << az << " " << angle << "\n\tchildren Shape {\n" << "\t\tgeometry Cylinder {\n\t\t\tradius\t" << c.radius << "\n\t\t}\n" << "\t\tappearance Appearance {\n" << "\t\t\tmaterial Material {\n" << "\t\t\t\tdiffuseColor\t" << c.color << "\n\t\t\t}\n\t\t}\n\t}\n}\n"; } m_sceneData += str.str(); } void VRMLVisitor::visit(MeshGeometry& geometry) { Core::Array v = geometry.vertices(); // If there are no triangles then don't bother doing anything if (v.size() == 0) return; ostringstream str, verts, iverts, colors; // save the points, coordinates, and colors to separate strings for (unsigned int i = 0; i < v.size(); ++i) { if (i == v.size() - 1) { verts << v[i].vertex; colors << v[i].color; break; } verts << v[i].vertex << ",\n"; colors << v[i].color << ", "; } // Now to write out the indices for (unsigned int i = 0; i < v.size(); i += 3) { iverts << i << ", " << i + 1 << ", " << i + 2 << ", -1,\n"; } // Now to write out the full mesh - could be pretty big... str << "Shape {\n" << "\tgeometry IndexedFaceSet {\n" << "\t\tcoord Coordinate {\n" << "\t\t\tpoint [" << verts.str() << "\t\t\t]\n\t\t}\n" << "\t\tcoordIndex[" << iverts.str() << "\t\t\t]\n" << "color Color {\n color [" << colors.str() << "]\n}\n}\n}"; m_sceneData += str.str(); } void VRMLVisitor::visit(LineStripGeometry&) {} } // namespace Avogadro::Rendering avogadrolibs-1.101.0/avogadro/rendering/vrmlvisitor.h000066400000000000000000000041351506155467400226760ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_RENDERING_VRMLVISITOR_H #define AVOGADRO_RENDERING_VRMLVISITOR_H #include "visitor.h" #include "avogadrorendering.h" #include "camera.h" #include namespace Avogadro { namespace Rendering { /** * @class VRMLVisitor vrmlvisitor.h * @brief Visitor that visits scene elements and creates a VRML input file. * * This visitor will render elements in the scene to a text file that contains * elements that can be rendered as VRML. */ class AVOGADRORENDERING_EXPORT VRMLVisitor : public Visitor { public: VRMLVisitor(const Camera& camera); ~VRMLVisitor() override; void begin(); std::string end(); /** * The overloaded visit functions, the base versions of which do nothing. */ void visit(Node&) override { return; } void visit(GroupNode&) override { return; } void visit(GeometryNode&) override { return; } void visit(Drawable&) override; void visit(SphereGeometry&) override; void visit(AmbientOcclusionSphereGeometry&) override; void visit(CurveGeometry&) override { return; } void visit(CylinderGeometry&) override; void visit(MeshGeometry&) override; void visit(TextLabel2D&) override { return; } void visit(TextLabel3D&) override { return; } void visit(LineStripGeometry& geometry) override; void setCamera(const Camera& c) { m_camera = c; } Camera camera() const { return m_camera; } void setBackgroundColor(const Vector3ub& c) { m_backgroundColor = c; } void setAmbientColor(const Vector3ub& c) { m_ambientColor = c; } void setAspectRatio(float ratio) { m_aspectRatio = ratio; } private: Camera m_camera; Vector3ub m_backgroundColor; Vector3ub m_ambientColor; float m_aspectRatio; std::string m_sceneData; }; } // End namespace Rendering } // End namespace Avogadro #endif // AVOGADRO_RENDERING_VRMLVISITOR_H avogadrolibs-1.101.0/avogadro/vtk/000077500000000000000000000000001506155467400167515ustar00rootroot00000000000000avogadrolibs-1.101.0/avogadro/vtk/CMakeLists.txt000066400000000000000000000024541506155467400215160ustar00rootroot00000000000000find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) if(WIN32 AND NOT BUILD_SHARED_LIBS) add_definitions(-DGLEW_STATIC) endif() find_package(Qt${QT_VERSION} COMPONENTS Widgets REQUIRED) find_package(VTK COMPONENTS RenderingOpenGL2 GUISupportQt DomainsChemistry RenderingVolumeOpenGL2 ViewsCore RenderingFreeType ChartsCore ViewsContext2D RenderingContextOpenGL2 REQUIRED) add_library(Vtk) avogadro_headers(Vtk chartdialog.h chartwidget.h qvtkwidget.h vtkAvogadroActor.h vtkglwidget.h ) target_sources(Vtk PRIVATE chartdialog.cpp chartwidget.cpp qvtkwidget.cpp vtkAvogadroActor.cpp vtkglwidget.cpp ) avogadro_add_library(Vtk ${HEADERS} ${SOURCES}) set_target_properties(Vtk PROPERTIES AUTOMOC TRUE) target_link_libraries(Vtk PUBLIC Avogadro::Rendering Avogadro::QtGui VTK::RenderingOpenGL2 VTK::GUISupportQt VTK::RenderingVolumeOpenGL2 VTK::RenderingFreeType VTK::InteractionStyle VTK::ChartsCore VTK::ViewsContext2D VTK::RenderingContextOpenGL2 VTK::DomainsChemistryOpenGL2 Qt::Widgets PRIVATE GLEW::GLEW OpenGL::GL) vtk_module_autoinit(TARGETS Vtk MODULES VTK::RenderingOpenGL2 VTK::GUISupportQt VTK::RenderingVolumeOpenGL2 VTK::RenderingFreeType VTK::InteractionStyle VTK::ChartsCore VTK::ViewsContext2D VTK::RenderingContextOpenGL2 VTK::DomainsChemistryOpenGL2) avogadrolibs-1.101.0/avogadro/vtk/chartdialog.cpp000066400000000000000000000013351506155467400217400ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "chartdialog.h" #include "chartwidget.h" #include namespace Avogadro::VTK { ChartDialog::ChartDialog(QWidget* p) : QDialog(p), m_chartWidget(new ChartWidget(this)) { auto* layout = new QVBoxLayout(); layout->addWidget(m_chartWidget); layout->setContentsMargins(0, 0, 0, 0); setLayout(layout); } ChartDialog::~ChartDialog() = default; ChartWidget* ChartDialog::chartWidget() { return m_chartWidget; } QSize ChartDialog::sizeHint() const { return QSize(600, 400); } QSize ChartDialog::minimumSizeHint() const { return QSize(200, 200); } } // namespace Avogadro::VTKavogadrolibs-1.101.0/avogadro/vtk/chartdialog.h000066400000000000000000000010661506155467400214060ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #pragma once #include "avogadrovtkexport.h" #include namespace Avogadro::VTK { class ChartWidget; class AVOGADROVTK_EXPORT ChartDialog : public QDialog { Q_OBJECT public: explicit ChartDialog(QWidget* p = nullptr); ~ChartDialog() override; ChartWidget* chartWidget(); QSize sizeHint() const override; QSize minimumSizeHint() const override; private: ChartWidget* m_chartWidget; }; } // namespace Avogadro::VTK avogadrolibs-1.101.0/avogadro/vtk/chartwidget.cpp000066400000000000000000000153531506155467400217710ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "chartwidget.h" #include "qvtkwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::VTK { ChartWidget::ChartWidget(QWidget* p) : QWidget(p), m_qvtk(new QVTKWidget(this)) { m_view->SetRenderWindow(m_qvtk->renderWindow()); m_view->SetInteractor(m_qvtk->interactor()); m_view->GetScene()->AddItem(m_chart); auto hLayout = new QHBoxLayout(this); hLayout->setContentsMargins(0, 0, 0, 0); hLayout->addWidget(m_qvtk); setLayout(hLayout); setMinimumWidth(100); setMinimumHeight(100); } ChartWidget::~ChartWidget() = default; bool ChartWidget::addPlot(const std::vector& x, const std::vector& y, const std::array& color) { // The x and y arrays must be of the same length, otherwise it is not x, y... if (x.size() != y.size()) return false; vtkNew xArr; xArr->SetName("x"); vtkNew yArr; yArr->SetName("y"); m_table->AddColumn(xArr); m_table->AddColumn(yArr); m_table->SetNumberOfRows(x.size()); for (size_t i = 0; i < x.size(); ++i) xArr->SetValue(i, x[i]); for (size_t i = 0; i < y.size(); ++i) yArr->SetValue(i, y[i]); auto* line = m_chart->AddPlot(vtkChart::LINE); line->SetInputData(m_table, 0, 1); line->SetWidth(m_lineWidth); line->SetColor(color[0], color[1], color[2], color[3]); return true; } bool ChartWidget::addSeries(const std::vector& newSeries, const std::array& color) { if (newSeries.empty()) return false; // check to see if the newSeries has the same number of rows // as the existing table if (m_table->GetNumberOfRows() != newSeries.size()) { return false; } // okay, we add a new column vtkNew yArr; // new column name auto numColumns = m_table->GetNumberOfColumns(); yArr->SetName(("y" + std::to_string(numColumns)).c_str()); yArr->SetNumberOfValues(newSeries.size()); m_table->AddColumn(yArr); for (size_t i = 0; i < newSeries.size(); ++i) { yArr->SetValue(i, newSeries[i]); } auto* line = m_chart->AddPlot(vtkChart::LINE); line->SetInputData(m_table, 0, numColumns); line->SetWidth(m_lineWidth); line->SetColor(color[0], color[1], color[2], color[3]); return true; } bool ChartWidget::addPlots(const std::vector>& plotData, const std::array& color) { // Need at least an x and a y. if (plotData.size() < 2) return false; // All arrays must be the same size to go in the same table. auto xSize = plotData[0].size(); for (const auto& d : plotData) if (xSize != d.size()) return false; std::vector vtkArrays; int col = 0; for (const auto& d : plotData) { // The vtkTable will hold a reference, the temp std::vector doesn't need to. AVO_UNUSED(d); vtkNew vtkArray; // Runtime errors without unique names, so construct something. vtkArray->SetName(("c" + std::to_string(col++)).c_str()); m_table->AddColumn(vtkArray); vtkArrays.push_back(vtkArray); } m_table->SetNumberOfRows(xSize); for (size_t i = 0; i < plotData.size(); ++i) { auto& s = plotData[i]; auto* d = vtkArrays[i]; // This is a fast path in VTK for setting array values with a known type. for (size_t j = 0; j < s.size(); ++j) { d->SetValue(j, s[j]); } // Add the plot. auto* line = m_chart->AddPlot(vtkChart::LINE); line->SetInputData(m_table, 0, i); line->SetWidth(m_lineWidth); line->SetColor(color[0], color[1], color[2], color[3]); } return true; } void ChartWidget::clearPlots() { m_chart->ClearPlots(); m_table->RemoveAllColumns(); } void ChartWidget::setXAxisTitle(const std::string& title) { auto* axis = m_chart->GetAxis(vtkAxis::BOTTOM); axis->SetTitle(title); axis->SetTitleVisible(true); axis->GetTitleProperties()->SetBold(true); axis->GetLabelProperties()->SetFontSize(14); } void ChartWidget::setYAxisTitle(const std::string& title) { auto* axis = m_chart->GetAxis(vtkAxis::LEFT); axis->SetTitle(title); axis->SetTitleVisible(true); axis->GetTitleProperties()->SetBold(true); } void ChartWidget::setFontSize(int size) { int titleSize = round(size * 1.25); auto* axis = m_chart->GetAxis(vtkAxis::BOTTOM); axis->GetLabelProperties()->SetFontSize(size); axis->GetTitleProperties()->SetFontSize(titleSize); axis = m_chart->GetAxis(vtkAxis::LEFT); axis->GetLabelProperties()->SetFontSize(size); axis->GetTitleProperties()->SetFontSize(titleSize); } void ChartWidget::setLineWidth(float width) { m_lineWidth = width; for (int i = 0; i < m_chart->GetNumberOfPlots(); ++i) { auto* plot = m_chart->GetPlot(i); plot->SetWidth(width); } } void ChartWidget::setTickLabels(Axis a, const std::vector& tickPositions, const std::vector& tickLabels) { auto customAxis = axis(a); // We need a valid axis and equal sizes vectors of points/labels. if (!customAxis) return; if (tickPositions.size() != tickLabels.size()) return; vtkNew doubleArray; doubleArray->SetName("Tick Positions"); for (const auto& pos : tickPositions) doubleArray->InsertNextValue(pos); vtkNew stringArray; stringArray->SetName("Tick Labels"); for (const auto& label : tickLabels) stringArray->InsertNextValue(label); customAxis->SetCustomTickPositions(doubleArray, stringArray); } void ChartWidget::setAxisLimits(Axis a, float min, float max) { auto customAxis = axis(a); // We need a valid axis and equal sizes vectors of points/labels. if (!customAxis) return; customAxis->SetRange(min, max); customAxis->SetBehavior(vtkAxis::FIXED); } void ChartWidget::setXAxisLimits(float min, float max) { setAxisLimits(Axis::x, min, max); } void ChartWidget::setYAxisLimits(float min, float max) { setAxisLimits(Axis::y, min, max); } void ChartWidget::setAxisLogScale(Axis a, bool logScale) { auto customAxis = axis(a); // We need a valid axis and equal sizes vectors of points/labels. if (!customAxis) return; customAxis->SetLogScale(logScale); } vtkAxis* ChartWidget::axis(Axis a) { if (a == Axis::x) return m_chart->GetAxis(vtkAxis::BOTTOM); else if (a == Axis::y) return m_chart->GetAxis(vtkAxis::LEFT); return nullptr; } } // namespace Avogadro::VTK avogadrolibs-1.101.0/avogadro/vtk/chartwidget.h000066400000000000000000000033211506155467400214260ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #pragma once #include "avogadrovtkexport.h" #include #include #include #include class vtkAxis; class vtkChartXY; class vtkContextView; class vtkTable; namespace Avogadro::VTK { typedef std::array color4ub; class QVTKWidget; class AVOGADROVTK_EXPORT ChartWidget : public QWidget { Q_OBJECT public: explicit ChartWidget(QWidget* p = nullptr); ~ChartWidget() override; enum class Axis { x, y }; bool addPlot(const std::vector& x, const std::vector& y, const color4ub& color = color4ub{ 0, 0, 0, 255 }); bool addSeries(const std::vector& y, const color4ub& color = color4ub{ 0, 0, 0, 255 }); bool addPlots(const std::vector>& plotData, const color4ub& color = color4ub{ 0, 0, 0, 255 }); void clearPlots(); void setXAxisTitle(const std::string& title); void setYAxisTitle(const std::string& title); void setTickLabels(Axis a, const std::vector& tickPositions, const std::vector& tickLabels); void setAxisLimits(Axis a, float min, float max); void setXAxisLimits(float min, float max); void setYAxisLimits(float min, float max); void setFontSize(int size = 14); void setLineWidth(float width = 1.0); void setAxisLogScale(Axis a, bool logScale); private: void renderViews(); float m_lineWidth = 1.0; vtkAxis* axis(Axis a); vtkNew m_view; vtkNew m_chart; vtkNew m_table; QVTKWidget* m_qvtk; }; } // namespace Avogadro::VTK avogadrolibs-1.101.0/avogadro/vtk/qvtkwidget.cpp000066400000000000000000000012761506155467400216540ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "qvtkwidget.h" #include #include #include #include namespace Avogadro::VTK { QVTKWidget::QVTKWidget(QWidget* parent, Qt::WindowFlags f) : QVTKOpenGLStereoWidget(parent, f) { // Set some defaults for our render window. vtkNew window; setRenderWindow(window); auto glFormat = QVTKOpenGLStereoWidget::defaultFormat(); glFormat.setSamples(8); setFormat(glFormat); } QVTKWidget::~QVTKWidget() = default; } // namespace Avogadro::VTK avogadrolibs-1.101.0/avogadro/vtk/qvtkwidget.h000066400000000000000000000010271506155467400213130ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #pragma once #include "avogadrovtkexport.h" #include #include namespace Avogadro::VTK { class AVOGADROVTK_EXPORT QVTKWidget : public QVTKOpenGLStereoWidget { Q_OBJECT public: explicit QVTKWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~QVTKWidget() override; }; } // namespace Avogadro::VTK avogadrolibs-1.101.0/avogadro/vtk/vtkAvogadroActor.cpp000066400000000000000000000061371506155467400227440ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include "vtkAvogadroActor.h" #include #include #include #include #include #include #include #include using Avogadro::Vector3f; using Avogadro::Rendering::Camera; using Avogadro::Rendering::GLRenderVisitor; using Eigen::Affine3f; vtkStandardNewMacro(vtkAvogadroActor) vtkAvogadroActor::vtkAvogadroActor() : m_scene(nullptr), m_initialized(false) { for (double& m_bound : m_bounds) m_bound = 0.0; } vtkAvogadroActor::~vtkAvogadroActor() {} int vtkAvogadroActor::RenderOpaqueGeometry(vtkViewport*) { if (!m_initialized) { GLenum result = glewInit(); if (result != GLEW_OK && result != GLEW_ERROR_NO_GLX_DISPLAY) { cout << "Error, could not initialize GLEW." << endl; return 0; } if (!GLEW_VERSION_2_1) { cout << "GL version 2.1 is not supported by your GPU." << endl; return 0; } m_initialized = true; } if (!m_scene) return 0; // Figure out the current model view and projection matrices for our camera. Camera camera; Affine3f mv, proj; glGetFloatv(GL_MODELVIEW_MATRIX, mv.matrix().data()); glGetFloatv(GL_PROJECTION_MATRIX, proj.matrix().data()); camera.setModelView(mv); camera.setProjection(proj); // Render the Avogadro scene using the GLRenderVisitor and return. GLRenderVisitor visitor(camera); visitor.setRenderPass(Avogadro::Rendering::OpaquePass); m_scene->rootNode().accept(visitor); return 1; } int vtkAvogadroActor::RenderTranslucentPolygonalGeometry(vtkViewport*) { // Figure out the current model view and projection matrices for our camera. Camera camera; Affine3f mv, proj; glGetFloatv(GL_MODELVIEW_MATRIX, mv.matrix().data()); glGetFloatv(GL_PROJECTION_MATRIX, proj.matrix().data()); camera.setModelView(mv); camera.setProjection(proj); // Render the Avogadro scene using the GLRenderVisitor and return. GLRenderVisitor visitor(camera); visitor.setRenderPass(Avogadro::Rendering::TranslucentPass); m_scene->rootNode().accept(visitor); return 1; } int vtkAvogadroActor::HasTranslucentPolygonalGeometry() { return 1; } double* vtkAvogadroActor::GetBounds() { if (!m_scene) return m_bounds; Vector3f center = m_scene->center(); float radius = m_scene->radius(); m_bounds[0] = center[0] - radius; m_bounds[1] = center[0] + radius; m_bounds[2] = center[1] - radius; m_bounds[3] = center[1] + radius; m_bounds[4] = center[2] - radius; m_bounds[5] = center[2] + radius; return m_bounds; } void vtkAvogadroActor::setScene(Avogadro::Rendering::Scene* scene) { m_scene = scene; } void vtkAvogadroActor::PrintSelf(ostream& os, vtkIndent indent) { Superclass::PrintSelf(os, indent); } avogadrolibs-1.101.0/avogadro/vtk/vtkAvogadroActor.h000066400000000000000000000041761506155467400224120ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #ifndef AVOGADRO_VTKAVOGADROACTOR_H #define AVOGADRO_VTKAVOGADROACTOR_H #include "avogadrovtkexport.h" #include namespace Avogadro { namespace Rendering { class Scene; } } // namespace Avogadro /** * @class vtkAvogadroActor vtkAvogadroActor.h * @brief Wrap an Avogadro::Rendering::Scene in a vtkActor derived container so * that it can be rendered in a standard VTK widget. * @author Marcus D. Hanwell */ class AVOGADROVTK_EXPORT vtkAvogadroActor : public vtkActor { public: /** Return a new instance of the vtkAvogadroActor. */ static vtkAvogadroActor* New(); /** Required type macro. */ vtkTypeMacro(vtkAvogadroActor, vtkActor) /** Print the state of the object. */ void PrintSelf(ostream& os, vtkIndent indent) override; /** Render the opaque geometry. */ int RenderOpaqueGeometry(vtkViewport* viewport) override; /** Render the translucent geometry. */ int RenderTranslucentPolygonalGeometry(vtkViewport* viewport) override; /** Does the actor have translucent geometry? */ int HasTranslucentPolygonalGeometry() override; /** * Get the bounds for this Actor as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax). (The * method GetBounds(double bounds[6]) is available from the superclass.) */ double* GetBounds() override; /** Set the scene on the actor, the actor assumes ownership of the scene. */ void setScene(Avogadro::Rendering::Scene* scene); /** Get the scene being rendered by the actor. */ Avogadro::Rendering::Scene* GetScene() { return m_scene; } protected: vtkAvogadroActor(); ~vtkAvogadroActor(); Avogadro::Rendering::Scene* m_scene; double m_bounds[6]; bool m_initialized; private: vtkAvogadroActor(const vtkAvogadroActor&); // Not implemented. void operator=(const vtkAvogadroActor&); // Not implemented. }; #endif // AVOGADRO_VTKAVOGADROACTOR_H avogadrolibs-1.101.0/avogadro/vtk/vtkglwidget.cpp000066400000000000000000000215141506155467400220130ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #include "vtkglwidget.h" #include #include #include #include #include #include "vtkAvogadroActor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Avogadro::VTK { using QtGui::Molecule; // The caller assumes ownership of the vtkImageData returned. vtkImageData* createCubeImageData(Core::Cube* cube) { auto data = vtkImageData::New(); // data->SetNumberOfScalarComponents(1, nullptr); Eigen::Vector3i dim = cube->dimensions(); data->SetExtent(0, dim.x() - 1, 0, dim.y() - 1, 0, dim.z() - 1); data->SetOrigin(cube->min().x(), cube->min().y(), cube->min().z()); data->SetSpacing(cube->spacing().data()); data->AllocateScalars(VTK_FLOAT, 1); auto* dataPtr = static_cast(data->GetScalarPointer()); std::vector* cubePtr = cube->data(); for (int i = 0; i < dim.x(); ++i) { for (int j = 0; j < dim.y(); ++j) { for (int k = 0; k < dim.z(); ++k) { dataPtr[(k * dim.y() + j) * dim.x() + i] = (*cubePtr)[(i * dim.y() + j) * dim.z() + k]; } } } return data; } void vtkGLWidget::cubeVolume(Core::Cube* cube) { m_imageData = createCubeImageData(cube); // Call delete to decrement the reference count now it is in a smart pointer. m_imageData->Delete(); vtkNew volumeMapper; vtkNew volumeProperty; volumeMapper->SetBlendModeToComposite(); volumeMapper->SetInputData(m_imageData); // volumeMapper->SetInputConnection(t->GetOutputPort()); volumeProperty->ShadeOff(); volumeProperty->SetInterpolationTypeToLinear(); auto compositeOpacity = m_opacityFunction.Get(); auto color = m_lut.Get(); if (color->GetSize() == 0) { // Initialize the color and opacity function. double range[2]; m_imageData->GetScalarRange(range); if (range[0] < 0.0) { // Likely a molecular orbital, let's make something symmetric. auto magnitude = std::max(std::fabs(range[0]), std::fabs(range[1])); color->AddRGBPoint(-magnitude, 1.0, 0.0, 0.0); color->AddRGBPoint(-0.01 * magnitude, 1.0, 0.0, 0.0); color->AddRGBPoint(0.01 * magnitude, 0.0, 0.0, 1.0); color->AddRGBPoint(magnitude, 0.0, 0.0, 1.0); compositeOpacity->AddPoint(-magnitude, 1.0); compositeOpacity->AddPoint(-0.2 * magnitude, 0.8); compositeOpacity->AddPoint(0, 0.0); compositeOpacity->AddPoint(0.2 * magnitude, 0.8); compositeOpacity->AddPoint(magnitude, 1.0); } } volumeProperty->SetScalarOpacity(compositeOpacity); // composite first. volumeProperty->SetColor(color); m_volume->SetMapper(volumeMapper); m_volume->SetProperty(volumeProperty); } vtkGLWidget::vtkGLWidget(QWidget* p, Qt::WindowFlags f) : QVTKWidget(p, f), m_activeTool(nullptr), m_defaultTool(nullptr) { setFocusPolicy(Qt::ClickFocus); connect(&m_scenePlugins, SIGNAL(pluginStateChanged(Avogadro::QtGui::ScenePlugin*)), SLOT(updateScene())); // Set up our renderer, window, scene, etc. vtkNew renderWin; setRenderWindow(renderWin); renderWindow()->AddRenderer(m_vtkRenderer); setFormat(QVTKOpenGLStereoWidget::defaultFormat()); vtkNew interact; interactor()->SetInteractorStyle(interact); interactor()->Initialize(); m_vtkRenderer->SetBackground(1.0, 1.0, 1.0); m_moleculeMapper->UseBallAndStickSettings(); m_actor->SetMapper(m_moleculeMapper); m_actor->GetProperty()->SetAmbient(0.0); m_actor->GetProperty()->SetDiffuse(1.0); m_actor->GetProperty()->SetSpecular(0.0); m_actor->GetProperty()->SetSpecularPower(40); m_vtkRenderer->AddActor(m_actor); m_vtkRenderer->AddViewProp(m_volume); // Set up the flying edges contour pipeline. m_contourMapper->SetInputConnection(m_flyingEdges->GetOutputPort()); m_contourActor->GetProperty()->SetOpacity(0.5); m_contourActor->SetMapper(m_contourMapper); m_vtkRenderer->AddActor(m_contourActor); m_contourActor->SetVisibility(0); } vtkGLWidget::~vtkGLWidget() {} void vtkGLWidget::setMolecule(QtGui::Molecule* mol) { clearScene(); if (m_molecule) disconnect(m_molecule, nullptr, nullptr, nullptr); m_molecule = mol; foreach (QtGui::ToolPlugin* tool, m_tools) tool->setMolecule(m_molecule); connect(m_molecule, SIGNAL(changed(unsigned int)), SLOT(updateScene())); connect(m_molecule, SIGNAL(changed(unsigned int)), SLOT(moleculeChanged(unsigned int))); updateCube(); // Reset the camera, re-render. m_vtkRenderer->ResetCamera(); renderWindow()->Render(); } void vtkGLWidget::updateCube() { auto mol = m_molecule; if (mol->cubeCount() > 0) { // Convert the cube to a vtkImageData for volume rendering/contouring. cubeVolume(mol->cube(0)); // Set up a connection for the contour filter too. m_flyingEdges->SetInputData(m_imageData); m_flyingEdges->GenerateValues(2, -0.05, 0.05); m_flyingEdges->ComputeNormalsOn(); m_flyingEdges->ComputeScalarsOn(); m_flyingEdges->SetArrayComponent(0); m_contourMapper->SetLookupTable(m_lut); m_contourMapper->SetScalarRange(m_imageData->GetScalarRange()); emit imageDataUpdated(); } } void vtkGLWidget::moleculeChanged(unsigned int c) { Q_ASSERT(m_molecule == qobject_cast(sender())); // I think we need to look at adding cubes to changes, flaky right now. auto changes = static_cast(c); if (changes & Molecule::Added || changes & Molecule::Removed) { updateCube(); renderWindow()->Render(); } } QtGui::Molecule* vtkGLWidget::molecule() { return m_molecule; } const QtGui::Molecule* vtkGLWidget::molecule() const { return m_molecule; } vtkColorTransferFunction* vtkGLWidget::lut() const { return m_lut; } vtkPiecewiseFunction* vtkGLWidget::opacityFunction() const { return m_opacityFunction; } vtkImageData* vtkGLWidget::imageData() const { return m_imageData; } void vtkGLWidget::renderVolume(bool enable) { m_volume->SetVisibility(enable ? 1 : 0); } void vtkGLWidget::renderIsosurface(bool enable) { m_contourActor->SetVisibility(enable ? 1 : 0); } void vtkGLWidget::setIsoValue(double value) { m_flyingEdges->SetNumberOfContours(2); m_flyingEdges->SetValue(0, -value); m_flyingEdges->SetValue(1, value); } void vtkGLWidget::setOpacity(double value) { m_contourActor->GetProperty()->SetOpacity(value); } void vtkGLWidget::updateScene() { if (m_molecule) { if (m_vtkMolecule) m_vtkMolecule->Delete(); m_vtkMolecule = vtkMolecule::New(); for (Index i = 0; i < m_molecule->atomCount(); ++i) { auto a = m_molecule->atom(i); m_vtkMolecule->AppendAtom(a.atomicNumber(), a.position3d().x(), a.position3d().y(), a.position3d().z()); } for (Index i = 0; i < m_molecule->bondCount(); ++i) { auto b = m_molecule->bond(i); m_vtkMolecule->AppendBond(b.atom1().index(), b.atom2().index(), b.order()); } m_moleculeMapper->SetInputData(m_vtkMolecule); return; } // Build up the scene with the scene plugins, creating the appropriate nodes. QtGui::Molecule* mol = m_molecule; if (!mol) mol = new QtGui::Molecule(this); if (mol) { Rendering::GroupNode& node = m_renderer.scene().rootNode(); node.clear(); auto* moleculeNode = new Rendering::GroupNode(&node); foreach (QtGui::ScenePlugin* scenePlugin, m_scenePlugins.activeScenePlugins()) { auto* engineNode = new Rendering::GroupNode(moleculeNode); scenePlugin->process(*mol, *engineNode); } // Let the tools perform any drawing they need to do. if (m_activeTool) { auto* toolNode = new Rendering::GroupNode(moleculeNode); m_activeTool->draw(*toolNode); } if (m_defaultTool) { auto* toolNode = new Rendering::GroupNode(moleculeNode); m_defaultTool->draw(*toolNode); } m_renderer.resetGeometry(); update(); } if (mol != m_molecule) delete mol; } void vtkGLWidget::clearScene() { m_renderer.scene().clear(); } void vtkGLWidget::resetCamera() { m_renderer.resetCamera(); update(); } void vtkGLWidget::resetGeometry() { m_renderer.resetGeometry(); } } // namespace Avogadro::VTK avogadrolibs-1.101.0/avogadro/vtk/vtkglwidget.h000066400000000000000000000074721506155467400214670ustar00rootroot00000000000000/* This source file is part of the Avogadro project. It is released under the 3-Clause BSD License, see "LICENSE". */ #pragma once #include "avogadrovtkexport.h" #include "qvtkwidget.h" #include #include #include #include class vtkActor; class vtkColorTransferFunction; class vtkFlyingEdges3D; class vtkMolecule; class vtkMoleculeMapper; class vtkPiecewiseFunction; class vtkPolyDataMapper; class vtkRenderer; class vtkVolume; class vtkImageData; namespace Avogadro { namespace Core { class Cube; } namespace QtGui { class Molecule; class ToolPlugin; } // namespace QtGui namespace VTK { class AVOGADROVTK_EXPORT vtkGLWidget : public QVTKWidget { Q_OBJECT public: vtkGLWidget(QWidget* p = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~vtkGLWidget(); /** Set the molecule the widget will render. */ void setMolecule(QtGui::Molecule* molecule); /** * Get the molecule being rendered by the widget. * @{ */ QtGui::Molecule* molecule(); const QtGui::Molecule* molecule() const; /** @}*/ /** Get a reference to the renderer for the widget. */ Rendering::GLRenderer& renderer() { return m_renderer; } /** * Get the GLWidget's ScenePluginModel, used to add, delete and modify the * scene plugin items. * @{ */ QtGui::ScenePluginModel& sceneModel() { return m_scenePlugins; } const QtGui::ScenePluginModel& sceneModel() const { return m_scenePlugins; } /** @}*/ /** * Get the color loop up table for the volume renderer. */ vtkColorTransferFunction* lut() const; /** * Get the opacity function for the volume renderer. */ vtkPiecewiseFunction* opacityFunction() const; /** * Get the vtkImageData that is being rendered. */ vtkImageData* imageData() const; /** * Set the cube to render. */ void setCube(Core::Cube* cube); /** * Get the cube being rendered, this is the input for the imageData. */ Core::Cube* cube(); /** * Display the volume rendering. */ void renderVolume(bool enable); /** * Display an isosurface. */ void renderIsosurface(bool enable); /** * Set the isovalue for the isosurface. */ void setIsoValue(double value); /** * Set the isovalue for the isosurface. */ void setOpacity(double value); signals: /** * Emitted if the image data is updated so that histograms etc can update. */ void imageDataUpdated(); public slots: /** * Update the scene plugins for the widget, this will generate geometry in * the scene etc. */ void updateScene(); /** * Clear the contents of the scene. */ void clearScene(); /** Reset the view to fit the entire scene. */ void resetCamera(); /** Reset the geometry when the molecule etc changes. */ void resetGeometry(); /** Volume render the supplied cube. */ void cubeVolume(Core::Cube* cube); private slots: void moleculeChanged(unsigned int c); void updateCube(); private: QPointer m_molecule; QList m_tools; QtGui::ToolPlugin* m_activeTool; QtGui::ToolPlugin* m_defaultTool; Rendering::GLRenderer m_renderer; QtGui::ScenePluginModel m_scenePlugins; // vtkNew m_context; vtkNew m_vtkRenderer; // The volume rendering pieces. vtkNew m_lut; vtkNew m_opacityFunction; vtkSmartPointer m_imageData; vtkNew m_volume; // The contour pieces. vtkNew m_contourActor; vtkNew m_contourMapper; vtkNew m_flyingEdges; // The molecule actor, data structure, mapper. vtkNew m_actor; vtkSmartPointer m_vtkMolecule; vtkNew m_moleculeMapper; }; } // namespace VTK } // namespace Avogadro avogadrolibs-1.101.0/cmake/000077500000000000000000000000001506155467400154235ustar00rootroot00000000000000avogadrolibs-1.101.0/cmake/AvogadroLibsConfig.cmake.in000066400000000000000000000027251506155467400225420ustar00rootroot00000000000000# AvogadroLibs CMake configuration file - http://www.openchemistry.org/ # If this file was found, then OpenQube has been found set(AvogadroLibs_FOUND 1) set(AvogadroLibs_VERSION_MAJOR "@AvogadroLibs_VERSION_MAJOR@") set(AvogadroLibs_VERSION_MINOR "@AvogadroLibs_VERSION_MINOR@") set(AvogadroLibs_VERSION_PATCH "@AvogadroLibs_VERSION_PATCH@") set(AvogadroLibs_VERSION "${AvogadroLibs_VERSION_MAJOR}.${AvogadroLibs_VERSION_MINOR}.${AvogadroLibs_VERSION_PATCH}") set(AvogadroLibs_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@") set(AvogadroLibs_INCLUDE_DIRS "${AvogadroLibs_INSTALL_PREFIX}/@INSTALL_INCLUDE_DIR@") set(AvogadroLibs_LIBRARY_DIR "${AvogadroLibs_INSTALL_PREFIX}/@INSTALL_LIBRARY_DIR@") set(AvogadroLibs_RUNTIME_DIR "${AvogadroLibs_INSTALL_PREFIX}/@INSTALL_RUNTIME_DIR@") set(AvogadroLibs_DATA_DIR "${AvogadroLibs_INSTALL_PREFIX}/@INSTALL_DATA_DIR@") set(AvogadroLibs_CMAKE_DIR "${AvogadroLibs_LIBRARY_DIR}/cmake/avogadrolibs") # List of target names that are plugins: set(AvogadroLibs_PLUGINS "@AvogadroLibs_PLUGINS@") set(AvogadroLibs_STATIC_PLUGINS "@AvogadroLibs_STATIC_PLUGINS@") include(CMakeFindDependencyMacro) if (@QT_VERSION@ EQUAL 6) find_dependency(Qt6OpenGLWidgets) find_dependency(Qt6Widgets) find_dependency(Qt6Core) find_dependency(Qt6Gui) find_dependency(Qt6Network) find_dependency(Qt6Concurrent) endif() if(NOT TARGET AvogadroCore) include("${AvogadroLibs_CMAKE_DIR}/AvogadroLibsTargets.cmake") endif() avogadrolibs-1.101.0/cmake/AvogadroLibsConfigVersion.cmake.in000066400000000000000000000011471506155467400241050ustar00rootroot00000000000000# AvogadroLibs CMake version file - http://www.openchemistry.org/ set(PACKAGE_VERSION @AvogadroLibs_VERSION_MAJOR@.@AvogadroLibs_VERSION_MINOR@.@AvogadroLibs_VERSION_PATCH@) if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_COMPATIBLE FALSE) else("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_COMPATIBLE TRUE) if("${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") set(PACKAGE_VERSION_EXACT TRUE) endif("${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") endif("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") avogadrolibs-1.101.0/cmake/BuildLocation.cmake000066400000000000000000000006031506155467400211540ustar00rootroot00000000000000# Set up our directory structure for output libraries and binaries if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") endif() if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) if(UNIX) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") else() set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") endif() endif() avogadrolibs-1.101.0/cmake/BuildType.cmake000066400000000000000000000011161506155467400203250ustar00rootroot00000000000000# Set a default build type if none was specified set(_build_type "Release") if(EXISTS "${CMAKE_SOURCE_DIR}/.git") set(_build_type "Debug") endif() if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to '${_build_type}' as none was specified.") set(CMAKE_BUILD_TYPE ${_build_type} CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo" "ASAN" "TSAN" "MSAN" "LSAN" "UBSAN") endif() avogadrolibs-1.101.0/cmake/CTestCustom.cmake.in000066400000000000000000000007351506155467400212540ustar00rootroot00000000000000set(CTEST_CUSTOM_COVERAGE_EXCLUDE ${CTEST_CUSTOM_COVERAGE_EXCLUDE} "tests.*.cpp" # Exclude all third party code. ".*/thirdparty/.*" # Exclude MOC files (Qt). "moc_" ) set(CTEST_CUSTOM_WARNING_EXCEPTION ${CTEST_CUSTOM_WARNING_EXCEPTION} # Exclude all third party code. ".*/thirdparty/.*" # Qt5Json snapshot ".*/qt5json/.*" # Nested Qt foreach loops produce this warning: "_container_.* shadows a previous local" "shadowed declaration is here" ) avogadrolibs-1.101.0/cmake/CheckCXXSymbolExists.cmake000066400000000000000000000037301506155467400224160ustar00rootroot00000000000000# - Check if a symbol exists as a function, variable, or macro in C++ # CHECK_CXX_SYMBOL_EXISTS( ) # # Check that the is available after including given header # and store the result in a . Specify the list # of files in one argument as a semicolon-separated list. # CHECK_CXX_SYMBOL_EXISTS() can be used to check in C++ files, as opposed # to CHECK_SYMBOL_EXISTS(), which works only for C. # # If the header files define the symbol as a macro it is considered # available and assumed to work. If the header files declare the # symbol as a function or variable then the symbol must also be # available for linking. If the symbol is a type or enum value # it will not be recognized (consider using CheckTypeSize or # CheckCSourceCompiles). # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # CMAKE_REQUIRED_LIBRARIES = list of libraries to link #============================================================================= # Copyright 2003-2011 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) INCLUDE(CheckSymbolExists) MACRO(CHECK_CXX_SYMBOL_EXISTS SYMBOL FILES VARIABLE) _CHECK_SYMBOL_EXISTS("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.cxx" "${SYMBOL}" "${FILES}" "${VARIABLE}" ) ENDMACRO(CHECK_CXX_SYMBOL_EXISTS) avogadrolibs-1.101.0/cmake/CheckSymbolExists.cmake000066400000000000000000000105521506155467400220330ustar00rootroot00000000000000# - Check if a symbol exists as a function, variable, or macro # CHECK_SYMBOL_EXISTS( ) # # Check that the is available after including given header # and store the result in a . Specify the list # of files in one argument as a semicolon-separated list. # # If the header files define the symbol as a macro it is considered # available and assumed to work. If the header files declare the # symbol as a function or variable then the symbol must also be # available for linking. If the symbol is a type or enum value # it will not be recognized (consider using CheckTypeSize or # CheckCSourceCompiles). # If the check needs to be done in C++, consider using CHECK_CXX_SYMBOL_EXISTS(), # which does the same as CHECK_SYMBOL_EXISTS(), but in C++. # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # CMAKE_REQUIRED_LIBRARIES = list of libraries to link #============================================================================= # Copyright 2003-2011 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) MACRO(CHECK_SYMBOL_EXISTS SYMBOL FILES VARIABLE) _CHECK_SYMBOL_EXISTS("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c" "${SYMBOL}" "${FILES}" "${VARIABLE}" ) ENDMACRO(CHECK_SYMBOL_EXISTS) MACRO(_CHECK_SYMBOL_EXISTS SOURCEFILE SYMBOL FILES VARIABLE) IF("${VARIABLE}" MATCHES "^${VARIABLE}$") SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") SET(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) IF(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_SYMBOL_EXISTS_LIBS "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") ELSE(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_SYMBOL_EXISTS_LIBS) ENDIF(CMAKE_REQUIRED_LIBRARIES) IF(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_SYMBOL_EXISTS_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") ELSE(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_SYMBOL_EXISTS_INCLUDES) ENDIF(CMAKE_REQUIRED_INCLUDES) FOREACH(FILE ${FILES}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n") ENDFOREACH(FILE) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nvoid cmakeRequireSymbol(int dummy,...){(void)dummy;}\nint main()\n{\n#ifndef ${SYMBOL}\n cmakeRequireSymbol(0,&${SYMBOL});\n#endif\n return 0;\n}\n") CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" "${SOURCEFILE}" @ONLY IMMEDIATE) MESSAGE(STATUS "Looking for ${SYMBOL}") TRY_COMPILE(${VARIABLE} ${CMAKE_BINARY_DIR} "${SOURCEFILE}" COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_SYMBOL_EXISTS_FLAGS} "${CHECK_SYMBOL_EXISTS_LIBS}" "${CMAKE_SYMBOL_EXISTS_INCLUDES}" OUTPUT_VARIABLE OUTPUT) IF(${VARIABLE}) MESSAGE(STATUS "Looking for ${SYMBOL} - found") SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol ${SYMBOL}") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if the ${SYMBOL} " "exist passed with the following output:\n" "${OUTPUT}\nFile ${SOURCEFILE}:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ELSE(${VARIABLE}) MESSAGE(STATUS "Looking for ${SYMBOL} - not found.") SET(${VARIABLE} "" CACHE INTERNAL "Have symbol ${SYMBOL}") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the ${SYMBOL} " "exist failed with the following output:\n" "${OUTPUT}\nFile ${SOURCEFILE}:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ENDIF(${VARIABLE}) ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$") ENDMACRO(_CHECK_SYMBOL_EXISTS) avogadrolibs-1.101.0/cmake/CompilerFlags.cmake000066400000000000000000000036241506155467400211610ustar00rootroot00000000000000if(CMAKE_COMPILER_IS_GNUCXX) include(CheckCXXCompilerFlag) # Additional warnings for GCC set(CMAKE_CXX_FLAGS_WARN "-Wnon-virtual-dtor -Wno-long-long -Wcast-align -Wchar-subscripts -Wall -Wpointer-arith -Wformat-security -Woverloaded-virtual -fno-check-new -fno-common") # This flag is useful as not returning from a non-void function is an error # with MSVC, but it is not supported on all GCC compiler versions check_cxx_compiler_flag("-Werror=return-type" HAVE_GCC_ERROR_RETURN_TYPE) if(HAVE_GCC_ERROR_RETURN_TYPE) set(CMAKE_CXX_FLAGS_ERROR "-Werror=return-type") endif() set(CMAKE_CXX_FLAGS_WARN "${CMAKE_CXX_FLAGS_WARN} -pedantic -Wshadow -Wextra") # If we are compiling on Linux then set some extra linker flags too if(CMAKE_SYSTEM_NAME MATCHES Linux) set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_MODULE_LINKER_FLAGS}") set (CMAKE_EXE_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_EXE_LINKER_FLAGS}") endif() # Set up the debug CXX_FLAGS for extra warnings set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${CMAKE_CXX_FLAGS_WARN}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_WARN} ${CMAKE_CXX_FLAGS_ERROR}") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") # Sanitizers set(CMAKE_CXX_FLAGS_TSAN "-fsanitize=thread -g -O1") set(CMAKE_CXX_FLAGS_ASAN "-fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1") set(CMAKE_CXX_FLAGS_LSAN "-fsanitize=leak -fno-omit-frame-pointer -g -O1") set(CMAKE_CXX_FLAGS_MSAN "-fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O2") set(CMAKE_CXX_FLAGS_UBSAN "-fsanitize=undefined") endif() avogadrolibs-1.101.0/cmake/DetermineVersion.cmake000066400000000000000000000042331506155467400217110ustar00rootroot00000000000000# Used to determine the version for OpenChemistry source using "git describe", if git # is found. On success sets following variables in caller's scope: # ${var_prefix}_VERSION # ${var_prefix}_VERSION_MAJOR # ${var_prefix}_VERSION_MINOR # ${var_prefix}_VERSION_PATCH # ${var_prefix}_VERSION_PATCH_EXTRA # ${var_prefix}_VERSION_IS_RELEASE is patch-extra is empty. # # If git is not found, or git describe cannot be run successfully, then these # variables are left unchanged and status message is printed. # # Arguments are: # source_dir : Source directory # git_command : git executable # var_prefix : prefix for variables e.g. "AvogadroApp". function(determine_version source_dir git_command var_prefix) set (major) set (minor) set (patch) set (full) set (patch_extra) if (EXISTS ${git_command}) execute_process( COMMAND ${git_command} describe WORKING_DIRECTORY ${source_dir} RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) if (${result} EQUAL 0) string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)[-]*(.*)" version_matches ${output}) if (CMAKE_MATCH_0) message(STATUS "Determined Source Version : ${CMAKE_MATCH_0}") set (full ${CMAKE_MATCH_0}) set (major ${CMAKE_MATCH_1}) set (minor ${CMAKE_MATCH_2}) set (patch ${CMAKE_MATCH_3}) set (patch_extra ${CMAKE_MATCH_4}) endif() endif() endif() if (full) set (${var_prefix}_VERSION ${full} PARENT_SCOPE) set (${var_prefix}_VERSION_MAJOR ${major} PARENT_SCOPE) set (${var_prefix}_VERSION_MINOR ${minor} PARENT_SCOPE) set (${var_prefix}_VERSION_PATCH ${patch} PARENT_SCOPE) set (${var_prefix}_VERSION_PATCH_EXTRA ${patch_extra} PARENT_SCOPE) if ("${major}.${minor}.${patch}" EQUAL "${full}") set (${var_prefix}_VERSION_IS_RELEASE TRUE PARENT_SCOPE) else () set (${var_prefix}_VERSION_IS_RELEASE FALSE PARENT_SCOPE) endif() else() message(STATUS "Could not use git to determine source version, using version ${${var_prefix}_VERSION}" ) endif() endfunction()avogadrolibs-1.101.0/cmake/DownloadGenXrdPattern.cmake000066400000000000000000000047461506155467400226550ustar00rootroot00000000000000# Written by Patrick S. Avery - 2018 # Downloads the executable if it doesn't already exist macro(DownloadGenXrdPattern) # Let's set the name. Windows likes to add '.exe' at the end if(WIN32) set(GENXRDPATTERN_NAME "genXrdPattern.exe") else(WIN32) set(GENXRDPATTERN_NAME "genXrdPattern") endif(WIN32) # If it already exists, don't download it again if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/bin/${GENXRDPATTERN_NAME}") set(GENXRDPATTERN_V "1.0-static") # Linux if(UNIX AND NOT APPLE) set(GENXRDPATTERN_DOWNLOAD_LOCATION "https://github.com/psavery/genXrdPattern/releases/download/${GENXRDPATTERN_V}/linux64-genXrdPattern") set(MD5 "e1b3c1d6b951ed83a037567490d75f1d") # Apple elseif(APPLE) set(GENXRDPATTERN_DOWNLOAD_LOCATION "https://github.com/psavery/genXrdPattern/releases/download/${GENXRDPATTERN_V}/osx64-genXrdPattern") set(MD5 "229b01c8efab981d812043684dae84fe") # Windows elseif(WIN32 AND NOT CYGWIN) set(GENXRDPATTERN_DOWNLOAD_LOCATION "https://github.com/psavery/genXrdPattern/releases/download/${GENXRDPATTERN_V}/win64-genXrdPattern.exe") set(MD5 "7b1a1e18a6044773c631189cbfd8b440") else() message(FATAL_ERROR "GenXrdPattern is not supported with the current OS type!") endif() message(STATUS "Downloading genXrdPattern executable from ${GENXRDPATTERN_DOWNLOAD_LOCATION}") # Install to a temporary directory so we can copy and change file # permissions file(DOWNLOAD "${GENXRDPATTERN_DOWNLOAD_LOCATION}" "${CMAKE_CURRENT_BINARY_DIR}/tmp/${GENXRDPATTERN_NAME}" SHOW_PROGRESS EXPECTED_MD5 ${MD5}) # We need to change the permissions file(COPY "${CMAKE_CURRENT_BINARY_DIR}/tmp/${GENXRDPATTERN_NAME}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/bin/" FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # Now remove the temporary directory file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/tmp") endif(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/bin/${GENXRDPATTERN_NAME}") set(GENXRDPATTERN_DESTINATION "bin") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/bin/${GENXRDPATTERN_NAME}" DESTINATION "${GENXRDPATTERN_DESTINATION}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endmacro(DownloadGenXrdPattern) avogadrolibs-1.101.0/cmake/FindMMTF.cmake000066400000000000000000000011001506155467400177610ustar00rootroot00000000000000# Find the MMTF library # # Defines: # # MMTF_FOUND - system has MMTF # MMTF_INCLUDE_DIRS - the MMTF include directories # find_path(MMTF_INCLUDE_DIR mmtf.hpp) set(MMTF_INCLUDE_DIRS "${MMTF_INCLUDE_DIR}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MMTF DEFAULT_MSG MMTF_INCLUDE_DIR) mark_as_advanced(MMTF_INCLUDE_DIR) if(MMTF_FOUND) if(NOT TARGET mmtf::mmtf) add_library(mmtf::mmtf INTERFACE IMPORTED) set_target_properties(mmtf::mmtf PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${MMTF_INCLUDE_DIR}") endif() endif() avogadrolibs-1.101.0/cmake/FindOpenBabel3.cmake000066400000000000000000000025221506155467400211410ustar00rootroot00000000000000# Find the OpenBabel3 library # # Defines: # # OpenBabel3_FOUND - system has OpenBabel # OpenBabel3_INCLUDE_DIRS - the OpenBabel include directories # OpenBabel3_LIBRARY - The OpenBabel library # find_path(OpenBabel3_INCLUDE_DIR openbabel3/openbabel/babelconfig.h) if(NOT EXISTS "${OpenBabel3_INCLUDE_DIR}/openbabel/babelconfig.h" AND EXISTS "${OpenBabel3_INCLUDE_DIR}/openbabel3/openbabel/babelconfig.h" ) # modify the variable in order that `#include ` works set(OpenBabel3_INCLUDE_DIR "${OpenBabel3_INCLUDE_DIR}/openbabel3" CACHE PATH "Path to a file." FORCE) endif() find_library(OpenBabel3_LIBRARY NAMES openbabel openbabel3 openbabel-3) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(OpenBabel3 DEFAULT_MSG OpenBabel3_INCLUDE_DIR OpenBabel3_LIBRARY) mark_as_advanced(OpenBabel3_INCLUDE_DIR OpenBabel3_LIBRARY) if(OpenBabel3_FOUND) set(OpenBabel3_INCLUDE_DIRS "${OpenBabel3_INCLUDE_DIR}") set(OpenBabel3_LIBRARIES "${OpenBabel3_LIBRARY}") if(NOT TARGET OpenBabel3) add_library(OpenBabel3 SHARED IMPORTED GLOBAL) set_target_properties(OpenBabel3 PROPERTIES IMPORTED_LOCATION "${OpenBabel3_LIBRARY}" IMPORTED_IMPLIB "${OpenBabel3_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${OpenBabel3_INCLUDE_DIR}") endif() endif() avogadrolibs-1.101.0/cmake/Findlibmsym.cmake000066400000000000000000000011271506155467400207030ustar00rootroot00000000000000# Find the libmsym library # # Defines: # # LIBMSYM_FOUND - system has LIBMSYM # LIBMSYM_INCLUDE_DIRS - the LIBMSYM include directories # LIBMSYM_LIBRARIES - The LIBMSYM library # find_path(LIBMSYM_INCLUDE_DIR libmsym/msym.h) find_library(LIBMSYM_LIBRARY NAMES libmsym) set(LIBMSYM_INCLUDE_DIRS "${LIBMSYM_INCLUDE_DIR}") set(LIBMSYM_LIBRARIES "${LIBMSYM_LIBRARY}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LIBMSYM DEFAULT_MSG LIBMSYM_INCLUDE_DIR LIBMSYM_LIBRARY) mark_as_advanced(LIBMSYM_INCLUDE_DIR LIBMSYM_LIBRARY) avogadrolibs-1.101.0/cmake/InstallLocation.cmake000066400000000000000000000032561506155467400215320ustar00rootroot00000000000000# Some default installation locations. These should be global, with any project # specific locations added to the end. These paths are all relative to the # install prefix. # # These paths attempt to adhere to the FHS, and are similar to those provided # by autotools and used in many Linux distributions. # # Use GNU install directories include(GNUInstallDirs) if(NOT INSTALL_RUNTIME_DIR) set(INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}") endif() if(NOT INSTALL_LIBRARY_DIR) set(INSTALL_LIBRARY_DIR "${CMAKE_INSTALL_LIBDIR}") endif() if(NOT INSTALL_ARCHIVE_DIR) set(INSTALL_ARCHIVE_DIR "${CMAKE_INSTALL_LIBDIR}") endif() if(NOT INSTALL_INCLUDE_DIR) set(INSTALL_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}") endif() if(NOT INSTALL_DATA_DIR) set(INSTALL_DATA_DIR "${CMAKE_INSTALL_DATAROOTDIR}") endif() if(NOT INSTALL_DOC_DIR) set(INSTALL_DOC_DIR "${CMAKE_INSTALL_DOCDIR}") endif() if(NOT INSTALL_MAN_DIR) set(INSTALL_MAN_DIR "${CMAKE_INSTALL_MANDIR}") endif() if(UNIX AND NOT APPLE) if(NOT INSTALL_XDG_APP_DIR) set(INSTALL_XDG_APPS_DIR "${INSTALL_DATA_DIR}/applications") endif() if(NOT INSTALL_XDG_ICON_DIR) set(INSTALL_XDG_ICON_DIR "${INSTALL_DATA_DIR}/pixmaps") endif() endif() # Set up RPATH for the project too. option(ENABLE_RPATH "Enable rpath support on Linux and Mac" ON) if(NOT CMAKE_INSTALL_RPATH) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBRARY_DIR}") endif() if(APPLE AND NOT CMAKE_INSTALL_NAME_DIR) set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBRARY_DIR}") endif() if(UNIX AND ENABLE_RPATH) set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif() avogadrolibs-1.101.0/cmake/avogadroplugin.cpp.in000066400000000000000000000021001506155467400215460ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. This source code is released under the 3-Clause BSD License, (see "LICENSE"). ******************************************************************************/ #include @PluginIncludes@ namespace Avogadro::QtPlugins { class @PluginName@Factory : public QObject, public QtGui::@PluginType@Factory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.openchemistry.avogadro.@PluginType@Factory") Q_INTERFACES(Avogadro::QtGui::@PluginType@Factory) public: explicit @PluginName@Factory(QObject* parent_ = nullptr) : QObject(parent_) {} QtGui::@PluginType@* createInstance(QObject* parent_ = nullptr) override { auto* object = new @PluginClass@(parent_); object->setObjectName("@PluginName@"); return object; } QString identifier() const override { return "@PluginName@"; } QString description() const override { return "@PluginDescription@"; } }; } // namespace Avogadro::QtPlugins #include "@PluginName@Plugin.moc" avogadrolibs-1.101.0/cmake/version.h.in000066400000000000000000000015601506155467400176700ustar00rootroot00000000000000/****************************************************************************** This source file is part of the Avogadro project. Copyright 2013 Kitware, Inc. This source code is released under the New BSD License, (the "License"). Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ #ifndef VERSION_H_ #define VERSION_H_ #include "avogadrocoreexport.h" #cmakedefine AvogadroLibs_VERSION "@AvogadroLibs_VERSION@" namespace Avogadro { AVOGADROCORE_EXPORT const char* version(); } // namespace Avogadro #endif /* VERSION_H_ */ avogadrolibs-1.101.0/docs/000077500000000000000000000000001506155467400152735ustar00rootroot00000000000000avogadrolibs-1.101.0/docs/CMakeLists.txt000066400000000000000000000007221506155467400200340ustar00rootroot00000000000000find_package(Doxygen REQUIRED) set(doxygen_source_dirs "${AvogadroLibs_SOURCE_DIR}/avogadro ${AvogadroLibs_SOURCE_DIR}/docs") set(doxygen_output_dir "${CMAKE_CURRENT_BINARY_DIR}") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/doxyfile.in" "${CMAKE_CURRENT_BINARY_DIR}/doxyfile") add_custom_target(documentation COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/html COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxyfile) avogadrolibs-1.101.0/docs/OpenChemistry128.png000066400000000000000000001032651506155467400210340ustar00rootroot00000000000000PNG  IHDR]׎gAMA a cHRMz&u0`:pQ<bKGD pHYs  orNTϢwIDATxgXGטjaݶ@ w٥wQ޻+"ػhlb14cLbya C {=3̜9FC @ @ @ @ؔ*Ǝ;6lu+VXhѢ[n]iӦ6m ~:Z @ F3r!!a A&BR?dr0p֭Ko[D o߾.]^Vk $$-ahA&Cll쭜&@ .իWF$`Kׇ@PRRz(--D-@ R^^uM.W@xxƿGZBq( 0@ .Ƶk|j6uTGEEE p '**FʿCTp?/ ~!Z@ 'r!2ܮRaĈQ9peee,.fCX<P .]>ʑڢVk`Q c.!h>]C@ 9}tL&?,,Rz@ pᒑ*%`<8m @ Ș1c:Stz9@ A ~V:M!бcz@ >v-2@ @AAAV@ ~6cƌ(e0@ vӢaj͓0p\II].B †ܽ{EFR;Ztց\xֵkwKKK=Q!alҥ]#>S*U.eN:ի ԃ@IQQQ`=Iѱ)aT@RWeee D@4/=zlN``c6)~) %txn۶m:Գ@Aee[3gV5/ 00w!@ jٹ3/cǎWd29w=j (M}-<@ .%%%> d29t&@ddTʕ+SQa@R\v9f̘Vܤ~]%$$rF#@ Mhvځ e7?@T=ݻ .A D#//O}^&7k}(jt:sΝi4[т@ FOiixA;J՟:ޮ %:CsD>\6"'gmw4r(w^I&͔J3|oHH(ɠ{gg^վ}vп#'O 6lJL(*ҥ|Ih@!!!*[aa_.X?^[T^ǚr/3>q,&&L&>n:4}Syy9,@,u޳DT휿6B|د߀FUW]\B3nZk]1B|ؿ^Dƍ{W(JP5ߍ;~ryyyK42|gAI>>пN<ې}؆ z8x^rCC*L&LR gz}wY9`0o5~i=߻? ::u6vE#@ N)SjŢP(!66㜜K{޽ׯ_Ѯ[.yݺu=rrrBϟ?W]]f D]t&+*r;x@@8m۶GGG WԠP(orݻWӵk3r®Wu:=T߇ <4aw.^ۭ[AAR:tz俍5j >|Μ9CBBn? R*ݟ9s渪D )++cXT~6wywRR]v5u=##cL&k.mܸي6;;{NJTm?44  %m۶.MJJJ$}ݡP(sǤ&0ջk׆DEE]w^F :3gN3 -b[#;~F 飑#Gy&F1@ ̩S[T=Ɋg^FaSoWhr媾QQ_3HRmNF#@KEEEsV*U?Z_m:ܨZ;ߺu>l*=$L&OvF8@ ł z!NbٲeeeeÆ >gةȑ#h#֭ {~F Jazz*jg۶mˬ]Ьjј1c\BG W+Wp'NN&akj5giiCϜ9ZniӦ jU*rEy]=@ nظj{3iܷo_gMif4[}7q?rٳC-Z2ΜmuꫯȘ;x ,pҭۻbH|C$S$y£؀3(lO䙧HC$).G9zĈ*WJxԩ<`#ۖOQG***Z@gRzRTF.W<ӧ撒Wn{v 5b"BCOxj6NAa8P@.H\gq(6,r }{ܱukK.Q;999q111WrE#Ń.F+͚5kHFakq;Q3ګ=u>j]6wYnt`ٜz- AˍBA6l=[VT3-E\@8p}PG{F߳]^qbA\|@0 @8W$!x|XqW?ol0pG+Wcƌɖ" (HK,fѳga{z0RӰa^z9tdԈsD;uߥK8mwno̟) w־ F#]#+W w۱cwǐO"+`…ь@ ^RRRumߛxyl={;&1b7J_Wry3۵?wdVVV5k֬:{^f ,,X]]D磏>rW(?vZZsgѢEӽ߷o/|6U?#;{#ǜhd]#+~sc'T[fYpaB2pp >|7͇ 2T]s)b'u4޽S À̀@ <s63ݫޠ{Gwm; pWba!W,\0ʑm͖.L4V { ӱc)g ~DG>}6XWD324uC ..:,]Ν;C/9v<PdaHtc\Iƭ3jԤ .Hz`0 ׯj箏MOKK=rc.3%C,dzMum?AǷoyܹ9&>vP*U@ `?-[f}b x|`O=Ē[geeذaݻ84￧uXտj5^;'@\̸ ElY#gȐ!!@ v퐐rg[߷36lea}.I jag߿_YYY5cHJ}8aӰĞb LFG:"J^ZZag \Sii'!DWǝ($$.3YY;|0ܦ[$ @urcǎɭYۂ{)qAa88\^e<{C:SBddWvD kF^W^^bd\dOO̞=.-^UUu]AA ̈́|߾}{VV(RkϠ@4yVZٙ@.W C obIYm݅"`x{x~6{%y`_$=t\&?qxT50f̘hf@ MVk8JŋŶ~/S,I`KdD6NXe0:T2rh|GnK-[iitAO( 谈߸q8r< !x찰*G Z}… 6=C3rdg5[$}DzWߗ,XЙ|c鑀X,7: 7`-UUUvll9G\Qkm8sxW+++PXn/ E{Tcg*<<r 4hqc3*oիDE-}c\ER9nsH6$oSXr+!6aaGOˋ)..fYܾ}kۮе9k㣣WFG&}|J[1qbbDFKܰnݰ[w)>۳gOЁCfFE숏{?X6RVBW%~AlT̞֬뛷=OjkWsv-']>:tzlٲo.][Vk=~ᇒWݻ2Tory  *fc Zm0Gl?(H  Yh宙nكΝe%##c,5ϻp9eʔT\+)d#d]BX"+$ˍ\Q):ySNu5&Ν;5cꌱJ6^߲bֵXB0Yr[w{5a̸Ig96Ym[TctƟmxUT{gϘHZBNNNhhhMBxc\֯_kzn\6'Ϗ/]G=N[:>-j5fuN:_Je`0hR1c k륐Ju{ K ]%͕w霼I~G0 l](! \|^h4ΐL0^>%mٶ+@a8yyѷWnc>}ꭷ!T_gj̘vp7otb4E}So۶Mfw3)++c 6|RŒs0j9zm~_h42X/M^|YzTUUZD]( 9ruFFT>;FknFgJJl'"4|/i{uCڵ*d%)L/.& E.MuGGI &frT 0I\tBɈa/G!1>>ߜ\<9[J狀zSiwn"C3B'))N7s|{AխKٖ$!ȯϜ9znnݺEmaCۋ\PU7n#F,47iJye]5WLt5b0=u[r?vqzL;uWS˗,IpHǶ&žFE=~ӳOZ*:%k_|1.n+3G%_>+++,o4jn~ngΜԏp׌3PΝ;>lY=+n%vCP+#Op:&?hؾy`0Yr)p?=|k{+;;{m#e$'vjr7oˍsD|_btu7`% |6+v dJk0Ex@aXեKL'M0Ѫ"<p3?dggwE|#9+W gs@#Wn@iwű0xoL A!j]gs( 9&'@P2LJ@))-Z>uvw=\|},._*xMP62Eן+WfF$O'A},uQHzr@ݱUN5C(*|qK᠖Kjee[=ujbm̧8~QB<~o x.+{YYgb `tzԓqi0wEa8 ?UΝ;m@0Pɉg l7h'@8ʩKJJZ Ň8(ڵ+^/#vvr Q}9''wu kĸ%c =IyTw\^+W$j$,4nF[0wn*ʬrׯ]D3} 0Cg +CqyybBsPLV`%`ȠA&>* '+``%8\$&5Ilݯb0h# 6933gsvr=r pHmLn|68 8+NR VkHw3&1dRՎW޽esvT<Œ#QJ(W+˖,rŊyёkR`o,7lo3Ba8 0`+ cƌ=w;?4tLz׎ez3[RuGT Vk3d=%YpIʦx~i&}pҙĉmlCsVKB.μAN UY:#ϣ@09c2ddaaada~~SNQ&UYYVQQu!QÆM9,Pnk"}|.+Gnm'pߔRYڵk{^6TUU}G& *!Y#559X@Ni!K€‰B))׽PSYZZ׎gx?%5k֤ty}$ da6ypQ(Y@Jn3ZKg0!sif=rI!'1WG$*<`ݷ>h^ޗqfW =ŒgիW'2Z~f?JRkt]ncH(6DGuE)I?oU.g0dӧLxʫ6׳M†A& ֭[fz9˃vŨCΝ6I*PE6 ^͘#[| âgł$5%kSiݺP[o:OgքtK /_jAdAV^m{2iT E- *"."ݫ!*%YX,}8v옘KR? SNI:*<@ߍ=:^WVV hwZ٤< 1JU F~^ǏgG9ryԾx1ڛ |6vvgAbu>}. ܜsIhz~$n6gr+O"ՆÇ}ч jW ,ph&`vj$k`xÞuzbɶjWhUVV ڣ`a{d5hJ;~bmɂNB`xdzsz;,[LkٛP\D@[m$xC̑`޼1pVΧm}uKky}QǏr;FgTY#j\(R4;Yl۶12|6;GFzUf/Ia8L??^HΖїslda {O:-ι/oϏ-u|5-^2S,AHpZgM+-jبYԑ#hw9}8s8\kQ9!M3CtJH܅?Wmx_p̾ku}IAǸv_8Ɖ+Fgw@gutxbKw]lDmu5) D sS^ĚksrzYމ@TsCa8L2-cسgOO̼AlӦM˗/yZ6F|_XXhlv ::IJbQ@M/]dӳukÅ~޿+Mw}x,$p^K6^U朗q>w1k|}/G\|z15@a8L8y%KzV!,?vx{ѣon`@v0vTeX8͘kw$m47R >YI`ǎYz c2#Idرc@C`v]JPv%xQu]{կsb\k & 嬋qv;f0pD\}Kv\x""vsM}{bXq@|h$j@e%)蛚:dDzlhs>lX.( +Vtt\|y(`ZUO:dݻw[x5^j򨫴u\tvKΩ݅"_TUU1\A &,[Zk2pؚ]6Naçں^7oNg0a K,m۶f(//o)n[Qp̙W~Pܪx$6:XCD O;#xTrņ 1_moW];|83sɒ8Zy`W.d-A";9( oѝ5cDWK, vűQ([0nAHX&Z<x]۶7tO"Hzes={oY[B>&t- mU'p=,@0>bZ`nRi+wMUt˶: 'ojL$,Ο8%Y7fEnɶz0نFU+V!F8Ś`*l %٪>׮>5^/svε@ {n96acsfJ2;3`ܹ;".`~g΄ܭKH3bLKHBrΝ;V'716,$ & pѝ`ɲ`޽{!PɑYz#Ǒpƚ@P\Xk:w ZV^k %KI&f# {yfkuF F>7bٳg]~gδIS$p0tА Hܟ޾}enrZNwPB68 7n\K`tdffZB]9dY.D8˗.*$&:2rH4)0lp,3*6 4tpm@h*┥Ap23gX[/_\}  k6 @2d7o4 `(@@},Vgs 2"jU.D 6^[8̵kY3Uʦ f 9Y, lH Š9$6eJ tFF~̷"P$ t%)#.(6tK(F W]C $l￯T8\ЪT׭xXYYH|xM(6$%H4f֭[B)@0ވ@lIM\*ݻw[XSNQ$ |S;W@i s P">zjd&^WJe_4YLBF 'OSW?|0;wΝ?"jVhY:,XA9>|'{8+p}@hzhiF5C~>~lx >H"ĉ bp؋ba͉`b l!Xb"ZG0Y@an.勖A9)3 &@хV8t@~lx >H"dNGLs]ڬ#΀իW[lpf(,,A $hϟo- TCP@;n],4gdž?wofq$ph$@ }IGC@[/A$ih_h믿&h$>[@ $&"B÷3bt}h497 @cQK@ $&fϜ9I0زqc*2Ɏ~iCk$ݓH8Ux%gs^MĝT-[}VMNl۶-s7\ l蔐d2sڴeA;zUp,YUp,Yh4y9fS*Ǐg]t $8<](1݅"p**++BfqhI!Hؼys !qKdAсQKE F$Y2oV+n߾+ G\s `m(`$@ F>"˜AI0}Ύaƍ8aV~> YG ɺkiܚǡ $XdIiLJ~c1.>ga$g6Bw mk.Ӌ휿woo"@#ׯ_D\޽z{Ā!;3mٙ%g$ ܉=Kh?"v̺ }e҄ fL"ڵm-vgp gh興3k,"@#Y˗&H4פ?m2ff`8 0hrH8>R5'㋷>d|@ E/, cƌ"@#{K̼ Dۇ|lDuY;BGN_PP 1<Ē K /Ng+٢.~ʼn߾ F F(h4mܘ1# 3ϝ$QF`g< qQQ[mY$Æ5k=/իWlQQ#F,.=.[ $pq!Ua,Y0}qAw'Yӂ-c2ilsEF](͛-꒹$3B,ٖeKF F(h4m˜qfOH"Hm5UE3'CL\T[ ,vry+N۪>9^GHc@ g*S,Aĕ3Ӧ `vs㱓,{ァC [x KKnb|»Diu֭[tE#@#46n̘l萵|ygDZcǼ) oI'1.n!'`ѶSݭ9 ,Ș5k0@ k׮1=E&!m0 C i\%!`ϗ"@~~'?[jh=DbW~!fz]z!r@ow@_7H4\p@hGfoI <DѪT8av;s"#sUU-;v*C>tWȨuޭMoG݆iidI}!׍ wާu eee<{kU3d(tJHDW_&FlI _bW| ^u?HRըPB.˗;l f֬I& ̙d۷3[Z7wHuII}H Peee>Çߩׇfdz}tyQ͛7+~3wu)`BB\`c,)ˍcƌoӧONW EEEZW+eJU{{x/EggV˚vFEt${"x}SU*/j̝l!<<uR7a^UU6ߙc"dA'F녹-Z(6d"{ku:֎G\gϔ W^e8[ Q#Fw֥tkk'ݺ֭[&5펹aЀq <5aLnDb RQcEw%w7njI-Z9{ڲNoueBPIJ 9S<dάYKqI5.brI_{WVoJbnͺM@J0A ZAAG^haɶhn볷իWZhJ`clbkS61}1:㉥0t޶`hXRt,GN\ʕ+B`tƣ9f;/^,-ܩGСCw*$ iCnn+~%%%\O$~m7/@|r->etKn<((1.n?2];Ҡ/b PxZlljbJŋ-ZJ<'f̜ݑ}l2-dݱƐڵk E!$pnFF_WO<)Li?pI 0:֮]vo߾[,=_5HII)urK1.IǙB`0PI-M!w^2e\a7s`TT%Kl)jE' nii,Ydd ٽG>mpZmwFWݻ+'M1?Ql蘐cg# u֥(Jנ ),[9Ėphl% zoe Bm6E5>/))5N_b|Oe> J &Ld㚭j`V^G_tg0ƿF?wꔻV˾8]x&LQhxȸlx%D +[8"?b!! fҭ.--e]D$YiYD fk?{9'L@2Y\Uۧ,7YӦxBluQ*|1Kx% Ѫoq m{Eq\@?uM ())x15` (N`@~{dIIYګ׸>))sR{Z7ז ln`@总^uvƹVMaDH !(6 &)SIU'?5%eUjϞKHkt={.M3O^;zyK~YT_I7/܅"9_:uB [JJ3gL ǻmܸ#Kl0q8Y޻w6[K~`l 4a{h4CCþs }(^4"8i(6 E6]+IR\eWUUV!^+ƉzۿHB`”S"Nt`ڴi ~[6nDaU;mxݚ?wH{:bſi.@8 bm>9./<$H9"N_q118̞ nF 1 $ݻ->uykd}Dm/rm/pOa!ƍ3)??_acw(-):iRZry{8 -8m3O_U}0G[7] h`>}7X0eҤ+W TWWZtƎ;UTd|d6L;&NgϞ͘5kdh˫fc8t(6=  $B.;Y`ɱ#G8~FF+ eoT*uC!! +ܽ{֒wF@4.\VXD@xxh4Z >9sj3ۯ~z'%v|v&`` GTjy &OzyQNsޜ92,;:fB]Y&>&&W?+ʟ"-}o$D⣏>r bUF{xW*U՜9s8?׾{ٵkWԻ?}h%ћcEGEǖ9+.*fkLDԊݻܵkאy;v}6ӕϜ9ׯ_ 絾`rj0ֶRPP:-ɟ՚Em ZR έ_>WО+ց\xesPOlGE߶9R`mL}~aaa=z'ɟTjh8 F 2uײszڪ=@ M}o=zȨJwJ J_EPssܸqൺ~sݺu1qqWJ]i?8q«W2PoZC;wr1(Hj5>M1޽{-Ϟ={qbbrBR!wg*++߲e @ ^.//߱cG3'̞=gs͜9{‚~G&~kҥ *nB 44 |fΝs֭.L48Xg~>J՟zmkf̘5:##cs͘1kʕ+{}'k׮16H \|=~,6=ԦOqSYY6u`t=|@ۘ N ܹBi-fNAܹs~-+++ERW3_ j՚@ W͛7'jVk@T0d9zZ ;v22ҥKS*?;jԨj{Gfw|S $N N^r76خ̓УGV*[UUY&oy/44 rttyǎM@ ;s={ ZxxDM8ş)))/^(nm)**=o`}~SNnkxH +BVVVrddQT=^̙gVM KJJw~b*8XAAǓ'Oή$xD@ Huu;-$;~BF{cݺu)Ь1ۍ7QFeI_yׇT*=R4屈@8s qY 99ݻ卭 z #>]dI.@ Ê2t:V5HOOhm"صkWm&̙3'S:%m;vlmۦmڴhZkD8L&9ZL… '64$H @-[tVk9z9oݻ7KnE!!! %H2P*Uׇ^w%Lz}ȣӹi憳-..JNG&?(R*UO;$Eh=iҤV8&&󜜜/W^^^k u (aAT2/K.M*fg.F {v w8:%l ׅ]:a%/^wm\ lrDEE^Gyy9/+W(aaaa:v@^۲eKjhh߫RA\vm8iҥo +Da/Vee3Mj%†@s wJ w PC Je7nqV+?t:] L&eoYY%_ZZޱe:oǎy;w޹s]ڵ;n߾]Z\\ב@ @ lJEEEYf ==Qт=ՆMN={i[nї,X92,b4 ઈF3PW?lOAC4mJ@ @ lƱcܥTʹ1Z z}ȝ4xm[TTwo07:. E A`b?d̘`+_$H ځ#""\C?,4-d_y mi4=|Tp ́&L !@@hj՚1_(W\>}9rߜ661ug( 5KGu0_߀ǣ[b1jJIڠ׳;nOA\4xq: w^+T`ʷk͡0}}EEE}z6(m%5%5-o۶M|}رcgΜW9yӧyh `HOO zj h4o333WWWcn[[&daK@ sc$ t۶mbg ֥.3'g% Uy}uWfdR[_G?|`tH}Ngǎa;w.vR{z.](|onnnˌP 4hΞ={BIx9:jĈgqw!ҝt@ C4Aif juUs~L;vg][68qr9 MѥS< Ӕo4ߴEO0! 3~(Ky&  ®燇}d?A*=ӧk׮KJJ$Д1ry}–=y^Sb  &̘:u,$c4\dɰ;*ƕ;˃nD @xKR&w|x]fhB @ 7/_hh؝y )--iRSNٜL'Y KKav>p/} @p(Æ ;n@>;ݿ~׳[LS^|'oٳ(vu 7gN:%++K?Dc̘q< ЩSnwm{Sl`3fmjp[TWWy˖ ə=|谕6M:uի.]}Id߿eəңGV{i/YfM!˖- tJѽ{Z[:033իggY{YfK-v6g@gJ-^57'gژQ'=%[󴗻/w5.M%ȋj[4qu{{"BÎLQlhuǦMl ><-/mbR],e|od Dr{g T7oǻ o!{G!9!q#ڸD )>~\p5fW0[bNC1_$ 6N|9!qG:?38 BQS0 8\Jtoرh|\Vk:dgy| .6v;olhp^8 m}|lݨyY&LIN0 z/PtkάYI쬬Q,7F"FgSm Pخi|fnvNw\ &uMbuM1#KrL3l]#pUU}{bvSG\nɅ/V" _g=\VWi4PXQ3}<=t aC tP vll\Ž{ZtRS8 7n-GE"Hze۶ k(==?0AL\~͛ڤy޽qⱀõF;wZR˻VD3:;{blѢxKRջ6$`jժҭZ`i&J}YjtF͸Lz"pk3g*B*Ss5u.iS#,S;%ύ Fl^nI]ptlڴ)f;G+WF֗m75u&N$///^*9\(J1c]:kL!e]P.'NpH;!K[׮FѦMLo˒5Pq2y-E@iii`$ 3+bC*U%K굘F8ޢK;d=Yre#|<6u|enlW{.SKL[SxH-\01 —kɛxzkHS6NɓGeoKRХS͎dc#LRԈ6n臬<ׇ8TۧN3g}F"YϹ'ttqcϗ7n 9OHBЮAQDDl9 6"@%٢Zy|$&p]^hve1L> @(]ۯw$ {V_}yDshLSdHvՋIa8P`0ks+:"!a7\mzƉn1{%riҭڶ.9/]QH 82,17#WHp }bJ&* c̞Kj&햔(.ʷvo߮  EaĬXTVXhv93gxז .Ǝ՝SO<ާ:$9u{'O>}Zd)›/>da# b஘/QD ]^^^oՕ+W@$tus֐r®u!S!aUUÞ1&,.i@'>+`h>~̘. `B̙-n&Wzb$uu{ p>GW^eL0aR||-LJ6{_)}!8X2#:4=H8oJU.?n`º5k4xIKKLL2"[ɳ( ݔNj YEVnA}g5BKkvvm?55Ld.YloZD]5ϣ?VuƌY늬WdG77g^"|zz]ռ)d/1aƍ1޽{$ Uf 0o[Yɓ7ĖT@&L&\a=zxoٲe %ly@6KF2qPrI zt붴%tƔ0F Mb:h um{KB?YsСzͲ9cc7M8y &,7oS@H`ϙ1U" rc0_X`֌vY ?P_8ᮝ;37`2ڠEДVrTVVU]]˓憭[.tӦM/BdH8n`]Lm 8\ЪTy ӧO{L֏xzѶstDdda_AQQՑ#Nڣ>__r Ƭ [ `ISp9sf5VY)p֬Ze n[ dm]2Da81j$\T\|Ynjrp .h,[)O`!*`l޼15' 9"Ql0XRRb-sv55^$ݺvL-)i5ׄZ}U^&VggJKK\mjSjpLnQ,ɓ'}i)]&5E^#5wlcتh* #`P\AFO1_`25Z X9s愛/":LqƎ,7zC\sWHOj^+mu#cM q!JY'.,>/k=bC~'*,Ńܻwhi&M"@( ~Xd]L}djS,ņv~% &( cu}"q}|/rr%Y3? @uիW8G[ WF/~>)>|./I/Qy@UUIg F5j?o@8 $\zkٵ{^ڀA UUUo#`9F$'&`ȠA(n/.~LH/ߘާ /.}:,lmDF5*޲o ɜBh$ " WZ暺8{CWv h#eB8s~I.HOܸq.}*`pM@hy;v(<<.L#^Hw 8 bv縄^sh0n8 יo>_N˫@cy}LNX &̟;? **B@C1 oL8HrFM0%$?JqSII uƎ]ҩI=onpI zu Y%Ø3kN'SYNىwL /'[Z왋k ZYVD0Y8\S@a'L@5mQQP"nݢ!!%nLֳCɑUB8 6.@ھ$%3L,-))** g9x`2C{~ljda^`IWE-* ԓ kR̞6sD}Q8 <%a.NkKsN#.R\G!,hSx EwEݸqƔ^*,ӧ=\BMDDmBT233{&b E%|x`* !??_$@im\7l & B=dߡ|k Fg@vmџ[ Et3~ٵk'uc4uՒpذaCҲe˺UgϚ11ΜhdNh43,p!0C?y„umN7hΝM+)|XZZ F-},@ڡeWbPt>ov$L3"=}f]:'EGBC;_ {'hB `tAðamSNaB.” Y:& { l ݤ ƍ]$# b7b-|w***Z 5۷o3{+VPIо&K0YM}!}kjWojbdy.`x#(9SB8{wY*x#$8=SR}94V@h {&vjx9{ `@hv.${/_n*8LJ мn@zf+fFEEFoOGXA ^ʎ;\/ KRl߲Ϫ~K}l sס6 )F=@qqq6N! }˶h={̡ Eq ;;)cW#\lYWKh4خiSοl }27ܹrx\nP"'NX<>\Ư3i<}ΜC߷,b8{J3 CKP_5<&#"kpIw鲭ɡ K|2rFWFcչёaaI&`Ç ͩwceeeolO\qQĵ'OlאU4/..nܹO/kKBrw?k?fLkzȴ82׭[](!H>< ؒ6*QѫG<7>8q[QQѺ1 F뒔k*j/\ BTUU.q$*<؂Y3}ӧ͘6miFL4iB*-l#q 3ege"u~^z7vPcd=lW Z&p4OsfϞ0}ϵ}fϞ߷w~^Wl?oknX֕da٦2Tk֌S fM٦pol Egs0 aF] E!HmU@@v`tIj O~1W^YKSZZ*L/4$Z >Wqy yA5\j߾}n Fі,YҞѪu˫ww@ l{A7#p4`@ep(++{ "1Hpu>ӠoFaM>,d䃗=G, oF||/^qp9mZOda.-ke+V\|m*Flס]Oq.8\.6 voa?](zVN=vMKX`n]lD7f75u/;NuA4233lT E )=z̶6(FS4_ݣ"6Llean@0Y.ݜ3kN7ڹ*++KRеsFdQ2֠p0Eb& &pIa=6={&)˽c p(?o2$lw^\:ݺunJ!?Ÿ]@2keeO̜>}u7o^H[/֎ˍ(.&fnU͛7ITv L xq^L8r=`A`aΞ'sLغqk$&F˕+WR?pyj ߅!7.I=(fN>6]xfH0Y?Lfk'b V2 n-Z%_|[Iwiex=wժMXngϷ/ AョXb_mO00qP&MV݀u FVp:L0Yռlk+|s-[=os:[ 8hoOO"-^x^`~||ƎjښU":wL!q[X9SNIDAT`޾'f[~֐2rqZ£׉b+--MѣgZ?{??2.191qk]qQ^YY"&ٳg%;wңKy];' \-W\Mbn[;Oݼysɓ'h|_W),,8pfx=??gY%%%޳u{:sNk%%NJ.7LVE`CJ%%ꚔsBbFNvv'N&4+** u޽{D9͋[Q/ @ܮP˽{ { .]fp` K6:9!q})ݻ_THeW( <.*r.]/tKJ.|…9o~j(׮]cYGRbڞ]\OB=3&O}ٗf8t[}[^^ҙaAAbaߛڳ\ԩ| ^G-XYii۶Jhcywfi1WfSM9 ss;@ Dwz]B hbd0n̸hbhTæBxSLvxF d6]#?M8G@ DCP3萻:j%@ sf6/Eύ"hd\pC߭+XÇ Z @ &ѣG۶jj_˲2j-@ ]*.g޹;}%#Cu\6r]O> @ \d?'cs@)C 9%!`tFUAA@ Dcݻ-*<~?~ѢC RYYI!;|6phr@4b6Z|jJxZAo߾@ ;vsscN0XP[p:w?oT}k֬ҟ#фfnݢ޽ݻyݻ7ݼw#w}@ @ @ @ @ @ uu WeXIfMM*JR1ZilHHAdobe ImageReadyҜ%tEXtdate:create2021-04-28T20:09:54+00:00'H%tEXtdate:modify2021-04-28T20:09:54+00:00z}tEXtexif:ColorSpace1ItEXtexif:ExifOffset108tEXtexif:PixelXDimension512.tEXtexif:PixelYDimension276Y,tEXtexif:SoftwareAdobe ImageReadyIENDB`avogadrolibs-1.101.0/docs/avogadro2_64.png000066400000000000000000000141411506155467400201770ustar00rootroot00000000000000PNG  IHDR@@iqgAMA a cHRMz&u0`:pQ<bKGDSIDATx[wxTe>߽7=!u2ЋtD,mW"(Bi{ Rf&Ԁ"-LIbSu}w<̝~NyKYK {T|_\X@T\*Ç; 9J>VU~YY?Zԯ2ZIZ@jj(* Qϫt1o:8z4/r v[@iy\МԦ/g|3&oЀJJ:ۓ\¢ 逐e١S?cęb8R8\RnFMf>wgi߱'ildJP+rr?^iʎRsErl{lV1o7&ݿnov!d;2 7>1l1(#.\Ylln)Qs ic#[ "6'Ol.p;,KRe^-OQ^Q㰃2ux!tȽ!fAlb4f鎜h̲]|] +'Y^@x \Чs}xhίv YTH8@{'P[`u圿&'6m!ӭOVdOMV4;؂$Ѩ ÿ$X Gyy]yEe V;\Nj|M/Ug!|/n_}]ee3ΰUgivVsr@:r-8ONĄ? k فx@hxȅpׇqyfjU= W*i<%Vؒr.j|1uvP?'Ν5ÂIS6u&}p\i =P  #<,yB\2Ib$qLRB6$ yq1cBCBjј%;[ ȇvW~} J `R1f&z=8a;x ls&0i\ԪG31OT_9٪swLWz 4i!j"࠶fYo@UNsb+԰MygUIG Er+,mYbH0̥{# ,'BDuZ]xs>̫4 sb\^t=@YNuAgΓkjaha*BZ/;>࿔nl(-ȃ[qɌF""kR_lMP2ÔC^3y0c䣦Me\uV/xRn'qL 9LJEk40K%P7jЉ@o#uY`zg,~w-,1'ukUg 6>O-),*,g~.:lo V͞Fϥ}nG *"F·vC>k0)0)On!X߂)灬+٫L.C&"q>42}j 3qvMR֨I۵[RiTT+imԶ3KLE0qI -"͛UphzٲSQgDڣo/m-iv;/>+ ˁw@մu:(-)m[l`艧;4;u6Ժ xK!h q<+k !L:1)}]"9 GpkϡZ4 aK A*)+ЬoupXV%`"f"v 5ظIfATBBHZ#͗Wj6fI;TO zUXy ;tKh+_1aٜ}ׄʕjԶor)g*U?tzT* `+O< !|ܢH7e05":=Tryz?Ym Ԩse=-:l`uiJeðff&Roneg\@ fDTJTJz_/ȴ2tlmnZ]R? ԳB i?T #>J9t"tZ092Dضϖ{}gg>r2FFǼY&jV#X>r۴BƒETl`k?"}Ɓُ 1Hk@4w~a ܚ 2 7yJm!LmƨBP/n6H`p.ɍڊ<4 m !Y]e+y(ڪ?}%(;/:3\,8liT2]feCմ=>LW y>[s>ole!4'rNjQ>̄Qvr%Ė6H)^V,"5u_: nYO8O7>0:O[oګSYW|ahHi/*f[vҗ!ܹ[w4簃fQ N3|)==2j5ĠѺL%("cíK2/z{\ZJb_H>C5+}NV {2+aˠ[{ /0` l{"lfW7 ]~-jlk=꓍0ˤnhk6@ԴG`u-;ULlk܎34|r\e_&}W{ !X VO_+me]>;.C_!L*U Q].V4=8Min :zi[˷EV7)EΔ$sB#Bǘƫ^_œ( L:= W }}lY3]IG(":W-KEN(*>4.< X 5F/= ]{ E3"čV>o"{ ?Da*K-pIo``h.АW* <:>-﹡>pd-#"Ryhm`h΅ j ^tCl!CЬl~uϯ s>lJeB+J<]oɖ,.lNpnKxynƂ;h]oV6CFQ,2M0́b!~MoQ,2j*(AAj%[[y.l٢4ڍ| 7xԺW?Kiъ@]h9W5Ž,36:zwGp[sZ{\i)"&_'NEFq6U귧#{tUNTdz/v7zej@èQju f,3\ޝW> BA!?Dy?}7$8#8,&lgGPmŇR_^~.GnT ,{Ϭ9bnrR?r[A7HOPo,mV_+ rԲ{eoZ}frXeROnu_N;ѥh{? :{]ά|MJ%LrLr( 9LjL:}ŢՊ:F2tj/8tbe H ?t,<(Q YP5luNPMPuVZV:U!J)eu.P辮t X.QsG ]:Lzax(Ɲ_RoU#|^f0kahtl.p_&A}_Wo˖(3]0)vtzhU1]M Y/QXT$)Dn\`sܤR+∈.;I.u{ [g:,RC_kzBOPTn:=Z_L{m7A-DfkЗ~>3afYcIWVY7ez7uGƈZO]Hr7HOUNFR ZA4bzR<\5P/]c m^eo2vU@ozceb6i48K'xi|V:fBʩ6\/Y֞ 5JcTMZ-t'SkT~0i36:BMIfQ'[Ӵ3Bo8}Ci~#M횤]=" PtϼT>q: M̫ 3'& a{_Kwlg@/[~?ߞ n͹܎|m}&c΀gOW(6 AYy6w,or)Z'^i`&V{jUkcS+w&3(tfzDzMd4uR!w>d=+-O$+D ]> >|VN+ϣЈ`R&MFd+[v|ܖ][t<{QmF*3z"#";?n"ß &'ִsu2z5^zku4 }t9:Ư+>$FMʺH׸- !"BU$O@P$}qѧ*,>rI"⢢VzvNIw)ԥ%uBIݺQ>}%4mhk&'$'/+{Oyw^A:nhrɯm(1ɯL4蓾}4^o5@h[%tEXtdate:create2021-04-28T20:09:54+00:00'H%tEXtdate:modify2021-04-28T20:09:54+00:00z}tEXtSoftwareAdobe ImageReadyqe<IENDB`avogadrolibs-1.101.0/docs/doxyfile.in000066400000000000000000003350711506155467400174570ustar00rootroot00000000000000# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = @CMAKE_PROJECT_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @AvogadroLibs_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = @doxygen_output_dir@ # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = NO # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 2 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = YES # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = YES # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which efficively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # The default value is: system dependent. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = YES # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = NO # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = NO # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = NO # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = NO # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @doxygen_source_dirs@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, # *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h \ *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = moc_*.cpp */qtplugins/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = images # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: # https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 14 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /