diff --git a/.dockerignore b/.dockerignore index 1c57b86f92b41623f270127fb0c9458b389a8dca..9507eec71385fb0ddbda5666e201a22faf9f1f13 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,4 +3,4 @@ **/.gitmodules **/.dockerignore -.gitlab-ci \ No newline at end of file +.gitlab-ci diff --git a/.gitignore b/.gitignore index cf2d66b37f63fbb90eee228f159ac46455a54f2f..e95580e746d6111fff8539771f76162b048a964b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,5 @@ -.vscode/ -.idea/ -.ipython/ -.keras/ -.local/ - -.DS_Store +# https://github.com/github/gitignore/blob/main/Python.gitignore # Byte-compiled / optimized / DLL files __pycache__/ @@ -29,6 +23,7 @@ parts/ sdist/ var/ wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg @@ -47,14 +42,17 @@ 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 @@ -64,6 +62,7 @@ coverage.xml *.log local_settings.py db.sqlite3 +db.sqlite3-journal # Flask stuff: instance/ @@ -76,16 +75,49 @@ instance/ 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 -# celery beat schedule file +# 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 @@ -111,3 +143,252 @@ venv.bak/ # mypy .mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + + +# https://github.com/github/gitignore/blob/main/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + + +# https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore/ + +.vscode/* +!.vscode/settings.json +# !.vscode/tasks.json +# !.vscode/launch.json +# !.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + + +# https://github.com/github/gitignore/blob/main/Global/macOS.gitignore + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +# https://github.com/github/gitignore/blob/main/Global/Windows.gitignore + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +# https://github.com/github/gitignore/blob/main/Global/Linux.gitignore + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + + +# Analitics +deployments +.keras/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e9d6879ba4d699a83adff62c388482b88248bdf5..879738baaeb6e892c5d8d1df82b0ec7c971506ad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,111 +1,131 @@ -# default installed image for docker executor is: python:3.6 -# using an image that can do git, docker, docker-compose -# https://docs.gitlab.com/ee/ci/docker/using_docker_build.html -image: docker:dind +image: gitlab-registry.mpcdf.mpg.de/nomad-lab/nomad-fair/ci-runner -variables: - GIT_SUBMODULE_STRATEGY: recursive - APP_REGISTRY: gitlab-registry.mpcdf.mpg.de/nomad-lab/aitoolkit-app - DEVELOP_REGISTRY: gitlab-registry.mpcdf.mpg.de/nomad-lab/aitoolkit-develop +# variables: +# DEV_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG} +# STAGING_IMAGE: ${CI_REGISTRY_IMAGE}:staging stages: - build - deploy - + - release build to develop: stage: build + variables: + GIT_SUBMODULE_STRATEGY: recursive + GIT_SUBMODULE_UPDATE_FLAGS: --jobs 4 before_script: - echo "Building the single user notebook image" - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin - - docker info - script: + # - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN gitlab-registry.mpcdf.mpg.de + # - docker info # Using cache to speed up the build process - - docker pull ${CI_REGISTRY_IMAGE}:develop || true - - docker build --cache-from ${CI_REGISTRY_IMAGE}:develop --tag ${CI_REGISTRY_IMAGE}:develop${CI_COMMIT_SHORT_SHA} --tag ${CI_REGISTRY_IMAGE}:develop --tag ${DEVELOP_REGISTRY}:develop${CI_COMMIT_SHORT_SHA} --tag ${DEVELOP_REGISTRY}:develop . - - docker push ${CI_REGISTRY_IMAGE}:develop${CI_COMMIT_SHORT_SHA} - - docker push ${CI_REGISTRY_IMAGE}:develop - - docker login -u ai_toolkit -p ${AI_TOOLKIT_TOKEN} ${DEVELOP_REGISTRY} - - docker push ${DEVELOP_REGISTRY}:develop${CI_COMMIT_SHORT_SHA} - - docker push ${DEVELOP_REGISTRY}:develop - - rules: - # Execute jobs when a new commit is pushed to develop branch - - if: $CI_COMMIT_BRANCH == "develop" - - -build to staging: - stage: build - before_script: - - echo "Building the single user notebook image" - - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin - - docker info + # - docker pull ${STAGING_IMAGE} || true script: - # Using cache to speed up the build process --cache-from ${CI_REGISTRY_IMAGE}:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} - - docker pull ${CI_REGISTRY_IMAGE}:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} || true - - docker build --tag ${CI_REGISTRY_IMAGE}:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}${CI_COMMIT_SHORT_SHA} --tag ${CI_REGISTRY_IMAGE}:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} . - - docker push ${CI_REGISTRY_IMAGE}:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}${CI_COMMIT_SHORT_SHA} - - docker push ${CI_REGISTRY_IMAGE}:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} + # Using cache to speed up the build process --cache-from ${CI_REGISTRY_IMAGE}:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} + - docker build -t ${DEV_IMAGE} . + - docker push ${DEV_IMAGE} rules: # Execute jobs when a new commit is pushed to master branch - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" + - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" +# build to staging: +# stage: build +# variables: +# GIT_SUBMODULE_STRATEGY: recursive +# GIT_SUBMODULE_UPDATE_FLAGS: --jobs 4 +# before_script: +# - echo "Building the single user notebook image" +# - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin +# script: +# - docker build -t ${STAGING_IMAGE} . +# - docker push ${STAGING_IMAGE} +# rules: +# # Execute jobs when a new commit is pushed to develop branch +# - if: $CI_COMMIT_BRANCH == "develop" -build to production: - stage: build - before_script: - - echo "Building the single user notebook image" - - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin - - docker info - script: - # Using cache to speed up the build process - - docker pull ${CI_REGISTRY_IMAGE}:latest || true - - docker build --cache-from ${CI_REGISTRY_IMAGE}:latest --build-arg CACHEBUST=$(date +%s) --tag ${CI_REGISTRY_IMAGE}:production${CI_COMMIT_SHORT_SHA} --tag ${CI_REGISTRY_IMAGE}:latest . - - docker push ${CI_REGISTRY_IMAGE}:production${CI_COMMIT_SHORT_SHA} - - docker push ${CI_REGISTRY_IMAGE}:latest - rules: - # Execute jobs when a new commit is pushed to master branch - - if: $CI_COMMIT_BRANCH == 'master' +# build to production: +# stage: build +# variables: +# GIT_SUBMODULE_STRATEGY: recursive +# GIT_SUBMODULE_UPDATE_FLAGS: --jobs 4 +# before_script: +# - echo "Building the single user notebook image" +# - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin +# script: +# - docker build -t ${STAGING_IMAGE} . +# - docker push ${STAGING_IMAGE} +# rules: +# # Execute jobs when a new commit is pushed to master branch +# - if: $CI_COMMIT_BRANCH == 'master' deploy to develop: - image: python:3.6 stage: deploy - variables: - GIT_SUBMODULE_STRATEGY: none - script: - - ./.gitlab-ci/update_tag_develop.sh - - ./.gitlab-ci/update_tag_app_develop.sh - - ./.gitlab-ci/update_metainfo_gui_develop.sh environment: - name: develop - url: https://analytics-toolkit.nomad-coe.eu/develop - rules: - # Execute jobs when a new commit is pushed to develop branch - - if: $CI_COMMIT_BRANCH == "develop" - -deploy to staging: - image: python:3.6 - stage: deploy + name: dev/$CI_COMMIT_REF_NAME + deployment_tier: development + url: https://analytics-toolkit.nomad-coe.eu/dev/${CI_ENVIRONMENT_SLUG} + auto_stop_in: 7 days + on_stop: stop deploy dev variables: - GIT_SUBMODULE_STRATEGY: none + NAMESPACE: analytics-develop + before_script: + - mkdir ~/.kube/ + - echo ${CI_KUBE_CONFIG} | base64 -d > ~/.kube/config + - helm repo add jupyterhub https://jupyterhub.github.io/helm-chart + - helm repo update + - helm version script: - - ./.gitlab-ci/update_tag_staging.sh - environment: - name: staging - url: https://nomad-lab.eu/dev/analytics/staging + - helm upgrade ${CI_ENVIRONMENT_SLUG} jupyterhub/jupyterhub + --install + --namespace ${NAMESPACE} + --version=1.2.0 + --timeout=40m0s + --cleanup-on-fail + --values deployments/dev-values.yaml + --set hub.baseUrl=/dev/${CI_ENVIRONMENT_SLUG} + --set fullnameOverride=${CI_ENVIRONMENT_SLUG} + --set singleuser.podNameTemplate="${CI_ENVIRONMENT_SLUG}-{username}" + --set hub.config.GenericOAuthenticator.oauth_callback_url=https://analytics-toolkit.nomad-coe.eu/dev/${CI_ENVIRONMENT_SLUG}/hub/oauth_callback + --set singleuser.image.name=${CI_REGISTRY_IMAGE} + --set singleuser.image.tag=${CI_COMMIT_REF_SLUG} + --set roll=true + --wait rules: # Execute jobs when a new commit is pushed to master branch - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" + - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" - -deploy to production: - image: python:3.6 +stop deploy dev: stage: deploy - script: - - ./.gitlab-ci/update_tag_production.sh environment: - name: production - url: https://nomad-lab.eu/prod/analytics/hub - rules: - # Execute jobs when a new commit is pushed to master branch - - if: $CI_COMMIT_BRANCH == 'master' + name: dev/$CI_COMMIT_REF_NAME + action: stop + before_script: + - mkdir ~/.kube/ + - echo ${CI_K8S_CONFIG} | base64 -d > ~/.kube/config + script: + - helm uninstall ${CI_ENVIRONMENT_SLUG} --namespace analytics + when: manual + needs: ["build to develop"] + +# deploy to staging: +# stage: deploy +# environment: +# name: staging +# url: https://nomad-lab.eu/dev/analytics/staging +# script: +# - ./.gitlab-ci/update_tag_staging.sh +# rules: +# # Execute jobs when a new commit is pushed to master branch +# - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" + +# deploy to production: +# stage: deploy +# environment: +# name: production +# url: https://nomad-lab.eu/prod/analytics/hub +# script: +# - ./.gitlab-ci/update_tag_production.sh +# rules: +# # Execute jobs when a new commit is pushed to master branch +# - if: $CI_COMMIT_BRANCH == 'develop' diff --git a/.gitlab-ci/update_metainfo_gui_develop.sh b/.gitlab-ci/update_metainfo_gui_develop.sh deleted file mode 100755 index 331a95675630139f9fa6ea328126c1e1fba394a8..0000000000000000000000000000000000000000 --- a/.gitlab-ci/update_metainfo_gui_develop.sh +++ /dev/null @@ -1,50 +0,0 @@ -# !/bin/bash - -# Based on: https://docs.gitlab.com/ee/ci/ssh_keys/README.html - -# Install ssh-agent if not already installed, it is required by Docker. -# (change apt-get to yum if you use an RPM-based image) -# - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' -# Run ssh-agent (inside the build environment) -eval $(ssh-agent -s) - -# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store -# We're using tr to fix line endings which makes ed25519 keys work -# without extra base64 encoding. -# https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 -echo "$SSH_PRIVATE_KEY_APP" | tr -d '\r' | ssh-add - - -# Create the SSH directory and give it the right permissions -mkdir -p ~/.ssh -chmod 700 ~/.ssh - -# Use ssh-keyscan to scan the keys of your private server. -ssh-keyscan gitlab.mpcdf.mpg.de >> ~/.ssh/known_hosts -chmod 644 ~/.ssh/known_hosts - -# Set the user name and email. -git config --global user.name $GITLAB_USER_NAME -git config --global user.email $GITLAB_USER_EMAIL - - -# Clone the private repositories -git clone -b develop --recurse git@gitlab.mpcdf.mpg.de:nomad-lab/analytics.git /tmp/analytics -git clone -b develop git@gitlab.mpcdf.mpg.de:nomad-lab/aitoolkit-gui.git /tmp/aitoolkit-gui - -# Create new metainfo based on develop version -cd /tmp/analytics -python generate_tutorials_json.py > toolkitMetadata.json - -# Copy new metainfo if files are different -cd /tmp -if ! cmp analytics/toolkitMetadata.json aitoolkit-gui/src/toolkitMetadata.json~ >/dev/null 2>&1 -then - echo 'Update GUI with new metadata' - cp analytics/toolkitMetadata.json aitoolkit-gui/src/toolkitMetadata.json - cd /tmp/aitoolkit-gui - git add src/toolkitMetadata.json - git commit -m "CI: Update metainfo in the GUI" - git push -fi - -rm -rf /tmp diff --git a/.gitlab-ci/update_tag_app_develop.sh b/.gitlab-ci/update_tag_app_develop.sh deleted file mode 100755 index 4202e2cd115ccb2de614a724312d3bbb0dde1298..0000000000000000000000000000000000000000 --- a/.gitlab-ci/update_tag_app_develop.sh +++ /dev/null @@ -1,42 +0,0 @@ -# !/bin/bash - -# Based on: https://docs.gitlab.com/ee/ci/ssh_keys/README.html - -# Install ssh-agent if not already installed, it is required by Docker. -# (change apt-get to yum if you use an RPM-based image) -# - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' -# Run ssh-agent (inside the build environment) -eval $(ssh-agent -s) - -# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store -# We're using tr to fix line endings which makes ed25519 keys work -# without extra base64 encoding. -# https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 -echo "$SSH_PRIVATE_KEY_APP" | tr -d '\r' | ssh-add - - -# Create the SSH directory and give it the right permissions -mkdir -p ~/.ssh -chmod 700 ~/.ssh - -# Use ssh-keyscan to scan the keys of your private server. -ssh-keyscan gitlab.mpcdf.mpg.de >> ~/.ssh/known_hosts -chmod 644 ~/.ssh/known_hosts - -# Set the user name and email. -git config --global user.name $GITLAB_USER_NAME -git config --global user.email $GITLAB_USER_EMAIL - - -# Clone the private repository -git clone git@gitlab.mpcdf.mpg.de:nomad-lab/aitoolkit-develop.git /tmp/aitoolkit-develop -cd /tmp/aitoolkit-develop - -# Update the tag of the docker image -sed -i "s/aitoolkit-develop:develop.*/aitoolkit-develop:develop$CI_COMMIT_SHORT_SHA/1" docker-compose.yml - -# Finally, commit and push the changes -git add docker-compose.yml -git commit -m "CI: Update the Analytics image ($CI_COMMIT_SHORT_SHA)" -git push - -rm -rf /tmp/aitoolkit-app diff --git a/.gitlab-ci/update_tag_develop.sh b/.gitlab-ci/update_tag_develop.sh deleted file mode 100755 index 55e60f965c762025acf751cc17bbdb3f1fbc7f1e..0000000000000000000000000000000000000000 --- a/.gitlab-ci/update_tag_develop.sh +++ /dev/null @@ -1,44 +0,0 @@ -# !/bin/bash - -# Based on: https://docs.gitlab.com/ee/ci/ssh_keys/README.html - -# Install ssh-agent if not already installed, it is required by Docker. -# (change apt-get to yum if you use an RPM-based image) -# - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' -# Run ssh-agent (inside the build environment) -eval $(ssh-agent -s) - -# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store -# We're using tr to fix line endings which makes ed25519 keys work -# without extra base64 encoding. -# https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 -echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - -# Create the SSH directory and give it the right permissions -mkdir -p ~/.ssh -chmod 700 ~/.ssh - -# Use ssh-keyscan to scan the keys of your private server. -ssh-keyscan gitlab.mpcdf.mpg.de >> ~/.ssh/known_hosts -chmod 644 ~/.ssh/known_hosts - -# Set the user name and email. -git config --global user.name $GITLAB_USER_NAME -git config --global user.email $GITLAB_USER_EMAIL -echo $GITLAB_USER_NAME -echo $GITLAB_USER_EMAIL - - -# Clone the private repository -git clone git@gitlab.mpcdf.mpg.de:nomad-lab/analytics-deployment.git /tmp/analytics-deployment -cd /tmp/analytics-deployment - -# Update the tag of the docker image -sed -i "s/^ tag\:.*/ tag\: develop$CI_COMMIT_SHORT_SHA/g" deployments/hub/develop.yaml - -# Finally, commit and push the changes -git add deployments/hub/develop.yaml -git commit -m "CI: Update the hub image for develop ($CI_PIPELINE_URL)" -git push - -rm -rf /tmp/analytics-deployment diff --git a/.gitlab-ci/update_tag_production.sh b/.gitlab-ci/update_tag_production.sh deleted file mode 100755 index d2c208cc883ace426559a783284651bf11869b50..0000000000000000000000000000000000000000 --- a/.gitlab-ci/update_tag_production.sh +++ /dev/null @@ -1,46 +0,0 @@ - # !/bin/bash - -# Based on: https://docs.gitlab.com/ee/ci/ssh_keys/README.html - -# Install ssh-agent if not already installed, it is required by Docker. -# (change apt-get to yum if you use an RPM-based image) -# - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' -# Run ssh-agent (inside the build environment) -eval $(ssh-agent -s) - -# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store -# We're using tr to fix line endings which makes ed25519 keys work -# without extra base64 encoding. -# https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 -echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - -# Create the SSH directory and give it the right permissions -mkdir -p ~/.ssh -chmod 700 ~/.ssh - -# Use ssh-keyscan to scan the keys of your private server. -ssh-keyscan gitlab.mpcdf.mpg.de >> ~/.ssh/known_hosts -chmod 644 ~/.ssh/known_hosts - -# Set the user name and email. -git config --global user.name $GITLAB_USER_NAME -git config --global user.email $GITLAB_USER_EMAIL - - -# Clone the private repository -git clone git@gitlab.mpcdf.mpg.de:nomad-lab/analytics-deployment.git /tmp/analytics-deployment - -cd /tmp/analytics-deployment - -# Update the tag of the docker image - -sed -i "s/^ tag\:.*/ tag\: production$CI_COMMIT_SHORT_SHA/g" deployments/hub/config.yaml -sed -i "s/^ tag\:.*/ tag\: production$CI_COMMIT_SHORT_SHA/g" deployments/hub/public.yaml - -# Finally, commit and push the changes -git add deployments/hub/config.yaml -git add deployments/hub/public.yaml -git commit -m "CI: Update the hub image for production ($CI_PIPELINE_URL)" -git push - -rm -rf /tmp/analytics-deployment diff --git a/.gitlab-ci/update_tag_staging.sh b/.gitlab-ci/update_tag_staging.sh deleted file mode 100755 index c6909bf3904a6a3a2a018407fb640ba7ba368fff..0000000000000000000000000000000000000000 --- a/.gitlab-ci/update_tag_staging.sh +++ /dev/null @@ -1,42 +0,0 @@ -# !/bin/bash - -# Based on: https://docs.gitlab.com/ee/ci/ssh_keys/README.html - -# Install ssh-agent if not already installed, it is required by Docker. -# (change apt-get to yum if you use an RPM-based image) -# - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' -# Run ssh-agent (inside the build environment) -eval $(ssh-agent -s) - -# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store -# We're using tr to fix line endings which makes ed25519 keys work -# without extra base64 encoding. -# https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 -echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - -# Create the SSH directory and give it the right permissions -mkdir -p ~/.ssh -chmod 700 ~/.ssh - -# Use ssh-keyscan to scan the keys of your private server. -ssh-keyscan gitlab.mpcdf.mpg.de >> ~/.ssh/known_hosts -chmod 644 ~/.ssh/known_hosts - -# Set the user name and email. -git config --global user.name $GITLAB_USER_NAME -git config --global user.email $GITLAB_USER_EMAIL - - -# Clone the private repository -git clone git@gitlab.mpcdf.mpg.de:nomad-lab/analytics-deployment.git /tmp/analytics-deployment -cd /tmp/analytics-deployment - -# Update the tag of the docker image -sed -i "s/^ tag\:.*/ tag\: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME$CI_COMMIT_SHORT_SHA/g" deployments/hub/staging.yaml - -# Finally, commit and push the changes -git add deployments/hub/staging.yaml -git commit -m "CI: Update the hub image for staging ($CI_PIPELINE_URL)" -git push - -rm -rf /tmp/analytics-deployment diff --git a/.gitmodules b/.gitmodules index ee4b871af3869b4b9cde45f95d9afac1f1ee6901..c117d8238f2714910bdecaac83b304b5f9ab16c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -85,9 +85,6 @@ [submodule "3rdparty/atomic-features-package"] path = 3rdparty/atomic-features-package url = https://gitlab.mpcdf.mpg.de/nomad-lab/atomic-features-package.git -[submodule "3rdparty/cpp_sisso"] - path = 3rdparty/cpp_sisso - url = https://gitlab.mpcdf.mpg.de/nomad-lab/cpp_sisso.git [submodule "tutorials/analytics-sgd-alloys-oxygen-reduction-evolution"] path = tutorials/analytics-sgd-alloys-oxygen-reduction-evolution url = https://gitlab.mpcdf.mpg.de/nomad-lab/analytics-sgd-alloys-oxygen-reduction-evolution.git @@ -118,3 +115,9 @@ [submodule "tutorials/analytics-kappa-screening-sisso"] path = tutorials/analytics-kappa-screening-sisso url = https://gitlab.mpcdf.mpg.de/nomad-lab/analytics-kappa-screening-sisso.git +[submodule "3rdparty/quip"] + path = 3rdparty/quip + url = git@github.com:libAtoms/QUIP.git +[submodule "3rdparty/sissopp"] + path = 3rdparty/sissopp + url = https://gitlab.com/sissopp_developers/sissopp.git diff --git a/3rdparty/cpp_sisso b/3rdparty/cpp_sisso deleted file mode 160000 index 7cf0dc12bae7481aaed1bb5660416dcb05bc6c44..0000000000000000000000000000000000000000 --- a/3rdparty/cpp_sisso +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7cf0dc12bae7481aaed1bb5660416dcb05bc6c44 diff --git a/3rdparty/gap/.gitignore b/3rdparty/gap/.gitignore deleted file mode 100644 index b25c15b81fae06e1c55946ac6270bfdb293870e8..0000000000000000000000000000000000000000 --- a/3rdparty/gap/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*~ diff --git a/3rdparty/gap/LICENSE.md b/3rdparty/gap/LICENSE.md deleted file mode 100644 index 39436d3a2fee59295942aa909a732f884b31f11f..0000000000000000000000000000000000000000 --- a/3rdparty/gap/LICENSE.md +++ /dev/null @@ -1,96 +0,0 @@ -### GAP suite — Non-commercial License Agreement - -*Last updated: 19th April 2017* - -The GAP suite of programs, comprising the GAP-filler and GAP packages -(together “the Software”), written by Dr Gabor Csanyi and Dr Albert -Bartok-Partay of the University of Cambridge (the “Authors”) for -describing the chemical environments of atoms and molecules and -predicting potential energy surfaces, is copyrighted by the University -of Cambridge and the Authors. Users at non-commercial institutions may -obtain a copy for Non-commercial Purposes. "Non-commercial Purposes" -means use of the Software and/or any **Software-derived data (including -interatomic potentials)** for academic research or other not-for-profit -or scholarly purposes which are undertaken at an educational, -non-profit, charitable or governmental institution that does not -involve the production or manufacture of products for sale or the -performance of services for a fee or other non-monetary value. - -For commercial use of either or both the Software or any -Software-derived data, please contact Cambridge Enterprise Limited at -. - -1. This is a legal agreement between you (“USER”), and the Authors. By - accepting, receiving, and using this Software, you are agreeing to - be bound by the terms of this Agreement. -2. The Authors hereby grant to the USER a non-exclusive, - non-transferable personal licence to use the Software. -3. The licence granted under this Agreement shall only entitle the USER - to use the Software for Non-commercial Purposes. The use of the - Software, or any code which is a modification of, enhancement to, - derived from or based upon the Software, or Software-derived data, - either directly or indirectly for any commercial purpose whatsoever - is expressly forbidden. The USER may not sublicense, distribute or - copy (except for archival purposes) the Software or enhancements - thereto. -4. If any academic paper or publication by the USER includes any set of - interatomic potentials or other Software-derived data then the USER - must timeously publish their full GAP model and source data (i.e. - the underlying quantum mechanical data which were used as input to - the GAP-filler). Any publication of any GAP models, interatomic - potentials or other Software-derived data must specifically state - that such data is provided for non-commercial use only. -5. If any academic paper or publication by the USER is based wholly or - partially, directly or indirectly on the Software or any results - derived from the Software then that paper or publication must cite: - - A. P. Bartok et al. *Physical Review Letters* **104** - 136403 (2010) - - A. P. Bartok et al. *Physical Review B* **87** 184115 (2013) - - That this Software is available for non-commercial use from - `www.libatoms.org` -6. The Software is the subject of copyright. Unauthorised copying of - the Software is expressly forbidden. The University of Cambridge and - the Authors retain all the rights in and title to the Software. -7. The Software is provided "AS IS" and except as expressly provided in - this Agreement no warranty, condition, undertaking or term, express - or implied, statutory or otherwise, as to the condition, - performance, satisfactory quality or fitness for purpose of the - Software is given or assumed by the Authors or the University of - Cambridge, and all such warranties, conditions, undertakings and - terms are hereby excluded. -8. The limitations and exclusions in this Agreement shall not apply in - respect of claims for personal injury or death caused by negligence - or in respect of fraud or fraudulent misrepresentation. -9. EXCEPT AS PROVIDED BY CLAUSE 8, neither the AUTHORS nor the - University OF CAMBRIDGE or its employees or students shall be liable - for any damages or expenses of whatsoever nature and howsoever - arising (including without limitation in contract, tort, negligence - or for breach of statutory duty or misrepresentation) in connection - with any right or licence granted or use of the Software or - otherwise in connection with this Licence or any relationships - established by it. Without prejudice to the generality of the - foregoing, in the event that the AUTHORS, the University OF - CAMBRIDGE, its employees or students should be found liable, then - their aggregate liability for direct damages shall be limited to - £100; and none of them shall be liable for any indirect, incidental, - consequential or special damages including without limitation loss - of profits, revenue, or business opportunity, loss of goodwill, data - loss, business disruption or computer failure. -10. The USER shall indemnify the Authors, the University of Cambridge, - its employees and students in full against each and every claim made - against any of them by any third party (including without limitation - in contract, tort, negligence or for breach of statutory duty or - misrepresentation) arising out of or in connection with the USER's - use of the Software. -11. The Authors accept no obligation to provide maintenance nor do they - guarantee the expected functionality of the Software or of any part - of the Software. -12. This Agreement is effective until terminated. This Agreement will - terminate automatically on notice from the Authors if the USER fails - to comply with any provision of this Agreement. Upon termination the - USER shall immediately destroy all copies of the Software. -13. This Agreement and any matters relating to it shall be governed and - construed in accordance with the laws of England and Wales and - Authors and the USER hereby irrevocably submit to the exclusive - jurisdiction of the English Courts. - diff --git a/3rdparty/gap/Makefile b/3rdparty/gap/Makefile deleted file mode 100644 index 9ad919445aa4d0d7ea0036713ec6c0e49839bca0..0000000000000000000000000000000000000000 --- a/3rdparty/gap/Makefile +++ /dev/null @@ -1,112 +0,0 @@ -# HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -# HND X -# HND X libAtoms+QUIP: atomistic simulation library -# HND X -# HND X Portions of this code were written by -# HND X Albert Bartok-Partay, Silvia Cereda, Gabor Csanyi, James Kermode, -# HND X Ivan Solt, Wojciech Szlachta, Csilla Varnai, Steven Winfield. -# HND X -# HND X Copyright 2006-2010. -# HND X -# HND X Not for distribution -# HND X -# HND X Portions of this code were written by Noam Bernstein as part of -# HND X his employment for the U.S. Government, and are not subject -# HND X to copyright in the USA. -# HND X -# HND X When using this software, please cite the following reference: -# HND X -# HND X http://www.libatoms.org -# HND X -# HND X Additional contributions by -# HND X Alessio Comisso, Chiara Gattinoni, and Gianpietro Moras -# HND X -# HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - - -ifeq (${QUIP_ARCH},) - include Makefile.arch -else - include Makefile.${QUIP_ARCH} -endif -include Makefile.inc -include Makefile.rules - -GAP1_F95_FILES = make_permutations_v2 descriptors gp_predict descriptors_wrapper clustering -GAP1_F95_SOURCES = ${addsuffix .f95, ${GAP1_F95_FILES}} -GAP1_F95_OBJS = ${addsuffix .o, ${GAP1_F95_FILES}} - -GAP2_F95_FILES = gp_fit gap_fit_module -GAP2_F95_SOURCES = ${addsuffix .f95, ${GAP2_F95_FILES}} -GAP2_F95_OBJS = ${addsuffix .o, ${GAP2_F95_FILES}} - -default: ${GAP_LIBFILE} - - - -ifeq (${USE_MAKEDEP},1) -GAP1_F95_FPP_FILES = ${addsuffix .fpp, ${GAP1_F95_FILES}} -GAP2_F95_FPP_FILES = ${addsuffix .fpp, ${GAP2_F95_FILES}} -GAP1.depend: ${GAP1_F95_FPP_FILES} - ${SCRIPT_PATH}/${MAKEDEP} ${MAKEDEP_ARGS} -- ${addprefix ../../src/GAP/,${GAP1_F95_SOURCES}} > GAP1.depend -GAP2.depend: ${GAP2_F95_FPP_FILES} ${GAP1_F95_FPP_FILES} - ${SCRIPT_PATH}/${MAKEDEP} ${MAKEDEP_ARGS} -- ${addprefix ../../src/GAP/,${GAP2_F95_SOURCES}} > GAP2.depend - --include GAP1.depend --include GAP2.depend -endif - - -PROGRAMS = gap_fit - -LIBS = -L. -lquiputils -lquip_core -lgap -latoms -ifeq (${HAVE_THIRDPARTY},1) - LIBS += -lthirdparty -endif -LIBFILES = libatoms.a ${GAP_LIBFILE} libquip_core.a libquiputils.a - -.PHONY : clean allclean depend doc install - -Programs: ${PROGRAMS} - cp ${QUIP_ROOT}/src/GAP/teach_sparse . - -${PROGRAMS}: % : ${LIBFILES} ${GAP2_F95_OBJS} ${GAPFIT_LIBFILE} %.o - $(LINKER) $(LINKFLAGS) -o $@ ${F90OPTS} $@.o ${GAPFIT_LIBFILE} ${LIBS} ${LINKOPTS} - - - -${GAP_LIBFILE}: ${GAP1_F95_OBJS} -ifneq (${LIBTOOL},) - ${LIBTOOL} -o ${GAP_LIBFILE} ${GAP1_F95_OBJS} -else - ${AR} ${AR_ADD} ${GAP_LIBFILE} $? -endif - -${GAPFIT_LIBFILE}: ${GAP2_F95_OBJS} -ifneq (${LIBTOOL},) - ${LIBTOOL} -o ${GAPFIT_LIBFILE} ${GAP2_F95_OBJS} -else - ${AR} ${AR_ADD} ${GAPFIT_LIBFILE} $? -endif - - - - -install: - @if [ ! -d ${QUIP_INSTALLDIR} ]; then \ - echo "make install: QUIP_INSTALLDIR '${QUIP_INSTALLDIR}' doesn't exist or isn't a directory"; \ - exit 1; \ - else \ - for f in ${PROGRAMS} ; do \ - echo "Copying $$f to ${QUIP_INSTALLDIR}/$${f}${QUIP_MPI_SUFFIX}" ; \ - cp $$f ${QUIP_INSTALLDIR}/$${f}${QUIP_MPI_SUFFIX} ; \ - done ;\ - cp ${QUIP_ROOT}/src/GAP/teach_sparse ${QUIP_INSTALLDIR}; \ - fi - - -clean: - rm -f *.o *.mod *.mod.save ${GAP_LIBFILE} ${GAPFIT_LIBFILE} ${PROGRAMS} GAP1.depend GAP2.depend - - diff --git a/3rdparty/gap/clustering.f95 b/3rdparty/gap/clustering.f95 deleted file mode 100644 index 026fe3ebebd452b5bb3fbd69464ab97010a6bf1e..0000000000000000000000000000000000000000 --- a/3rdparty/gap/clustering.f95 +++ /dev/null @@ -1,928 +0,0 @@ -! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -! HND X -! HND X libAtoms+QUIP: atomistic simulation library -! HND X -! HND X Portions of this code were written by -! HND X Albert Bartok-Partay, Silvia Cereda, Gabor Csanyi, James Kermode, -! HND X Ivan Solt, Wojciech Szlachta, Csilla Varnai, Steven Winfield. -! HND X -! HND X Copyright 2006-2010. -! HND X -! HND X Not for distribution -! HND X -! HND X Portions of this code were written by Noam Bernstein as part of -! HND X his employment for the U.S. Government, and are not subject -! HND X to copyright in the USA. -! HND X -! HND X When using this software, please cite the following reference: -! HND X -! HND X http://www.libatoms.org -! HND X -! HND X Additional contributions by -! HND X Alessio Comisso, Chiara Gattinoni, and Gianpietro Moras -! HND X -! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -#include "error.inc" - -module clustering_module - - ! use libatoms_module - use error_module - use system_module ! , only : dp, optional_default, ran_uniform, reallocate - use linearalgebra_module - - implicit none - private - - public :: pivot, bisect_kmedoids, cluster_kmeans, select_uniform, cluster_fuzzy_cmeans, cur_decomposition - - integer, parameter :: n_trial = 10 - integer, parameter :: n_trial_k_med = 100 - real(dp), parameter :: cluster_jitter = 1.0e-7_dp - real(dp), parameter :: KMEANS_THRESHOLD = 1.0e-6_dp - - type lst - integer, dimension(:), allocatable :: object - integer :: medoid - real(dp) :: sse - integer :: N - endtype lst - - type clstr - type(lst), dimension(:), allocatable :: cluster - real(dp), dimension(:,:), pointer :: dm - integer :: N - endtype clstr - - contains - - subroutine distance_matrix(x,dm,theta_fac,theta) - real(dp), dimension(:,:), intent(in) :: x - real(dp), dimension(:,:), intent(out) :: dm - real(dp), intent(in), optional :: theta_fac - real(dp), dimension(:), intent(in), target, optional :: theta - - real(dp), dimension(:), pointer :: my_theta => null() - real(dp) :: my_theta_fac - integer :: i, j, d, n - - my_theta_fac = optional_default(1.0_dp, theta_fac) - d = size(x,1) - n = size(x,2) - - if( present(theta) ) then - if( size(theta) == d) then - my_theta => theta - else - allocate(my_theta(d)) - my_theta = theta(1) - endif - else - allocate(my_theta(d)) - - do i = 1, d - my_theta(i) = ( maxval(x(i,:)) - minval(x(i,:)) ) - ! theta(i) = sqrt( & !take square root - ! & sum( x(i,:)**2 ) / size(x(i,:)) - & - ! & (sum( x(i,:) ) / size(x(i,:)))**2 ) - if( my_theta(i) .feq. 0.0_dp ) my_theta(i) = 1.0_dp - enddo - my_theta = my_theta * my_theta_fac - endif - - do i = 1, n - do j = i + 1, n - dm(j,i) = cluster_jitter*ran_uniform() - enddo - dm(i,i) = 0.0_dp - enddo - -!$omp parallel do default(none) shared(dm,n,x,my_theta) private(i,j) schedule(dynamic) - do i = 1, n - do j = i + 1, n - dm(j,i) = dm(j,i) + sqrt( sum( ( (x(:,j) - x(:,i)) / my_theta )**2 ) ) - dm(i,j) = dm(j,i) - enddo - enddo -!$omp end parallel do - - do i = 1, n - do j = i + 1, n - dm(i,j) = dm(j,i) - enddo - enddo - - if( present(theta) ) then - my_theta => null() - else - deallocate(my_theta) - endif - - endsubroutine distance_matrix - - subroutine pca(x,x_mean,v) - - real(dp), dimension(:,:), intent(in) :: x - real(dp), dimension(:), intent(out) :: x_mean - real(dp), dimension(:,:), intent(out) :: v - - real(dp), dimension(:), allocatable :: diag_c - real(dp), dimension(:,:), allocatable :: cov - integer :: i, j, d, n - - d = size(x,1) - n = size(x,2) - allocate(cov(d,d),diag_c(d)) - - x_mean = sum(x,dim=2) / n ! empirical mean - - do i = 1, d - do j = 1, d - cov(j,i) = dot_product(x(i,:),x(j,:)) / n - x_mean(i)*x_mean(j) - enddo - enddo - - call diagonalise(cov,diag_c, evects=v) - - deallocate(cov, diag_c) - - endsubroutine pca - - subroutine pivot(x,pivout,theta_fac,theta) - real(dp), dimension(:,:), intent(in) :: x - integer, dimension(:), intent(out) :: pivout - real(dp), intent(in), optional :: theta_fac - real(dp), dimension(:), intent(in), optional :: theta - - real(dp), dimension(:,:), allocatable :: knn - real(dp), dimension(:), allocatable :: ktmp - integer, dimension(:), allocatable :: pivin - - integer :: stat, i, j, k, d, m, n, jtmp, jmax - real(dp) :: dmax - - d = size(x,1) - n = size(x,2) - - m = size(pivout) - - if( m > n ) call system_abort('pivot: required number of changes ('//m//') greater than possible number of changes ('//n//')') - - allocate(knn(n,n),stat=stat) - if(stat /=0 ) call system_abort('pivot: could not allocate knn matrix.') - - allocate(pivin(n),ktmp(n)) - - call distance_matrix(x,knn,theta_fac=theta_fac,theta=theta) - do i = 1, n - do j = 1, n - knn(j,i) = exp(-0.5_dp*knn(j,i)) - enddo - enddo - - pivin = (/ (i, i=1,n) /) - - do k = 1, m - dmax = 0.0_dp - do j = k, n - if( dmax < knn(j,j) ) then - jmax = j - dmax = knn(j,j) - endif - enddo - if( jmax /= k ) then - jtmp = pivin(jmax) - pivin(jmax) = pivin(k) - pivin(k) = jtmp - - ktmp = knn(k,:) - knn(k,:) = knn(jmax,:) - knn(jmax,:) = ktmp - - ktmp = knn(:,k) - knn(:,k) = knn(:,jmax) - knn(:,jmax) = ktmp - endif - - knn(k,k) = sqrt(knn(k,k)) - - knn(k+1:n,k) = knn(k+1:n,k)/knn(k,k) - do j = k+1, n - knn(j:n,j) = knn(j:n,j) - knn(j:n,k)*knn(j,k) - enddo - - do j = 1, n - do i = j+1,n - knn(j,i) = knn(i,j) - enddo - enddo - enddo - - pivout = pivin(1:m) - - deallocate(knn,pivin,ktmp) - - endsubroutine pivot - - subroutine bisect_kmedoids(dat,n_clusters_in, c,med, theta_fac,theta, is_distance_matrix) - real(dp), dimension(:,:), intent(in), target :: dat - integer, intent(in) :: n_clusters_in - integer, dimension(:), intent(out),optional :: c, med - real(dp), intent(in), optional :: theta_fac - real(dp), dimension(:), intent(in), optional :: theta - logical, intent(in), optional :: is_distance_matrix - - type(clstr) :: my_cluster, tmp - - logical :: must_calculate_distance - real(dp), dimension(:,:), allocatable, target :: dm - - real(dp), dimension(:), allocatable :: dv - real(dp) :: max_sse, min_sse, sse - - integer, dimension(:), allocatable :: sub_cluster1, sub_cluster2, sub_cluster1_min, sub_cluster2_min - integer, dimension(1) :: ml - integer :: stat, i, j, k, km, m, n, nc, & - lo_med, hi_med, lo_med_new, hi_med_new, lo_med_min, hi_med_min, n1, n2, n1_min, n2_min, iter - - must_calculate_distance = .not. optional_default(.true., is_distance_matrix) - - n = size(dat,2) - if (.not. must_calculate_distance) then - if (size(dat,1) /= n) call system_abort('is_distance_matrix but not square') - endif - - if( n_clusters_in > n ) call system_abort('bisect_kmedoids: required number of cluster greater than total number of data points') - - if(present(c) ) c = 0 - - if (must_calculate_distance) then - allocate(dm(n,n), stat=stat) - if(stat /=0 ) call system_abort('bisect_kmedoids: could not allocate dm matrix.') - - call print('Started distance matrix calculation', verbosity=PRINT_NERD) - call distance_matrix(dat, dm, theta_fac=theta_fac,theta=theta) - call print('Finished distance matrix calculation', verbosity=PRINT_NERD) - my_cluster%dm => dm - else - my_cluster%dm => dat - endif - - ! start clustering - my_cluster%N = 1 ! start with one big cluster - allocate( my_cluster%cluster(1) ) - my_cluster%cluster(1)%N = n ! put every object in the initial cluster - allocate( my_cluster%cluster(1)%object(n) ) - my_cluster%cluster(1)%object = (/(i,i=1,n)/) - - allocate(dv(n)) ! distance vector, the sum of square of distances of points from central object - dv = sum(my_cluster%dm,dim=1) - my_cluster%cluster(1)%sse = minval( dv ) ! determine initial medoid, the object that is the - ml = minloc( dv ) ! closest to any other object in cluster - my_cluster%cluster(1)%medoid = ml(1) - deallocate(dv) - - ! main loop starts here, bisects initial clusters until desired number of - ! clusters are found - - iter = 0 - do - iter = iter + 1 - call print("Starting iteration "//iter,verbosity=PRINT_NERD) - - if( my_cluster%N == n_clusters_in ) exit - max_sse = -1.0_dp ! select cluster with greatest sse - do j = 1, my_cluster%N - if( max_sse < my_cluster%cluster(j)%sse ) then - i = j - max_sse = my_cluster%cluster(j)%sse - endif - enddo - nc = my_cluster%cluster(i)%N - if( nc==1 ) cycle - allocate( sub_cluster1(nc), sub_cluster2(nc), sub_cluster1_min(nc),sub_cluster2_min(nc) ) - - min_sse = huge(1.0_dp) - do j = 1, n_trial - m = ceiling( ran_uniform()*(nc-1) ) ! choose a bisecting point randomly - ml = minloc( sum( my_cluster%dm( my_cluster%cluster(i)%object(:m), my_cluster%cluster(i)%object(:m) ), dim=1) ) - lo_med_new = my_cluster%cluster(i)%object(ml(1)) - - ml = minloc( sum( my_cluster%dm( my_cluster%cluster(i)%object(m+1:), my_cluster%cluster(i)%object(m+1:) ), dim=1) ) - - hi_med_new = my_cluster%cluster(i)%object(ml(1) + m) - - ! the median of the 2 subclusters determined - lo_med = 0 - hi_med = 0 - - ! perform k-medoid clustering on the two subclusters - do km = 1, n_trial_k_med - if( (lo_med_new == lo_med) .and. (hi_med_new == hi_med) ) exit - lo_med = lo_med_new - hi_med = hi_med_new - n1 = 0 - n2 = 0 - !n1 = 1 - !n2 = 1 - !sub_cluster1(n1) = lo_med - !sub_cluster1(n2) = hi_med - - do k = 1, my_cluster%cluster(i)%N - if( my_cluster%dm(lo_med,my_cluster%cluster(i)%object(k)) < & - & my_cluster%dm(hi_med,my_cluster%cluster(i)%object(k)) ) then - n1 = n1 + 1 - sub_cluster1(n1) = my_cluster%cluster(i)%object(k) - else - n2 = n2 + 1 - sub_cluster2(n2) = my_cluster%cluster(i)%object(k) - endif - enddo - - ml = minloc( sum( my_cluster%dm( sub_cluster1(:n1), sub_cluster1(:n1) ), dim=1) ) - lo_med_new = sub_cluster1(ml(1)) - ml = minloc( sum( my_cluster%dm( sub_cluster2(:n2), sub_cluster2(:n2) ), dim=1) ) - hi_med_new = sub_cluster2(ml(1)) - enddo - sse = sum( my_cluster%dm(lo_med_new,sub_cluster1(:n1)) ) + sum( my_cluster%dm(hi_med_new,sub_cluster2(:n2)) ) - - ! choose the clustering that resulted the smallest sse - if( sse < min_sse ) then - min_sse = sse - sub_cluster1_min = sub_cluster1 - sub_cluster2_min = sub_cluster2 - n1_min = n1 - n2_min = n2 - lo_med_min = lo_med_new - hi_med_min = hi_med_new - endif - enddo - - ! now update the the clusters with the two new subclusters - tmp = my_cluster - - do j = 1, my_cluster%N - deallocate( my_cluster%cluster(j)%object ) - enddo - deallocate( my_cluster%cluster ) - my_cluster%N = my_cluster%N + 1 - allocate( my_cluster%cluster( my_cluster%N ) ) - - do j = 1, my_cluster%N - 1 - if( i == j ) then - allocate( my_cluster%cluster(j)%object(n1_min) ) - my_cluster%cluster(j)%N = n1_min - my_cluster%cluster(j)%object = sub_cluster1_min(:n1_min) - my_cluster%cluster(j)%sse = sum( my_cluster%dm(lo_med_min,sub_cluster1_min(:n1_min)) ) - my_cluster%cluster(j)%medoid = lo_med_min - else - my_cluster%cluster(j) = tmp%cluster(j) - endif - enddo - allocate( my_cluster%cluster(my_cluster%N)%object(n2_min) ) - my_cluster%cluster(my_cluster%N)%N = n2_min - my_cluster%cluster(my_cluster%N)%object = sub_cluster2_min(:n2_min) - my_cluster%cluster(my_cluster%N)%sse = sum( my_cluster%dm(hi_med_min,sub_cluster2_min(:n2_min)) ) - my_cluster%cluster(my_cluster%N)%medoid = hi_med_min - - do j = 1, tmp%N - deallocate( tmp%cluster(j)%object ) - enddo - deallocate( tmp%cluster, sub_cluster1, sub_cluster2, sub_cluster1_min, sub_cluster2_min ) - - call kmedoid(my_cluster) - enddo - - if( present(c) ) then - do j = 1, my_cluster%N - do k = 1, my_cluster%cluster(j)%N - i = my_cluster%cluster(j)%object(k) - c(i) = j - enddo - enddo - endif - - if( present(med) ) then - do j = 1, my_cluster%N - med(j) = my_cluster%cluster(j)%medoid - enddo - endif - - do j = 1, my_cluster%N - deallocate( my_cluster%cluster(j)%object ) - enddo - deallocate(my_cluster%cluster) - if (allocated(dm)) deallocate(dm) - - endsubroutine bisect_kmedoids - - subroutine kmedoid(this) - type(clstr), intent(inout) :: this - - type(clstr) :: tmp - integer, dimension(:), allocatable :: medoids - integer, dimension(1) :: ml - integer :: n, j, k - logical :: refined - - ! k-medoid-refinement - n = size(this%dm,1) - ! n: total number of objects - - tmp%N = this%N - allocate( tmp%cluster(tmp%N), medoids(tmp%N) ) - do j = 1, tmp%N - allocate( tmp%cluster(j)%object(n) ) - medoids(j) = this%cluster(j)%medoid - enddo - - ! main loop starts here, perfom k-medoid clustering until medoids don't - ! change anymore - do - do j = 1, tmp%N - tmp%cluster(j)%N = 0 - enddo - do j = 1, n - ml = minloc( this%dm(j,medoids) ) ! determine to which medoid each object belongs - k = ml(1) - tmp%cluster(k)%N = tmp%cluster(k)%N + 1 - tmp%cluster(k)%object(tmp%cluster(k)%N) = j - enddo - - ! re-determine the medoid in each cluster - do j = 1, tmp%N - ml = minloc( sum( this%dm( tmp%cluster(j)%object(:tmp%cluster(j)%N), & - & tmp%cluster(j)%object(:tmp%cluster(j)%N) ), dim=1) ) - tmp%cluster(j)%medoid = tmp%cluster(j)%object(ml(1)) - enddo - - refined = .true. - - ! check whether medoids have changed - do j = 1, tmp%N - refined = refined .and. (tmp%cluster(j)%medoid == medoids(j)) - medoids(j) = tmp%cluster(j)%medoid - enddo - if(refined) exit - enddo - - ! write results - do j = 1, tmp%N - deallocate( this%cluster(j)%object ) - allocate( this%cluster(j)%object( tmp%cluster(j)%N ) ) - this%cluster(j)%object = tmp%cluster(j)%object(:tmp%cluster(j)%N) - this%cluster(j)%N = tmp%cluster(j)%N - this%cluster(j)%medoid = tmp%cluster(j)%medoid - this%cluster(j)%sse = sum( this%dm(this%cluster(j)%medoid,& - & this%cluster(j)%object ) ) - - deallocate( tmp%cluster(j)%object ) - enddo - deallocate( tmp%cluster, medoids ) - - endsubroutine kmedoid - - subroutine cluster_kmeans(x,cluster_index,theta_fac,theta) - real(dp), dimension(:,:), intent(in) :: x - integer, dimension(:), intent(out) :: cluster_index - real(dp), intent(in), optional :: theta_fac - real(dp), dimension(:), intent(in), target, optional :: theta - - real(dp), dimension(:), pointer :: my_theta => null() - real(dp) :: my_theta_fac, d_min, d_ij, d_total, d_total_prev - - real(dp), dimension(:,:), allocatable :: cluster_centre - integer, dimension(:), allocatable :: cluster_info - integer :: d, n, m, i, j, k, cluster_info_old, iter, n_points_cluster_j - logical :: cluster_same - - d = size(x,1) - n = size(x,2) - m = size(cluster_index) - if( m > n ) call system_abort('cluster_kmeans: required number of clusters ('//m//') greater than total number of points ('//n//')') - - my_theta_fac = optional_default(1.0_dp, theta_fac) - if( present(theta) ) then - if( size(theta) == d) then - my_theta => theta - else - allocate(my_theta(d)) - my_theta = theta(1) - endif - else - allocate(my_theta(d)) - do i = 1, d - my_theta(i) = ( maxval(x(i,:)) - minval(x(i,:)) ) - if( my_theta(i) .feq. 0.0_dp ) my_theta(i) = 1.0_dp - enddo - my_theta = my_theta * my_theta_fac - endif - - allocate(cluster_centre(d,m),cluster_info(n)) - - call fill_random_integer(cluster_index, n) !choose random points as cluster centres. - - cluster_centre = x(:,cluster_index) - cluster_info = 0 - - iter = 0 - d_total = huge(1.0_dp) - do - iter = iter + 1 - call print("iteration: "//iter,verbosity=PRINT_NERD) - cluster_same = .true. - - d_total_prev = d_total - d_total = 0.0_dp -!$omp parallel do default(none) shared(n,m,x,cluster_info,cluster_centre,my_theta) & -!$omp reduction(.and.:cluster_same) & -!$omp private(i,j,d_min,d_ij,cluster_info_old) reduction(+:d_total) - do i = 1, n - d_min = huge(0.0_dp) - cluster_info_old = cluster_info(i) - do j = 1, m - d_ij = sum(( (cluster_centre(:,j) - x(:,i))/my_theta )**2) - if( d_ij < d_min ) then - d_min = d_ij - cluster_info(i) = j - endif - enddo - if( cluster_info_old /= cluster_info(i) ) cluster_same = cluster_same .and. .false. - d_total = d_total + d_min - enddo -!$omp end parallel do - call print("cluster_kmeans iteration="//iter//" d_total="//d_total) - -!$omp parallel do default(none) shared(x,cluster_centre,cluster_info,m,d,n) private(j,k,n_points_cluster_j) - do j = 1, m - n_points_cluster_j = count(cluster_info==j) - if( n_points_cluster_j == 0 ) then - cluster_centre(:,j) = x(:,ceiling(ran_uniform()*n)) - else - do k = 1, d - cluster_centre(k,j) = sum(x(k,:),mask=(cluster_info==j)) / n_points_cluster_j - enddo - endif - enddo -!$omp end parallel do - if( cluster_same ) exit - if( abs(d_total - d_total_prev) < KMEANS_THRESHOLD * d_total ) exit - enddo - - do j = 1, m - d_min = huge(0.0_dp) - do i = 1, n - d_ij = sum(( (cluster_centre(:,j) - x(:,i))/my_theta )**2) - if( d_ij < d_min ) then - d_min = d_ij - cluster_index(j) = i - endif - enddo - enddo - - deallocate(cluster_centre, cluster_info) - - if(present(theta)) then - my_theta => null() - else - deallocate(my_theta) - endif - - endsubroutine cluster_kmeans - - ! https://sites.google.com/site/dataclusteringalgorithms/fuzzy-c-means-clustering-algorithm - subroutine cluster_fuzzy_cmeans(x,cluster_index,theta_fac,theta,fuzziness) - real(dp), dimension(:,:), intent(in) :: x - integer, dimension(:), intent(out) :: cluster_index - real(dp), intent(in), optional :: theta_fac - real(dp), dimension(:), intent(in), target, optional :: theta - real(dp), intent(in), optional :: fuzziness - - real(dp), dimension(:), pointer :: my_theta => null() - real(dp) :: my_theta_fac, d_min, d_ij, d_total, d_total_prev - - real(dp), dimension(:,:), allocatable :: cluster_centre - real(dp), dimension(:,:), allocatable :: w - real(dp), dimension(:), allocatable, save :: wx_j, d_i - real(dp) :: w_j, w_old, my_fuzziness, alpha - integer :: d, n, m, i, j, iter - logical :: cluster_same -!$omp threadprivate(d_i, wx_j) - - d = size(x,1) - n = size(x,2) - m = size(cluster_index) - if( m > n ) call system_abort('cluster_fuzzy_cmeans: required number of clusters ('//m//') greater than total number of points ('//n//')') - - my_theta_fac = optional_default(1.0_dp, theta_fac) - my_fuzziness = optional_default(4.0_dp, fuzziness) - if( present(theta) ) then - if( size(theta) == d) then - my_theta => theta - else - allocate(my_theta(d)) - my_theta = theta(1) - endif - else - allocate(my_theta(d)) - do i = 1, d - my_theta(i) = ( maxval(x(i,:)) - minval(x(i,:)) ) - if( my_theta(i) .feq. 0.0_dp ) my_theta(i) = 1.0_dp - enddo - my_theta = my_theta * my_theta_fac - endif - - allocate(cluster_centre(d,m), w(n,m)) -!$omp parallel - allocate(d_i(m), wx_j(d)) -!$omp end parallel - - call fill_random_integer(cluster_index, n) !choose random points as cluster centres. - - cluster_centre = x(:,cluster_index) - do i = 1, m - do j = 1, d - cluster_centre(j,i) = cluster_centre(j,i) + ( ran_uniform() - 0.5_dp ) * cluster_jitter - enddo - enddo - - w = 0.0_dp - - iter = 0 - d_total = huge(1.0_dp) - do - iter = iter + 1 - call print("iteration: "//iter,verbosity=PRINT_NERD) - cluster_same = .true. - - d_total_prev = d_total - d_total = 0.0_dp - ! Calculate fuzzy membership -!$omp parallel do default(none) shared(n,m,my_theta,my_fuzziness,w,x,cluster_centre) & -!$omp private(i,j,alpha,w_old) reduction(.and.:cluster_same) reduction(+:d_total) - do i = 1, n - alpha = 0.0_dp - do j = 1, m - d_i(j) = sqrt(sum(( (cluster_centre(:,j) - x(:,i))/my_theta )**2)) - alpha = alpha + 1.0_dp / d_i(j)**(2.0_dp / (my_fuzziness - 1.0_dp)) - enddo - - do j = 1, m - w_old = w(i,j) - w(i,j) = 0.0_dp - - w(i,j) = 1.0_dp / d_i(j)**(2.0_dp / (my_fuzziness - 1.0_dp)) / alpha - if( w_old .fne. w(i,j) ) cluster_same = cluster_same .and. .false. - - d_total = d_total + d_i(j)**2 * w(i,j)**my_fuzziness - enddo - enddo -!$omp end parallel do - call print("cluster_fuzzy_cmeans iteration="//iter//" d_total="//d_total) - - ! Calculate fuzzy centres -!$omp parallel do default(none) shared(m,n,w,x,my_fuzziness,cluster_centre) & -!$omp private(i,j,w_j) - do j = 1, m - w_j = 0.0_dp - wx_j = 0.0_dp - - do i = 1, n - w_j = w_j + w(i,j)**my_fuzziness - wx_j = wx_j + x(:,i) * w(i,j)**my_fuzziness - enddo - - cluster_centre(:,j) = wx_j / w_j - enddo -!$omp end parallel do - - call print("cluster_same: "//cluster_same,verbosity=PRINT_NERD) - call print("d_total: "//d_total,verbosity=PRINT_NERD) - call print("d_total_prev: "//d_total_prev,verbosity=PRINT_NERD) - call print("d_total-d_total_prev: "//(d_total-d_total_prev),verbosity=PRINT_NERD) - - if( cluster_same ) exit - if( abs(d_total - d_total_prev) < KMEANS_THRESHOLD * d_total ) exit - enddo - - ! Allocate cluster centres to nearest points - do j = 1, m - d_min = huge(0.0_dp) - do i = 1, n - d_ij = sum(( (cluster_centre(:,j) - x(:,i))/my_theta )**2) - if( d_ij < d_min ) then - d_min = d_ij - cluster_index(j) = i - endif - enddo - enddo - - deallocate(cluster_centre, w) -!$omp parallel - if(allocated(d_i)) deallocate(d_i) - if(allocated(wx_j)) deallocate(wx_j) -!$omp end parallel - - if(present(theta)) then - my_theta => null() - else - deallocate(my_theta) - endif - - endsubroutine cluster_fuzzy_cmeans - - subroutine select_uniform(x,index_out) - real(dp), dimension(:,:), intent(in) :: x - integer, dimension(:), intent(out) :: index_out - - integer :: i, j, d, n, m, n_grid, i_global, i_index_out - integer, dimension(:), allocatable :: p_grid, i_hist, histogram, x_histogram, index_out_histogram - real(dp), dimension(:), allocatable :: lower_bound, upper_bound, x_range - - d = size(x,1) - n = size(x,2) - m = size(index_out) - - if( n < m ) call system_abort('select_uniform: n = '//n//' < m = '//m) - - allocate(lower_bound(d), upper_bound(d), x_range(d), p_grid(d), i_hist(d)) - - lower_bound = minval(x,dim=2) - upper_bound = maxval(x,dim=2) - x_range = upper_bound - lower_bound - - n_grid = ceiling( real(m,kind=dp)**(1.0_dp/real(d,kind=dp)) ) - p_grid = (/ ( n_grid**(i-1), i = 1, d ) /) - - allocate(histogram(n_grid**d)) - allocate(x_histogram(n),index_out_histogram(m)) - - histogram = 0 - - do i = 1, n - ! for each datapoint x(:,i) compute the bin index in each d direction - i_hist = nint( ( x(:,i) - lower_bound ) / x_range * (n_grid-1) ) + 1 - - ! map the bin index to a flat histogram bin index - i_global = sum((i_hist-1)*p_grid)+1 - histogram(i_global) = histogram(i_global) + 1 - - ! the i-th datapoint belongs to the i_global-th index in the histogram - x_histogram(i) = i_global - enddo - - index_out = 0 - i_index_out = 0 - - ! To monitor which bins the sparse points belong to. - index_out_histogram = 0 - - do i = 1, n - ! That's the exit condition if all sparse points are assigned before we - ! finish with the data points - if( all(index_out /= 0) ) exit - - if( all(x_histogram(i) /= index_out_histogram) ) then - ! We have just found a point which belongs to a bin that we haven't - ! selected yet in the sparse points - i_index_out = i_index_out + 1 - index_out(i_index_out) = i - index_out_histogram(i_index_out) = x_histogram(i) - endif - enddo - - do while ( any(index_out == 0) ) - ! We haven't yet assigned all sparse points. - - ! Select a bin randomly - i_global = ceiling( ran_uniform() * size(histogram) ) - - ! cycle if the bin is empty - if( histogram(i_global) == 0 ) cycle - - ! check if there are points belonging to this bin which we haven't - ! selected yet - if( count(x_histogram == i_global) == count(index_out_histogram == i_global) ) cycle - - do while (.true.) - ! select a point from x which belongs to that bin and add it to - ! the output. - i = ceiling( ran_uniform() * n ) - if( x_histogram(i) /= i_global .or. any(index_out == i) ) then - cycle - else - i_index_out = i_index_out + 1 - index_out(i_index_out) = i - index_out_histogram(i_index_out) = x_histogram(i) - exit - endif - enddo - enddo - - deallocate(lower_bound, upper_bound, x_range, p_grid, i_hist, histogram, x_histogram,index_out_histogram) - - if (.not. all(index_out /= 0)) call system_abort('select_uniform: could not assign all sparse points') - - endsubroutine select_uniform - - subroutine cur_decomposition(this, index_out, rank, n_iter) - ! based on 10.1073/pnas.0803205106 - - real(dp), intent(in), dimension(:,:) :: this - integer, dimension(:), intent(out) :: index_out - integer, intent(in), optional :: rank, n_iter - - integer :: n - integer :: expected_columns - integer :: my_n_iter, my_rank - type(LA_Matrix) :: LA_this - real(dp), allocatable, dimension(:) :: p, s, p_minus_ran_uniform - real(dp), allocatable, dimension(:,:) :: v - integer :: j, l - integer, allocatable, dimension(:), target :: p_index - integer, pointer, dimension(:) :: tmp_index_out => null() - real(dp), allocatable, dimension(:,:) :: C, Cp - real(dp) :: err, min_err - integer :: error - - expected_columns = size(index_out) - - if( expected_columns <= 0 ) then - call print_warning("cur_decomposition: called with expected_columns "//expected_columns//", can't be zero or less") - return - endif - - call initialise(LA_this,this) - - my_n_iter = optional_default(1, n_iter) - - if (present(rank)) then - call LA_Matrix_SVD_Allocate(LA_this,v=v,error=error) - HANDLE_ERROR(error) - call LA_Matrix_SVD(LA_this,v=v,error=error) - HANDLE_ERROR(error) - my_rank = rank - else - call LA_Matrix_SVD_Allocate(LA_this,s=s,v=v,error=error) - HANDLE_ERROR(error) - call LA_Matrix_SVD(LA_this,s=s,v=v,error=error) - HANDLE_ERROR(error) - my_rank = count(s > TOL_SVD) / 2 - endif - - n = size(v,1) - allocate(p(n), p_minus_ran_uniform(n), p_index(n)) - allocate( C(size(this,1),expected_columns), Cp(expected_columns,size(this,1)) ) - - p = sum(v(:,1:my_rank)**2, dim=2) - p = p * expected_columns - p = p / my_rank - p = min(p,1.0_dp) - - if(my_n_iter <= 0) then ! do not do probabilistic selection of columns - p_index = (/(j, j=1,n )/) - p_minus_ran_uniform = -p - call heap_sort(p_minus_ran_uniform,i_data=p_index) - index_out = p_index(1:expected_columns) - else - min_err = huge(1.0_dp) - do l = 1, my_n_iter - - ! randomly select columns according to the probabilities - do j = 1, n - p_minus_ran_uniform(j) = ran_uniform() - p(j) - p_index(j) = j ! initialise index array - end do - - call heap_sort(p_minus_ran_uniform,i_data=p_index) - tmp_index_out => p_index(1:expected_columns) - - C = this(:,tmp_index_out) - ! pinv: Moore-Penrose pseudo-inverse - call pseudo_inverse(C,Cp) - err = sum( (this - ( C .mult. Cp .mult. this))**2 ) - - call print("cur_decomposition: iteration: "//l//", error: "//err) - if(err < min_err) then ! this happens at least once - index_out = tmp_index_out - min_err = err - endif - - end do - endif - - call finalise(LA_this) - - tmp_index_out => null() - if(allocated(s)) deallocate(s) - if(allocated(v)) deallocate(v) - if(allocated(p)) deallocate(p) - if(allocated(p_minus_ran_uniform)) deallocate(p_minus_ran_uniform) - if(allocated(p_index)) deallocate(p_index) - if(allocated(C)) deallocate(C) - if(allocated(Cp)) deallocate(Cp) - - end subroutine cur_decomposition - -endmodule clustering_module diff --git a/3rdparty/gap/descriptors.f95 b/3rdparty/gap/descriptors.f95 deleted file mode 100644 index 927a0b7f555895459b01e1bec7c883d82e5be513..0000000000000000000000000000000000000000 --- a/3rdparty/gap/descriptors.f95 +++ /dev/null @@ -1,15880 +0,0 @@ -! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -! HND X -! HND X libAtoms+QUIP: atomistic simulation library -! HND X -! HND X Portions of this code were written by -! HND X Albert Bartok-Partay, Silvia Cereda, Gabor Csanyi, James Kermode, -! HND X Ivan Solt, Wojciech Szlachta, Csilla Varnai, Steven Winfield. -! HND X -! HND X Copyright 2006-2010. -! HND X -! HND X Not for distribution -! HND X -! HND X Portions of this code were written by Noam Bernstein as part of -! HND X his employment for the U.S. Government, and are not subject -! HND X to copyright in the USA. -! HND X -! HND X When using this software, please cite the following reference: -! HND X -! HND X http://www.libatoms.org -! HND X -! HND X Additional contributions by -! HND X Alessio Comisso, Chiara Gattinoni, and Gianpietro Moras -! HND X -! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -#include "error.inc" - -module descriptors_module - - use error_module - use system_module, only : dp, print, optional_default, system_timer, operator(//), split_string, string_to_int, split_string_simple, inoutput, OUTPUT, PRINT_VERBOSE, PRINT_NERD - use linkedlist_module - use units_module - use periodictable_module - use linearalgebra_module - use dictionary_module - use paramreader_module - use atoms_module - use atoms_types_module - use topology_module - use mpi_context_module - use table_module - use permutation_maker_module - use CInOutput_module - use clusters_module - use connection_module - use angular_functions_module - - implicit none - - private -#ifdef GAP_VERSION - integer, parameter :: gap_version = GAP_VERSION -#else - integer, parameter :: gap_version = 0 -#endif - - - integer, parameter, public :: DT_NONE = 0 - integer, parameter, public :: DT_BISPECTRUM_SO4 = 1 - integer, parameter, public :: DT_BISPECTRUM_SO3 = 2 - integer, parameter, public :: DT_BEHLER = 3 - integer, parameter, public :: DT_DISTANCE_2B = 4 - integer, parameter, public :: DT_COORDINATION = 5 - integer, parameter, public :: DT_ANGLE_3B = 6 - integer, parameter, public :: DT_CO_ANGLE_3B = 7 - integer, parameter, public :: DT_CO_DISTANCE_2B = 8 - integer, parameter, public :: DT_COSNX = 9 - integer, parameter, public :: DT_TRIHIS = 10 - integer, parameter, public :: DT_WATER_MONOMER = 11 - integer, parameter, public :: DT_WATER_DIMER = 12 - integer, parameter, public :: DT_A2_DIMER = 13 - integer, parameter, public :: DT_AB_DIMER = 14 - integer, parameter, public :: DT_BOND_REAL_SPACE = 15 - integer, parameter, public :: DT_ATOM_REAL_SPACE = 16 - integer, parameter, public :: DT_POWER_SO3 = 17 - integer, parameter, public :: DT_POWER_SO4 = 18 - integer, parameter, public :: DT_SOAP = 19 - integer, parameter, public :: DT_AN_MONOMER = 20 - integer, parameter, public :: DT_GENERAL_MONOMER = 21 - integer, parameter, public :: DT_GENERAL_DIMER = 22 - integer, parameter, public :: DT_GENERAL_TRIMER = 23 - integer, parameter, public :: DT_RDF = 24 - integer, parameter, public :: DT_AS_DISTANCE_2B = 25 - integer, parameter, public :: DT_MOLECULE_LO_D = 26 - integer, parameter, public :: DT_alex = 27 - integer, parameter, public :: DT_COM_DIMER = 28 - integer, parameter, public :: DT_DISTANCE_NB = 29 - integer, parameter, public :: DT_SOAP_EXPRESS = 30 - - integer, parameter :: NP_WATER_DIMER = 8 - integer, parameter :: NP_A2_DIMER = 8 - integer, parameter :: NP_AB_DIMER = 2 - - type transfer_parameters_type - logical :: do_transfer - real(dp) :: factor, r0, width - endtype transfer_parameters_type - - type descriptor_data_mono - real(dp), dimension(:), allocatable :: data - real(dp), dimension(:,:,:), allocatable :: grad_data - ! ci : atom indices amongst which to distribute energy of descriptor - ! ii : all atoms involved in descriptor (for partial derivatives) - integer, dimension(:), allocatable :: ci, ii - real(dp), dimension(:,:), allocatable :: pos - logical :: has_data - logical, dimension(:), allocatable :: has_grad_data - - real(dp) :: covariance_cutoff = 1.0_dp - real(dp), dimension(:,:), allocatable :: grad_covariance_cutoff - endtype descriptor_data_mono - - type cplx_2d - complex(dp), dimension(:,:), allocatable :: mm - endtype cplx_2d - - type real_2d - real(dp), dimension(:,:), allocatable :: mm - endtype real_2d - - type cplx_3d - complex(dp), dimension(:,:,:), allocatable :: mm - endtype cplx_3d - - type RadialFunction_type - integer :: n_max - real(dp) :: cutoff, min_cutoff - real(dp), dimension(:,:), allocatable :: RadialTransform - real(dp), dimension(:), allocatable :: NormFunction - - logical :: initialised = .false. - endtype RadialFunction_type - - type fourier_SO4_type - real(dp) :: cutoff - real(dp) :: z0_ratio - real(dp) :: z0 - integer :: j_max, Z - integer, dimension(:), allocatable :: species_Z - real(dp), dimension(:), allocatable :: w - - logical :: initialised = .false. - endtype fourier_SO4_type - - type bispectrum_SO4 - real(dp), pointer :: cutoff - integer, pointer :: j_max, Z - real(dp), pointer :: z0_ratio - real(dp), pointer :: z0 - - integer, dimension(:), pointer :: species_Z - real(dp), dimension(:), pointer :: w - - type(fourier_SO4_type) :: fourier_SO4 - - logical :: initialised = .false. - - endtype bispectrum_SO4 - - type bispectrum_SO3 - - integer :: l_max, n_max, Z - real(dp) :: cutoff, min_cutoff - - type(RadialFunction_type) :: radial - - integer, dimension(:), allocatable :: species_Z - real(dp), dimension(:), allocatable :: w - - logical :: initialised = .false. - - endtype bispectrum_SO3 - - type behler_g2 - real(dp) :: eta - real(dp) :: rs - real(dp) :: rc - endtype behler_g2 - - type behler_g3 - real(dp) :: eta - real(dp) :: lambda - real(dp) :: zeta - real(dp) :: rc - endtype behler_g3 - - type behler - - real(dp) :: cutoff = 0.0_dp - logical :: initialised = .false. - - integer :: n_g2, n_g3 - type(behler_g2), dimension(:), allocatable :: g2 - type(behler_g3), dimension(:), allocatable :: g3 - - endtype behler - - type distance_2b - real(dp) :: cutoff - real(dp) :: cutoff_transition_width - integer :: Z1, Z2 - character(STRING_LENGTH) :: resid_name - logical :: only_intra, only_inter - - integer :: n_exponents, tail_exponent - real(dp) :: tail_range - integer, dimension(:), allocatable :: exponents - - logical :: has_tail - logical :: initialised = .false. - - endtype distance_2b - - type coordination - real(dp) :: cutoff - real(dp) :: transition_width - integer :: Z - - logical :: initialised = .false. - - endtype coordination - - type angle_3b - real(dp) :: cutoff - real(dp) :: cutoff_transition_width - integer :: Z, Z1, Z2 - - logical :: initialised = .false. - - endtype angle_3b - - type co_angle_3b - real(dp) :: cutoff - real(dp) :: coordination_cutoff - real(dp) :: coordination_transition_width - integer :: Z, Z1, Z2 - - logical :: initialised = .false. - - endtype co_angle_3b - - type co_distance_2b - real(dp) :: cutoff - real(dp) :: transition_width - real(dp) :: coordination_cutoff - real(dp) :: coordination_transition_width - integer :: Z1, Z2 - - logical :: initialised = .false. - - endtype co_distance_2b - - type cosnx - - integer :: l_max, n_max, Z - real(dp) :: cutoff, min_cutoff - - type(RadialFunction_type) :: radial - - integer, dimension(:), allocatable :: species_Z - real(dp), dimension(:), allocatable :: w - - logical :: initialised = .false. - - endtype cosnx - - type trihis - real(dp) :: cutoff - integer :: n_gauss - - real(dp), dimension(:,:), allocatable :: gauss_centre - real(dp), dimension(:,:), allocatable :: gauss_width - - logical :: initialised = .false. - - endtype trihis - - type water_monomer - real(dp) :: cutoff - - logical :: initialised = .false. - - endtype water_monomer - - type water_dimer - real(dp) :: cutoff, cutoff_transition_width - real(dp) :: monomer_cutoff - logical :: OHH_ordercheck - real(dp) :: power - - logical :: initialised = .false. - - endtype water_dimer - - type A2_dimer - real(dp) :: cutoff - real(dp) :: monomer_cutoff - integer :: atomic_number - - logical :: initialised = .false. - - endtype A2_dimer - - type AB_dimer - real(dp) :: cutoff - real(dp) :: monomer_cutoff - integer :: atomic_number1, atomic_number2 - - logical :: initialised = .false. - - endtype AB_dimer - - type bond_real_space - real(dp) :: bond_cutoff - real(dp) :: bond_transition_width - real(dp) :: cutoff - real(dp) :: transition_width - real(dp) :: atom_sigma - integer :: max_neighbours - - logical :: initialised = .false. - - endtype bond_real_space - - type atom_real_space - real(dp) :: cutoff - real(dp) :: cutoff_transition_width - integer :: l_max - real(dp) :: alpha - real(dp) :: zeta - - logical :: initialised = .false. - - endtype atom_real_space - - type power_so3 - integer :: l_max, n_max, Z - real(dp) :: cutoff, min_cutoff - - type(RadialFunction_type) :: radial - - integer, dimension(:), allocatable :: species_Z - real(dp), dimension(:), allocatable :: w - - logical :: initialised = .false. - endtype power_so3 - - type power_SO4 - real(dp), pointer :: cutoff - integer, pointer :: j_max, Z - real(dp), pointer :: z0_ratio - real(dp), pointer :: z0 - - integer, dimension(:), pointer :: species_Z - real(dp), dimension(:), pointer :: w - - type(fourier_SO4_type) :: fourier_SO4 - - logical :: initialised = .false. - - endtype power_SO4 - - type soap - real(dp) :: cutoff - real(dp) :: cutoff_transition_width - real(dp) :: alpha, atom_sigma, covariance_sigma0, central_weight - - integer :: cutoff_dexp - real(dp) :: cutoff_scale - real(dp) :: cutoff_rate - integer :: l_max, n_max, n_Z, n_species - integer, dimension(:), allocatable :: species_Z, Z - real(dp), dimension(:), allocatable :: r_basis - real(dp), dimension(:,:), allocatable :: transform_basis,cholesky_overlap_basis - - logical :: global = .false. - logical :: central_reference_all_species = .false. - logical :: diagonal_radial = .false. - logical :: normalise = .true. - logical :: initialised = .false. - endtype soap - public :: soap - - type AN_monomer - real(dp) :: cutoff - integer :: atomic_number - integer :: N - - logical :: initialised = .false. - logical :: do_atomic = .false. - - endtype AN_monomer - - type general_monomer - type(permutation_data_type) :: permutation_data - integer, dimension(:), allocatable :: signature - real(dp) :: cutoff, cutoff_transition_width - logical :: atom_ordercheck, internal_swaps_only - logical :: strict - real(dp) :: power - logical :: initialised = .false. - endtype general_monomer - public :: general_monomer - - type general_dimer - type(permutation_data_type) :: permutation_data - integer, dimension(:), allocatable :: signature_one, signature_two - integer, dimension(:,:), allocatable :: component_atoms - real(dp) :: cutoff, cutoff_transition_width, monomer_one_cutoff, monomer_two_cutoff - logical :: atom_ordercheck, internal_swaps_only, use_smooth_cutoff, monomers_identical,double_count - logical :: strict, use_com, mpifind, strict_mask - type(transfer_parameters_type) :: transfer_parameters - logical :: initialised = .false. - logical, dimension(:), allocatable :: is_intermolecular, cutoff_contributor - real(dp) :: power - endtype general_dimer - - type general_trimer - type(permutation_data_type) :: permutation_data - integer, dimension(:), allocatable :: signature_one, signature_two, signature_three - integer, dimension(:,:), allocatable :: component_atoms - real(dp) :: cutoff, cutoff_transition_width, monomer_one_cutoff, monomer_two_cutoff, monomer_three_cutoff - logical :: atom_ordercheck, internal_swaps_only, use_smooth_cutoff, one_two_identical, one_three_identical, two_three_identical - logical :: strict, use_com, mpifind - logical :: initialised = .false. - logical, dimension(:), allocatable :: is_intermolecular, cutoff_contributor - real(dp) :: power - endtype general_trimer - - type rdf - real(dp) :: cutoff - real(dp) :: transition_width, w_gauss - integer :: Z, n_gauss - real(dp), dimension(:), allocatable :: r_gauss - - logical :: initialised = .false. - - endtype rdf - - type as_distance_2b - real(dp) :: min_cutoff, max_cutoff, as_cutoff, overlap_alpha - real(dp) :: min_transition_width, max_transition_width, as_transition_width - real(dp) :: coordination_cutoff - real(dp) :: coordination_transition_width - integer :: Z1, Z2 - - logical :: initialised = .false. - - endtype as_distance_2b - - type molecule_lo_d - type(permutation_data_type) :: permutation_data - type(Atoms) :: template_atoms - integer :: n_atoms, max_dimension ! max_dimension is descriptor dimension if include all interatomic distances - integer, dimension(:), allocatable :: signature, included_components - integer, dimension(:,:), allocatable :: component_atoms - real(dp) :: cutoff, cutoff_transition_width - integer :: neighbour_graph_depth - logical :: atom_ordercheck, use_smooth_cutoff - logical :: initialised = .false. - type(Table) :: bonds, atom_pairs - integer :: desctype - endtype molecule_lo_d - - type alex - - integer :: Z, power_min, power_max - real(dp) :: cutoff - - integer :: n_species - integer, dimension(:), allocatable :: species_Z - - logical :: initialised = .false. - endtype alex - - type com_dimer - integer, dimension(:), allocatable :: signature_one, signature_two - real(dp) :: cutoff, cutoff_transition_width, monomer_one_cutoff, monomer_two_cutoff - logical :: atom_ordercheck, use_smooth_cutoff, monomers_identical - logical :: strict, mpifind - type(transfer_parameters_type) :: transfer_parameters - logical :: initialised = .false. - logical, dimension(:), allocatable :: is_intermolecular, cutoff_contributor - endtype com_dimer - - type distance_Nb - real(dp) :: cutoff - real(dp) :: cutoff_transition_width - integer :: order - integer, dimension(:), allocatable :: Z - integer :: n_permutations - integer, dimension(:,:), allocatable :: permutations - logical, dimension(:,:,:), allocatable :: monomerConnectivities - logical :: compact_clusters = .false. - logical :: initialised = .false. - endtype distance_Nb - - type soap_express - ! User controllable parameters - real(dp) :: cutoff - real(dp) :: cutoff_transition_width - integer :: cutoff_decay_rate - - real(dp) :: atom_sigma_radial, atom_sigma_angular, central_weight, covariance_sigma0, & - atom_sigma_scaling_radial, atom_sigma_scaling_angular, amplitude_scaling - - integer :: l_max, n_max - - ! Internal pre-initialised variables - real(dp), dimension(:,:), allocatable :: basis_transformation_coefficients, & - overlap - real(dp), dimension(:), allocatable :: semifactorial_table, Y_lm_prefactor - - integer :: angular_array_size - - logical :: initialised = .false. - endtype soap_express - - type descriptor - integer :: descriptor_type = DT_NONE - - type(bispectrum_SO4) :: descriptor_bispectrum_SO4 - type(bispectrum_SO3) :: descriptor_bispectrum_SO3 - type(behler) :: descriptor_behler - type(distance_2b) :: descriptor_distance_2b - type(coordination) :: descriptor_coordination - type(angle_3b) :: descriptor_angle_3b - type(co_angle_3b) :: descriptor_co_angle_3b - type(co_distance_2b) :: descriptor_co_distance_2b - type(cosnx) :: descriptor_cosnx - type(trihis) :: descriptor_trihis - type(water_monomer) :: descriptor_water_monomer - type(water_dimer) :: descriptor_water_dimer - type(A2_dimer) :: descriptor_A2_dimer - type(AB_dimer) :: descriptor_AB_dimer - type(bond_real_space) :: descriptor_bond_real_space - type(atom_real_space) :: descriptor_atom_real_space - type(power_so3) :: descriptor_power_so3 - type(power_SO4) :: descriptor_power_SO4 - type(soap) :: descriptor_soap - type(AN_monomer) :: descriptor_AN_monomer - type(general_monomer) :: descriptor_general_monomer - type(general_dimer) :: descriptor_general_dimer - type(general_trimer) :: descriptor_general_trimer - type(rdf) :: descriptor_rdf - type(as_distance_2b) :: descriptor_as_distance_2b - type(molecule_lo_d) :: descriptor_molecule_lo_d - type(alex) :: descriptor_alex - type(com_dimer) :: descriptor_com_dimer - type(distance_Nb) :: descriptor_distance_Nb - type(soap_express) :: descriptor_soap_express - endtype - - type descriptor_data - type(descriptor_data_mono), dimension(:), allocatable :: x - endtype descriptor_data - - type cplx_1d - complex(dp), dimension(:), allocatable :: m - endtype cplx_1d - - type real_1d - real(dp), dimension(:), allocatable :: m - endtype real_1d - - type spherical_harmonics_type - type(cplx_1d), dimension(:), allocatable :: spherical_harmonics - type(cplx_2d), dimension(:), allocatable :: grad_spherical_harmonics - real(dp) :: r - real(dp), dimension(3) :: u - endtype spherical_harmonics_type - - type neighbour_type - type(spherical_harmonics_type), dimension(:), allocatable :: neighbour - endtype neighbour_type - - type grad_spherical_harmonics_overlap_type - type(cplx_3d), dimension(:), allocatable :: grad_integral - endtype grad_spherical_harmonics_overlap_type - - public :: neighbour_type, real_space_fourier_coefficients, real_space_covariance_coefficient - public :: SphericalYCartesian - - interface initialise - module procedure descriptor_initialise, RadialFunction_initialise, fourier_so4_initialise, & - bispectrum_SO4_initialise, bispectrum_SO3_initialise, behler_initialise, distance_2b_initialise, & - coordination_initialise, angle_3b_initialise, co_angle_3b_initialise, co_distance_2b_initialise, cosnx_initialise, trihis_initialise, & - water_monomer_initialise, water_dimer_initialise, A2_dimer_initialise, AB_dimer_initialise, & - bond_real_space_initialise, atom_real_space_initialise, power_so3_initialise, power_SO4_initialise, soap_initialise, AN_monomer_initialise, & - general_monomer_initialise, general_dimer_initialise, general_trimer_initialise, rdf_initialise, as_distance_2b_initialise, molecule_lo_d_initialise, alex_initialise, & - transfer_initialise, com_dimer_initialise, distance_Nb_initialise, soap_express_initialise - endinterface initialise - public :: initialise - - interface finalise - module procedure descriptor_finalise, descriptor_data_finalise, RadialFunction_finalise, fourier_so4_finalise, cplx_2d_array1_finalise, cplx_3d_array2_finalise, & - bispectrum_SO4_finalise, bispectrum_SO3_finalise, behler_finalise, distance_2b_finalise, coordination_finalise, angle_3b_finalise, co_angle_3b_finalise, & - co_distance_2b_finalise, cosnx_finalise, trihis_finalise, water_monomer_finalise, water_dimer_finalise, & - A2_dimer_finalise, AB_dimer_finalise, bond_real_space_finalise, atom_real_space_finalise, power_so3_finalise, power_SO4_finalise, soap_finalise, & - AN_monomer_finalise, general_monomer_finalise, general_dimer_finalise, general_trimer_finalise, rdf_finalise, as_distance_2b_finalise, molecule_lo_d_finalise, alex_finalise, com_dimer_finalise, & - distance_Nb_finalise, soap_express_finalise - endinterface finalise - public :: finalise - - interface calc - module procedure descriptor_calc, descriptor_calc_array, bispectrum_SO4_calc, bispectrum_SO3_calc, behler_calc, distance_2b_calc, coordination_calc, angle_3b_calc, co_angle_3b_calc, & - co_distance_2b_calc, cosnx_calc, trihis_calc, water_monomer_calc, water_dimer_calc, A2_dimer_calc, AB_dimer_calc, bond_real_space_calc, atom_real_space_calc, & - power_so3_calc, power_SO4_calc, soap_calc, AN_monomer_calc, general_monomer_calc, general_dimer_calc, general_trimer_calc, rdf_calc, as_distance_2b_calc, molecule_lo_d_calc, alex_calc, com_dimer_calc, & - distance_Nb_calc, soap_express_calc - endinterface calc - public :: calc - - interface cutoff - module procedure descriptor_cutoff, bispectrum_SO4_cutoff, bispectrum_SO3_cutoff, behler_cutoff, distance_2b_cutoff, coordination_cutoff, angle_3b_cutoff, co_angle_3b_cutoff, & - co_distance_2b_cutoff, cosnx_cutoff, trihis_cutoff, water_monomer_cutoff, water_dimer_cutoff, A2_dimer_cutoff, AB_dimer_cutoff, bond_real_space_cutoff, atom_real_space_cutoff, & - power_so3_cutoff, power_SO4_cutoff, soap_cutoff, AN_monomer_cutoff, general_monomer_cutoff, general_dimer_cutoff, general_trimer_cutoff, rdf_cutoff, as_distance_2b_cutoff, & - molecule_lo_d_cutoff, alex_cutoff, com_dimer_cutoff, distance_Nb_cutoff, soap_express_cutoff - endinterface cutoff - public :: cutoff - - interface descriptor_sizes - module procedure descriptor_sizes, bispectrum_SO4_sizes, bispectrum_SO3_sizes, behler_sizes, distance_2b_sizes, coordination_sizes, angle_3b_sizes, co_angle_3b_sizes, & - co_distance_2b_sizes, cosnx_sizes, trihis_sizes, water_monomer_sizes, water_dimer_sizes, A2_dimer_sizes, AB_dimer_sizes, bond_real_space_sizes, atom_real_space_sizes, & - power_so3_sizes, power_SO4_sizes, soap_sizes, AN_monomer_sizes, general_monomer_sizes, general_dimer_sizes, general_trimer_sizes, rdf_sizes, as_distance_2b_sizes, & - molecule_lo_d_sizes, alex_sizes, com_dimer_sizes, distance_Nb_sizes, soap_express_sizes - endinterface descriptor_sizes - public :: descriptor_sizes - - public :: descriptor_MPI_setup - - public :: descriptor, descriptor_data, descriptor_dimensions, descriptor_n_permutations, descriptor_permutations, descriptor_str_add_species - public :: real_space_covariance - public :: cplx_1d, cplx_2d - - ! for quippy3 to compile, not needed for GAP functionality. - ! TODO: exclude the ones not necessary or sole it in some dofferent way on the QUIP side - public :: bispectrum_so4, bispectrum_so3, behler, distance_2b, coordination, angle_3b, co_angle_3b, co_distance_2b, cosnx, trihis, water_monomer, & - water_dimer, a2_dimer, bond_real_space, power_so3, power_so4, an_monomer, general_dimer, general_trimer, rdf, & - as_distance_2b, molecule_lo_d, alex, com_dimer, distance_nb, descriptor_data_mono, & - fourier_so4_type, radialfunction_type, transfer_parameters_type, ab_dimer, atom_real_space, spherical_harmonics_type, & - behler_g2, behler_g3, soap_express - - contains - - function get_descriptor_type(args_str,error) - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - integer :: get_descriptor_type - - type(Dictionary) :: params - logical :: is_bispectrum_so4, is_bispectrum_so3, is_behler, is_distance_2b, is_coordination, is_angle_3b, & - is_co_angle_3b, is_co_distance_2b, is_cosnx, is_trihis, is_water_monomer, is_water_dimer, is_A2_dimer, & - is_AB_dimer, is_bond_real_space, is_atom_real_space, is_power_so3, is_power_so4, is_soap, & - is_AN_monomer, is_general_monomer, is_general_dimer, is_general_trimer, is_rdf, is_as_distance_2b, & - is_molecule_lo_d, is_alex, is_com_dimer, is_distance_Nb, is_soap_express - - INIT_ERROR(error) - - call initialise(params) - call param_register(params, 'bispectrum_so4', 'false', is_bispectrum_so4, help_string="Type of descriptor is bispectrum_so4.") - call param_register(params, 'bispectrum_so3', 'false', is_bispectrum_so3, help_string="Type of descriptor is bispectrum_so3.") - call param_register(params, 'behler', 'false', is_behler, help_string="Type of descriptor is behler.") - call param_register(params, 'distance_2b', 'false', is_distance_2b, help_string="Type of descriptor is distance_2b.") - call param_register(params, 'coordination', 'false', is_coordination, help_string="Type of descriptor is coordination.") - call param_register(params, 'angle_3b', 'false', is_angle_3b, help_string="Type of descriptor is angle_3b.") - call param_register(params, 'co_angle_3b', 'false', is_co_angle_3b, help_string="Type of descriptor is co_angle_3b.") - call param_register(params, 'co_distance_2b', 'false', is_co_distance_2b, help_string="Type of descriptor is co_distance_2b.") - call param_register(params, 'cosnx', 'false', is_cosnx, help_string="Type of descriptor is cosnx.") - call param_register(params, 'trihis', 'false', is_trihis, help_string="Type of descriptor is trihis.") - call param_register(params, 'water_monomer', 'false', is_water_monomer, help_string="Type of descriptor is water_monomer.") - call param_register(params, 'water_dimer', 'false', is_water_dimer, help_string="Type of descriptor is water_dimer.") - call param_register(params, 'A2_dimer', 'false', is_A2_dimer, help_string="Type of descriptor is A2_dimer.") - call param_register(params, 'AB_dimer', 'false', is_AB_dimer, help_string="Type of descriptor is AB_dimer.") - call param_register(params, 'bond_real_space', 'false', is_bond_real_space, help_string="Type of descriptor is bond_real_space.") - call param_register(params, 'atom_real_space', 'false', is_atom_real_space, help_string="Type of descriptor is atom_real_space.") - call param_register(params, 'power_so3', 'false', is_power_so3, help_string="Type of descriptor is power_so3.") - call param_register(params, 'power_so4', 'false', is_power_so4, help_string="Type of descriptor is power_so4.") - call param_register(params, 'soap', 'false', is_soap, help_string="Type of descriptor is soap.") - call param_register(params, 'AN_monomer', 'false', is_AN_monomer, help_string="Type of descriptor is AN_monomer.") - call param_register(params, 'general_monomer', 'false', is_general_monomer, help_string="Type of descriptor is general_monomer.") - call param_register(params, 'general_dimer', 'false', is_general_dimer, help_string="Type of descriptor is general_dimer.") - call param_register(params, 'general_trimer', 'false', is_general_trimer, help_string="Type of descriptor is general_trimer.") - call param_register(params, 'rdf', 'false', is_rdf, help_string="Type of descriptor is rdf.") - call param_register(params, 'as_distance_2b', 'false', is_as_distance_2b, help_string="Type of descriptor is as_distance_2b.") - call param_register(params, 'molecule_lo_d', 'false', is_molecule_lo_d, help_string="Type of descriptor is molecule_lo_d.") - call param_register(params, 'alex', 'false', is_alex, help_string="Type of descriptor is alex.") - call param_register(params, 'com_dimer', 'false', is_com_dimer, help_string="Type of descriptor is com_dimer.") - call param_register(params, 'distance_Nb', 'false', is_distance_Nb, help_string="Type of descriptor is distance_Nb.") - call param_register(params, 'soap_express', 'false', is_soap_express, help_string="Type of descriptor is soap_express.") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='descriptor_initialise args_str')) then - RAISE_ERROR("descriptor_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - if (count( (/is_bispectrum_so4, is_bispectrum_so3, is_behler, is_distance_2b, is_coordination, is_angle_3b, is_co_angle_3b, is_co_distance_2b, & - is_cosnx, is_trihis, is_water_monomer, is_water_dimer, is_A2_dimer, is_AB_dimer, is_bond_real_space, is_atom_real_space, is_power_so3, is_power_so4, & - is_soap, is_AN_monomer, is_general_monomer, is_general_dimer, is_general_trimer, is_rdf, is_as_distance_2b, is_molecule_lo_d, is_alex, is_com_dimer, & - is_distance_Nb, is_soap_express /) ) /= 1) then - RAISE_ERROR("descriptor_initialise found too few or too many IP Model types args_str='"//trim(args_str)//"'", error) - endif - - get_descriptor_type = DT_NONE - - if( is_bispectrum_so4 ) then - get_descriptor_type = DT_BISPECTRUM_SO4 - elseif( is_bispectrum_so3 ) then - get_descriptor_type = DT_BISPECTRUM_SO3 - elseif( is_behler ) then - get_descriptor_type = DT_BEHLER - elseif( is_distance_2b ) then - get_descriptor_type = DT_DISTANCE_2B - elseif( is_coordination ) then - get_descriptor_type = DT_COORDINATION - elseif( is_angle_3b ) then - get_descriptor_type = DT_ANGLE_3B - elseif( is_co_angle_3b ) then - get_descriptor_type = DT_CO_ANGLE_3B - elseif( is_co_distance_2b ) then - get_descriptor_type = DT_CO_DISTANCE_2B - elseif( is_cosnx ) then - get_descriptor_type = DT_COSNX - elseif( is_trihis ) then - get_descriptor_type = DT_TRIHIS - elseif( is_water_monomer ) then - get_descriptor_type = DT_WATER_MONOMER - elseif( is_water_dimer ) then - get_descriptor_type = DT_WATER_DIMER - elseif( is_A2_dimer ) then - get_descriptor_type = DT_A2_DIMER - elseif( is_AB_dimer ) then - get_descriptor_type = DT_AB_DIMER - elseif( is_bond_real_space ) then - get_descriptor_type = DT_BOND_REAL_SPACE - elseif( is_atom_real_space ) then - get_descriptor_type = DT_ATOM_REAL_SPACE - elseif( is_power_so3 ) then - get_descriptor_type = DT_POWER_SO3 - elseif( is_power_so4 ) then - get_descriptor_type = DT_POWER_SO4 - elseif( is_soap ) then - get_descriptor_type = DT_SOAP - elseif( is_AN_monomer ) then - get_descriptor_type = DT_AN_MONOMER - elseif( is_general_monomer ) then - get_descriptor_type = DT_GENERAL_MONOMER - elseif( is_general_dimer ) then - get_descriptor_type = DT_GENERAL_DIMER - elseif( is_general_trimer ) then - get_descriptor_type = DT_GENERAL_TRIMER - elseif( is_rdf ) then - get_descriptor_type = DT_RDF - elseif( is_as_distance_2b ) then - get_descriptor_type = DT_AS_DISTANCE_2B - elseif( is_molecule_lo_d ) then - get_descriptor_type = DT_MOLECULE_LO_D - elseif( is_alex ) then - get_descriptor_type = DT_ALEX - elseif( is_com_dimer ) then - get_descriptor_type = DT_COM_DIMER - elseif( is_distance_Nb ) then - get_descriptor_type = DT_DISTANCE_NB - elseif( is_soap_express ) then - get_descriptor_type = DT_SOAP_EXPRESS - endif - - endfunction get_descriptor_type - - subroutine descriptor_initialise(this,args_str,error) - type(descriptor), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - call finalise(this) - - this%descriptor_type = get_descriptor_type(args_str,error) - - select case(this%descriptor_type) - case(DT_BISPECTRUM_SO4) - call initialise(this%descriptor_bispectrum_SO4,args_str,error) - case(DT_BISPECTRUM_SO3) - call initialise(this%descriptor_bispectrum_SO3,args_str,error) - case(DT_BEHLER) - call initialise(this%descriptor_behler,args_str,error) - case(DT_DISTANCE_2B) - call initialise(this%descriptor_distance_2b,args_str,error) - case(DT_COORDINATION) - call initialise(this%descriptor_coordination,args_str,error) - case(DT_ANGLE_3B) - call initialise(this%descriptor_angle_3b,args_str,error) - case(DT_CO_ANGLE_3B) - call initialise(this%descriptor_co_angle_3b,args_str,error) - case(DT_CO_DISTANCE_2B) - call initialise(this%descriptor_co_distance_2b,args_str,error) - case(DT_COSNX) - call initialise(this%descriptor_cosnx,args_str,error) - case(DT_TRIHIS) - call initialise(this%descriptor_trihis,args_str,error) - case(DT_WATER_MONOMER) - call initialise(this%descriptor_water_monomer,args_str,error) - case(DT_WATER_DIMER) - call initialise(this%descriptor_water_dimer,args_str,error) - case(DT_A2_DIMER) - call initialise(this%descriptor_A2_dimer,args_str,error) - case(DT_AB_DIMER) - call initialise(this%descriptor_AB_dimer,args_str,error) - case(DT_BOND_REAL_SPACE) - call initialise(this%descriptor_bond_real_space,args_str,error) - case(DT_ATOM_REAL_SPACE) - call initialise(this%descriptor_atom_real_space,args_str,error) - case(DT_POWER_SO3) - call initialise(this%descriptor_power_so3,args_str,error) - case(DT_POWER_SO4) - call initialise(this%descriptor_power_so4,args_str,error) - case(DT_SOAP) - call initialise(this%descriptor_soap,args_str,error) - case(DT_AN_MONOMER) - call initialise(this%descriptor_AN_monomer,args_str,error) - case(DT_GENERAL_MONOMER) - call initialise(this%descriptor_general_monomer,args_str,error) - case(DT_GENERAL_DIMER) - call initialise(this%descriptor_general_dimer,args_str,error) - case(DT_GENERAL_TRIMER) - call initialise(this%descriptor_general_trimer,args_str,error) - case(DT_RDF) - call initialise(this%descriptor_rdf,args_str,error) - case(DT_AS_DISTANCE_2B) - call initialise(this%descriptor_as_distance_2b,args_str,error) - case(DT_MOLECULE_LO_D) - call initialise(this%descriptor_molecule_lo_d,args_str,error) - case(DT_ALEX) - call initialise(this%descriptor_alex,args_str,error) - case(DT_COM_DIMER) - call initialise(this%descriptor_com_dimer,args_str,error) - case(DT_DISTANCE_NB) - call initialise(this%descriptor_distance_Nb,args_str,error) - case(DT_SOAP_EXPRESS) - call initialise(this%descriptor_soap_express,args_str,error) - endselect - - endsubroutine descriptor_initialise - - subroutine descriptor_finalise(this,error) - type(descriptor), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - selectcase(this%descriptor_type) - case(DT_BISPECTRUM_SO4) - call finalise(this%descriptor_bispectrum_SO4,error) - case(DT_BISPECTRUM_SO3) - call finalise(this%descriptor_bispectrum_SO3,error) - case(DT_BEHLER) - call finalise(this%descriptor_behler,error) - case(DT_DISTANCE_2b) - call finalise(this%descriptor_distance_2b,error) - case(DT_COORDINATION) - call finalise(this%descriptor_coordination,error) - case(DT_ANGLE_3B) - call finalise(this%descriptor_angle_3b,error) - case(DT_CO_ANGLE_3B) - call finalise(this%descriptor_co_angle_3b,error) - case(DT_CO_DISTANCE_2b) - call finalise(this%descriptor_co_distance_2b,error) - case(DT_COSNX) - call finalise(this%descriptor_cosnx,error) - case(DT_TRIHIS) - call finalise(this%descriptor_trihis,error) - case(DT_WATER_MONOMER) - call finalise(this%descriptor_water_monomer,error) - case(DT_WATER_DIMER) - call finalise(this%descriptor_water_dimer,error) - case(DT_A2_dimer) - call finalise(this%descriptor_A2_dimer,error) - case(DT_AB_dimer) - call finalise(this%descriptor_AB_dimer,error) - case(DT_BOND_REAL_SPACE) - call finalise(this%descriptor_bond_real_space,error) - case(DT_ATOM_REAL_SPACE) - call finalise(this%descriptor_atom_real_space,error) - case(DT_POWER_SO3) - call finalise(this%descriptor_power_so3,error) - case(DT_POWER_SO4) - call finalise(this%descriptor_power_so4,error) - case(DT_SOAP) - call finalise(this%descriptor_soap,error) - case(DT_GENERAL_MONOMER) - call finalise(this%descriptor_general_monomer,error) - case(DT_GENERAL_DIMER) - call finalise(this%descriptor_general_dimer,error) - case(DT_GENERAL_TRIMER) - call finalise(this%descriptor_general_trimer,error) - case(DT_RDF) - call finalise(this%descriptor_rdf,error) - case(DT_AS_DISTANCE_2b) - call finalise(this%descriptor_as_distance_2b,error) - case(DT_MOLECULE_LO_D) - call finalise(this%descriptor_molecule_lo_d,error) - case(DT_ALEX) - call finalise(this%descriptor_alex,error) - case(DT_COM_DIMER) - call finalise(this%descriptor_com_dimer,error) - case(DT_DISTANCE_Nb) - call finalise(this%descriptor_distance_Nb,error) - case(DT_SOAP_EXPRESS) - call finalise(this%descriptor_soap_express,error) - endselect - - this%descriptor_type = DT_NONE - - endsubroutine descriptor_finalise - - subroutine descriptor_MPI_setup(this,at,mpi,mpi_mask,error) - type(descriptor), intent(in) :: this - type(atoms), intent(in) :: at - type(MPI_Context), intent(in) :: mpi - logical, dimension(:), intent(out) :: mpi_mask - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(mpi%active) then - select case(this%descriptor_type) - case(DT_BISPECTRUM_SO4) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_BISPECTRUM_SO3) - RAISE_ERROR("descriptor_MPI_setup: bispectrum_so3 not MPI ready.", error) - case(DT_BEHLER) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_DISTANCE_2B) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_COORDINATION) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_ANGLE_3B) - RAISE_ERROR("descriptor_MPI_setup: angle_3b not MPI ready.", error) - case(DT_CO_ANGLE_3B) - RAISE_ERROR("descriptor_MPI_setup: co_angle_3b not MPI ready.", error) - case(DT_CO_DISTANCE_2B) - RAISE_ERROR("descriptor_MPI_setup: co_distance_2b not MPI ready.", error) - case(DT_COSNX) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_TRIHIS) - RAISE_ERROR("descriptor_MPI_setup: trihis not MPI ready.", error) - case(DT_WATER_MONOMER) - call descriptor_water_monomer_dimer_MPI_setup(at,mpi,mpi_mask,error) - case(DT_WATER_DIMER) - call descriptor_water_monomer_dimer_MPI_setup(at,mpi,mpi_mask,error) - case(DT_A2_DIMER) - RAISE_ERROR("descriptor_MPI_setup: A2_dimer not MPI ready.", error) - case(DT_AB_DIMER) - RAISE_ERROR("descriptor_MPI_setup: AB_dimer not MPI ready.", error) - case(DT_BOND_REAL_SPACE) - RAISE_ERROR("descriptor_MPI_setup: bond_real_space not MPI ready.", error) - case(DT_ATOM_REAL_SPACE) - RAISE_ERROR("descriptor_MPI_setup: atom_real_space not MPI ready.", error) - case(DT_POWER_SO3) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_POWER_SO4) - RAISE_ERROR("descriptor_MPI_setup: power_SO4 not MPI ready.", error) - case(DT_SOAP) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_AN_MONOMER) - RAISE_ERROR("descriptor_MPI_setup: AN_monomer not MPI ready.", error) - case(DT_GENERAL_MONOMER) - call descriptor_general_monomer_nmer_MPI_setup(this,at,mpi,mpi_mask,error) - case(DT_GENERAL_DIMER) - call descriptor_general_monomer_nmer_MPI_setup(this,at,mpi,mpi_mask,error) - case(DT_GENERAL_TRIMER) - call descriptor_general_monomer_nmer_MPI_setup(this,at,mpi,mpi_mask,error) - case(DT_RDF) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_AS_DISTANCE_2B) - RAISE_ERROR("descriptor_MPI_setup: as_distance_2b not MPI ready.", error) - case(DT_MOLECULE_LO_D) - RAISE_ERROR("descriptor_MPI_setup: molecule_lo_d not MPI ready.", error) - case(DT_ALEX) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_COM_DIMER) - call descriptor_general_monomer_nmer_MPI_setup(this,at,mpi,mpi_mask,error) - case(DT_DISTANCE_NB) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case(DT_SOAP_EXPRESS) - call descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - case default - RAISE_ERROR("descriptor_MPI_setup: descriptor type "//this%descriptor_type//" not recognised.",error) - endselect - else - mpi_mask = .true. - endif - - endsubroutine descriptor_MPI_setup - - subroutine descriptor_atomic_MPI_setup(at,mpi,mpi_mask,error) - type(atoms), intent(in) :: at - type(MPI_Context), intent(in) :: mpi - logical, dimension(:), intent(out) :: mpi_mask - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - mpi_mask = .false. - do i = 1, at%N - if( mod(i-1, mpi%n_procs) == mpi%my_proc ) mpi_mask(i) = .true. - enddo - - endsubroutine descriptor_atomic_MPI_setup - - subroutine descriptor_water_monomer_dimer_MPI_setup(at,mpi,mpi_mask,error) - type(atoms), intent(in) :: at - type(MPI_Context), intent(in) :: mpi - logical, dimension(:), intent(out) :: mpi_mask - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - mpi_mask = .false. - do i = 1, at%N - if( at%Z(i) == 8 .and. mod(i-1, mpi%n_procs) == mpi%my_proc ) mpi_mask(i) = .true. - enddo - - endsubroutine descriptor_water_monomer_dimer_MPI_setup - - subroutine descriptor_general_monomer_nmer_MPI_setup(this,at,mpi,mpi_mask,error) - type(descriptor), intent(in) :: this - type(atoms), intent(in) :: at - type(MPI_Context), intent(in) :: mpi - logical, dimension(:), intent(out) :: mpi_mask - integer, optional, intent(out) :: error - - integer, dimension(:,:), allocatable :: monomer_index - logical, dimension(at%N) :: associated_to_monomer - integer :: n_monomer - - integer :: i - - INIT_ERROR(error) - - associated_to_monomer = .false. - select case(this%descriptor_type) - case(DT_GENERAL_MONOMER) - call find_general_monomer(at,monomer_index,this%descriptor_general_monomer%signature,associated_to_monomer,this%descriptor_general_monomer%cutoff,this%descriptor_general_monomer%atom_ordercheck,error) - case(DT_GENERAL_DIMER) - call find_general_monomer(at,monomer_index,this%descriptor_general_dimer%signature_one,associated_to_monomer,this%descriptor_general_dimer%monomer_one_cutoff,this%descriptor_general_dimer%atom_ordercheck,error) - case(DT_GENERAL_TRIMER) - call find_general_monomer(at,monomer_index,this%descriptor_general_trimer%signature_one,associated_to_monomer,this%descriptor_general_trimer%monomer_one_cutoff,this%descriptor_general_trimer%atom_ordercheck,error) - case(DT_COM_DIMER) - call find_general_monomer(at,monomer_index,this%descriptor_com_dimer%signature_one,associated_to_monomer,this%descriptor_com_dimer%monomer_one_cutoff,this%descriptor_com_dimer%atom_ordercheck,error) - case default - RAISE_ERROR("descriptor_general_monomer_nmer_MPI_setup: descriptor type "//this%descriptor_type//" not recognised.",error) - endselect - - n_monomer = size(monomer_index,2) - - mpi_mask = .false. - do i = 1, n_monomer ! for dimer, trimer this is the first monomer (signature_one) - if( mod(i-1, mpi%n_procs) == mpi%my_proc ) then - mpi_mask(monomer_index(:,i)) = .true. - endif - enddo - - deallocate(monomer_index) - - endsubroutine descriptor_general_monomer_nmer_MPI_setup - - subroutine descriptor_data_finalise(this,error) - type(descriptor_data), intent(inout) :: this - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(allocated(this%x)) then - do i = 1, size(this%x) - if(allocated(this%x(i)%data)) deallocate(this%x(i)%data) - if(allocated(this%x(i)%grad_data)) deallocate(this%x(i)%grad_data) - if(allocated(this%x(i)%ci)) deallocate(this%x(i)%ci) - if(allocated(this%x(i)%ii)) deallocate(this%x(i)%ii) - if(allocated(this%x(i)%pos)) deallocate(this%x(i)%pos) - if(allocated(this%x(i)%has_grad_data)) deallocate(this%x(i)%has_grad_data) - if(allocated(this%x(i)%grad_covariance_cutoff)) deallocate(this%x(i)%grad_covariance_cutoff) - enddo - deallocate(this%x) - endif - - endsubroutine descriptor_data_finalise - - subroutine RadialFunction_initialise(this,n_max,cutoff, min_cutoff,error) - type(RadialFunction_type), intent(inout) :: this - integer, intent(in) :: n_max - real(dp), intent(in) :: cutoff, min_cutoff - integer, optional, intent(out) :: error - - real(dp), dimension(:,:), allocatable :: S, vS - real(dp), dimension(:), allocatable :: eS - integer :: i, j - - INIT_ERROR(error) - - call finalise(this) - - this%n_max = n_max - this%cutoff = cutoff - this%min_cutoff = min_cutoff - - allocate(this%RadialTransform(this%n_max,this%n_max),this%NormFunction(this%n_max)) - allocate(S(this%n_max,this%n_max), vS(this%n_max,this%n_max), eS(this%n_max)) - - do i = 1, this%n_max - this%NormFunction(i) = sqrt(this%cutoff**(2.0_dp*i+5.0_dp)/(2.0_dp*i+5.0_dp)) - do j = 1, this%n_max - S(j,i) = sqrt((2.0_dp*i+5)*(2.0_dp*j+5))/(i+j+5.0_dp) - enddo - enddo - - call diagonalise(S,eS,vS) - this%RadialTransform = matmul(matmul(vS,diag(1.0_dp/sqrt(eS))),transpose(vS)) - - if(allocated(S)) deallocate(S) - if(allocated(vS)) deallocate(vS) - if(allocated(eS)) deallocate(eS) - - this%initialised = .true. - - endsubroutine RadialFunction_initialise - - subroutine RadialFunction_finalise(this,error) - type(RadialFunction_type), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%min_cutoff = 0.0_dp - this%n_max = 0 - - if(allocated(this%RadialTransform)) deallocate(this%RadialTransform) - if(allocated(this%NormFunction)) deallocate(this%NormFunction) - - this%initialised = .false. - - endsubroutine RadialFunction_finalise - - subroutine cplx_2d_array1_finalise(this) - type(cplx_2d), dimension(:), allocatable, intent(inout) :: this - integer :: j - - if(allocated(this)) then - do j = lbound(this,1), ubound(this,1) - if(allocated(this(j)%mm)) deallocate(this(j)%mm) - enddo - deallocate(this) - endif - endsubroutine cplx_2d_array1_finalise - - subroutine cplx_3d_array2_finalise(this) - type(cplx_3d), dimension(:,:), allocatable, intent(inout) :: this - integer :: i, j - - if(allocated(this)) then - do j = lbound(this,2), ubound(this,2) - do i = lbound(this,1), ubound(this,1) - if(allocated(this(i,j)%mm)) deallocate(this(i,j)%mm) - enddo - enddo - deallocate(this) - endif - - endsubroutine cplx_3d_array2_finalise - - subroutine fourier_SO4_calc(this,at,i,U,dU,args_str,error) - type(fourier_SO4_type), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(in) :: i - type(cplx_2d), dimension(:), allocatable, intent(inout) :: U - type(cplx_3d), dimension(:,:), allocatable, intent(inout), optional :: dU - integer, optional, intent(out) :: error - character(len=*), intent(in), optional :: args_str - - complex(dp), dimension(:,:), allocatable :: Uc, Up - complex(dp), dimension(:,:,:), allocatable :: dUc, dUp - complex(dp) :: z0_pls_Iz, z0_min_Iz, x_pls_Iy, x_min_Iy - complex(dp), dimension(3) :: dz0_pls_Iz, dz0_min_Iz, dx_pls_Iy, dx_min_Iy - real(dp), dimension(3) :: diff, u_ij, dfcut, dz0, dr0 - real(dp) :: r0, r, fcut, z0, theta0 - integer :: n, n_i, ji, j, m1, m2 - integer, dimension(total_elements) :: species_map - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR('fourier_SO4_calc: object not initialised',error) - endif - - species_map = 0 - do j = 1, size(this%species_Z) - if(this%species_Z(j) == 0) then - species_map = 1 - else - species_map(this%species_Z(j)) = j - endif - enddo - - - if(allocated(U)) then - if(lbound(U,1) /= 0 .or. ubound(U,1) /= this%j_max) call finalise(U) - endif - - if(.not.allocated(U)) then - allocate( U(0:this%j_max) ) - do j = 0, this%j_max - allocate( U(j)%mm(-j:j,-j:j) ) - U(j)%mm = CPLX_ZERO - enddo - endif - - do j = 0, this%j_max - U(j)%mm = CPLX_ZERO - do m1 = -j, j, 2 - U(j)%mm(m1,m1) = CPLX_ONE - enddo - enddo - - allocate( Uc(-this%j_max:this%j_max, -this%j_max:this%j_max), & - Up(-this%j_max:this%j_max, -this%j_max:this%j_max) ) - - Uc = CPLX_ZERO - Up = CPLX_ZERO - - if(present(dU)) then - if(allocated(dU)) call finalise(dU) - - ! dU is not allocated, allocate and zero it - allocate( dU(0:this%j_max,0:n_neighbours(at,i,max_dist=this%cutoff)) ) - do j = 0, this%j_max - allocate( dU(j,0)%mm(3,-j:j,-j:j) ) - dU(j,0)%mm = CPLX_ZERO - enddo - - allocate( dUc(3,-this%j_max:this%j_max, -this%j_max:this%j_max), & - dUp(3,-this%j_max:this%j_max, -this%j_max:this%j_max) ) - dUc = CPLX_ZERO - dUp = CPLX_ZERO - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - ji = neighbour(at, i, n, distance=r, diff=diff, cosines=u_ij) - if( r >= this%cutoff ) cycle - - n_i = n_i + 1 - - theta0 = r / this%z0 - z0 = r / tan( theta0 ) - r0 = sin( theta0 ) / r - - z0_pls_Iz = ( z0 + CPLX_IMAG*diff(3) ) * r0 - z0_min_Iz = ( z0 - CPLX_IMAG*diff(3) ) * r0 - x_pls_Iy = ( diff(1) + CPLX_IMAG*diff(2) ) * r0 - x_min_Iy = ( diff(1) - CPLX_IMAG*diff(2) ) * r0 - - fcut = cos_cutoff_function(r,this%cutoff) * this%w(species_map(at%Z(ji))) - - U(0)%mm(0,0) = U(0)%mm(0,0) + fcut - Up(0:0,0:0) = CPLX_ONE - - if(present(dU)) then - - dfcut = -dcos_cutoff_function(r,this%cutoff)*u_ij * this%w(species_map(at%Z(ji))) - dz0 = ( 1.0_dp / tan( theta0 ) - theta0 / sin(theta0)**2 ) * u_ij - dr0 = ( cos( theta0 ) / (r*this%z0) - r0 / r ) * u_ij - - dz0_pls_Iz = ( z0 + CPLX_IMAG*diff(3) )*dr0 + dz0*r0 - dz0_pls_Iz(3) = dz0_pls_Iz(3) + CPLX_IMAG*r0 - - dz0_min_Iz = ( z0 - CPLX_IMAG*diff(3) )*dr0 + dz0*r0 - dz0_min_Iz(3) = dz0_min_Iz(3) - CPLX_IMAG*r0 - - dx_pls_Iy = ( diff(1) + CPLX_IMAG*diff(2) )*dr0 - dx_pls_Iy(1) = dx_pls_Iy(1) + r0 - dx_pls_Iy(2) = dx_pls_Iy(2) + CPLX_IMAG*r0 - - dx_min_Iy = ( diff(1) - CPLX_IMAG*diff(2) )*dr0 - dx_min_Iy(1) = dx_min_Iy(1) + r0 - dx_min_Iy(2) = dx_min_Iy(2) - CPLX_IMAG*r0 - - dUc = CPLX_ZERO - dUp = CPLX_ZERO - - dU(0,0)%mm(:,0,0) = dU(0,0)%mm(:,0,0) + dfcut*CPLX_ONE - - allocate( dU(0,n_i)%mm(3,-0:0,-0:0) ) - - dU(0,n_i)%mm(:,0,0) = - dfcut*CPLX_ONE - endif - - do j = 1, this%j_max - Uc(-j:j,-j:j) = CPLX_ZERO - if(present(dU)) then - dUc(:,-j:j,-j:j) = CPLX_ZERO - allocate( dU(j,n_i)%mm(3,-j:j,-j:j) ) - dU(j,n_i)%mm = CPLX_ZERO - endif - - do m1 = -j, j-2, 2 - do m2 = -j, j, 2 - if( (j-m2) /= 0 ) then - Uc(m2,m1) = Uc(m2,m1) + & - sqrt( real(j-m2,dp)/real(j-m1,dp) ) * z0_pls_Iz * Up(m2+1,m1+1) - - if(present(dU)) dUc(:,m2,m1) = dUc(:,m2,m1) + & - sqrt( real(j-m2,dp)/real(j-m1,dp) ) * & - ( dz0_pls_Iz * Up(m2+1,m1+1) + z0_pls_Iz * dUp(:,m2+1,m1+1) ) - endif - - if( (j+m2) /= 0 ) then - Uc(m2,m1) = Uc(m2,m1) - & - CPLX_IMAG * sqrt( real(j+m2,dp)/real(j-m1,dp) ) * x_min_Iy * Up(m2-1,m1+1) - - if(present(dU)) dUc(:,m2,m1) = dUc(:,m2,m1) - & - CPLX_IMAG * sqrt( real(j+m2,dp)/real(j-m1,dp) ) * & - ( dx_min_Iy * Up(m2-1,m1+1) + x_min_Iy * dUp(:,m2-1,m1+1) ) - - endif - enddo - enddo - - m1 = j - do m2 = -j, j, 2 - if( (j+m2) /= 0 ) then - Uc(m2,m1) = Uc(m2,m1) + & - sqrt( real(j+m2,dp)/real(j+m1,dp) ) * z0_min_Iz * Up(m2-1,m1-1) - - if(present(dU)) dUc(:,m2,m1) = dUc(:,m2,m1) + & - sqrt( real(j+m2,dp)/real(j+m1,dp) ) * & - ( dz0_min_Iz * Up(m2-1,m1-1) + z0_min_Iz * dUp(:,m2-1,m1-1) ) - endif - - if( (j-m2) /= 0 ) then - Uc(m2,m1) = Uc(m2,m1) - & - CPLX_IMAG * sqrt( real(j-m2,dp)/real(j+m1,dp) ) * x_pls_Iy * Up(m2+1,m1-1) - - if(present(dU)) dUc(:,m2,m1) = dUc(:,m2,m1) - & - CPLX_IMAG * sqrt( real(j-m2,dp)/real(j+m1,dp) ) * & - ( dx_pls_Iy * Up(m2+1,m1-1) + x_pls_Iy * dUp(:,m2+1,m1-1) ) - endif - enddo - - U(j)%mm = U(j)%mm + Uc(-j:j,-j:j) * fcut - Up(-j:j,-j:j) = Uc(-j:j,-j:j) - if(present(dU)) then - dUp(:,-j:j,-j:j) = dUc(:,-j:j,-j:j) - dU(j,0)%mm = dU(j,0)%mm - dUc(:,-j:j,-j:j) * fcut - dU(j,n_i)%mm = dU(j,n_i)%mm + dUc(:,-j:j,-j:j) * fcut - do m1 = -j, j, 2 - do m2 = -j, j, 2 - dU(j,0)%mm(:,m2,m1) = dU(j,0)%mm(:,m2,m1) & - + Uc(m2,m1) * dfcut - dU(j,n_i)%mm(:,m2,m1) = dU(j,n_i)%mm(:,m2,m1) & - - Uc(m2,m1) * dfcut - enddo - enddo - endif - - enddo ! j - enddo ! n - - if(allocated(Up)) deallocate(Up) - if(allocated(Uc)) deallocate(Uc) - if(allocated(dUp)) deallocate(dUp) - if(allocated(dUc)) deallocate(dUc) - - endsubroutine fourier_SO4_calc - - subroutine fourier_so4_initialise(this,args_str,error) - type(fourier_SO4_type), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - integer :: n_species - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '2.75', this%cutoff, help_string="Cutoff for SO4 bispectrum") - call param_register(params, 'z0_ratio', '0.0', this%z0_ratio, help_string="Ratio of radius of 4D projection sphere times PI and the cutoff.") - call param_register(params, 'j_max', '4', this%j_max, help_string="Max of expansion of bispectrum, i.e. resulution") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'n_species', '1', n_species, help_string="Number of species for the descriptor") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='fourier_so4_initialise args_str')) then - RAISE_ERROR("fourier_so4_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - allocate(this%species_Z(n_species), this%w(n_species)) - - call initialise(params) - if( n_species == 1 ) then - call param_register(params, 'species_Z', '0', this%species_Z(1), help_string="Atomic number of species") - call param_register(params, 'w', '1.0', this%w(1), help_string="Weight associated to each atomic type") - else - call param_register(params, 'species_Z', PARAM_MANDATORY, this%species_Z, help_string="Atomic number of species") - call param_register(params, 'w', PARAM_MANDATORY, this%w, help_string="Weight associated to each atomic type") - endif - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='fourier_so4_initialise args_str')) then - RAISE_ERROR("fourier_so4_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%z0 = max(1.0_dp,this%z0_ratio) * this%cutoff/(PI-0.02_dp) - - this%initialised = .true. - - - endsubroutine fourier_so4_initialise - - subroutine fourier_so4_finalise(this,error) - type(fourier_so4_type), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - - this%cutoff = 0.0_dp - this%j_max = 0 - this%z0_ratio = 0.0_dp - this%z0 = 0.0_dp - this%Z = 0 - - if(allocated(this%species_Z)) deallocate(this%species_Z) - if(allocated(this%w)) deallocate(this%w) - - this%initialised = .false. - - endsubroutine fourier_so4_finalise - - subroutine bispectrum_so4_initialise(this,args_str,error) - type(bispectrum_so4), intent(inout), target :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - call finalise(this) - - call initialise(this%fourier_SO4,args_str,error) - - this%cutoff => this%fourier_SO4%cutoff - this%z0_ratio => this%fourier_SO4%z0_ratio - this%z0 => this%fourier_SO4%z0 - this%j_max => this%fourier_SO4%j_max - this%Z => this%fourier_SO4%Z - this%cutoff => this%fourier_SO4%cutoff - this%species_Z => this%fourier_SO4%species_Z - this%w => this%fourier_SO4%w - - this%initialised = .true. - - endsubroutine bispectrum_so4_initialise - - subroutine bispectrum_so4_finalise(this,error) - type(bispectrum_so4), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - - call finalise(this%fourier_SO4,error) - - this%cutoff => null() - this%z0_ratio => null() - this%z0 => null() - this%j_max => null() - this%Z => null() - this%cutoff => null() - this%species_Z => null() - this%w => null() - - this%initialised = .false. - - endsubroutine bispectrum_so4_finalise - - subroutine bispectrum_so3_initialise(this,args_str,error) - type(bispectrum_so3), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - integer :: n_species - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for bispectrum_so3-type descriptors") - call param_register(params, 'min_cutoff', '0.00', this%min_cutoff, help_string="Cutoff for minimal distances in bispectrum_so3-type descriptors") - call param_register(params, 'l_max', '4', this%l_max, help_string="L_max for bispectrum_so3-type descriptors") - call param_register(params, 'n_max', '4', this%n_max, help_string="N_max for bispectrum_so3-type descriptors") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'n_species', '1', n_species, help_string="Number of species for the descriptor") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='bispectrum_so3_initialise args_str')) then - RAISE_ERROR("bispectrum_so3_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - allocate(this%species_Z(n_species), this%w(n_species)) - - call initialise(params) - if( n_species == 1 ) then - call param_register(params, 'species_Z', '0', this%species_Z(1), help_string="Atomic number of species") - call param_register(params, 'w', '1.0', this%w(1), help_string="Weight associated to each atomic type") - else - call param_register(params, 'species_Z', PARAM_MANDATORY, this%species_Z, help_string="Atomic number of species") - call param_register(params, 'w', PARAM_MANDATORY, this%w, help_string="Weight associated to each atomic type") - endif - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='bispectrum_so3_initialise args_str')) then - RAISE_ERROR("bispectrum_so3_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call initialise(this%Radial,this%n_max,this%cutoff,this%min_cutoff,error) - - this%initialised = .true. - - call print('Dimensions: '//bispectrum_so3_dimensions(this,error)) - - endsubroutine bispectrum_so3_initialise - - subroutine bispectrum_so3_finalise(this,error) - type(bispectrum_so3), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - - this%cutoff = 0.0_dp - this%min_cutoff = 0.0_dp - this%l_max = 0 - this%n_max = 0 - this%Z = 0 - - if(allocated(this%species_Z)) deallocate(this%species_Z) - if(allocated(this%w)) deallocate(this%w) - - call finalise(this%Radial) - - this%initialised = .false. - - endsubroutine bispectrum_so3_finalise - - subroutine behler_initialise(this,args_str,error) - type(behler), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - !call param_register(params, 'behler_cutoff', '2.75', this%cutoff, help_string="Cutoff for Behler-type descriptors") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='behler_initialise args_str')) then - RAISE_ERROR("behler_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - this%n_g2 = 8 - this%n_g3 = 43 - - allocate(this%g2(this%n_g2), this%g3(this%n_g3)) - - this%g2(1)%eta = 0.001_dp / BOHR**2; this%g2(1)%rs = 0.000_dp * BOHR; this%g2(1)%rc = 11.338_dp * BOHR - this%g2(2)%eta = 0.010_dp / BOHR**2; this%g2(2)%rs = 0.000_dp * BOHR; this%g2(2)%rc = 11.338_dp * BOHR - this%g2(3)%eta = 0.020_dp / BOHR**2; this%g2(3)%rs = 0.000_dp * BOHR; this%g2(3)%rc = 11.338_dp * BOHR - this%g2(4)%eta = 0.035_dp / BOHR**2; this%g2(4)%rs = 0.000_dp * BOHR; this%g2(4)%rc = 11.338_dp * BOHR - this%g2(5)%eta = 0.060_dp / BOHR**2; this%g2(5)%rs = 0.000_dp * BOHR; this%g2(5)%rc = 11.338_dp * BOHR - this%g2(6)%eta = 0.100_dp / BOHR**2; this%g2(6)%rs = 0.000_dp * BOHR; this%g2(6)%rc = 11.338_dp * BOHR - this%g2(7)%eta = 0.200_dp / BOHR**2; this%g2(7)%rs = 0.000_dp * BOHR; this%g2(7)%rc = 11.338_dp * BOHR - this%g2(8)%eta = 0.400_dp / BOHR**2; this%g2(8)%rs = 0.000_dp * BOHR; this%g2(8)%rc = 11.338_dp * BOHR - - this%g3( 1)%eta = 0.0001_dp / BOHR**2; this%g3( 1)%lambda = -1.000_dp; this%g3( 1)%zeta = 1.000_dp; this%g3( 1)%rc = 11.338_dp * BOHR - this%g3( 2)%eta = 0.0001_dp / BOHR**2; this%g3( 2)%lambda = 1.000_dp; this%g3( 2)%zeta = 1.000_dp; this%g3( 2)%rc = 11.338_dp * BOHR - this%g3( 3)%eta = 0.0001_dp / BOHR**2; this%g3( 3)%lambda = -1.000_dp; this%g3( 3)%zeta = 2.000_dp; this%g3( 3)%rc = 11.338_dp * BOHR - this%g3( 4)%eta = 0.0001_dp / BOHR**2; this%g3( 4)%lambda = 1.000_dp; this%g3( 4)%zeta = 2.000_dp; this%g3( 4)%rc = 11.338_dp * BOHR - this%g3( 5)%eta = 0.0030_dp / BOHR**2; this%g3( 5)%lambda = -1.000_dp; this%g3( 5)%zeta = 1.000_dp; this%g3( 5)%rc = 11.338_dp * BOHR - this%g3( 6)%eta = 0.0030_dp / BOHR**2; this%g3( 6)%lambda = 1.000_dp; this%g3( 6)%zeta = 1.000_dp; this%g3( 6)%rc = 11.338_dp * BOHR - this%g3( 7)%eta = 0.0030_dp / BOHR**2; this%g3( 7)%lambda = -1.000_dp; this%g3( 7)%zeta = 2.000_dp; this%g3( 7)%rc = 11.338_dp * BOHR - this%g3( 8)%eta = 0.0030_dp / BOHR**2; this%g3( 8)%lambda = 1.000_dp; this%g3( 8)%zeta = 2.000_dp; this%g3( 8)%rc = 11.338_dp * BOHR - this%g3( 9)%eta = 0.0080_dp / BOHR**2; this%g3( 9)%lambda = -1.000_dp; this%g3( 9)%zeta = 1.000_dp; this%g3( 9)%rc = 11.338_dp * BOHR - this%g3(10)%eta = 0.0080_dp / BOHR**2; this%g3(10)%lambda = 1.000_dp; this%g3(10)%zeta = 1.000_dp; this%g3(10)%rc = 11.338_dp * BOHR - this%g3(11)%eta = 0.0080_dp / BOHR**2; this%g3(11)%lambda = -1.000_dp; this%g3(11)%zeta = 2.000_dp; this%g3(11)%rc = 11.338_dp * BOHR - this%g3(12)%eta = 0.0080_dp / BOHR**2; this%g3(12)%lambda = 1.000_dp; this%g3(12)%zeta = 2.000_dp; this%g3(12)%rc = 11.338_dp * BOHR - this%g3(13)%eta = 0.0150_dp / BOHR**2; this%g3(13)%lambda = -1.000_dp; this%g3(13)%zeta = 1.000_dp; this%g3(13)%rc = 11.338_dp * BOHR - this%g3(14)%eta = 0.0150_dp / BOHR**2; this%g3(14)%lambda = 1.000_dp; this%g3(14)%zeta = 1.000_dp; this%g3(14)%rc = 11.338_dp * BOHR - this%g3(15)%eta = 0.0150_dp / BOHR**2; this%g3(15)%lambda = -1.000_dp; this%g3(15)%zeta = 2.000_dp; this%g3(15)%rc = 11.338_dp * BOHR - this%g3(16)%eta = 0.0150_dp / BOHR**2; this%g3(16)%lambda = 1.000_dp; this%g3(16)%zeta = 2.000_dp; this%g3(16)%rc = 11.338_dp * BOHR - this%g3(17)%eta = 0.0150_dp / BOHR**2; this%g3(17)%lambda = -1.000_dp; this%g3(17)%zeta = 4.000_dp; this%g3(17)%rc = 11.338_dp * BOHR - this%g3(18)%eta = 0.0150_dp / BOHR**2; this%g3(18)%lambda = 1.000_dp; this%g3(18)%zeta = 4.000_dp; this%g3(18)%rc = 11.338_dp * BOHR - this%g3(19)%eta = 0.0150_dp / BOHR**2; this%g3(19)%lambda = -1.000_dp; this%g3(19)%zeta = 16.000_dp; this%g3(19)%rc = 11.338_dp * BOHR - this%g3(20)%eta = 0.0150_dp / BOHR**2; this%g3(20)%lambda = 1.000_dp; this%g3(20)%zeta = 16.000_dp; this%g3(20)%rc = 11.338_dp * BOHR - this%g3(21)%eta = 0.0250_dp / BOHR**2; this%g3(21)%lambda = -1.000_dp; this%g3(21)%zeta = 1.000_dp; this%g3(21)%rc = 11.338_dp * BOHR - this%g3(22)%eta = 0.0250_dp / BOHR**2; this%g3(22)%lambda = 1.000_dp; this%g3(22)%zeta = 1.000_dp; this%g3(22)%rc = 11.338_dp * BOHR - this%g3(23)%eta = 0.0250_dp / BOHR**2; this%g3(23)%lambda = -1.000_dp; this%g3(23)%zeta = 2.000_dp; this%g3(23)%rc = 11.338_dp * BOHR - this%g3(24)%eta = 0.0250_dp / BOHR**2; this%g3(24)%lambda = 1.000_dp; this%g3(24)%zeta = 2.000_dp; this%g3(24)%rc = 11.338_dp * BOHR - this%g3(25)%eta = 0.0250_dp / BOHR**2; this%g3(25)%lambda = -1.000_dp; this%g3(25)%zeta = 4.000_dp; this%g3(25)%rc = 11.338_dp * BOHR - this%g3(26)%eta = 0.0250_dp / BOHR**2; this%g3(26)%lambda = 1.000_dp; this%g3(26)%zeta = 4.000_dp; this%g3(26)%rc = 11.338_dp * BOHR - this%g3(27)%eta = 0.0250_dp / BOHR**2; this%g3(27)%lambda = -1.000_dp; this%g3(27)%zeta = 16.000_dp; this%g3(27)%rc = 11.338_dp * BOHR - this%g3(28)%eta = 0.0250_dp / BOHR**2; this%g3(28)%lambda = 1.000_dp; this%g3(28)%zeta = 16.000_dp; this%g3(28)%rc = 11.338_dp * BOHR - this%g3(29)%eta = 0.0450_dp / BOHR**2; this%g3(29)%lambda = -1.000_dp; this%g3(29)%zeta = 1.000_dp; this%g3(29)%rc = 11.338_dp * BOHR - this%g3(30)%eta = 0.0450_dp / BOHR**2; this%g3(30)%lambda = 1.000_dp; this%g3(30)%zeta = 1.000_dp; this%g3(30)%rc = 11.338_dp * BOHR - this%g3(31)%eta = 0.0450_dp / BOHR**2; this%g3(31)%lambda = -1.000_dp; this%g3(31)%zeta = 2.000_dp; this%g3(31)%rc = 11.338_dp * BOHR - this%g3(32)%eta = 0.0450_dp / BOHR**2; this%g3(32)%lambda = 1.000_dp; this%g3(32)%zeta = 2.000_dp; this%g3(32)%rc = 11.338_dp * BOHR - this%g3(33)%eta = 0.0450_dp / BOHR**2; this%g3(33)%lambda = -1.000_dp; this%g3(33)%zeta = 4.000_dp; this%g3(33)%rc = 11.338_dp * BOHR - this%g3(34)%eta = 0.0450_dp / BOHR**2; this%g3(34)%lambda = 1.000_dp; this%g3(34)%zeta = 4.000_dp; this%g3(34)%rc = 11.338_dp * BOHR - this%g3(35)%eta = 0.0450_dp / BOHR**2; this%g3(35)%lambda = -1.000_dp; this%g3(35)%zeta = 16.000_dp; this%g3(35)%rc = 11.338_dp * BOHR - this%g3(36)%eta = 0.0450_dp / BOHR**2; this%g3(36)%lambda = 1.000_dp; this%g3(36)%zeta = 16.000_dp; this%g3(36)%rc = 11.338_dp * BOHR - this%g3(37)%eta = 0.0800_dp / BOHR**2; this%g3(37)%lambda = -1.000_dp; this%g3(37)%zeta = 1.000_dp; this%g3(37)%rc = 11.338_dp * BOHR - this%g3(38)%eta = 0.0800_dp / BOHR**2; this%g3(38)%lambda = 1.000_dp; this%g3(38)%zeta = 1.000_dp; this%g3(38)%rc = 11.338_dp * BOHR - this%g3(39)%eta = 0.0800_dp / BOHR**2; this%g3(39)%lambda = -1.000_dp; this%g3(39)%zeta = 2.000_dp; this%g3(39)%rc = 11.338_dp * BOHR - this%g3(40)%eta = 0.0800_dp / BOHR**2; this%g3(40)%lambda = 1.000_dp; this%g3(40)%zeta = 2.000_dp; this%g3(40)%rc = 11.338_dp * BOHR - this%g3(41)%eta = 0.0800_dp / BOHR**2; this%g3(41)%lambda = -1.000_dp; this%g3(41)%zeta = 4.000_dp; this%g3(41)%rc = 11.338_dp * BOHR - this%g3(42)%eta = 0.0800_dp / BOHR**2; this%g3(42)%lambda = 1.000_dp; this%g3(42)%zeta = 4.000_dp; this%g3(42)%rc = 11.338_dp * BOHR - this%g3(43)%eta = 0.0800_dp / BOHR**2; this%g3(43)%lambda = 1.000_dp; this%g3(43)%zeta = 16.000_dp; this%g3(43)%rc = 11.338_dp * BOHR - - this%cutoff = 11.338_dp * BOHR - - this%initialised = .true. - - endsubroutine behler_initialise - - subroutine behler_finalise(this,error) - type(behler), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - - this%cutoff = 0.0_dp - this%n_g2 = 0 - this%n_g3 = 0 - - if(allocated(this%g2)) deallocate(this%g2) - if(allocated(this%g3)) deallocate(this%g3) - - this%initialised = .false. - - endsubroutine behler_finalise - - subroutine distance_2b_initialise(this,args_str,error) - type(distance_2b), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - logical :: has_resid_name, has_exponents - integer :: i - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for distance_2b-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.5', this%cutoff_transition_width, help_string="Transition width of cutoff for distance_2b-type descriptors") - call param_register(params, 'Z1', '0', this%Z1, help_string="Atom type #1 in bond") - call param_register(params, 'Z2', '0', this%Z2, help_string="Atom type #2 in bond") - call param_register(params, 'resid_name', '', this%resid_name, has_value_target=has_resid_name, help_string="Name of an integer property in the atoms object giving the residue id of the molecule to which the atom belongs.") - call param_register(params, 'only_intra', 'F', this%only_intra, help_string="Only calculate INTRAmolecular pairs with equal residue ids (bonds)") - call param_register(params, 'only_inter', 'F', this%only_inter, help_string="Only apply to INTERmolecular pairs with different residue ids (non-bonded)") - - call param_register(params, 'n_exponents', '1', this%n_exponents, help_string="Number of exponents") - call param_register(params, 'tail_range', '1.0', this%tail_range, help_string="Tail order") - call param_register(params, 'tail_exponent', '0', this%tail_exponent, & - has_value_target = this%has_tail, help_string="Tail range") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='distance_2b_initialise args_str')) then - RAISE_ERROR("distance_2b_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - if (this%only_intra .and. this%only_inter) then - RAISE_ERROR("distance_2b_initialise: cannot specify both only_inter AND only_intra", error) - end if - if ((this%only_intra .or. this%only_inter) .and. (.not. has_resid_name)) then - RAISE_ERROR("distance_2b_initialise: only_intra and only_inter require resid_name to be given as well", error) - end if - - allocate(this%exponents(this%n_exponents)) - call initialise(params) - if( this%n_exponents == 1 ) then - call param_register(params, 'exponents',"1", this%exponents(1), & - has_value_target=has_exponents,help_string="Exponents") - else - call param_register(params, 'exponents',repeat(" 1 ",this%n_exponents), this%exponents, & - has_value_target=has_exponents,help_string="Exponents") - endif - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='distance_2b_initialise args_str')) then - RAISE_ERROR("distance_2b_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - if( .not. has_exponents .and. this%n_exponents > 1 ) then - do i = 1, this%n_exponents - this%exponents(i) = -i - enddo - endif - - this%initialised = .true. - - endsubroutine distance_2b_initialise - - subroutine distance_2b_finalise(this,error) - type(distance_2b), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.5_dp - this%Z1 = 0 - this%Z2 = 0 - - this%resid_name = '' - this%only_intra = .false. - this%only_inter = .false. - - this%tail_exponent = 0 - this%tail_range = 0.0_dp - this%has_tail = .false. - - this%n_exponents = 0 - if(allocated(this%exponents)) deallocate(this%exponents) - - this%initialised = .false. - - endsubroutine distance_2b_finalise - - subroutine coordination_initialise(this,args_str,error) - type(coordination), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for coordination-type descriptors") - call param_register(params, 'transition_width', '0.20', this%transition_width, help_string="Width of transition region from 1 to 0") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='coordination_initialise args_str')) then - RAISE_ERROR("coordination_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine coordination_initialise - - subroutine coordination_finalise(this,error) - type(coordination), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%transition_width = 0.0_dp - this%Z = 0 - - this%initialised = .false. - - endsubroutine coordination_finalise - - subroutine angle_3b_initialise(this,args_str,error) - type(angle_3b), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for angle_3b-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.50', this%cutoff_transition_width, help_string="Cutoff transition width for angle_3b-type descriptors") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'Z1', '0', this%Z1, help_string="Atomic number of neighbour #1") - call param_register(params, 'Z2', '0', this%Z2, help_string="Atomic number of neighbour #2") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='angle_3b_initialise args_str')) then - RAISE_ERROR("angle_3b_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine angle_3b_initialise - - subroutine angle_3b_finalise(this,error) - type(angle_3b), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%Z = 0 - this%Z1 = 0 - this%Z2 = 0 - - this%initialised = .false. - - endsubroutine angle_3b_finalise - - subroutine co_angle_3b_initialise(this,args_str,error) - type(co_angle_3b), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for co_angle_3b-type descriptors") - call param_register(params, 'coordination_cutoff', '0.00', this%coordination_cutoff, help_string="Cutoff for coordination function in co_angle_3b-type descriptors") - call param_register(params, 'coordination_transition_width', '0.00', this%coordination_transition_width, help_string="Transition width for co_angle_3b-type descriptors") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'Z1', '0', this%Z1, help_string="Atomic number of neighbour #1") - call param_register(params, 'Z2', '0', this%Z2, help_string="Atomic number of neighbour #2") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='co_angle_3b_initialise args_str')) then - RAISE_ERROR("co_angle_3b_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine co_angle_3b_initialise - - subroutine co_angle_3b_finalise(this,error) - type(co_angle_3b), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%coordination_cutoff = 0.0_dp - this%coordination_transition_width = 0.0_dp - this%Z = 0 - this%Z1 = 0 - this%Z2 = 0 - - this%initialised = .false. - - endsubroutine co_angle_3b_finalise - - subroutine co_distance_2b_initialise(this,args_str,error) - type(co_distance_2b), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for co_distance_2b-type descriptors") - call param_register(params, 'transition_width', '0.50', this%transition_width, help_string="Transition width of cutoff for co_distance_2b-type descriptors") - call param_register(params, 'coordination_cutoff', '0.00', this%coordination_cutoff, help_string="Cutoff for coordination function in co_distance_2b-type descriptors") - call param_register(params, 'coordination_transition_width', '0.00', this%coordination_transition_width, help_string="Transition width for co_distance_2b-type descriptors") - call param_register(params, 'Z1', '0', this%Z1, help_string="Atom type #1 in bond") - call param_register(params, 'Z2', '0', this%Z2, help_string="Atom type #2 in bond") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='co_distance_2b_initialise args_str')) then - RAISE_ERROR("co_distance_2b_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine co_distance_2b_initialise - - subroutine co_distance_2b_finalise(this,error) - type(co_distance_2b), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%coordination_cutoff = 0.0_dp - this%coordination_transition_width = 0.0_dp - this%Z1 = 0 - this%Z2 = 0 - - this%initialised = .false. - - endsubroutine co_distance_2b_finalise - - subroutine cosnx_initialise(this,args_str,error) - type(cosnx), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - integer :: n_species - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for cosnx-type descriptors") - call param_register(params, 'min_cutoff', '0.00', this%min_cutoff, help_string="Cutoff for minimal distances in cosnx-type descriptors") - call param_register(params, 'l_max', '4', this%l_max, help_string="L_max for cosnx-type descriptors") - call param_register(params, 'n_max', '4', this%n_max, help_string="N_max for cosnx-type descriptors") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'n_species', '1', n_species, help_string="Number of species for the descriptor") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='cosnx_initialise args_str')) then - RAISE_ERROR("cosnx_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - allocate(this%species_Z(n_species), this%w(n_species)) - - call initialise(params) - if( n_species == 1 ) then - call param_register(params, 'species_Z', '0', this%species_Z(1), help_string="Atomic number of species") - call param_register(params, 'w', '1.0', this%w(1), help_string="Weight associated to each atomic type") - else - call param_register(params, 'species_Z', PARAM_MANDATORY, this%species_Z, help_string="Atomic number of species") - call param_register(params, 'w', PARAM_MANDATORY, this%w, help_string="Weight associated to each atomic type") - endif - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='cosnx_initialise args_str')) then - RAISE_ERROR("cosnx_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call initialise(this%Radial,this%n_max,this%cutoff,this%min_cutoff,error) - - this%initialised = .true. - - endsubroutine cosnx_initialise - - subroutine cosnx_finalise(this,error) - type(cosnx), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%min_cutoff = 0.0_dp - this%l_max = 0 - this%n_max = 0 - - if(allocated(this%species_Z)) deallocate(this%species_Z) - if(allocated(this%w)) deallocate(this%w) - - call finalise(this%Radial) - - this%initialised = .false. - - endsubroutine cosnx_finalise - - subroutine trihis_initialise(this,args_str,error) - type(trihis), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - real(dp), dimension(:), allocatable :: gauss_centre1D, gauss_width1D - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for trihis-type descriptors") - call param_register(params, 'n_gauss', '0', this%n_gauss, help_string="Number of Gaussians for trihis-type descriptors") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='trihis_initialise args_str')) then - RAISE_ERROR("trihis_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - allocate(gauss_centre1D(3*this%n_gauss),gauss_width1D(3*this%n_gauss)) - allocate(this%gauss_centre(3,this%n_gauss),this%gauss_width(3,this%n_gauss)) - - call initialise(params) - call param_register(params, 'trihis_gauss_centre', PARAM_MANDATORY, gauss_centre1D, help_string="Number of Gaussians for trihis-type descriptors") - call param_register(params, 'trihis_gauss_width', PARAM_MANDATORY, gauss_width1D, help_string="Number of Gaussians for trihis-type descriptors") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='trihis_initialise args_str')) then - RAISE_ERROR("trihis_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%gauss_centre = reshape(gauss_centre1D,(/3,this%n_gauss/)) - this%gauss_width = reshape(gauss_width1D,(/3,this%n_gauss/)) - - deallocate(gauss_centre1D,gauss_width1D) - - this%initialised = .true. - - endsubroutine trihis_initialise - - subroutine trihis_finalise(this,error) - type(trihis), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%n_gauss = 0 - - if(allocated(this%gauss_centre)) deallocate(this%gauss_centre) - if(allocated(this%gauss_width)) deallocate(this%gauss_width) - - this%initialised = .false. - - endsubroutine trihis_finalise - - subroutine water_monomer_initialise(this,args_str,error) - type(water_monomer), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for water_monomer-type descriptors") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='water_monomer_initialise args_str')) then - RAISE_ERROR("water_monomer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine water_monomer_initialise - - subroutine water_monomer_finalise(this,error) - type(water_monomer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - - this%initialised = .false. - - endsubroutine water_monomer_finalise - - subroutine water_dimer_initialise(this,args_str,error) - type(water_dimer), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for water_dimer-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.50', this%cutoff_transition_width, help_string="Width of smooth cutoff region for water_dimer-type descriptors") - call param_register(params, 'monomer_cutoff', '1.50', this%monomer_cutoff, help_string="Monomer cutoff for water_dimer-type descriptors") - call param_register(params, 'OHH_ordercheck', 'T', this%OHH_ordercheck, help_string="T: find water molecules. F: use default order OHH") - call param_register(params, 'power', '1.0', this%power, help_string="Power of distances to be used in the kernel") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='water_dimer_initialise args_str')) then - RAISE_ERROR("water_dimer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine water_dimer_initialise - - subroutine water_dimer_finalise(this,error) - type(water_dimer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.0_dp - this%monomer_cutoff = 0.0_dp - this%OHH_ordercheck = .true. - this%power = 1.0_dp - - this%initialised = .false. - - endsubroutine water_dimer_finalise - - subroutine A2_dimer_initialise(this,args_str,error) - type(A2_dimer), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for A2_dimer-type descriptors") - call param_register(params, 'monomer_cutoff', '1.50', this%monomer_cutoff, help_string="Monomer cutoff for A2_dimer-type descriptors") - call param_register(params, 'atomic_number', '1', this%atomic_number, help_string="Atomic number in A2_dimer-type descriptors") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='A2_dimer_initialise args_str')) then - RAISE_ERROR("A2_dimer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine A2_dimer_initialise - - subroutine A2_dimer_finalise(this,error) - type(A2_dimer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%monomer_cutoff = 0.0_dp - this%atomic_number = 0 - - this%initialised = .false. - - endsubroutine A2_dimer_finalise - - subroutine AB_dimer_initialise(this,args_str,error) - type(AB_dimer), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for AB_dimer-type descriptors") - call param_register(params, 'monomer_cutoff', '1.50', this%monomer_cutoff, help_string="Monomer cutoff for AB_dimer-type descriptors") - call param_register(params, 'atomic_number1', '1', this%atomic_number1, help_string="Atomic number of atom 1 in AB_dimer-type descriptors") - call param_register(params, 'atomic_number2', '9', this%atomic_number2, help_string="Atomic number of atom 2 in AB_dimer-type descriptors") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='AB_dimer_initialise args_str')) then - RAISE_ERROR("AB_dimer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - if( this%atomic_number1 == this%atomic_number2 ) then - RAISE_ERROR("AB_dimer_initialise: AB_dimer_atomic_number1 = AB_dimer_atomic_number2 = "//this%atomic_number1//" which would require addtional permutational symmetries. Use A2_dimer descriptor instead.",error) - endif - - this%initialised = .true. - - endsubroutine AB_dimer_initialise - - subroutine AB_dimer_finalise(this,error) - type(AB_dimer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%monomer_cutoff = 0.0_dp - this%atomic_number1 = 0 - this%atomic_number2 = 0 - - this%initialised = .false. - - endsubroutine AB_dimer_finalise - - subroutine bond_real_space_initialise(this,args_str,error) - type(bond_real_space), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'bond_cutoff', '0.00', this%bond_cutoff, help_string="Bond cutoff for bond_real_space-type descriptors") - call param_register(params, 'bond_transition_width', '0.00', this%bond_transition_width, help_string="Bond transition width for bond_real_space-type descriptors") - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Space cutoff for bond_real_space-type descriptors") - call param_register(params, 'transition_width', '0.00', this%transition_width, help_string="Space transition width for bond_real_space-type descriptors") - call param_register(params, 'atom_sigma', '0.00', this%atom_sigma, help_string="Atom sigma for bond_real_space-type descriptors") - call param_register(params, 'max_neighbours', '0', this%max_neighbours, help_string="Maximum number of neighbours") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='bond_real_space_initialise args_str')) then - RAISE_ERROR("bond_real_space_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine bond_real_space_initialise - - subroutine bond_real_space_finalise(this,error) - type(bond_real_space), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%bond_cutoff = 0.0_dp - this%bond_transition_width = 0.0_dp - this%cutoff = 0.0_dp - this%transition_width = 0.0_dp - this%atom_sigma = 0.0_dp - this%max_neighbours = 0 - - this%initialised = .false. - - endsubroutine bond_real_space_finalise - - subroutine atom_real_space_initialise(this,args_str,error) - type(atom_real_space), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Space cutoff for atom_real_space-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.00', this%cutoff_transition_width, help_string="Space transition width for atom_real_space-type descriptors") - call param_register(params, 'l_max', '0', this%l_max, help_string="Cutoff for spherical harmonics expansion") - call param_register(params, 'alpha', '1.0', this%alpha, help_string="Width of atomic Gaussians") - call param_register(params, 'zeta', '1.0', this%zeta, help_string="Exponent of covariance function") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='atom_real_space_initialise args_str')) then - RAISE_ERROR("atom_real_space_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine atom_real_space_initialise - - subroutine atom_real_space_finalise(this,error) - type(atom_real_space), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.0_dp - this%l_max = 0 - this%alpha = 0.0_dp - this%zeta = 0.0_dp - - this%initialised = .false. - - endsubroutine atom_real_space_finalise - - subroutine power_so3_initialise(this,args_str,error) - type(power_so3), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - integer :: n_species - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for power_so3-type descriptors") - call param_register(params, 'min_cutoff', '0.00', this%min_cutoff, help_string="Cutoff for minimal distances in power_so3-type descriptors") - call param_register(params, 'l_max', '4', this%l_max, help_string="L_max for power_so3-type descriptors") - call param_register(params, 'n_max', '4', this%n_max, help_string="N_max for power_so3-type descriptors") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'n_species', '1', n_species, help_string="Number of species for the descriptor") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='power_so3_initialise args_str')) then - RAISE_ERROR("power_so3_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - allocate(this%species_Z(n_species), this%w(n_species)) - - call initialise(params) - if( n_species == 1 ) then - call param_register(params, 'species_Z', '0', this%species_Z(1), help_string="Atomic number of species") - call param_register(params, 'w', '1.0', this%w(1), help_string="Weight associated to each atomic type") - else - call param_register(params, 'species_Z', PARAM_MANDATORY, this%species_Z, help_string="Atomic number of species") - call param_register(params, 'w', PARAM_MANDATORY, this%w, help_string="Weight associated to each atomic type") - endif - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='power_so3_initialise args_str')) then - RAISE_ERROR("power_so3_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call initialise(this%Radial,this%n_max,this%cutoff,this%min_cutoff,error) - - this%initialised = .true. - - endsubroutine power_so3_initialise - - subroutine power_so3_finalise(this,error) - type(power_so3), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%min_cutoff = 0.0_dp - this%l_max = 0 - this%n_max = 0 - this%Z = 0 - - if(allocated(this%species_Z)) deallocate(this%species_Z) - if(allocated(this%w)) deallocate(this%w) - - call finalise(this%Radial) - - this%initialised = .false. - - endsubroutine power_so3_finalise - - subroutine power_so4_initialise(this,args_str,error) - type(power_so4), intent(inout), target :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - call finalise(this) - - call initialise(this%fourier_SO4,args_str,error) - - this%cutoff => this%fourier_SO4%cutoff - this%z0_ratio => this%fourier_SO4%z0_ratio - this%z0 => this%fourier_SO4%z0 - this%j_max => this%fourier_SO4%j_max - this%Z => this%fourier_SO4%Z - this%cutoff => this%fourier_SO4%cutoff - this%species_Z => this%fourier_SO4%species_Z - this%w => this%fourier_SO4%w - - this%initialised = .true. - - endsubroutine power_so4_initialise - - subroutine power_so4_finalise(this,error) - type(power_so4), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - - call finalise(this%fourier_SO4,error) - - this%cutoff => null() - this%z0_ratio => null() - this%z0 => null() - this%j_max => null() - this%Z => null() - this%cutoff => null() - this%species_Z => null() - this%w => null() - - this%initialised = .false. - - endsubroutine power_so4_finalise - - subroutine soap_initialise(this,args_str,error) - type(soap), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - real(dp) :: alpha_basis, spacing_basis, cutoff_basis, basis_error_exponent - real(dp), dimension(:,:), allocatable :: covariance_basis, overlap_basis, cholesky_overlap_basis - integer :: i, j, xml_version - - type(LA_Matrix) :: LA_covariance_basis, LA_overlap_basis - character(len=STRING_LENGTH) :: species_Z_str - logical :: has_n_species, has_species_Z, has_central_reference_all_species - - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', PARAM_MANDATORY, this%cutoff, help_string="Cutoff for soap-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.50', this%cutoff_transition_width, help_string="Cutoff transition width for soap-type descriptors") - - call param_register(params, 'cutoff_dexp', '0', this%cutoff_dexp, help_string="Cutoff decay exponent") - call param_register(params, 'cutoff_scale', '1.0', this%cutoff_scale, help_string="Cutoff decay scale") - call param_register(params, 'cutoff_rate', '1.0', this%cutoff_rate, help_string="Inverse cutoff decay rate") - - call param_register(params, 'l_max', PARAM_MANDATORY, this%l_max, help_string="L_max (spherical harmonics basis band limit) for soap-type descriptors") - call param_register(params, 'n_max', PARAM_MANDATORY, this%n_max, help_string="N_max (number of radial basis functions) for soap-type descriptors") - call param_register(params, 'atom_sigma', PARAM_MANDATORY, this%atom_sigma, help_string="Width of atomic Gaussians for soap-type descriptors") - call param_register(params, 'central_weight', '1.0', this%central_weight, help_string="Weight of central atom in environment") - call param_register(params, 'central_reference_all_species', 'F', this%central_reference_all_species, has_value_target=has_central_reference_all_species, & - help_string="Place a Gaussian reference for all atom species densities."// & - "By default (F) only consider when neighbour is the same species as centre") - call param_register(params, 'average', 'F', this%global, help_string="Whether to calculate averaged SOAP - one descriptor per atoms object. If false (default) atomic SOAP is returned.") - call param_register(params, 'diagonal_radial', 'F', this%diagonal_radial, help_string="Only return the n1=n2 elements of the power spectrum.") - - call param_register(params, 'covariance_sigma0', '0.0', this%covariance_sigma0, help_string="sigma_0 parameter in polynomial covariance function") - call param_register(params, 'normalise', 'T', this%normalise, help_string="Normalise descriptor so magnitude is 1. In this case the kernel of two equivalent environments is 1.") - call param_register(params, 'basis_error_exponent', '10.0', basis_error_exponent, help_string="10^(-basis_error_exponent) is the max difference between the target and the expanded function") - - call param_register(params, 'n_Z', '1', this%n_Z, help_string="How many different types of central atoms to consider") - call param_register(params, 'n_species', '1', this%n_species, has_value_target=has_n_species, help_string="Number of species for the descriptor") - call param_register(params, 'species_Z', '', species_Z_str, has_value_target=has_species_Z, help_string="Atomic number of species") - call param_register(params, 'xml_version', '1426512068', xml_version, help_string="Version of GAP the XML potential file was created") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='soap_initialise args_str')) then - RAISE_ERROR("soap_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - ! backwards compatibility: the default used to be different before this version number - if( xml_version < 1426512068 ) this%central_reference_all_species = .true. - - allocate(this%species_Z(this%n_species)) - allocate(this%Z(this%n_Z)) - - if( has_species_Z .and. .not. has_n_species ) then - RAISE_ERROR("soap_initialise: is species_Z is present, n_species must be present, too.",error) - endif - - call initialise(params) - - if( this%cutoff_dexp < 0 ) then - RAISE_ERROR("soap_initialise: cutoff_dexp may not be less than 0",error) - endif - - if( this%cutoff_scale <= 0.0_dp ) then - RAISE_ERROR("soap_initialise: cutoff_scale must be greater than 0",error) - endif - - if( this%cutoff_rate < 0.0_dp ) then - RAISE_ERROR("soap_initialise: cutoff_rate may not be less than 0",error) - endif - - if( has_n_species ) then - if(this%n_species == 1) then - call param_register(params, 'species_Z', '0', this%species_Z(1), help_string="Atomic number of species") - else - call param_register(params, 'species_Z', '//MANDATORY//', this%species_Z, help_string="Atomic number of species") - endif - else - call param_register(params, 'species_Z', '0', this%species_Z(1), help_string="Atomic number of species") - endif - - if( .not. has_central_reference_all_species .and. this%n_species == 1 ) this%central_reference_all_species = .true. - - if( this%n_Z == 1 ) then - call param_register(params, 'Z', '0', this%Z(1), help_string="Atomic number of central atom, 0 is the wild-card") - else - call param_register(params, 'Z', '//MANDATORY//', this%Z, help_string="Atomic numbers to be considered for central atom, must be a list") - endif - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='soap_initialise args_str')) then - RAISE_ERROR("soap_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%alpha = 0.5_dp / this%atom_sigma**2 - - alpha_basis = this%alpha - cutoff_basis = this%cutoff + this%atom_sigma * sqrt(2.0_dp * basis_error_exponent * log(10.0_dp)) - spacing_basis = cutoff_basis / this%n_max - - allocate(this%r_basis(this%n_max), this%transform_basis(this%n_max,this%n_max), & - covariance_basis(this%n_max,this%n_max), overlap_basis(this%n_max,this%n_max), this%cholesky_overlap_basis(this%n_max,this%n_max)) - - !this%r_basis(this%n_max) = cutoff_basis - !do i = this%n_max-1, 1, -1 - ! this%r_basis(i) = this%r_basis(i+1) - spacing_basis - !enddo - - this%r_basis(1) = 0.0_dp - do i = 2, this%n_max - this%r_basis(i) = this%r_basis(i-1) + spacing_basis - enddo - - do i = 1, this%n_max - do j = 1, this%n_max - covariance_basis(j,i) = exp(-alpha_basis * (this%r_basis(i) - this%r_basis(j))**2) - !overlap_basis(j,i) = exp(-0.5_dp * alpha_basis* (this%r_basis(i) - this%r_basis(j))**2) * ( 1.0_dp + erf( sqrt(alpha_basis/2.0_dp) * (this%r_basis(i) + this%r_basis(j)) ) ) - !print*, 'A', exp( -alpha_basis*(this%r_basis(i)**2+this%r_basis(j)**2) ) - !print*, 'B', sqrt(2.0_dp) * alpha_basis**1.5_dp * (this%r_basis(i) + this%r_basis(j)) - !print*, 'C', alpha_basis*exp(0.5_dp * alpha_basis * (this%r_basis(i) + this%r_basis(j))**2)*sqrt(PI)*(1.0_dp + alpha_basis*(this%r_basis(i) + this%r_basis(j))**2 ) - !print*, 'D', ( 1.0_dp + erf( sqrt(alpha_basis/2.0_dp) * (this%r_basis(i) + this%r_basis(j)) ) ) - !overlap_basis(j,i) = exp( -alpha_basis*(this%r_basis(i)**2+this%r_basis(j)**2) ) * & - ! ( sqrt(2.0_dp) * alpha_basis**1.5_dp * (this%r_basis(i) + this%r_basis(j)) + & - ! alpha_basis*exp(0.5_dp * alpha_basis * (this%r_basis(i) + this%r_basis(j))**2)*sqrt(PI)*(1.0_dp + alpha_basis*(this%r_basis(i) + this%r_basis(j))**2 ) * & - ! ( 1.0_dp + erf( sqrt(alpha_basis/2.0_dp) * (this%r_basis(i) + this%r_basis(j)) ) ) ) - - overlap_basis(j,i) = ( exp( -alpha_basis*(this%r_basis(i)**2+this%r_basis(j)**2) ) * & - sqrt(2.0_dp) * alpha_basis**1.5_dp * (this%r_basis(i) + this%r_basis(j)) + & - alpha_basis*exp(-0.5_dp * alpha_basis * (this%r_basis(i) - this%r_basis(j))**2)*sqrt(PI)*(1.0_dp + alpha_basis*(this%r_basis(i) + this%r_basis(j))**2 ) * & - ( 1.0_dp + erf( sqrt(alpha_basis/2.0_dp) * (this%r_basis(i) + this%r_basis(j)) ) ) ) - enddo - enddo - - !overlap_basis = overlap_basis * sqrt(pi / ( 8.0_dp * alpha_basis ) ) - overlap_basis = overlap_basis / sqrt(128.0_dp * alpha_basis**5) - - call initialise(LA_covariance_basis,covariance_basis) - call initialise(LA_overlap_basis,overlap_basis) - call LA_Matrix_Factorise(LA_overlap_basis, this%cholesky_overlap_basis) - do i = 1, this%n_max - do j = 1, i-1 !i + 1, this%n_max - this%cholesky_overlap_basis(j,i) = 0.0_dp - enddo - enddo - - call Matrix_Solve(LA_covariance_basis,this%cholesky_overlap_basis,this%transform_basis) - - call finalise(LA_covariance_basis) - call finalise(LA_overlap_basis) - - if(allocated(covariance_basis)) deallocate(covariance_basis) - if(allocated(overlap_basis)) deallocate(overlap_basis) - if(allocated(cholesky_overlap_basis)) deallocate(cholesky_overlap_basis) - - this%initialised = .true. - - endsubroutine soap_initialise - - subroutine soap_finalise(this,error) - type(soap), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff_dexp = 0 - this%cutoff_scale = 1.0_dp - this%cutoff_rate = 1.0_dp - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.0_dp - this%l_max = 0 - this%alpha = 0.0_dp - this%central_weight = 0.0_dp - this%central_reference_all_species = .false. - this%global = .false. - this%diagonal_radial = .false. - this%covariance_sigma0 = 0.0_dp - this%normalise = .true. - - this%n_max = 0 - this%n_Z = 0 - this%n_species = 0 - - if(allocated(this%r_basis)) deallocate(this%r_basis) - if(allocated(this%transform_basis)) deallocate(this%transform_basis) - if(allocated(this%cholesky_overlap_basis)) deallocate(this%cholesky_overlap_basis) - if(allocated(this%species_Z)) deallocate(this%species_Z) - if(allocated(this%Z)) deallocate(this%Z) - - this%initialised = .false. - - endsubroutine soap_finalise - - subroutine AN_monomer_initialise(this,args_str,error) - type(AN_monomer), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for AN_monomer-type descriptors") - call param_register(params, 'atomic_number', '1', this%atomic_number, help_string="Atomic number in AN_monomer-type descriptors") - call param_register(params, 'N', '4', this%N, help_string="Number of atoms in cluster") - call param_register(params, 'do_atomic', 'T', this%do_atomic, help_string="Descriptors are cluster based or atom-based") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='AN_monomer_initialise args_str')) then - RAISE_ERROR("AN_monomer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine AN_monomer_initialise - - subroutine AN_monomer_finalise(this,error) - type(AN_monomer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%atomic_number = 0 - this%N = 0 - - this%do_atomic = .false. - this%initialised = .false. - - endsubroutine AN_monomer_finalise - - subroutine general_monomer_initialise(this,args_str,error) - type(general_monomer), intent(inout) :: this - character(len=*), intent(in) :: args_str - character(len=STRING_LENGTH) :: signature_string - character(len=STRING_LENGTH), dimension(99) :: signature_fields - integer, optional, intent(out) :: error - integer :: i,n_atoms,j - - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for general_monomer-type descriptors") - call param_register(params, 'signature', PARAM_MANDATORY, signature_string, help_string="Atomic numbers of monomer one, format {Z1 Z2 Z3 ...}") - call param_register(params, 'atom_ordercheck', 'true', this%atom_ordercheck, help_string="T: find molecules. F: go by order of atoms") - call param_register(params, 'strict', 'true', this%strict, help_string="Raise error if not all atoms assigned to monomer") - call param_register(params, 'power', '1.0', this%power, help_string="Power of distances to be used in the kernel") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='general_monomer_initialise args_str')) then - RAISE_ERROR("general_monomer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call split_string(signature_string,' ','{}',signature_fields(:),n_atoms,matching=.true.) - allocate(this%signature(n_atoms)) - - do i=1,n_atoms - this%signature(i) = string_to_int(signature_fields(i)) - end do - - call permutation_data_initialise(this%permutation_data,signature_one=this%signature,error=error) - - this%initialised = .true. - - endsubroutine general_monomer_initialise - - subroutine general_monomer_finalise(this,error) - type(general_monomer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - - this%cutoff = 0.0_dp - this%power = 1.0_dp - if(allocated(this%signature)) deallocate(this%signature) - - this%initialised = .false. - - endsubroutine general_monomer_finalise - - subroutine com_dimer_initialise(this,args_str,error) - type(com_dimer), intent(inout) :: this - character(len=*), intent(in) :: args_str - character(len=STRING_LENGTH) :: signature_one_string, signature_two_string - character(len=STRING_LENGTH), dimension(99) :: signature_one_fields, signature_two_fields - integer, optional, intent(out) :: error - integer :: i, n_atoms_one, n_atoms_two - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff(intermolecular) for com_dimer-type descriptors") - call param_register(params, 'monomer_one_cutoff', '0.00', this%monomer_one_cutoff, help_string="Cutoff(mono1) for com_dimer-type descriptors") - call param_register(params, 'monomer_two_cutoff', '0.00', this%monomer_two_cutoff, help_string="Cutoff(mono2) for com_dimer-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.50', this%cutoff_transition_width, help_string="Width of smooth cutoff region for com_dimer-type descriptors") - call param_register(params, 'atom_ordercheck', 'true', this%atom_ordercheck, help_string="T: find molecules. F: go by order of atoms") - call param_register(params, 'strict', 'true', this%strict, help_string="Raise error if not all atoms assigned to monomer or if no monomer pairs found") - call param_register(params, 'mpifind', 'false', this%mpifind, help_string="Use find_monomer_pairs_MPI") - call param_register(params, 'signature_one', PARAM_MANDATORY, signature_one_string, help_string="Atomic numbers of monomer one, format {Z1 Z2 Z3 ...}") - call param_register(params, 'signature_two', PARAM_MANDATORY, signature_two_string, help_string="Atomic numbers of monomer two, format {Z1 Z2 Z3 ...}") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='com_dimer_initialise args_str')) then - RAISE_ERROR("com_dimer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call initialise(this%transfer_parameters, args_str, error) - - call split_string(signature_one_string,' ','{}',signature_one_fields(:),n_atoms_one,matching=.true.) - call split_string(signature_two_string,' ','{}',signature_two_fields(:),n_atoms_two,matching=.true.) - allocate(this%signature_one(n_atoms_one)) - allocate(this%signature_two(n_atoms_two)) - - do i=1,n_atoms_one - this%signature_one(i) = string_to_int(signature_one_fields(i)) - end do - do i=1,n_atoms_two - this%signature_two(i) = string_to_int(signature_two_fields(i)) - end do - - this%monomers_identical=.False. - if (size(this%signature_one) == size(this%signature_two)) then - if (all(this%signature_one == this%signature_two)) then - this%monomers_identical = .True. - end if - end if - - this%initialised = .true. - endsubroutine com_dimer_initialise - - subroutine com_dimer_finalise(this,error) - type(com_dimer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - if(.not. this%initialised) return - - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.0_dp - this%monomer_one_cutoff = 0.0_dp - this%monomer_two_cutoff = 0.0_dp - this%atom_ordercheck = .true. - this%use_smooth_cutoff = .false. - if(allocated(this%signature_one)) deallocate(this%signature_one) - if(allocated(this%signature_two)) deallocate(this%signature_two) - - this%initialised = .false. - - endsubroutine com_dimer_finalise - - subroutine general_dimer_initialise(this,args_str,error) - type(general_dimer), intent(inout) :: this - character(len=*), intent(in) :: args_str - character(len=STRING_LENGTH) :: signature_one_string, signature_two_string - character(len=STRING_LENGTH), dimension(99) :: signature_one_fields, signature_two_fields - integer, optional, intent(out) :: error - integer :: i,j, n_atoms_one, n_atoms_two, dimer_size, start, finish, d - logical, dimension(:,:), allocatable :: intermolecular - integer, dimension(:), allocatable :: signature - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff(intermolecular) for general_dimer-type descriptors") - call param_register(params, 'monomer_one_cutoff', '0.00', this%monomer_one_cutoff, help_string="Cutoff(mono1) for general_dimer-type descriptors") - call param_register(params, 'monomer_two_cutoff', '0.00', this%monomer_two_cutoff, help_string="Cutoff(mono2) for general_dimer-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.50', this%cutoff_transition_width, help_string="Width of smooth cutoff region for general_dimer-type descriptors") - call param_register(params, 'internal_swaps_only', 'true', this%internal_swaps_only, help_string="F: energies will be symmetrised over swaps of nuclei between monomers") - call param_register(params, 'atom_ordercheck', 'true', this%atom_ordercheck, help_string="T: find molecules. F: go by order of atoms") - call param_register(params, 'double_count', 'false', this%double_count, help_string="T: double count when constructing the dimers, for compatibility with water dimer descriptor, default False") - call param_register(params, 'strict', 'true', this%strict, help_string="Raise error if not all atoms assigned to monomer or if no monomer pairs found") - call param_register(params, 'strict_mask', 'true', this%strict_mask, help_string="Raise error if atom mask includes only part of a monomer") - call param_register(params, 'use_com', 'false', this%use_com, help_string="Use COM instead of COG") - call param_register(params, 'mpifind', 'false', this%mpifind, help_string="Use find_monomer_pairs_MPI") - call param_register(params, 'signature_one', PARAM_MANDATORY, signature_one_string, help_string="Atomic numbers of monomer one, format {Z1 Z2 Z3 ...}") - call param_register(params, 'signature_two', PARAM_MANDATORY, signature_two_string, help_string="Atomic numbers of monomer two, format {Z1 Z2 Z3 ...}") - call param_register(params, 'power', '1.00', this%power, help_string="Power of interatomic distances to be used in the kernel.") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='general_dimer_initialise args_str')) then - RAISE_ERROR("general_dimer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call initialise(this%transfer_parameters, args_str, error) - - call split_string(signature_one_string,' ','{}',signature_one_fields(:),n_atoms_one,matching=.true.) - call split_string(signature_two_string,' ','{}',signature_two_fields(:),n_atoms_two,matching=.true.) - allocate(this%signature_one(n_atoms_one)) - allocate(this%signature_two(n_atoms_two)) - - do i=1,n_atoms_one - this%signature_one(i) = string_to_int(signature_one_fields(i)) - end do - do i=1,n_atoms_two - this%signature_two(i) = string_to_int(signature_two_fields(i)) - end do - - this%monomers_identical=.False. - if (size(this%signature_one) == size(this%signature_two)) then - if (all(this%signature_one == this%signature_two)) then - this%monomers_identical = .True. - end if - end if - - call permutation_data_initialise(this%permutation_data,signature_one=this%signature_one,signature_two=this%signature_two,internal_swaps_only=this%internal_swaps_only,error=error) - - dimer_size=n_atoms_one + n_atoms_two - d=dimer_size*(dimer_size-1)/2 - - allocate(signature(dimer_size)) - allocate(intermolecular(dimer_size,dimer_size)) - allocate(this%is_intermolecular(d)) - allocate(this%cutoff_contributor(d)) - allocate(this%component_atoms(d,2)) - - signature(1:n_atoms_one) = this%signature_one - signature(1+n_atoms_one:dimer_size) = this%signature_two - intermolecular = .false. - this%cutoff_contributor=.false. - - do i=1,n_atoms_one - do j=1+n_atoms_one,dimer_size - intermolecular(i,j)=.true. - end do - end do - - start = 0 - finish=dimer_size-1 - do i=1,dimer_size - do j=1,finish-start - this%is_intermolecular(start+j) = intermolecular(i,i+j) - this%component_atoms(start+j,:) = (/ i, i+j /) - end do - start = finish - finish=finish + dimer_size-i-1 - end do - - - do i=1,d - if (this%is_intermolecular(i)) then - if (.not. signature(this%component_atoms(i,1))==1 ) then - if (.not. signature(this%component_atoms(i,2))==1 ) then - this%cutoff_contributor(i)=.true. - end if - end if - end if - end do - - this%initialised = .true. - - deallocate(signature) - deallocate(intermolecular) - endsubroutine general_dimer_initialise - - subroutine general_dimer_finalise(this,error) - type(general_dimer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - if(.not. this%initialised) return - - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.0_dp - this%monomer_one_cutoff = 0.0_dp - this%monomer_two_cutoff = 0.0_dp - this%atom_ordercheck = .true. - this%internal_swaps_only = .true. - this%use_smooth_cutoff = .false. - this%power = 1.0_dp - if(allocated(this%signature_one)) deallocate(this%signature_one) - if(allocated(this%signature_two)) deallocate(this%signature_two) - if(allocated(this%is_intermolecular)) deallocate(this%is_intermolecular) - if(allocated(this%component_atoms)) deallocate(this%component_atoms) - if(allocated(this%cutoff_contributor)) deallocate(this%cutoff_contributor) - - this%initialised = .false. - - endsubroutine general_dimer_finalise - - subroutine general_trimer_initialise(this,args_str,error) - type(general_trimer), intent(inout) :: this - character(len=*), intent(in) :: args_str - character(len=STRING_LENGTH) :: signature_one_string, signature_two_string, signature_three_string - character(len=STRING_LENGTH), dimension(99) :: signature_one_fields, signature_two_fields, signature_three_fields - integer, optional, intent(out) :: error - integer :: i,j, n_atoms_one, n_atoms_two, n_atoms_three, trimer_size, start, finish,d - logical, dimension(:,:), allocatable :: intermolecular - integer, dimension(:), allocatable :: signature - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff(intermolecular) for general_trimer-type descriptors") - call param_register(params, 'monomer_one_cutoff', '0.00', this%monomer_one_cutoff, help_string="Cutoff(mono1) for general_trimer-type descriptors") - call param_register(params, 'monomer_two_cutoff', '0.00', this%monomer_two_cutoff, help_string="Cutoff(mono2) for general_trimer-type descriptors") - call param_register(params, 'monomer_three_cutoff', '0.00', this%monomer_three_cutoff, help_string="Cutoff(mono3) for general_trimer-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.50', this%cutoff_transition_width, help_string="Width of smooth cutoff region for general_trimer-type descriptors") - call param_register(params, 'internal_swaps_only', 'true', this%internal_swaps_only, help_string="F: energies will be symmetrised over swaps of nuclei between monomers") - call param_register(params, 'atom_ordercheck', 'true', this%atom_ordercheck, help_string="T: find molecules. F: go by order of atoms") - call param_register(params, 'strict', 'true', this%strict, help_string="Raise error if not all atoms assigned to monomer or if no monomer pairs found") - call param_register(params, 'use_com', 'false', this%use_com, help_string="Use COM instead of COG") - call param_register(params, 'mpifind', 'false', this%mpifind, help_string="Use find_monomer_triplets_MPI") - call param_register(params, 'signature_one', PARAM_MANDATORY, signature_one_string, help_string="Atomic numbers of monomer one, format {Z1 Z2 Z3 ...}") - call param_register(params, 'signature_two', PARAM_MANDATORY, signature_two_string, help_string="Atomic numbers of monomer two, format {Z1 Z2 Z3 ...}") - call param_register(params, 'signature_three', PARAM_MANDATORY, signature_three_string, help_string="Atomic numbers of monomer three, format {Z1 Z2 Z3 ...}") - call param_register(params, 'power', '1.0', this%power, help_string="Power of distances to be used in the kernel") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='general_trimer_initialise args_str')) then - RAISE_ERROR("general_trimer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call split_string(signature_one_string,' ','{}',signature_one_fields(:),n_atoms_one,matching=.true.) - call split_string(signature_two_string,' ','{}',signature_two_fields(:),n_atoms_two,matching=.true.) - call split_string(signature_three_string,' ','{}',signature_three_fields(:),n_atoms_three,matching=.true.) - allocate(this%signature_one(n_atoms_one)) - allocate(this%signature_two(n_atoms_two)) - allocate(this%signature_three(n_atoms_three)) - - do i=1,n_atoms_one - this%signature_one(i) = string_to_int(signature_one_fields(i)) - end do - do i=1,n_atoms_two - this%signature_two(i) = string_to_int(signature_two_fields(i)) - end do - do i=1,n_atoms_three - this%signature_three(i) = string_to_int(signature_three_fields(i)) - end do - - this%one_two_identical = .false. - this%one_three_identical = .false. - this%two_three_identical = .false. - - if (size(this%signature_one) == size(this%signature_two)) then - if (all(this%signature_one == this%signature_two)) then - this%one_two_identical = .True. - end if - end if - - if (size(this%signature_one) == size(this%signature_three)) then - if (all(this%signature_one == this%signature_three)) then - this%one_three_identical = .True. - end if - end if - - if (size(this%signature_two) == size(this%signature_three)) then - if (all(this%signature_two == this%signature_three)) then - this%two_three_identical = .True. - end if - end if - - call permutation_data_initialise(this%permutation_data,signature_one=this%signature_one,signature_two=this%signature_two,signature_three=this%signature_three,internal_swaps_only=this%internal_swaps_only,error=error) - - trimer_size=n_atoms_one + n_atoms_two + n_atoms_three - d=trimer_size*(trimer_size-1)/2 - - allocate(signature(trimer_size)) - allocate(intermolecular(trimer_size,trimer_size)) - allocate(this%is_intermolecular(d)) - allocate(this%cutoff_contributor(d)) - allocate(this%component_atoms(d,2)) - - signature(1:n_atoms_one) = this%signature_one - signature(1+n_atoms_one:n_atoms_one+n_atoms_two) = this%signature_two - signature(1+n_atoms_one+n_atoms_two:trimer_size) = this%signature_three - intermolecular = .false. - this%cutoff_contributor=.false. - - do i=1,n_atoms_one - do j=1+n_atoms_one,trimer_size - intermolecular(i,j)=.true. - intermolecular(j,i)=.true. - end do - end do - do i=1+n_atoms_one,n_atoms_one+n_atoms_two - do j=1+n_atoms_one+n_atoms_two,trimer_size - intermolecular(i,j)=.true. - intermolecular(j,i)=.true. - end do - end do - - start = 0 - finish=trimer_size-1 - do i=1,trimer_size - do j=1,finish-start - this%is_intermolecular(start+j) = intermolecular(i,i+j) - this%component_atoms(start+j,:) = (/ i, i+j /) - end do - start = finish - finish=finish + trimer_size-i-1 - end do - - do i=1,d - if (this%is_intermolecular(i)) then - if (.not. signature(this%component_atoms(i,1))==1 ) then - if (.not. signature(this%component_atoms(i,2))==1 ) then - this%cutoff_contributor(i)=.true. - end if - end if - end if - end do - - this%initialised = .true. - deallocate(signature) - deallocate(intermolecular) - endsubroutine general_trimer_initialise - - subroutine general_trimer_finalise(this,error) - type(general_trimer), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.0_dp - this%monomer_one_cutoff = 0.0_dp - this%monomer_two_cutoff = 0.0_dp - this%monomer_three_cutoff = 0.0_dp - this%atom_ordercheck = .true. - this%internal_swaps_only = .true. - this%use_smooth_cutoff = .false. - this%power = 1.0_dp - if(allocated(this%signature_one)) deallocate(this%signature_one) - if(allocated(this%signature_two)) deallocate(this%signature_two) - if(allocated(this%signature_three)) deallocate(this%signature_three) - - this%initialised = .false. - - endsubroutine general_trimer_finalise - - subroutine rdf_initialise(this,args_str,error) - type(rdf), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - integer :: i - real(dp) :: r_min, r_max - logical :: has_r_max, has_w_gauss - - INIT_ERROR(error) - - call finalise(this) - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for rdf-type descriptors") - call param_register(params, 'transition_width', '0.20', this%transition_width, help_string="Width of transition region from 1 to 0") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'r_min', '0.0', r_min, help_string="Atomic number of central atom") - call param_register(params, 'r_max', '0.0', r_max, has_value_target = has_r_max, help_string="Atomic number of central atom") - call param_register(params, 'n_gauss', '10', this%n_gauss, help_string="Atomic number of central atom") - call param_register(params, 'w_gauss', '0.0', this%w_gauss, has_value_target = has_w_gauss, help_string="Atomic number of central atom") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='rdf_initialise args_str')) then - RAISE_ERROR("rdf_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - allocate(this%r_gauss(this%n_gauss)) - if(.not. has_w_gauss) this%w_gauss = this%cutoff / this%n_gauss * 2.0_dp - if(.not. has_r_max) r_max = this%cutoff - this%w_gauss / 2.0_dp - this%r_gauss = real( (/(i,i=1,this%n_gauss)/), kind=dp ) / real(this%n_gauss,kind=dp) * (r_max - r_min) + r_min - - this%initialised = .true. - - endsubroutine rdf_initialise - - subroutine rdf_finalise(this,error) - type(rdf), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%transition_width = 0.0_dp - this%Z = 0 - this%n_gauss = 0 - if( allocated(this%r_gauss) ) deallocate(this%r_gauss) - - this%initialised = .false. - - endsubroutine rdf_finalise - - subroutine as_distance_2b_initialise(this,args_str,error) - type(as_distance_2b), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'min_cutoff', '0.00', this%min_cutoff, help_string="Lower cutoff for as_distance_2b-type descriptors") - call param_register(params, 'max_cutoff', PARAM_MANDATORY, this%max_cutoff, help_string="Higher cutoff for as_distance_2b-type descriptors") - call param_register(params, 'as_cutoff', PARAM_MANDATORY, this%as_cutoff, help_string="Cutoff of asymmetricity") - call param_register(params, 'overlap_alpha', '0.50', this%as_cutoff, help_string="Cutoff of asymmetricity") - call param_register(params, 'min_transition_width', '0.50', this%min_transition_width, help_string="Transition width of lower cutoff for as_distance_2b-type descriptors") - call param_register(params, 'max_transition_width', '0.50', this%max_transition_width, help_string="Transition width of higher cutoff for as_distance_2b-type descriptors") - call param_register(params, 'as_transition_width', '0.10', this%as_transition_width, help_string="Transition width of asymmetricity cutoff for as_distance_2b-type descriptors") - call param_register(params, 'coordination_cutoff', PARAM_MANDATORY, this%coordination_cutoff, help_string="Cutoff for coordination function in as_distance_2b-type descriptors") - call param_register(params, 'coordination_transition_width', '0.50', this%coordination_transition_width, help_string="Transition width for as_distance_2b-type descriptors") - call param_register(params, 'Z1', '0', this%Z1, help_string="Atom type #1 in bond") - call param_register(params, 'Z2', '0', this%Z2, help_string="Atom type #2 in bond") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='as_distance_2b_initialise args_str')) then - RAISE_ERROR("as_distance_2b_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine as_distance_2b_initialise - - subroutine as_distance_2b_finalise(this,error) - type(as_distance_2b), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%min_cutoff = 0.0_dp - this%max_cutoff = 0.0_dp - this%as_cutoff = 0.0_dp - this%overlap_alpha = 0.0_dp - this%min_transition_width = 0.0_dp - this%max_transition_width = 0.0_dp - this%as_transition_width = 0.0_dp - this%coordination_cutoff = 0.0_dp - this%coordination_transition_width = 0.0_dp - this%Z1 = 0 - this%Z2 = 0 - - this%initialised = .false. - - endsubroutine as_distance_2b_finalise - - subroutine molecule_lo_d_initialise(this,args_str,error) - type(molecule_lo_d), intent(inout) :: this - character(len=*), intent(in) :: args_str - character(len=STRING_LENGTH) :: signature_string, atoms_template_string, symmetry_string, symmetry_property_name, append_file, append_string - character(len=STRING_LENGTH), dimension(99) :: signature_fields, symmetry_rows, row_fields, append_rows,template_rows - integer, optional, intent(out) :: error - integer :: i,n_atoms,j,n_symm_rows, current_depth, start, finish, i_component, atom_j, N_atom_pairs, atom_k,n_append_rows, old_size, n_perms,n_template_rows - integer, dimension(:,:), allocatable :: equivalents_input, bonds_to_append, tmp_permutations - logical :: signature_given, symmetries_given, add_bond, j_k_present, k_j_present - integer, dimension(:,:), pointer :: symm_2d - integer, dimension(:), pointer :: symm_1d - - type(Table) :: atom_a, atom_b - type(CInOutput) :: tempatoms - type(inoutput) :: tempfile - type(inoutput) :: symmetry_inout - type(inoutput) :: append_inout - type(Connection) :: at_connect - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for molecule_lo_d-type descriptors") - call param_register(params, 'atoms_template_string', PARAM_MANDATORY, atoms_template_string , help_string="Atoms object which serves as a template - written to a string") - call param_register(params, 'neighbour_graph_depth', '2', this%neighbour_graph_depth, help_string="Ignore distances between atoms separated by more than this number of bonds") - call param_register(params, 'signature', '', signature_string, help_string="Atomic numbers of monomer one, format {Z1 Z2 Z3 ...}") - call param_register(params, 'symmetry_property_name', 'symm', symmetry_property_name, help_string="Integer arrays specifying symmetries - see header of make_permutations_v2.f95 for format") - call param_register(params, 'append_file', '', append_file, help_string="Pairs of atoms for which we want the distance to be additionally included in the descriptor") - call param_register(params, 'atom_ordercheck', 'T', this%atom_ordercheck, help_string= & - "T: basic check that atoms in same order as in template F: assume all xyz frames have atoms in same order") - call param_register(params, 'desctype', '0', this%desctype, help_string="0: distance matrix, 1: inverse distance matrix, 2: Coulomb matrix, 3: exponential") - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='molecule_lo_d_initialise args_str')) then - RAISE_ERROR("molecule_lo_d_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - do i=1,len_trim(atoms_template_string) - if(atoms_template_string(i:i)=='%') then - atoms_template_string(i:i)=' ' - end if - end do - - ! read in the atoms object in the sample geometry file - call initialise(tempfile, filename="temp.xyz",action=OUTPUT) - call split_string(atoms_template_string,';','{}',template_rows(:),n_template_rows,matching=.true.) - do i=1,n_template_rows - call print(template_rows(i),file=tempfile) - end do - call finalise(tempfile) - call initialise(tempatoms,"temp.xyz") - call read(this%template_atoms, tempatoms, error=error) - - this%n_atoms = this%template_atoms%N - ! make a table of bonds - this copied from topology module private function create_bond_list - call calc_connect(at_connect,this%template_atoms,error=error) - if (this%neighbour_graph_depth > 0) then - do i=1,this%template_atoms%N - - call initialise(atom_a,4,0,0,0,0) - call append(atom_a,(/i,0,0,0/)) - call bfs_step(this%template_atoms,atom_a,atom_b,nneighb_only=.false.,min_images_only=.true.,alt_connect=at_connect) - - do j = 1,atom_b%N - atom_j = atom_b%int(1,j) - if (atom_j.gt.i) then - add_bond = .true. - - if (add_bond) then - call append(this%bonds,(/i,atom_j/)) - endif - - else -! call print('not added '//i//' -- '//atom_j) - endif - enddo - call finalise(atom_a) - call finalise(atom_b) - enddo - ! add atom pairs separated by up to neighbour_graph_depth bonds - current_depth = 1 - this%atom_pairs=this%bonds - do while (current_depth < this%neighbour_graph_depth) - current_depth = current_depth + 1 - call bond_list_next_layer(this%bonds,this%atom_pairs) - end do - endif - - ! append any manually specified bonds, if not already present - if (.not. append_file .eq. '') then - call initialise(append_inout,trim(append_file)) - read(append_inout%unit,'(a)') append_string - call split_string(append_string,',','{}',append_rows(:),n_append_rows,matching=.true.) - - do i = 1, n_append_rows - call split_string(append_rows(i),' ','{}',row_fields(:),n_atoms,matching=.true.) - if ( n_atoms .ne. 2) then - RAISE_ERROR("append_file incorrectly formatted, expected atoms in pairs",error) - end if - if (.not. allocated(bonds_to_append)) allocate(bonds_to_append(n_append_rows,n_atoms)) - do j=1,n_atoms - bonds_to_append(i,j) = string_to_int(row_fields(j)) - end do - end do - - - do i=1,size(bonds_to_append,1) - atom_j = bonds_to_append(i,1) - atom_k = bonds_to_append(i,2) - if ( this%neighbour_graph_depth > 0) then - N_atom_pairs = this%atom_pairs%N - j_k_present = any(this%atom_pairs%int(1,:N_atom_pairs) .eq. atom_j .and. (this%atom_pairs%int(2,:N_atom_pairs) .eq. atom_k)) - k_j_present = any(this%atom_pairs%int(1,:N_atom_pairs) .eq. atom_k .and. (this%atom_pairs%int(2,:N_atom_pairs) .eq. atom_j)) - else - j_k_present = .false. - k_j_present = .false. - end if - if (.not. j_k_present .and. .not. k_j_present) then - call append(this%atom_pairs,(/atom_j,atom_k/)) - end if - end do - end if - - !call print('table of atom pairs included in descriptor') - !call print(this%atom_pairs) - signature_given = .False. - symmetries_given = .False. - call permutation_data_finalise(this%permutation_data) - ! parse signature, if given - if (.not. signature_string .eq. '') then - signature_given=.True. - call split_string(signature_string,' ','{}',signature_fields(:),n_atoms,matching=.true.) - if (.not. n_atoms .eq. this%n_atoms) then - RAISE_ERROR('signature does not have correct number of entries', error) - end if - allocate(this%signature(this%n_atoms)) - - do i=1,this%n_atoms - this%signature(i) = string_to_int(signature_fields(i)) - end do - call permutation_data_initialise(this%permutation_data,signature_one=this%signature,error=error) - end if - - ! parse symmetries, if given - symmetries_given = has_property(this%template_atoms,symmetry_property_name) - if (symmetries_given) then - if (.not. assign_pointer(this%template_atoms, symmetry_property_name, symm_1d)) then - if (.not. assign_pointer(this%template_atoms, symmetry_property_name, symm_2d)) then - RAISE_ERROR('IPModel_Coulomb_Calc failed to assign pointer to "'//trim(symmetry_property_name)//'" property', error) - else - if (.not. allocated(equivalents_input)) allocate(equivalents_input(size(symm_2d,1),size(symm_2d,2))) - equivalents_input=symm_2d - end if - else - if (.not. allocated(equivalents_input)) allocate(equivalents_input(1,size(symm_1d))) - equivalents_input(1,:)=symm_1d - endif - call permutation_data_initialise(this%permutation_data,equivalents_input=equivalents_input,error=error) - end if - - ! If no signature is given and no symmetries are manually specified we just make a dummy signature to initialise the permutation data - if (.not. (signature_given) .and. .not. (symmetries_given)) then - allocate(this%signature(this%n_atoms)) - do i=1,this%n_atoms - this%signature(i) = i - end do - call permutation_data_initialise(this%permutation_data,signature_one=this%signature,error=error) - end if - - ! make a mapping from i,j pairs to dist_vec components, and compile the list of these which actually make up the descriptor - - this%max_dimension = this%n_atoms * (this%n_atoms -1 ) / 2 - N_atom_pairs = this%atom_pairs%N - allocate(this%component_atoms(this%max_dimension,2)) - allocate(this%included_components(N_atom_pairs)) - this%included_components=0 - - i_component=1 - start = 0 - finish=this%n_atoms-1 - do i=1,this%n_atoms - do j=1,finish-start - this%component_atoms(start+j,:) = (/ i, i+j /) - if (any(this%atom_pairs%int(1,:n_atom_pairs) .eq. i .and. this%atom_pairs%int(2,:n_atom_pairs) .eq. i+j) .or. & - any(this%atom_pairs%int(2,:n_atom_pairs) .eq. i .and. this%atom_pairs%int(1,:n_atom_pairs) .eq. i+j)) then - this%included_components(i_component) = start+j - i_component = i_component + 1 - end if - end do - start = finish - finish=finish + this%n_atoms-i-1 - end do - - if (any(this%included_components .eq. 0)) then - RAISE_ERROR('molecule_lo_d_initialise : something went wrong picking out the correct interatomic distances',error) - end if - - this%initialised = .true. - - endsubroutine molecule_lo_d_initialise - - subroutine molecule_lo_d_finalise(this,error) - type(molecule_lo_d), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - if (allocated(this%signature)) deallocate(this%signature) - if (allocated(this%component_atoms)) deallocate(this%component_atoms) - if (allocated(this%included_components)) deallocate(this%included_components) - - - - this%initialised = .false. - - endsubroutine molecule_lo_d_finalise - - subroutine alex_initialise(this,args_str,error) - type(alex), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', '0.00', this%cutoff, help_string="Cutoff for alex-type descriptors") - call param_register(params, 'Z', '0', this%Z, help_string="Atomic number of central atom") - call param_register(params, 'power_min', '5', this%power_min, help_string="Minimum power of radial basis for the descriptor") - call param_register(params, 'power_max', '10', this%power_max, help_string="Maximum power of the radial basis for the descriptor") - call param_register(params, 'n_species', '1', this%n_species, help_string="Number of species for the descriptor") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='alex_initialise args_str')) then - RAISE_ERROR("alex_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - allocate(this%species_Z(this%n_species)) - - call initialise(params) - if( this%n_species == 1 ) then - call param_register(params, 'species_Z', '0', this%species_Z(1), help_string="Atomic number of species") - else - call param_register(params, 'species_Z', PARAM_MANDATORY, this%species_Z, help_string="Atomic number of species") - endif - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='alex_initialise args_str')) then - RAISE_ERROR("alex_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - this%initialised = .true. - - endsubroutine alex_initialise - - subroutine alex_finalise(this,error) - type(alex), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - - if(allocated(this%species_Z)) deallocate(this%species_Z) - - this%initialised = .false. - - endsubroutine alex_finalise - - subroutine distance_Nb_initialise(this,args_str,error) - type(distance_Nb), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(len=STRING_LENGTH) :: default_Z = "" - integer :: i, j, k, i_p - integer :: nEdges, nConnectivities, nMonomerConnectivities - integer, dimension(:), allocatable :: n_permutations, connectivityList - integer, dimension(:,:), allocatable :: atom_permutations, distance_matrix_index, edges - - logical, dimension(:,:,:), allocatable :: allConnectivities - - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', PARAM_MANDATORY, this%cutoff, help_string="Cutoff for distance_Nb-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.5', this%cutoff_transition_width, help_string="Transition width of cutoff for distance_Nb-type descriptors") - call param_register(params, 'order', PARAM_MANDATORY, this%order, help_string="Many-body order, in terms of number of neighbours") - call param_register(params, 'compact_clusters', "F", this%compact_clusters, help_string="If true, generate clusters where the atoms have at least one connection to the central atom. If false, only clusters where all atoms are connected are generated.") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='distance_Nb_initialise args_str')) then - RAISE_ERROR("distance_Nb_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - if( this%order < 1 ) then - RAISE_ERROR("distance_Nb_initialise: order must be greater than 0",error) - endif - - allocate(this%Z(this%order)) - default_Z = "" - do i = 1, this%order - default_Z = trim(default_Z) // " 0" - enddo - - call initialise(params) - if( this%order == 1 ) then - call param_register(params, 'Z', trim(default_Z), this%Z(1), help_string="Atomic type of neighbours") - else - call param_register(params, 'Z', trim(default_Z), this%Z, help_string="Atomic type of neighbours") - endif - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='distance_Nb_initialise args_str')) then - RAISE_ERROR("distance_Nb_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - call sort_array(this%Z) - call distance_Nb_n_permutations(this%Z, n_permutations) - this%n_permutations = product(factorial_int(n_permutations)) - - allocate(atom_permutations(this%order,this%n_permutations)) - call distance_Nb_permutations(n_permutations,atom_permutations) - - allocate(distance_matrix_index(this%order,this%order)) - allocate(this%permutations( max(1,(this%order - 1) * this%order / 2), this%n_permutations)) - - if( this%order == 1 ) then - this%permutations = 1 - else - k = 0 - do i = 1, this%order - do j = i+1, this%order - k = k + 1 - distance_matrix_index(j,i) = k - distance_matrix_index(i,j) = k - enddo - enddo - - do i_p = 1, this%n_permutations - k = 0 - do i = 1, this%order - do j = i+1, this%order - k = k + 1 - this%permutations(k,i_p) = distance_matrix_index(atom_permutations(j,i_p), atom_permutations(i,i_p)) - enddo - enddo - enddo - endif - - nEdges = this%order * (this%order - 1) / 2 - allocate( edges(2,nEdges)) - - k = 0 - do i = 1, this%order - do j = i+1, this%order - k = k + 1 - edges(:,k) = (/i,j/) - enddo - enddo - - nConnectivities = 2**nEdges - - allocate(allConnectivities(this%order,this%order,nConnectivities)) - allocate(connectivityList(nEdges)) - - nMonomerConnectivities = 0 - do i = 1, nConnectivities - call integerDigits(i-1,2,connectivityList) - allConnectivities(:,:,i) = .false. - do j = 1, nEdges - allConnectivities(edges(1,j),edges(2,j),i) = ( connectivityList(j) == 1 ) - allConnectivities(edges(2,j),edges(1,j),i) = ( connectivityList(j) == 1 ) - enddo - - if( graphIsConnected( allConnectivities(:,:,i) ) ) nMonomerConnectivities = nMonomerConnectivities + 1 - enddo - - allocate(this%monomerConnectivities(this%order,this%order,nMonomerConnectivities)) - j = 0 - do i = 1, nConnectivities - if( graphIsConnected( allConnectivities(:,:,i) ) ) then - j = j + 1 - this%monomerConnectivities(:,:,j) = allConnectivities(:,:,i) - endif - enddo - - if(allocated(n_permutations)) deallocate(n_permutations) - if(allocated(atom_permutations)) deallocate(atom_permutations) - if(allocated(distance_matrix_index)) deallocate(distance_matrix_index) - if(allocated(edges)) deallocate(edges) - if(allocated(allConnectivities)) deallocate(allConnectivities) - if(allocated(connectivityList)) deallocate(connectivityList) - this%initialised = .true. - - endsubroutine distance_Nb_initialise - - subroutine distance_Nb_finalise(this,error) - type(distance_Nb), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.5_dp - this%order = 0 - this%n_permutations = 0 - this%compact_clusters = .false. - if(allocated(this%Z)) deallocate(this%Z) - if(allocated(this%permutations)) deallocate(this%permutations) - if(allocated(this%monomerConnectivities)) deallocate(this%monomerConnectivities) - - this%initialised = .false. - - endsubroutine distance_Nb_finalise - - subroutine soap_express_initialise(this,args_str,error) - type(soap_express), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - logical :: has_atom_sigma_angular - - integer :: l, k, i, j, m, n - real(dp) :: fact, fact1, fact2, ppi, atom_sigma_radial_normalised, cutoff_hard,& - s2, I_n, N_n, N_np1, N_np2, I_np1, I_np2, C2 - - type(LA_Matrix) :: LA_overlap - real(dp), dimension(:), allocatable :: s - real(dp), dimension(:,:), allocatable :: sqrt_overlap, u, v - real(dp), parameter :: sqrt_two = sqrt(2.0_dp) - - INIT_ERROR(error) - - call finalise(this) - - call initialise(params) - call param_register(params, 'cutoff', PARAM_MANDATORY, this%cutoff, help_string="Cutoff for soap-type descriptors") - call param_register(params, 'cutoff_transition_width', '0.50', this%cutoff_transition_width, help_string="Cutoff transition width for soap-type descriptors") - call param_register(params, 'cutoff_decay_rate', '4', this%cutoff_decay_rate, help_string="Cutoff decay rate for soap-type descriptors") - - call param_register(params, 'atom_sigma_radial', PARAM_MANDATORY, this%atom_sigma_radial, & - help_string="Width of atomic Gaussians for soap-type descriptors in the radial direction") - - call param_register(params, 'atom_sigma_angular', '0.0', this%atom_sigma_angular, & - help_string="Width of atomic Gaussians for soap-type descriptors in the angular direction",has_value_target = has_atom_sigma_angular) - - call param_register(params, 'central_weight', '1.0', this%central_weight, help_string="Weight of central atom in environment") - call param_register(params, 'covariance_sigma0', '0.0', this%covariance_sigma0, help_string="sigma_0 parameter in polynomial covariance function") - call param_register(params, 'atom_sigma_scaling_radial', '0.0', this%atom_sigma_scaling_radial, & - help_string="Scaling rate of radial sigma: scaled as a function of neighbour distance") - call param_register(params, 'atom_sigma_scaling_angular', '0.0', this%atom_sigma_scaling_angular, & - help_string="Scaling rate of angular sigma: scaled as a function of neighbour distance") - call param_register(params, 'amplitude_scaling', '0.0', this%amplitude_scaling, & - help_string="Scaling rate of amplitude: scaled as an inverse function of neighbour distance") - - call param_register(params, 'l_max', PARAM_MANDATORY, this%l_max, help_string="L_max (spherical harmonics basis band limit) for soap-type descriptors") - call param_register(params, 'n_max', PARAM_MANDATORY, this%n_max, help_string="N_max (number of radial basis functions) for soap-type descriptors") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true.,task='soap_express_initialise args_str')) then - RAISE_ERROR("soap_express_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - if( this%n_max > 12 ) then - RAISE_ERROR("soap_express_initialise: radial expansion unstable for n_max > 12",error) - endif - - if( this%l_max > 12 ) then - RAISE_ERROR("soap_express_initialise: radial expansion unstable for l_max > 12",error) - endif - - if( .not. has_atom_sigma_angular ) this%atom_sigma_angular = this%atom_sigma_radial - - allocate(this%semifactorial_table(0:this%l_max)) - If( this%l_max >= 0 )then - fact = 1.0_dp - do l = 0, this%l_max - fact = fact * (2.0_dp*l + 1.0_dp) - this%semifactorial_table(l) = fact - enddo - else - RAISE_ERROR("soap_express_initialise: l_max must be greater or equal to zero", error) - endif - this%angular_array_size = 1 + ( this%l_max*(this%l_max+1) ) / 2 + this%l_max - - allocate(this%Y_lm_prefactor(this%angular_array_size)) - - k = 1 - do l = 0, this%l_max - ppi = sqrt((2*l+1)/4.0_dp/PI) - fact2 = 1.0_dp - do i = 1, l - fact2 = fact2 * i - enddo - fact1 = fact2 - do m = 0, l - if( m > 0 )then - ! This is the factorial of (l-m) - fact1 = fact1 / (l+1-m) - ! This is the factorial of (l+m) - fact2 = fact2 * (l+m) - end if - this%Y_lm_prefactor(k) = ppi * sqrt(fact1/fact2) - k = k + 1 - end do - end do - - allocate( this%overlap(this%n_max, this%n_max) ) - allocate( this%basis_transformation_coefficients(this%n_max, this%n_max) ) - allocate( sqrt_overlap(this%n_max, this%n_max) ) - - ! These are the overlap integrals for the polynomial functions - do i = 1, this%n_max-1 - this%overlap(i,i) = 1.0_dp - do j = i+1, this%n_max-1 - this%overlap(i,j) = sqrt( (5.0_dp+2.0_dp*i) * (5.0_dp+2.0_dp*j) ) / real(5+i+j,kind=dp) - this%overlap(j,i) = this%overlap(i,j) - end do - end do - -! These are the overlap integrals between the Gaussian and the polynomials -! See derivation of radial expansion coefficients to understand this code -! **** New basis **** - atom_sigma_radial_normalised = this%atom_sigma_radial/this%cutoff - cutoff_hard = 1.0_dp -! ******************* - s2 = atom_sigma_radial_normalised**2 - I_n = 0.0_dp - N_n = 1.0_dp - N_np1 = descriptor_soap_express_N_a(cutoff_hard, -2) - I_np1 = sqrt(PI/2.0_dp) * atom_sigma_radial_normalised * & - erf( cutoff_hard/sqrt_two/atom_sigma_radial_normalised ) / N_np1 - C2 = s2 / cutoff_hard - do n = -1, this%n_max-1 - C2 = C2 * cutoff_hard - N_np2 = descriptor_soap_express_N_a(cutoff_hard, n) - I_np2 = s2 * (n+1) * N_n/ N_np2 * I_n & - + N_np1 * cutoff_hard / N_np2 * I_np1 & - - C2 / N_np2 - if(n > 0)then - ! Include the normalization factor of the Gaussian - this%overlap(this%n_max, n) = I_np2 * sqrt_two / & - sqrt(atom_sigma_radial_normalised) / PI**0.25_dp - this%overlap(n, this%n_max) = this%overlap(this%n_max, n) - end if - N_n = N_np1 - N_np1 = N_np2 - I_n = I_np1 - I_np1 = I_np2 - end do - this%overlap(this%n_max,this%n_max) = 1.0_dp - - call initialise(LA_overlap,this%overlap) - call LA_Matrix_SVD_Allocate(LA_overlap,s,u,v,error) - call LA_Matrix_SVD(LA_overlap,s,u,v,error) - - sqrt_overlap = 0.0_dp - do i = 1, this%n_max - sqrt_overlap(i,i) = sqrt(s(i)) - end do - sqrt_overlap = matmul(U,sqrt_overlap) - sqrt_overlap = matmul(sqrt_overlap,transpose(V)) - - LA_overlap = sqrt_overlap - call LA_Matrix_Factorise(LA_overlap,error=error) - call LA_Matrix_Inverse(LA_overlap,this%basis_transformation_coefficients) - call Finalise(LA_overlap) - if(allocated(sqrt_overlap)) deallocate(sqrt_overlap) - if(allocated(s)) deallocate(s) - if(allocated(u)) deallocate(u) - if(allocated(v)) deallocate(v) - - this%initialised = .true. - - endsubroutine soap_express_initialise - - subroutine soap_express_finalise(this,error) - type(soap_express), intent(inout) :: this - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(.not. this%initialised) return - this%cutoff = 0.0_dp - this%cutoff_transition_width = 0.0_dp - this%cutoff_decay_rate = 0.0_dp - this%l_max = 0 - this%n_max = 0 - - this%atom_sigma_radial = 0.0_dp - this%atom_sigma_angular = 0.0_dp - this%central_weight = 0.0_dp - this%covariance_sigma0 = 0.0_dp - this%atom_sigma_scaling_radial = 0.0_dp - this%atom_sigma_scaling_angular = 0.0_dp - this%amplitude_scaling = 0.0_dp - - if(allocated(this%basis_transformation_coefficients)) deallocate(this%basis_transformation_coefficients) - if(allocated(this%overlap)) deallocate(this%overlap) - if(allocated(this%semifactorial_table)) deallocate(this%semifactorial_table) - if(allocated(this%Y_lm_prefactor)) deallocate(this%Y_lm_prefactor) - - this%initialised = .false. - - endsubroutine soap_express_finalise - - subroutine distance_Nb_n_permutations(Z,n_permutations,error) - integer, dimension(:), intent(in) :: Z - integer, dimension(:), allocatable :: n_permutations - integer, optional, intent(out) :: error - - integer :: i - integer, dimension(:), allocatable :: uniq_Z - - INIT_ERROR(error) - - call uniq(Z,uniq_Z) - call reallocate(n_permutations,size(uniq_Z)) - - do i = 1, size(uniq_Z) - n_permutations(i) = count( uniq_Z(i) == Z ) - enddo - - if(allocated(uniq_Z)) deallocate(uniq_Z) - - endsubroutine distance_Nb_n_permutations - - recursive subroutine distance_Nb_permutations(n_permutations,permutations) - integer, dimension(:), intent(in) :: n_permutations - integer, dimension(sum(n_permutations),product(factorial_int(n_permutations))), intent(inout) :: permutations - - integer, dimension(:), allocatable, save :: current_permutation - integer :: i, j, n_lo, n_hi - integer, save :: recursion_level = 0, i_current_permutation = 0 - - recursion_level = recursion_level + 1 - - - if( recursion_level == 1 ) then - i_current_permutation = 0 - allocate(current_permutation(sum(n_permutations))) - current_permutation = 0 - endif - - - do i = 1, size(n_permutations) - if( i == 1 ) then - n_lo = 1 - else - n_lo = sum(n_permutations(1:i-1)) + 1 - endif - n_hi = sum(n_permutations(1:i)) - do j = n_lo, n_hi - if( i_current_permutation < size(permutations,2) ) then - if( .not. any(j==current_permutation) .and. recursion_level >= n_lo .and. recursion_level <= n_hi ) then - - current_permutation(recursion_level) = j - if( recursion_level == sum(n_permutations) ) then - i_current_permutation = i_current_permutation + 1 - permutations(:,i_current_permutation) = current_permutation - else - call distance_Nb_permutations(n_permutations,permutations) - endif - endif - endif - enddo - enddo - - current_permutation(recursion_level) = 0 - - recursion_level = recursion_level - 1 - - if( recursion_level == 0 ) then - deallocate(current_permutation) - endif - - endsubroutine distance_Nb_permutations - - subroutine descriptor_str_add_species(this,species,descriptor_str,error) - character(len=*), intent(in) :: this - integer, dimension(:), intent(in) :: species - character(len=STRING_LENGTH), dimension(:), allocatable, intent(out) :: descriptor_str - integer, optional, intent(out) :: error - - integer :: my_descriptor_type, i, j, k, l, n_species, order, n - integer, dimension(:,:), allocatable :: ZN - real(dp), dimension(:), allocatable :: w - type(Dictionary) :: params - - INIT_ERROR(error) - - if(allocated(descriptor_str)) deallocate(descriptor_str) - - my_descriptor_type = get_descriptor_type(this,error) - n_species = size(species) - - select case(my_descriptor_type) - case(DT_BISPECTRUM_SO4,DT_BISPECTRUM_SO3,DT_BEHLER,DT_COSNX,DT_POWER_SO3,DT_POWER_SO4) - allocate(w(n_species)) - allocate(descriptor_str(n_species)) - - if( n_species == 1 ) then - w = 1.0_dp - else - w = real( (/ (i, i=0, n_species-1) /), kind=dp ) / (n_species-1) * 0.5_dp + 0.5_dp - endif - - do i = 1, n_species - descriptor_str(i) = trim(this)//" n_species="//n_species//" Z="//species(i)//" species_Z={"//species//"} w={"//w//"}" - enddo - - deallocate(w) - case(DT_SOAP) - allocate(descriptor_str(n_species)) - do i = 1, n_species - descriptor_str(i) = trim(this)//" n_species="//n_species//" Z="//species(i)//" species_Z={"//species//"}" - enddo - case(DT_DISTANCE_2B,DT_CO_DISTANCE_2B,DT_AS_DISTANCE_2B) - allocate(descriptor_str(n_species * (n_species+1) / 2)) - - l = 0 - do i = 1, n_species - do j = i, n_species - l = l + 1 - descriptor_str(l) = trim(this)//" Z1="//species(i)//" Z2="//species(j) - enddo - enddo - - case(DT_COORDINATION,DT_RDF) - allocate(descriptor_str(n_species)) - do i = 1, n_species - descriptor_str(i) = trim(this)//" Z="//species(i) - enddo - case(DT_ANGLE_3B,DT_CO_ANGLE_3B) - allocate(descriptor_str(n_species * n_species * (n_species+1) / 2)) - l = 0 - do i = 1, n_species - do j = 1, n_species - do k = j, n_species - l = l + 1 - descriptor_str(l) = trim(this)//" Z="//species(i)//" Z1="//species(j)//" Z2="//species(k) - enddo - enddo - enddo - case(DT_GENERAL_MONOMER,DT_GENERAL_DIMER,DT_WATER_MONOMER,DT_WATER_DIMER,DT_A2_DIMER,DT_AB_DIMER,DT_TRIHIS,DT_BOND_REAL_SPACE,DT_ATOM_REAL_SPACE,DT_AN_MONOMER) - allocate(descriptor_str(1)) - descriptor_str(1) = trim(this) - case(DT_DISTANCE_NB) - call initialise(params) - call param_register(params, 'order', PARAM_MANDATORY, order, help_string="Many-body order, in terms of number of neighbours") - if (.not. param_read_line(params, this, ignore_unknown=.true.,task='descriptor_str_add_species this')) then - RAISE_ERROR("descriptor_str_add_species failed to parse descriptor string='"//trim(this)//"'", error) - endif - call finalise(params) - - n = 1 - do i = 1, order - n = n * ( n_species + i - 1 ) / i ! avoids double counting - enddo - - allocate(ZN(order,n),descriptor_str(n)) - - call descriptor_str_add_species_distance_Nb(ZN,species,order) - - do i = 1, n - descriptor_str(i) = trim(this)//" Z={"//ZN(:,i)//"}" - enddo - deallocate(ZN) - - case(DT_SOAP_EXPRESS) - RAISE_ERROR("descriptor_str_add_species: no recipe for "//my_descriptor_type//" yet.",error) - case default - RAISE_ERROR("descriptor_str_add_species: unknown descriptor type "//my_descriptor_type,error) - endselect - - endsubroutine descriptor_str_add_species - - recursive subroutine descriptor_str_add_species_distance_Nb(ZN,species,order) - integer, dimension(:,:), intent(inout) :: ZN - integer, dimension(:), intent(in) :: species - integer, intent(in) :: order - - integer :: i_species, n_species - integer, save :: current_descriptor, current_order = 0 - integer, dimension(:), allocatable, save :: ZN_current - - n_species = size(species) - - if( current_order == 0 ) then ! first run, outermost order. - current_descriptor = 0 ! keeps track of descriptor - current_order = 1 ! keeps track of order - allocate(ZN_current(order)) ! builds/updates atomic numbers gradually for each descriptor - endif - - do i_species = 1, n_species - if( current_order > 1 ) then ! no special atom, all atoms equivalent - if( species(i_species) < ZN_current(current_order-1) ) cycle ! avoids double-counting of neighbours - endif - - ZN_current(current_order) = species(i_species) - if( current_order < order ) then ! calls recursively until we reach the last order - current_order = current_order + 1 - call descriptor_str_add_species_distance_Nb(ZN,species,order) - else ! when we reached the last order, fill the atomic numbers in the loop - current_descriptor = current_descriptor + 1 ! and add them to the output array - ZN(:,current_descriptor) = ZN_current - endif - enddo - - current_order = current_order - 1 ! when the loop finished, step one level down - - if( current_order == 0 ) deallocate(ZN_current) ! when we reach zero, we finished. - - endsubroutine descriptor_str_add_species_distance_Nb - - subroutine descriptor_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(descriptor), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - if(cutoff(this) > at%cutoff) then - RAISE_ERROR("descriptor_calc: descriptor cutoff ("//cutoff(this)//") larger than atoms cutoff("//at%cutoff//")",error) - endif - - selectcase(this%descriptor_type) - case(DT_BISPECTRUM_SO4) - call calc(this%descriptor_bispectrum_SO4,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_BISPECTRUM_SO3) - call calc(this%descriptor_bispectrum_SO3,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error=error) - case(DT_BEHLER) - call calc(this%descriptor_behler,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error=error) - case(DT_DISTANCE_2b) - call calc(this%descriptor_distance_2b,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_COORDINATION) - call calc(this%descriptor_coordination,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_ANGLE_3B) - call calc(this%descriptor_angle_3b,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_CO_ANGLE_3B) - call calc(this%descriptor_co_angle_3b,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_CO_DISTANCE_2b) - call calc(this%descriptor_co_distance_2b,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_COSNX) - call calc(this%descriptor_cosnx,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_TRIHIS) - call calc(this%descriptor_trihis,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_WATER_MONOMER) - call calc(this%descriptor_water_monomer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_WATER_DIMER) - call calc(this%descriptor_water_dimer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_A2_DIMER) - call calc(this%descriptor_A2_dimer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_AB_DIMER) - call calc(this%descriptor_AB_dimer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_BOND_REAL_SPACE) - call calc(this%descriptor_bond_real_space,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_ATOM_REAL_SPACE) - call calc(this%descriptor_atom_real_space,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_POWER_SO3) - call calc(this%descriptor_power_so3,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_POWER_SO4) - call calc(this%descriptor_power_so4,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_SOAP) - call calc(this%descriptor_soap,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_AN_MONOMER) - call calc(this%descriptor_AN_monomer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_GENERAL_MONOMER) - call calc(this%descriptor_general_monomer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_GENERAL_DIMER) - call calc(this%descriptor_general_dimer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_GENERAL_TRIMER) - call calc(this%descriptor_general_trimer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_RDF) - call calc(this%descriptor_rdf,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_AS_DISTANCE_2b) - call calc(this%descriptor_as_distance_2b,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_MOLECULE_LO_D) - call calc(this%descriptor_molecule_lo_d,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_ALEX) - call calc(this%descriptor_alex,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_COM_DIMER) - call calc(this%descriptor_com_dimer,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_DISTANCE_Nb) - call calc(this%descriptor_distance_Nb,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case(DT_SOAP_EXPRESS) - call calc(this%descriptor_soap_express,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - case default - RAISE_ERROR("descriptor_calc: unknown descriptor type "//this%descriptor_type,error) - endselect - - endsubroutine descriptor_calc - - subroutine descriptor_calc_array(this,at,descriptor_out,covariance_cutoff,descriptor_index, & - grad_descriptor_out,grad_descriptor_index,grad_descriptor_pos,grad_covariance_cutoff,args_str,error) - - type(descriptor), intent(in) :: this - type(atoms), intent(in) :: at - real(dp), dimension(:,:), intent(out), optional :: descriptor_out - real(dp), dimension(:), intent(out), optional :: covariance_cutoff - integer, dimension(:,:), intent(out), optional :: descriptor_index - real(dp), dimension(:,:,:), intent(out), optional :: grad_descriptor_out - integer, dimension(:,:), intent(out), optional :: grad_descriptor_index - real(dp), dimension(:,:), intent(out), optional :: grad_descriptor_pos - real(dp), dimension(:,:), intent(out), optional :: grad_covariance_cutoff - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(descriptor_data) :: my_descriptor_data - integer :: i, n, i_d, n_descriptors, n_cross, n_index - logical :: do_grad_descriptor, do_descriptor - - INIT_ERROR(error) - - do_descriptor = present(descriptor_out) - do_grad_descriptor = present(grad_descriptor_out) .or. present(grad_descriptor_index) .or. present(grad_descriptor_pos) - - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - - call calc(this,at,my_descriptor_data,do_descriptor=do_descriptor,do_grad_descriptor=do_grad_descriptor,args_str=args_str,error=error) - - if(present(descriptor_out)) & - call check_size('descriptor_out',descriptor_out, (/descriptor_dimensions(this),n_descriptors/),'descriptor_calc_array',error) - - if(present(covariance_cutoff)) & - call check_size('covariance_cutoff',covariance_cutoff,(/n_descriptors/),'descriptor_calc_array',error) - - if(present(descriptor_index)) & - call check_size('descriptor_index',descriptor_index,(/n_index,n_descriptors/),'descriptor_calc_array',error) - - if(present(grad_descriptor_out)) & - call check_size('grad_descriptor_out',grad_descriptor_out,(/descriptor_dimensions(this),3,n_cross/),'descriptor_calc_array',error) - - if(present(grad_descriptor_index)) & - call check_size('grad_descriptor_index',grad_descriptor_index,(/2,n_cross/),'descriptor_calc_array',error) - - if(present(grad_descriptor_pos)) & - call check_size('grad_descriptor_pos',grad_descriptor_pos,(/3,n_cross/),'descriptor_calc_array',error) - - if(present(grad_covariance_cutoff)) & - call check_size('grad_covariance_cutoff',grad_covariance_cutoff,(/3,n_cross/),'descriptor_calc_array',error) - - if(do_descriptor) then - do i = 1, n_descriptors - descriptor_out(:,i) = my_descriptor_data%x(i)%data - if(present(covariance_cutoff)) covariance_cutoff(i) = my_descriptor_data%x(i)%covariance_cutoff - if(present(descriptor_index)) descriptor_index(:,i) = my_descriptor_data%x(i)%ci - enddo - endif - - if(do_grad_descriptor) then - i_d = 0 - do i = 1, n_descriptors - do n = lbound(my_descriptor_data%x(i)%ii,1),ubound(my_descriptor_data%x(i)%ii,1) - i_d = i_d + 1 - if(present(grad_descriptor_index)) grad_descriptor_index(:,i_d) = (/i,my_descriptor_data%x(i)%ii(n)/) - if(present(grad_descriptor_out)) grad_descriptor_out(:,:,i_d) = my_descriptor_data%x(i)%grad_data(:,:,n) - if(present(grad_descriptor_pos)) grad_descriptor_pos(:,i_d) = my_descriptor_data%x(i)%pos(:,n) - if(present(grad_covariance_cutoff)) grad_covariance_cutoff(:,i_d) = my_descriptor_data%x(i)%grad_covariance_cutoff(:,n) - enddo - enddo - endif - - call finalise(my_descriptor_data,error=error) - - endsubroutine descriptor_calc_array - - subroutine bispectrum_SO4_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(bispectrum_SO4), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - type(cplx_2d), dimension(:), allocatable :: U - type(cplx_3d), dimension(:,:), allocatable :: dU - - complex(dp) :: sub - complex(dp), dimension(3) :: dsub - real(dp), dimension(3) :: diff, u_ij - real(dp) :: r, tmp_cg - integer :: i, n, n_i, ji, jn, j, m1, m2, j1, j2, m11, m12, m21, m22, & - i_desc, i_bisp, d, n_descriptors, n_cross, l_n_neighbours, n_index - integer, dimension(3) :: shift - integer, dimension(total_elements) :: species_map - logical :: my_do_descriptor, my_do_grad_descriptor - - INIT_ERROR(error) - - call system_timer('bispectrum_SO4_calc') - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_SO4_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='bispectrum_SO4_calc args_str')) then - RAISE_ERROR("bispectrum_SO4_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("bispectrum_SO4_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - species_map = 0 - do i = 1, size(this%species_Z) - if(this%species_Z(i) == 0) then - species_map = 1 - else - species_map(this%species_Z(i)) = i - endif - enddo - - call cg_initialise(this%j_max, 2) - - call finalise(descriptor_out) - - d = bispectrum_SO4_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - - i_desc = i_desc + 1 - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - - enddo - - i_desc = 0 - do i = 1, at%N - - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - - if(my_do_grad_descriptor) then - ! dU is not allocated, allocate and zero it - allocate( dU(0:this%j_max,0:n_neighbours(at,i,max_dist=this%cutoff)) ) - do j = 0, this%j_max - allocate( dU(j,0)%mm(3,-j:j,-j:j) ) - dU(j,0)%mm = CPLX_ZERO - enddo - - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - ji = neighbour(at, i, n, jn=jn, distance=r, diff=diff, cosines=u_ij,shift=shift) - if( r >= this%cutoff ) cycle - - n_i = n_i + 1 - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(n_i) = ji - descriptor_out%x(i_desc)%pos(:,n_i) = at%pos(:,ji) + matmul(at%lattice,shift) - descriptor_out%x(i_desc)%has_grad_data(n_i) = .true. - endif - enddo - - if(my_do_grad_descriptor) then - call fourier_SO4_calc(this%fourier_SO4,at,i,U,dU,args_str,error=error) - else - call fourier_SO4_calc(this%fourier_SO4,at,i,U,args_str=args_str,error=error) - endif - - if(my_do_descriptor) then - - i_bisp = 0 - do j1 = 0, this%j_max - j2 = j1 - !do j2 = 0, this%j_max - do j = abs(j1-j2), min(this%j_max,j1+j2) - if( mod(j1+j2+j,2) == 1 ) cycle - - i_bisp = i_bisp + 1 - - !do m1 = -j, j, 2 - ! do m2 = -j, j, 2 - ! sub = CPLX_ZERO - ! do m11 = max(-j1-m1,-j1), min(j1-m1,j1), 2 - ! do m21 = max(-j2-m2,-j2), min(j2-m2,j2), 2 - ! sub = sub + cg_array(j1,m11,j,m1,j1,m11+m1) & - ! * cg_array(j2,m21,j,m2,j2,m21+m2) & - ! * U(j1)%mm(m11,m11+m1) * U(j2)%mm(m21,m21+m2) - ! enddo - ! enddo - ! descriptor_out%x(i_desc)%data(i_bisp) = descriptor_out%x(i_desc)%data(i_bisp) + sub*conjg(U(j)%mm(-m2,m1))*(-1)**(m2/2) - ! enddo - !enddo - - do m1 = -j, j, 2 - do m2 = -j, j, 2 - sub = CPLX_ZERO - do m11 = max(-j1,m1-j2), min(j1,m1+j2), 2 - do m12 = max(-j1,m2-j2), min(j1,m2+j2), 2 - sub = sub + cg_array(j1,m11,j2,m1-m11,j,m1) & - * cg_array(j1,m12,j2,m2-m12,j,m2) & - * U(j1)%mm(m11,m12) * U(j2)%mm(m1-m11,m2-m12) - enddo - enddo - descriptor_out%x(i_desc)%data(i_bisp) = descriptor_out%x(i_desc)%data(i_bisp) + sub*conjg(U(j)%mm(m1,m2)) - enddo - enddo - - enddo - !enddo - enddo - endif - - if(my_do_grad_descriptor) then - n_i = 0 - do n = 0, n_neighbours(at,i) - if( n>0 ) then - ji = neighbour(at, i, n, distance=r) - if( r >= this%cutoff ) cycle - n_i = n_i + 1 - endif - i_bisp = 0 - do j1 = 0, this%j_max - j2 = j1 - !do j2 = 0, this%j_max - do j = abs(j1-j2), min(this%j_max,j1+j2) - if( mod(j1+j2+j,2) == 1 ) cycle - - i_bisp = i_bisp + 1 - - !do m1 = -j, j, 2 - ! do m2 = -j, j, 2 - ! sub = CPLX_ZERO - ! dsub = CPLX_ZERO - - ! do m11 = max(-j1-m1,-j1), min(j1-m1,j1), 2 - ! do m21 = max(-j2-m2,-j2), min(j2-m2,j2), 2 - ! tmp_cg = cg_array(j1,m11,j,m1,j1,m11+m1) & - ! * cg_array(j2,m21,j,m2,j2,m21+m2) - - ! sub = sub + tmp_cg & - ! * U(j1)%mm(m11,m1+m11) * U(j2)%mm(m21,m2+m21) - ! dsub = dsub + tmp_cg & - ! * ( dU(j1,n_i)%mm(:,m11,m1+m11) * U(j2)%mm(m21,m2+m21) + & - ! U(j1)%mm(m11,m1+m11) * dU(j2,n_i)%mm(:,m21,m2+m21) ) - ! enddo - ! enddo - ! descriptor_out%x(i_desc)%grad_data(i_bisp,:,n_i) = & - ! descriptor_out%x(i_desc)%grad_data(i_bisp,:,n_i) + & - ! ( dsub*conjg(U(j)%mm(-m2,m1)) + sub*conjg(dU(j,n_i)%mm(:,-m2,m1)) )*(-1)**(m2/2) - ! enddo - !enddo - do m1 = -j, j, 2 - do m2 = -j, j, 2 - sub = CPLX_ZERO - dsub = CPLX_ZERO - do m11 = max(-j1,m1-j2), min(j1,m1+j2), 2 - do m12 = max(-j1,m2-j2), min(j1,m2+j2), 2 - - tmp_cg = cg_array(j1,m11,j2,m1-m11,j,m1) & - * cg_array(j1,m12,j2,m2-m12,j,m2) - - sub = sub + tmp_cg & - * U(j1)%mm(m11,m12) * U(j2)%mm(m1-m11,m2-m12) - dsub = dsub + tmp_cg & - * ( dU(j1,n_i)%mm(:,m11,m12) * U(j2)%mm(m1-m11,m2-m12) + & - U(j1)%mm(m11,m12) * dU(j2,n_i)%mm(:,m1-m11,m2-m12) ) - enddo - enddo - descriptor_out%x(i_desc)%grad_data(i_bisp,:,n_i) = & - descriptor_out%x(i_desc)%grad_data(i_bisp,:,n_i) + & - dsub*conjg(U(j)%mm(m1,m2)) + sub*conjg(dU(j,n_i)%mm(:,m1,m2)) - enddo - enddo - - enddo - !enddo - enddo - enddo - endif - - call finalise(dU) - enddo ! i - - ! clear U from the memory - call finalise(U) - - call system_timer('bispectrum_SO4_calc') - - endsubroutine bispectrum_SO4_calc - - subroutine bispectrum_so3_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(bispectrum_so3), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(cplx_1d), dimension(:), allocatable :: SphericalY_ij - type(cplx_1d), dimension(:,:), allocatable :: fourier_so3 - - type(cplx_2d), dimension(:), allocatable :: dSphericalY_ij - type(cplx_2d), dimension(:,:,:), allocatable :: dfourier_so3 - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, i, j, n, a, l, m, l1, l2, m1, i_desc, i_pow, l_n_neighbours, & - n_i, n_descriptors, n_cross, n_index - integer, dimension(3) :: shift_ij - real(dp) :: r_ij - real(dp), dimension(3) :: u_ij, d_ij - real(dp), dimension(:), allocatable :: Rad_ij - real(dp), dimension(:,:), allocatable :: dRad_ij - - complex(dp) :: sub, dsub(3) - - integer, dimension(total_elements) :: species_map - - INIT_ERROR(error) - - call system_timer('bispectrum_so3_calc') - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_so3_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - species_map = 0 - do i = 1, size(this%species_Z) - if(this%species_Z(i) == 0) then - species_map = 1 - else - species_map(this%species_Z(i)) = i - endif - enddo - - call cg_initialise(this%l_max) - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='bispectrum_SO3_calc args_str')) then - RAISE_ERROR("bispectrum_SO3_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("bispectrum_SO3_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("bispectrum_SO3_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = bispectrum_so3_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - i_desc = i_desc + 1 - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - allocate(fourier_so3(0:this%l_max,this%n_max),SphericalY_ij(0:this%l_max),Rad_ij(this%n_max)) - do a = 1, this%n_max - do l = 0, this%l_max - allocate(fourier_so3(l,a)%m(-l:l)) - fourier_so3(l,a)%m(:) = CPLX_ZERO - enddo - enddo - do l = 0, this%l_max - allocate(SphericalY_ij(l)%m(-l:l)) - enddo - - if(my_do_grad_descriptor) then - allocate( dRad_ij(3,this%n_max), dSphericalY_ij(0:this%l_max) ) - do l = 0, this%l_max - allocate(dSphericalY_ij(l)%mm(3,-l:l)) - enddo - endif - - i_desc = 0 - do i = 1, at%N - - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - i_desc = i_desc + 1 - - do a = 1, this%n_max - do l = 0, this%l_max - fourier_so3(l,a)%m(:) = CPLX_ZERO - enddo - enddo - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - - if(my_do_grad_descriptor) then - allocate( dfourier_so3(0:this%l_max,this%n_max,0:n_neighbours(at,i,max_dist=this%cutoff)) ) - do n = 0, n_neighbours(at,i,max_dist=this%cutoff) - do a = 1, this%n_max - do l = 0, this%l_max - allocate(dfourier_so3(l,a,n)%mm(3,-l:l)) - dfourier_so3(l,a,n)%mm(:,:) = CPLX_ZERO - enddo - enddo - enddo - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines=u_ij, diff=d_ij, shift=shift_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(n_i) = j - descriptor_out%x(i_desc)%pos(:,n_i) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc)%has_grad_data(n_i) = .true. - endif - - do a = 1, this%n_max - Rad_ij(a) = RadialFunction(this%Radial, r_ij, a) - if(my_do_grad_descriptor) dRad_ij(:,a) = GradRadialFunction(this%Radial, r_ij, a) * u_ij - enddo - - do l = 0, this%l_max - do m = -l, l - SphericalY_ij(l)%m(m) = SphericalYCartesian(l,m,d_ij) - if(my_do_grad_descriptor) dSphericalY_ij(l)%mm(:,m) = GradSphericalYCartesian(l,m,d_ij) - enddo - enddo - - do a = 1, this%n_max - do l = 0, this%l_max - do m = -l, l - fourier_so3(l,a)%m(m) = fourier_so3(l,a)%m(m) + Rad_ij(a)*SphericalY_ij(l)%m(m) - if(my_do_grad_descriptor) then - dfourier_so3(l,a,n_i)%mm(:,m) = dfourier_so3(l,a,n_i)%mm(:,m) + & - dRad_ij(:,a) * SphericalY_ij(l)%m(m) + Rad_ij(a)*dSphericalY_ij(l)%mm(:,m) - endif - enddo - enddo - enddo - - enddo ! n - - if(my_do_descriptor) then - i_pow = 0 - do a = 1, this%n_max - do l1 = 0, this%l_max - l2 = l1 - !do l2 = 0, this%l_max - do l = abs(l1-l2), min(this%l_max,l1+l2) - if( mod(l1,2)==1 .and. mod(l2,2)==1 .and. mod(l,2)==1 ) cycle - i_pow = i_pow + 1 - - do m = -l, l - sub = CPLX_ZERO - do m1 = max(-l1,m-l2),min(l1,m+l2) - sub = sub + cg_array(l1,m1,l2,m-m1,l,m) * conjg(fourier_so3(l1,a)%m(m1)) * conjg(fourier_so3(l2,a)%m(m-m1)) - enddo - - descriptor_out%x(i_desc)%data(i_pow) = descriptor_out%x(i_desc)%data(i_pow) + fourier_so3(l,a)%m(m) * sub - enddo - - enddo - !enddo - enddo - enddo - endif - - if(my_do_grad_descriptor) then - do n = 1, n_neighbours(at,i,max_dist=this%cutoff) - i_pow = 0 - do a = 1, this%n_max - do l1 = 0, this%l_max - l2 = l1 - !do l2 = 0, this%l_max - do l = abs(l1-l2), min(this%l_max,l1+l2) - if( mod(l1,2)==1 .and. mod(l2,2)==1 .and. mod(l,2)==1 ) cycle - i_pow = i_pow + 1 - - do m = -l, l - sub = CPLX_ZERO - dsub = CPLX_ZERO - do m1 = max(-l1,m-l2),min(l1,m+l2) - dsub = dsub + cg_array(l1,m1,l2,m-m1,l,m) * & - ( conjg(dfourier_so3(l1,a,n)%mm(:,m1)) * conjg(fourier_so3(l2,a)%m(m-m1)) + & - conjg(fourier_so3(l1,a)%m(m1)) * conjg(dfourier_so3(l2,a,n)%mm(:,m-m1)) ) - sub = sub + cg_array(l1,m1,l2,m-m1,l,m) * conjg(fourier_so3(l1,a)%m(m1)) * conjg(fourier_so3(l2,a)%m(m-m1)) - enddo - - descriptor_out%x(i_desc)%grad_data(i_pow,:,n) = descriptor_out%x(i_desc)%grad_data(i_pow,:,n) + & - fourier_so3(l,a)%m(m) * dsub + dfourier_so3(l,a,n)%mm(:,m) * sub - enddo - enddo - !enddo - enddo - enddo - descriptor_out%x(i_desc)%grad_data(:,:,0) = descriptor_out%x(i_desc)%grad_data(:,:,0) - descriptor_out%x(i_desc)%grad_data(:,:,n) - enddo - endif - - if(allocated(dfourier_so3)) then - do n = lbound(dfourier_so3,3), ubound(dfourier_so3,3) - do a = lbound(dfourier_so3,2), ubound(dfourier_so3,2) - do l = lbound(dfourier_so3,1), ubound(dfourier_so3,1) - deallocate(dfourier_so3(l,a,n)%mm) - enddo - enddo - enddo - deallocate(dfourier_so3) - endif - - enddo ! i - - if(allocated(Rad_ij)) deallocate(Rad_ij) - if(allocated(dRad_ij)) deallocate(dRad_ij) - - if(allocated(fourier_so3)) then - do a = lbound(fourier_so3,2), ubound(fourier_so3,2) - do l = lbound(fourier_so3,1), ubound(fourier_so3,1) - deallocate(fourier_so3(l,a)%m) - enddo - enddo - deallocate(fourier_so3) - endif - - if(allocated(SphericalY_ij)) then - do l = lbound(SphericalY_ij,1), ubound(SphericalY_ij,1) - deallocate(SphericalY_ij(l)%m) - enddo - deallocate(SphericalY_ij) - endif - - if(allocated(dSphericalY_ij)) then - do l = lbound(dSphericalY_ij,1), ubound(dSphericalY_ij,1) - deallocate(dSphericalY_ij(l)%mm) - enddo - deallocate(dSphericalY_ij) - endif - - call system_timer('bispectrum_so3_calc') - - endsubroutine bispectrum_so3_calc - - subroutine behler_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(behler), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, i, j, k, n, m, a, b, i_desc, l_n_neighbours, & - n_i, m_i, n_descriptors, n_cross, n_index - integer, dimension(3) :: shift_ij - real(dp) :: r_ij, r_ik, r_jk, cos_ijk, Ang, dAng, Rad, dRad_ij, dRad_ik, dRad_jk, f_cut_ij, f_cut_ik, f_cut_jk, df_cut_ij, df_cut_ik, df_cut_jk, g2, dg2 - real(dp), dimension(3) :: u_ij, u_ik, u_jk, d_ij, d_ik, d_jk, dcosijk_ij, dcosijk_ik - - INIT_ERROR(error) - - call system_timer('behler_calc') - - if(.not. this%initialised) then - RAISE_ERROR("behler_calc: descriptor object not initialised", error) - endif - - if( at%cutoff < this%cutoff ) then - RAISE_ERROR("behler_calc: cutoff of atoms object ("//at%cutoff//") less than cutoff of descriptor ("//this%cutoff//")", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='behler_calc args_str')) then - RAISE_ERROR("behler_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("behler_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - call finalise(descriptor_out) - - d = behler_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - do i = 1, at%N - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - i_desc = 0 - do i = 1, at%N - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines=u_ij, diff=d_ij, shift=shift_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(n_i) = j - descriptor_out%x(i_desc)%pos(:,n_i) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc)%has_grad_data(n_i) = .true. - endif - - f_cut_ij = cos_cutoff_function(r_ij,this%cutoff) - if(my_do_grad_descriptor) df_cut_ij = dcos_cutoff_function(r_ij,this%cutoff) - - do a = 1, this%n_g2 - g2 = exp(-this%g2(a)%eta * (r_ij-this%g2(a)%rs)**2) - if(my_do_descriptor) descriptor_out%x(i_desc)%data(a) = descriptor_out%x(i_desc)%data(a) + g2 * f_cut_ij - if(my_do_grad_descriptor) then - dg2 = -2.0_dp * this%g2(a)%eta * (r_ij-this%g2(a)%rs) * g2 - descriptor_out%x(i_desc)%grad_data(a,:,n_i) = ( dg2 * f_cut_ij + g2 * df_cut_ij ) * u_ij - descriptor_out%x(i_desc)%grad_data(a,:,0) = descriptor_out%x(i_desc)%grad_data(a,:,0) - descriptor_out%x(i_desc)%grad_data(a,:,n_i) - endif - enddo - - - m_i = 0 - do m = 1, n_neighbours(at,i) - k = neighbour(at, i, m, distance = r_ik, cosines=u_ik, diff=d_ik) - if( r_ik >= this%cutoff ) cycle - - m_i = m_i + 1 - - d_jk = d_ik - d_ij - r_jk = norm(d_jk) - if( r_jk .feq. 0.0_dp ) cycle - - u_jk = d_jk / r_jk - - f_cut_ik = cos_cutoff_function(r_ik,this%cutoff) - f_cut_jk = cos_cutoff_function(r_jk,this%cutoff) - - cos_ijk = dot_product(u_ij,u_ik) - - if(my_do_grad_descriptor) then - df_cut_ik = dcos_cutoff_function(r_ik,this%cutoff) - df_cut_jk = dcos_cutoff_function(r_jk,this%cutoff) - - dcosijk_ij = ( u_ik - cos_ijk * u_ij ) / r_ij - dcosijk_ik = ( u_ij - cos_ijk * u_ik ) / r_ik - endif - - do b = 1, this%n_g3 - a = b + this%n_g2 - - Ang = (1.0_dp + this%g3(b)%lambda * cos_ijk)**this%g3(b)%zeta - Rad = exp( -this%g3(b)%eta * (r_ij**2 + r_ik**2 + r_jk**2) ) - - if(my_do_descriptor) descriptor_out%x(i_desc)%data(a) = descriptor_out%x(i_desc)%data(a) + 0.5_dp * Ang * Rad * f_cut_ij * f_cut_ik * f_cut_jk - if(my_do_grad_descriptor) then - dAng = this%g3(b)%zeta * (1.0_dp + this%g3(b)%lambda * cos_ijk)**(this%g3(b)%zeta -1.0_dp) * this%g3(b)%lambda - dRad_ij = -this%g3(b)%eta * 2.0_dp * r_ij * Rad - dRad_ik = -this%g3(b)%eta * 2.0_dp * r_ik * Rad - dRad_jk = -this%g3(b)%eta * 2.0_dp * r_jk * Rad - - descriptor_out%x(i_desc)%grad_data(a,:,n_i) = descriptor_out%x(i_desc)%grad_data(a,:,n_i) + 0.5_dp * & - ( ( dAng * dcosijk_ij * Rad + Ang * ( dRad_ij * u_ij - dRad_jk * u_jk ) ) * f_cut_ij * f_cut_ik * f_cut_jk + & - Ang * Rad * f_cut_ik * ( df_cut_ij * u_ij * f_cut_jk - f_cut_ij * df_cut_jk * u_jk ) ) - - descriptor_out%x(i_desc)%grad_data(a,:,m_i) = descriptor_out%x(i_desc)%grad_data(a,:,m_i) + 0.5_dp * & - ( ( dAng * dcosijk_ik * Rad + Ang * ( dRad_ik * u_ik + dRad_jk * u_jk ) ) * f_cut_ij * f_cut_ik * f_cut_jk + & - Ang * Rad * f_cut_ij * ( df_cut_ik * u_ik * f_cut_jk + f_cut_ik * df_cut_jk * u_jk ) ) - - descriptor_out%x(i_desc)%grad_data(a,:,0) = descriptor_out%x(i_desc)%grad_data(a,:,0) - 0.5_dp * & - ( ( dAng * (dcosijk_ij+dcosijk_ik) * Rad + Ang * (dRad_ij * u_ij + dRad_ik * u_ik) ) * f_cut_ij * f_cut_ik * f_cut_jk + & - Ang * Rad * f_cut_jk * ( df_cut_ij * u_ij * f_cut_ik + f_cut_ij * df_cut_ik * u_ik ) ) - endif - - - enddo - - enddo - enddo - - do b = 1, this%n_g3 - a = b + this%n_g2 - - if(my_do_descriptor) descriptor_out%x(i_desc)%data(a) = descriptor_out%x(i_desc)%data(a) * 2.0_dp**(1.0_dp-this%g3(b)%zeta) - if(my_do_grad_descriptor) descriptor_out%x(i_desc)%grad_data(a,:,:) = descriptor_out%x(i_desc)%grad_data(a,:,:) * 2.0_dp**(1.0_dp-this%g3(b)%zeta) - enddo - enddo - - call system_timer('behler_calc') - - endsubroutine behler_calc - - subroutine distance_2b_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(distance_2b), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical :: needs_resid - logical, dimension(:), pointer :: atom_mask_pointer - integer, dimension(:), pointer :: resid_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor, Zi1, Zi2, Zj1, Zj2 - integer :: d, n_descriptors, n_cross, i_desc, i, j, n, n_index - integer, dimension(3) :: shift - real(dp) :: r_ij, covariance_cutoff, dcovariance_cutoff, tail, dtail - real(dp), dimension(3) :: u_ij - - INIT_ERROR(error) - - call system_timer('distance_2b_calc') - - if(.not. this%initialised) then - RAISE_ERROR("distance_2b_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='distance_2b_calc args_str')) then - RAISE_ERROR("distance_2b_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("distance_2b_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - needs_resid = this%only_intra .or. this%only_inter - if (needs_resid) then - if (.not. assign_pointer(at, trim(this%resid_name), resid_pointer)) then - RAISE_ERROR("distance_2b_calc did not find "//trim(this%resid_name)//" property (residue id) in the atoms object.", error) - end if - else - resid_pointer => null() - end if - - d = distance_2b_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%ci = 0 - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,0:1)) - allocate(descriptor_out%x(i)%ii(0:1)) - allocate(descriptor_out%x(i)%pos(3,0:1)) - allocate(descriptor_out%x(i)%has_grad_data(0:1)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,0:1)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - i_desc = 0 - do i = 1, at%N - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, shift=shift) - if( r_ij >= this%cutoff ) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - if (needs_resid) then - if (this%only_intra .and. resid_pointer(i) /= resid_pointer(j)) cycle - if (this%only_inter .and. resid_pointer(i) == resid_pointer(j)) cycle - end if - - i_desc = i_desc + 1 - - covariance_cutoff = coordination_function(r_ij,this%cutoff,this%cutoff_transition_width) - if( this%has_tail .and. this%tail_exponent /= 0 ) then - tail = ( erf(this%tail_range*r_ij) / r_ij )**this%tail_exponent - else - tail = 1.0_dp - endif - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%data(:) = r_ij**this%exponents - descriptor_out%x(i_desc)%ci(1:2) = (/i,j/) - descriptor_out%x(i_desc)%has_data = .true. - - descriptor_out%x(i_desc)%covariance_cutoff = covariance_cutoff * tail - endif - if(my_do_grad_descriptor) then - dcovariance_cutoff = dcoordination_function(r_ij,this%cutoff,this%cutoff_transition_width) - if( this%has_tail .and. this%tail_exponent /= 0 ) then - dtail = tail * this%tail_exponent * ( 2.0_dp*this%tail_range*exp(-this%tail_range**2*r_ij**2) / & - sqrt(pi) / erf(this%tail_range*r_ij) - 1.0_dp / r_ij ) - else - dtail = 0.0_dp - endif - - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - descriptor_out%x(i_desc)%grad_data(:,:,0) = -( this%exponents*r_ij**(this%exponents-1) ) .outer. u_ij - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) = -(dcovariance_cutoff*tail + covariance_cutoff*dtail)*u_ij - - descriptor_out%x(i_desc)%ii(1) = j - descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,j) + matmul(at%lattice,shift) - descriptor_out%x(i_desc)%has_grad_data(1) = .true. - descriptor_out%x(i_desc)%grad_data(:,:,1) = - descriptor_out%x(i_desc)%grad_data(:,:,0) - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = -descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) - - endif - enddo - enddo - - call system_timer('distance_2b_calc') - - endsubroutine distance_2b_calc - - subroutine coordination_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(coordination), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, i, j, n, i_n, l_n_neighbours, i_desc, n_descriptors, n_cross, n_index - integer, dimension(3) :: shift - real(dp) :: r_ij - real(dp), dimension(3) :: u_ij, df_cut - - INIT_ERROR(error) - - call system_timer('coordination_calc') - - if(.not. this%initialised) then - RAISE_ERROR("coordination_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='coordination_calc args_str')) then - RAISE_ERROR("coordination_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("coordination_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - call finalise(descriptor_out) - - d = coordination_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - - i_desc = i_desc + 1 - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - i_desc = 0 - do i = 1, at%N - - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - i_n = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, shift=shift) - - if( r_ij >= this%cutoff ) cycle - i_n = i_n + 1 - - if(my_do_descriptor) & - descriptor_out%x(i_desc)%data(1) = descriptor_out%x(i_desc)%data(1) + coordination_function(r_ij,this%cutoff,this%transition_width) - - if(my_do_grad_descriptor) then - df_cut = dcoordination_function(r_ij,this%cutoff,this%transition_width) * u_ij - - descriptor_out%x(i_desc)%grad_data(1,:,0) = descriptor_out%x(i_desc)%grad_data(1,:,0) - df_cut - - descriptor_out%x(i_desc)%ii(i_n) = j - descriptor_out%x(i_desc)%pos(:,i_n) = at%pos(:,j) + matmul(at%lattice,shift) - descriptor_out%x(i_desc)%has_grad_data(i_n) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,i_n) = df_cut - endif - enddo - enddo - - call system_timer('coordination_calc') - - endsubroutine coordination_calc - - subroutine angle_3b_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(angle_3b), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor, Zk1, Zk2, Zj1, Zj2 - integer :: d, n_descriptors, n_cross, i_desc, i, j, k, n, m, n_index - integer, dimension(3) :: shift_ij, shift_ik - real(dp) :: r_ij, r_ik, r_jk, cos_ijk, fc_j, fc_k, dfc_j, dfc_k - real(dp), dimension(3) :: u_ij, u_ik, u_jk, d_ij, d_ik, d_jk, dcosijk_ij, dcosijk_ik - - INIT_ERROR(error) - - call system_timer('angle_3b_calc') - - if(.not. this%initialised) then - RAISE_ERROR("angle_3b_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='angle_3b_calc args_str')) then - RAISE_ERROR("angle_3b_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("angle_3b_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - d = angle_3b_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%has_data = .false. - endif - - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,0:2)) - allocate(descriptor_out%x(i)%ii(0:2)) - allocate(descriptor_out%x(i)%pos(3,0:2)) - allocate(descriptor_out%x(i)%has_grad_data(0:2)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,0:2)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - i_desc = 0 - do i = 1, at%N - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - if( (this%Z /=0) .and. (at%Z(i) /= this%Z) ) cycle - - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, diff = d_ij, shift=shift_ij) - - if( r_ij >= this%cutoff ) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - - fc_j = coordination_function(r_ij,this%cutoff,this%cutoff_transition_width) - dfc_j = dcoordination_function(r_ij,this%cutoff,this%cutoff_transition_width) - - do m = 1, n_neighbours(at,i) - - if( n == m ) cycle - - k = neighbour(at, i, m, distance = r_ik, cosines = u_ik, diff = d_ik, shift=shift_ik) - if( r_ik >= this%cutoff ) cycle - - Zk1 = (this%Z1 == 0) .or. (at%Z(k) == this%Z1) - Zk2 = (this%Z2 == 0) .or. (at%Z(k) == this%Z2) - - if( .not. ( ( Zk1 .and. Zj2 ) .or. ( Zk2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - d_jk = d_ij - d_ik - r_jk = norm(d_jk) - u_jk = d_jk / r_jk - - fc_k = coordination_function(r_ik,this%cutoff,this%cutoff_transition_width) - dfc_k = dcoordination_function(r_ik,this%cutoff,this%cutoff_transition_width) - - cos_ijk = dot_product(d_ij,d_ik)/(r_ij*r_ik) - - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%data(1) = r_ij + r_ik - descriptor_out%x(i_desc)%data(2) = (r_ij - r_ik)**2 - descriptor_out%x(i_desc)%data(3) = r_jk !cos_ijk - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - - descriptor_out%x(i_desc)%covariance_cutoff = fc_j*fc_k - endif - - if(my_do_grad_descriptor) then - dcosijk_ij = ( u_ik - cos_ijk * u_ij ) / r_ij - dcosijk_ik = ( u_ij - cos_ijk * u_ik ) / r_ik - - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,0) = - u_ij - u_ik - descriptor_out%x(i_desc)%grad_data(2,:,0) = 2.0_dp * (r_ij - r_ik)*(-u_ij + u_ik) - descriptor_out%x(i_desc)%grad_data(3,:,0) = 0.0_dp !-dcosijk_ij - dcosijk_ik - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) = - dfc_j*fc_k*u_ij - dfc_k*fc_j*u_ik - - descriptor_out%x(i_desc)%ii(1) = j - descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc)%has_grad_data(1) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,1) = u_ij - descriptor_out%x(i_desc)%grad_data(2,:,1) = 2.0_dp * (r_ij - r_ik)*u_ij - descriptor_out%x(i_desc)%grad_data(3,:,1) = u_jk !dcosijk_ij - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = dfc_j*fc_k*u_ij - - descriptor_out%x(i_desc)%ii(2) = k - descriptor_out%x(i_desc)%pos(:,2) = at%pos(:,k) + matmul(at%lattice,shift_ik) - descriptor_out%x(i_desc)%has_grad_data(2) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,2) = u_ik - descriptor_out%x(i_desc)%grad_data(2,:,2) = 2.0_dp * (r_ij - r_ik)*(-u_ik) - descriptor_out%x(i_desc)%grad_data(3,:,2) = -u_jk !dcosijk_ik - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,2) = dfc_k*fc_j*u_ik - endif - enddo - enddo - enddo - - call system_timer('angle_3b_calc') - - endsubroutine angle_3b_calc - - subroutine co_angle_3b_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(co_angle_3b), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(descriptor) :: my_coordination - type(descriptor_data) :: descriptor_coordination - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor, Zk1, Zk2, Zj1, Zj2 - integer :: d, n_descriptors, n_cross, i_desc, i, j, k, n, m, & - l_n_neighbours_coordination, n_index - integer, dimension(3) :: shift_ij, shift_ik - real(dp) :: r_ij, r_ik, r_jk, cos_ijk, fc_j, fc_k, dfc_j, dfc_k - real(dp), dimension(3) :: u_ij, u_ik, u_jk, d_ij, d_ik, d_jk, dcosijk_ij, dcosijk_ik - - INIT_ERROR(error) - - call system_timer('co_angle_3b_calc') - - if(.not. this%initialised) then - RAISE_ERROR("co_angle_3b_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='co_angle_3b_calc args_str')) then - RAISE_ERROR("co_angle_3b_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("co_angle_3b_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("co_angle_3b_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = co_angle_3b_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - i_desc = 0 - do i = 1, at%N - if( (this%Z /=0) .and. (at%Z(i) /= this%Z) ) cycle - l_n_neighbours_coordination = n_neighbours(at,i,max_dist=this%coordination_cutoff) - - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij) - if( r_ij >= this%cutoff ) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - - do m = 1, n_neighbours(at,i) - - if( n == m ) cycle - - k = neighbour(at, i, m, distance = r_ik) - if( r_ik >= this%cutoff ) cycle - - Zk1 = (this%Z1 == 0) .or. (at%Z(k) == this%Z1) - Zk2 = (this%Z2 == 0) .or. (at%Z(k) == this%Z2) - - if( .not. ( ( Zk1 .and. Zj2 ) .or. ( Zk2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - i_desc = i_desc + 1 - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - endif - - if(my_do_grad_descriptor) then - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:2+l_n_neighbours_coordination)) - allocate(descriptor_out%x(i_desc)%ii(0:2+l_n_neighbours_coordination)) - allocate(descriptor_out%x(i_desc)%pos(3,0:2+l_n_neighbours_coordination)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:2+l_n_neighbours_coordination)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:2+l_n_neighbours_coordination)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - enddo - enddo - - call initialise(my_coordination,'coordination cutoff='//this%coordination_cutoff//' coordination_transition_width='//this%coordination_transition_width,error) - call calc(my_coordination,at,descriptor_coordination,do_descriptor,do_grad_descriptor,args_str,error) - - i_desc = 0 - do i = 1, at%N - if( (this%Z /=0) .and. (at%Z(i) /= this%Z) ) cycle - - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, diff = d_ij, shift=shift_ij) - - if( r_ij >= this%cutoff ) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - - fc_j = coordination_function(r_ij,this%cutoff,0.5_dp) - dfc_j = dcoordination_function(r_ij,this%cutoff,0.5_dp) - - do m = 1, n_neighbours(at,i) - if( n == m ) cycle - - k = neighbour(at, i, m, distance = r_ik, cosines = u_ik, diff = d_ik, shift=shift_ik) - if( r_ik >= this%cutoff ) cycle - - Zk1 = (this%Z1 == 0) .or. (at%Z(k) == this%Z1) - Zk2 = (this%Z2 == 0) .or. (at%Z(k) == this%Z2) - - if( .not. ( ( Zk1 .and. Zj2 ) .or. ( Zk2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - d_jk = d_ij - d_ik - r_jk = norm(d_jk) - u_jk = d_jk / r_jk - - fc_k = coordination_function(r_ik,this%cutoff,0.5_dp) - dfc_k = dcoordination_function(r_ik,this%cutoff,0.5_dp) - - cos_ijk = dot_product(d_ij,d_ik)/(r_ij*r_ik) - - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%data(1) = r_ij + r_ik - descriptor_out%x(i_desc)%data(2) = (r_ij - r_ik)**2 - descriptor_out%x(i_desc)%data(3) = r_jk !cos_ijk - descriptor_out%x(i_desc)%data(4) = descriptor_coordination%x(i)%data(1) - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - - descriptor_out%x(i_desc)%covariance_cutoff = fc_j*fc_k - endif - - if(my_do_grad_descriptor) then - dcosijk_ij = ( u_ik - cos_ijk * u_ij ) / r_ij - dcosijk_ik = ( u_ij - cos_ijk * u_ik ) / r_ik - - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,0) = - u_ij - u_ik - descriptor_out%x(i_desc)%grad_data(2,:,0) = 2.0_dp * (r_ij - r_ik)*(-u_ij + u_ik) - descriptor_out%x(i_desc)%grad_data(3,:,0) = 0.0_dp !-dcosijk_ij - dcosijk_ik - descriptor_out%x(i_desc)%grad_data(4,:,0) = descriptor_coordination%x(i)%grad_data(1,:,0) - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) = - dfc_j*fc_k*u_ij - dfc_k*fc_j*u_ik - - descriptor_out%x(i_desc)%ii(1) = j - descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc)%has_grad_data(1) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,1) = u_ij - descriptor_out%x(i_desc)%grad_data(2,:,1) = 2.0_dp * (r_ij - r_ik)*u_ij - descriptor_out%x(i_desc)%grad_data(3,:,1) = u_jk !dcosijk_ij - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = dfc_j*fc_k*u_ij - - descriptor_out%x(i_desc)%ii(2) = k - descriptor_out%x(i_desc)%pos(:,2) = at%pos(:,k) + matmul(at%lattice,shift_ik) - descriptor_out%x(i_desc)%has_grad_data(2) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,2) = u_ik - descriptor_out%x(i_desc)%grad_data(2,:,2) = 2.0_dp * (r_ij - r_ik)*(-u_ik) - descriptor_out%x(i_desc)%grad_data(3,:,2) = -u_jk !dcosijk_ik - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,2) = dfc_k*fc_j*u_ik - - descriptor_out%x(i_desc)%ii(3:) = descriptor_coordination%x(i)%ii(1:) - descriptor_out%x(i_desc)%pos(:,3:) = descriptor_coordination%x(i)%pos(:,1:) - descriptor_out%x(i_desc)%has_grad_data(3:) = descriptor_coordination%x(i)%has_grad_data(1:) - descriptor_out%x(i_desc)%grad_data(4,:,3:) = descriptor_coordination%x(i)%grad_data(1,:,1:) - endif - enddo - enddo - enddo - - call finalise(my_coordination) - call finalise(descriptor_coordination) - - call system_timer('co_angle_3b_calc') - - endsubroutine co_angle_3b_calc - - subroutine co_distance_2b_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(co_distance_2b), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(descriptor) :: my_coordination - type(descriptor_data) :: descriptor_coordination - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor, Zi1, Zi2, Zj1, Zj2 - integer :: d, n_descriptors, n_cross, i_desc, i, j, n, & - n_neighbours_coordination_i, n_neighbours_coordination_ij, n_index - integer, dimension(3) :: shift - real(dp) :: r_ij - real(dp), dimension(3) :: u_ij - - INIT_ERROR(error) - call system_timer('co_distance_2b_calc') - - if(.not. this%initialised) then - RAISE_ERROR("co_distance_2b_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='co_distance_2b_calc args_str')) then - RAISE_ERROR("co_distance_2b_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("co_distance_2b_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("co_distance_2b_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = co_distance_2b_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - i_desc = 0 - do i = 1, at%N - - if( associated(atom_mask_pointer) ) then - if( .not. atom_mask_pointer(i) ) cycle - endif - - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance=r_ij) - - if(r_ij >= this%cutoff) cycle -!if(r_ij <3.5_dp) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - i_desc = i_desc + 1 - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - endif - - if(my_do_grad_descriptor) then - n_neighbours_coordination_ij = n_neighbours(at,i,max_dist=this%coordination_cutoff) + & - n_neighbours(at,j,max_dist=this%coordination_cutoff) + 2 - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:1+n_neighbours_coordination_ij)) - allocate(descriptor_out%x(i_desc)%ii(0:1+n_neighbours_coordination_ij)) - allocate(descriptor_out%x(i_desc)%pos(3,0:1+n_neighbours_coordination_ij)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:1+n_neighbours_coordination_ij)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:1+n_neighbours_coordination_ij)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - enddo - - call initialise(my_coordination,'coordination cutoff='//this%coordination_cutoff//' transition_width='//this%coordination_transition_width,error) - call calc(my_coordination,at,descriptor_coordination,.true.,do_grad_descriptor,args_str,error) - - i_desc = 0 - do i = 1, at%N - - if( associated(atom_mask_pointer) ) then - if( .not. atom_mask_pointer(i) ) cycle - endif - - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, shift=shift) - if( r_ij >= this%cutoff ) cycle -!if(r_ij <3.5_dp) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - i_desc = i_desc + 1 - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1:2) = (/i,j/) - - descriptor_out%x(i_desc)%has_data = .true. - - descriptor_out%x(i_desc)%data(1) = r_ij - descriptor_out%x(i_desc)%data(2) = descriptor_coordination%x(i)%data(1) + descriptor_coordination%x(j)%data(1) - descriptor_out%x(i_desc)%data(3) = (descriptor_coordination%x(i)%data(1) - descriptor_coordination%x(j)%data(1))**2 - - descriptor_out%x(i_desc)%covariance_cutoff = coordination_function(r_ij, this%cutoff,this%transition_width) !coordination_function(r_ij,3.5_dp, 0.5_dp, this%cutoff,this%transition_width) - endif - if(my_do_grad_descriptor) then - n_neighbours_coordination_i = n_neighbours(at,i,max_dist=this%coordination_cutoff) - - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,0) = -u_ij(:) - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) = -dcoordination_function(r_ij,this%cutoff,this%transition_width)*u_ij !-dcoordination_function(r_ij,3.5_dp, 0.5_dp, this%cutoff,this%transition_width)*u_ij - - descriptor_out%x(i_desc)%ii(1) = j - descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,j) + matmul(at%lattice,shift) - descriptor_out%x(i_desc)%has_grad_data(1) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,1) = u_ij(:) - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = -descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) - - descriptor_out%x(i_desc)%ii(2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%ii(:) - descriptor_out%x(i_desc)%pos(:,2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%pos(:,:) - descriptor_out%x(i_desc)%has_grad_data(2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%has_grad_data(:) - descriptor_out%x(i_desc)%grad_data(2,:,2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%grad_data(1,:,:) - descriptor_out%x(i_desc)%grad_data(3,:,2:n_neighbours_coordination_i+2) = 2.0_dp*(descriptor_coordination%x(i)%data(1) - descriptor_coordination%x(j)%data(1))*& - descriptor_coordination%x(i)%grad_data(1,:,:) - - descriptor_out%x(i_desc)%ii(n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%ii(:) - descriptor_out%x(i_desc)%pos(:,n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%pos(:,:) - descriptor_out%x(i_desc)%has_grad_data(n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%has_grad_data(:) - descriptor_out%x(i_desc)%grad_data(2,:,n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%grad_data(1,:,:) - descriptor_out%x(i_desc)%grad_data(3,:,n_neighbours_coordination_i+3:) = -2.0_dp*(descriptor_coordination%x(i)%data(1) - descriptor_coordination%x(j)%data(1))*& - descriptor_coordination%x(j)%grad_data(1,:,:) - - endif - enddo - enddo - - call finalise(my_coordination) - call finalise(descriptor_coordination) - - call system_timer('co_distance_2b_calc') - - endsubroutine co_distance_2b_calc - - subroutine cosnx_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(cosnx), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, i, j, k, n, m, a, b, i_desc, i_cosnx, l_n_neighbours, n_i, & - n_descriptors, n_cross, n_index - integer, dimension(3) :: shift_ij - real(dp) :: r_ij, r_ik, r_jk, cos_ijk, T_0_cos_ijk, T_1_cos_ijk, T_n_cos_ijk, U_0_cos_ijk, U_1_cos_ijk, U_n_cos_ijk, Ang - real(dp), dimension(3) :: u_ij, u_ik, d_ij, d_ik, d_jk, dcosijk_ij, dcosijk_ik, dAng_ij, dAng_ik - real(dp), dimension(:), allocatable :: Rad_ij, Rad_ik, T_cos_ijk, U_cos_ijk - real(dp), dimension(:,:), allocatable :: dRad_ij, dRad_ik - integer, dimension(total_elements) :: species_map - - INIT_ERROR(error) - - call system_timer('cosnx_calc') - - if(.not. this%initialised) then - RAISE_ERROR("cosnx_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='cosnx_calc args_str')) then - RAISE_ERROR("cosnx_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("cosnx_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - species_map = 0 - do i = 1, size(this%species_Z) - if(this%species_Z(i) == 0) then - species_map = 1 - else - species_map(this%species_Z(i)) = i - endif - enddo - - call finalise(descriptor_out) - - d = cosnx_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - allocate(Rad_ij(this%n_max), Rad_ik(this%n_max)) - allocate(T_cos_ijk(0:this%l_max)) - if(my_do_grad_descriptor) then - allocate(U_cos_ijk(-1:this%l_max)) - allocate(dRad_ij(3,this%n_max), dRad_ik(3,this%n_max)) - endif - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines=u_ij, diff=d_ij, shift=shift_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - - do a = 1, this%n_max - Rad_ij(a) = RadialFunction(this%Radial, r_ij, a) * this%w(species_map(at%Z(j))) - if(my_do_grad_descriptor) dRad_ij(:,a) = GradRadialFunction(this%Radial, r_ij, a) * u_ij * this%w(species_map(at%Z(j))) - enddo - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(n_i) = j - descriptor_out%x(i_desc)%pos(:,n_i) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc)%has_grad_data(n_i) = .true. - endif - - do m = 1, n_neighbours(at,i) - k = neighbour(at, i, m, distance = r_ik, cosines=u_ik, diff=d_ik) - if( r_ik >= this%cutoff ) cycle - - d_jk = d_ik - d_ij - r_jk = norm(d_jk) - if( r_jk .feq. 0.0_dp ) cycle - - cos_ijk = dot_product(u_ij,u_ik) - if(my_do_grad_descriptor) then - dcosijk_ij = ( u_ik - cos_ijk * u_ij ) / r_ij - dcosijk_ik = ( u_ij - cos_ijk * u_ik ) / r_ik - endif - - do a = 1, this%n_max - Rad_ik(a) = RadialFunction(this%Radial, r_ik, a) * this%w(species_map(at%Z(k))) - if(my_do_grad_descriptor) dRad_ik(:,a) = GradRadialFunction(this%Radial, r_ik, a) * u_ik * this%w(species_map(at%Z(k))) - enddo - - if(this%l_max >= 0) then - T_cos_ijk(0) = 1.0_dp - T_0_cos_ijk = T_cos_ijk(0) - if(my_do_grad_descriptor) then - U_cos_ijk(-1) = 0.0_dp - U_cos_ijk(0) = 1.0_dp - U_0_cos_ijk = U_cos_ijk(0) - endif - endif - - if(this%l_max >= 1) then - T_cos_ijk(1) = cos_ijk - T_1_cos_ijk = T_cos_ijk(1) - if(my_do_grad_descriptor) then - U_cos_ijk(1) = 2.0_dp*cos_ijk - U_1_cos_ijk = U_cos_ijk(1) - endif - endif - - do b = 2, this%l_max - T_n_cos_ijk = 2*cos_ijk*T_1_cos_ijk - T_0_cos_ijk - T_0_cos_ijk = T_1_cos_ijk - T_1_cos_ijk = T_n_cos_ijk - - T_cos_ijk(b) = T_n_cos_ijk - - if(my_do_grad_descriptor) then - U_n_cos_ijk = 2*cos_ijk*U_1_cos_ijk - U_0_cos_ijk - U_0_cos_ijk = U_1_cos_ijk - U_1_cos_ijk = U_n_cos_ijk - - U_cos_ijk(b) = U_n_cos_ijk - endif - enddo - - i_cosnx = 0 - do a = 1, this%n_max - do b = 0, this%l_max - i_cosnx = i_cosnx + 1 - - Ang = T_cos_ijk(b) - - if(my_do_descriptor) & - descriptor_out%x(i_desc)%data(i_cosnx) = descriptor_out%x(i_desc)%data(i_cosnx) + Rad_ij(a)*Rad_ik(a)*Ang*0.5_dp - - if(my_do_grad_descriptor) then - - dAng_ij = b*U_cos_ijk(b-1) * dcosijk_ij - dAng_ik = b*U_cos_ijk(b-1) * dcosijk_ik - - descriptor_out%x(i_desc)%grad_data(i_cosnx,:,0) = descriptor_out%x(i_desc)%grad_data(i_cosnx,:,0) - & - ( Rad_ij(a)*Rad_ik(a)*(dAng_ij+dAng_ik) + dRad_ij(:,a)*Rad_ik(a)*Ang + Rad_ij(a)*dRad_ik(:,a)*Ang ) * 0.5_dp - - descriptor_out%x(i_desc)%grad_data(i_cosnx,:,n_i) = descriptor_out%x(i_desc)%grad_data(i_cosnx,:,n_i) + & - (Rad_ij(a)*Rad_ik(a)*dAng_ij + dRad_ij(:,a)*Rad_ik(a)*Ang) - endif - enddo - enddo - enddo - enddo - enddo - - if(allocated(Rad_ij)) deallocate(Rad_ij) - if(allocated(Rad_ik)) deallocate(Rad_ik) - if(allocated(T_cos_ijk)) deallocate(T_cos_ijk) - if(allocated(U_cos_ijk)) deallocate(U_cos_ijk) - if(allocated(dRad_ij)) deallocate(dRad_ij) - if(allocated(dRad_ik)) deallocate(dRad_ik) - - call system_timer('cosnx_calc') - - endsubroutine cosnx_calc - - subroutine trihis_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(trihis), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, i, j, k, n, m, i_desc, n_index - integer, dimension(3) :: shift_ij - real(dp) :: r_ij, r_ik, r_jk, cos_ijk, Sym_Cor_S, Sym_Cor_A, exp_desc - real(dp), dimension(3) :: u_ij, u_ik, d_ij, d_ik, d_jk, dcosijk_ij, dcosijk_ik, x, exp_arg, dexp_desc - real(dp), dimension(3,3) :: dx_j, dx_k - - INIT_ERROR(error) - - call system_timer('trihis_calc') - - if(.not. this%initialised) then - RAISE_ERROR("trihis_calc: descriptor object not initialised", error) - endif - RAISE_ERROR("trihis_calc: ab686 noticed that this routine needs updating. Remove this line if you know what you are doing, then proceed.", error) - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='trihis_calc args_str')) then - RAISE_ERROR("trihis_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("trihis_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("trihis_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = trihis_dimensions(this,error) - - allocate(descriptor_out%x(at%N)) - do i = 1, at%N - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%has_data = .false. - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,0:n_neighbours(at,i))) - allocate(descriptor_out%x(i)%ii(0:n_neighbours(at,i))) - allocate(descriptor_out%x(i)%pos(3,0:n_neighbours(at,i))) - allocate(descriptor_out%x(i)%has_grad_data(0:n_neighbours(at,i))) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - endif - enddo - - do i = 1, at%N - - if(my_do_descriptor) then - descriptor_out%x(i)%ci(1) = i - descriptor_out%x(i)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i)%ii(0) = i - descriptor_out%x(i)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i)%has_grad_data(0) = .true. - endif - - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines=u_ij, diff=d_ij, shift=shift_ij) - if( r_ij >= this%cutoff ) cycle - - if(my_do_grad_descriptor) then - descriptor_out%x(i)%ii(n) = j - descriptor_out%x(i)%pos(:,n) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i)%has_grad_data(n) = .true. - endif - - do m = 1, n_neighbours(at,i) - k = neighbour(at, i, m, distance = r_ik, cosines=u_ik, diff=d_ik) - if( r_ik >= this%cutoff ) cycle - - d_jk = d_ik - d_ij - r_jk = norm(d_jk) - if( r_jk .feq. 0.0_dp ) cycle - - cos_ijk = dot_product(u_ij,u_ik) - Sym_Cor_S = r_ij + r_ik - Sym_Cor_A = (r_ij - r_ik)**2 - - x = (/Sym_Cor_S, Sym_Cor_A, cos_ijk/) - - if(my_do_grad_descriptor) then - dcosijk_ij = ( u_ik - cos_ijk * u_ij ) / r_ij - dcosijk_ik = ( u_ij - cos_ijk * u_ik ) / r_ik - - dx_j(:,1) = u_ij - dx_j(:,2) = 2.0_dp*(r_ij - r_ik)*u_ij - dx_j(:,3) = dcosijk_ij - - dx_k(:,1) = u_ik - dx_k(:,2) = -2.0_dp*(r_ij - r_ik)*u_ik - dx_k(:,3) = dcosijk_ik - endif - - do i_desc = 1, this%n_gauss - - exp_arg = (x - this%gauss_centre(:,i_desc))/this%gauss_width(:,i_desc) - exp_desc = exp(-0.5_dp*sum(exp_arg**2)) - - if(my_do_descriptor) & - descriptor_out%x(i)%data(i_desc) = descriptor_out%x(i)%data(i_desc) + exp_desc - - if(my_do_grad_descriptor) then - dexp_desc = -exp_desc * exp_arg / this%gauss_width(:,i_desc) - - descriptor_out%x(i)%grad_data(i_desc,:,0) = descriptor_out%x(i)%grad_data(i_desc,:,0) - & - matmul(dx_j+dx_k,dexp_desc) - descriptor_out%x(i)%grad_data(i_desc,:,n) = descriptor_out%x(i)%grad_data(i_desc,:,n) + & - 2.0_dp*matmul(dx_j,dexp_desc) - endif - enddo - enddo - enddo - enddo - - call system_timer('trihis_calc') - - endsubroutine trihis_calc - - subroutine water_monomer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(water_monomer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, i, iO, iH1, iH2, n_index - integer :: i_desc, mpi_n_procs, mpi_my_proc - integer, dimension(3) :: shift_1, shift_2 - integer, dimension(:,:), allocatable :: water_monomer_index - real(dp) :: r1, r2 - real(dp), dimension(3) :: v1, v2, u1, u2 - - INIT_ERROR(error) - - call system_timer('water_monomer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("water_monomer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='water_monomer_calc args_str')) then - RAISE_ERROR("water_monomer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("water_monomer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - d = water_monomer_dimensions(this,error) - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index, error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index, error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,3)) - allocate(descriptor_out%x(i)%ii(3)) - allocate(descriptor_out%x(i)%pos(3,3)) - allocate(descriptor_out%x(i)%has_grad_data(3)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,3)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - allocate(water_monomer_index(3,count(at%Z==8))) - call find_water_monomer(at,water_monomer_index,error=error) - - i_desc = 0 - do i = 1, count(at%Z==8) - - iO = water_monomer_index(1,i) - iH1 = water_monomer_index(2,i) - iH2 = water_monomer_index(3,i) - - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(iO)) cycle - endif - i_desc = i_desc + 1 - - v1 = diff_min_image(at,iO,iH1,shift=shift_1) - v2 = diff_min_image(at,iO,iH2,shift=shift_2) - r1 = sqrt(dot_product(v1,v1)) - r2 = sqrt(dot_product(v2,v2)) - u1 = v1 / r1 - u2 = v2 / r2 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(:) = water_monomer_index(:,i) - descriptor_out%x(i_desc)%has_data = .true. - descriptor_out%x(i_desc)%data(1) = r1+r2 - descriptor_out%x(i_desc)%data(2) = (r1-r2)**2 - descriptor_out%x(i_desc)%data(3) = dot_product(v1,v2) - endif - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(:) = water_monomer_index(:,i) - descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,iO) - descriptor_out%x(i_desc)%pos(:,2) = at%pos(:,iH1) + matmul(at%lattice,shift_1) - descriptor_out%x(i_desc)%pos(:,3) = at%pos(:,iH2) + matmul(at%lattice,shift_2) - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - - descriptor_out%x(i_desc)%grad_data(1,:,1) = -u1-u2 ! 1st descriptor wrt rO - descriptor_out%x(i_desc)%grad_data(1,:,2) = u1 ! 1st descriptor wrt rH1 - descriptor_out%x(i_desc)%grad_data(1,:,3) = u2 ! 1st descriptor wrt rH2 - descriptor_out%x(i_desc)%grad_data(2,:,1) = 2.0_dp*(r1-r2)*(u2-u1) ! 2nd descriptor wrt rO - descriptor_out%x(i_desc)%grad_data(2,:,2) = 2.0_dp*(r1-r2)*u1 ! 2nd descriptor wrt rH1 - descriptor_out%x(i_desc)%grad_data(2,:,3) = -2.0_dp*(r1-r2)*u2 ! 2nd descriptor wrt rH2 - descriptor_out%x(i_desc)%grad_data(3,:,1) = -v1-v2 ! 3rd descriptor wrt rO - descriptor_out%x(i_desc)%grad_data(3,:,2) = v2 ! 3rd descriptor wrt rH1 - descriptor_out%x(i_desc)%grad_data(3,:,3) = v1 ! 3rd descriptor wrt rH2 - endif - - enddo - - deallocate(water_monomer_index) - call system_timer('water_monomer_calc') - - endsubroutine water_monomer_calc - - subroutine water_dimer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(water_dimer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, n_monomers, i_desc, i, j, n, & - iAO, iAH1, iAH2, iBO, iBH1, iBH2, i_distance, n_index - integer :: mpi_n_procs, mpi_my_proc - integer, dimension(3) :: shift_AO_BO, shift_AO_AH1, shift_AO_AH2, shift_AO_BH1, shift_AO_BH2, & - shift_BO_AH1, shift_BO_AH2, shift_BO_BH1, shift_BO_BH2, & - shift_AH1_AH2, shift_AH1_BH1, shift_AH1_BH2, shift_AH2_BH1, shift_AH2_BH2, shift_BH1_BH2 - real(dp), dimension(3) :: diff_AO_BO, diff_AO_AH1, diff_AO_AH2, diff_AO_BH1, diff_AO_BH2, & - diff_BO_AH1, diff_BO_AH2, diff_BO_BH1, diff_BO_BH2, & - diff_AH1_AH2, diff_AH1_BH1, diff_AH1_BH2, diff_AH2_BH1, diff_AH2_BH2, diff_BH1_BH2 - integer, dimension(:,:), allocatable :: water_monomer_index - real(dp) :: r_AO_BO, r_AO_AH1, r_AO_AH2, r_AO_BH1, r_AO_BH2, r_BO_AH1, r_BO_AH2, r_BO_BH1, r_BO_BH2, & - r_AH1_AH2, r_AH1_BH1, r_AH1_BH2, r_AH2_BH1, r_AH2_BH2, r_BH1_BH2 - integer, dimension(1) :: j_array - real(dp), dimension(15) :: distances - - INIT_ERROR(error) - - call system_timer('water_dimer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("water_dimer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='water_dimer_calc args_str')) then - RAISE_ERROR("water_dimer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("water_dimer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - d = water_dimer_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%has_data = .false. - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,6)) - allocate(descriptor_out%x(i)%ii(6)) - allocate(descriptor_out%x(i)%pos(3,6)) - allocate(descriptor_out%x(i)%has_grad_data(6)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,6)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - - endif - enddo - - n_monomers = 0 - do i = 1, at%N - if(at%Z(i) == 8) n_monomers = n_monomers+1 - enddo - - allocate(water_monomer_index(3,n_monomers)) - call find_water_monomer(at,water_monomer_index,OHH_ordercheck=this%OHH_ordercheck,monomer_cutoff=this%monomer_cutoff,error=error) - - i_desc = 0 - do i = 1, n_monomers - iAO = water_monomer_index(1,i) - iAH1 = water_monomer_index(2,i) - iAH2 = water_monomer_index(3,i) - - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(iAO)) cycle - endif - - diff_AO_AH1 = diff_min_image(at,iAO,iAH1,shift=shift_AO_AH1) - diff_AO_AH2 = diff_min_image(at,iAO,iAH2,shift=shift_AO_AH2) - diff_AH1_AH2 = diff_min_image(at,iAH1,iAH2,shift=shift_AH1_AH2) - - r_AO_AH1 = norm(diff_AO_AH1) - r_AO_AH2 = norm(diff_AO_AH2) - r_AH1_AH2 = norm(diff_AH1_AH2) - - do n = 1, n_neighbours(at,iAO) - iBO = neighbour(at,iAO,n,distance=r_AO_BO, diff=diff_AO_BO, shift=shift_AO_BO ) - if(at%Z(iBO) /= 8) cycle - if( r_AO_BO >= this%cutoff ) cycle - i_desc = i_desc + 1 - j_array = find(water_monomer_index(1,:) == iBO) - j = j_array(1) - - iBH1 = water_monomer_index(2,j) - iBH2 = water_monomer_index(3,j) - - diff_BO_BH1 = diff_min_image(at,iBO,iBH1,shift=shift_BO_BH1) - diff_BO_BH2 = diff_min_image(at,iBO,iBH2,shift=shift_BO_BH2) - diff_BH1_BH2 = diff_min_image(at,iBH1,iBH2,shift=shift_BH1_BH2) - - r_BO_BH1 = norm(diff_BO_BH1) - r_BO_BH2 = norm(diff_BO_BH2) - r_BH1_BH2 = norm(diff_BH1_BH2) - - diff_AO_BH1 = diff_AO_BO + diff_BO_BH1 - diff_AO_BH2 = diff_AO_BO + diff_BO_BH2 - shift_AO_BH1 = shift_AO_BO + shift_BO_BH1 - shift_AO_BH2 = shift_AO_BO + shift_BO_BH2 - - r_AO_BH1 = norm(diff_AO_BH1) - r_AO_BH2 = norm(diff_AO_BH2) - - diff_BO_AH1 = -diff_AO_BO + diff_AO_AH1 - diff_BO_AH2 = -diff_AO_BO + diff_AO_AH2 - - shift_BO_AH1 = -shift_AO_BO + shift_AO_AH1 - shift_BO_AH2 = -shift_AO_BO + shift_AO_AH2 - - r_BO_AH1 = norm(diff_BO_AH1) - r_BO_AH2 = norm(diff_BO_AH2) - - diff_AH1_BH1 = -diff_AO_AH1 + diff_AO_BO + diff_BO_BH1 - diff_AH1_BH2 = -diff_AO_AH1 + diff_AO_BO + diff_BO_BH2 - diff_AH2_BH1 = -diff_AO_AH2 + diff_AO_BO + diff_BO_BH1 - diff_AH2_BH2 = -diff_AO_AH2 + diff_AO_BO + diff_BO_BH2 - - shift_AH1_BH1 = -shift_AO_AH1 + shift_AO_BO + shift_BO_BH1 - shift_AH1_BH2 = -shift_AO_AH1 + shift_AO_BO + shift_BO_BH2 - shift_AH2_BH1 = -shift_AO_AH2 + shift_AO_BO + shift_BO_BH1 - shift_AH2_BH2 = -shift_AO_AH2 + shift_AO_BO + shift_BO_BH2 - - r_AH1_BH1 = norm(diff_AH1_BH1) - r_AH1_BH2 = norm(diff_AH1_BH2) - r_AH2_BH1 = norm(diff_AH2_BH1) - r_AH2_BH2 = norm(diff_AH2_BH2) - - - distances = (/r_AO_BO, & - r_AO_AH1, r_AO_AH2, r_AO_BH1, r_AO_BH2, r_BO_AH1, r_BO_AH2, r_BO_BH1, r_BO_BH2, & - r_AH1_AH2, r_AH1_BH1, r_AH1_BH2, r_AH2_BH1, r_AH2_BH2, r_BH1_BH2/) - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(:) = (/ water_monomer_index(:,i),water_monomer_index(:,j) /) - descriptor_out%x(i_desc)%has_data = .true. - descriptor_out%x(i_desc)%data(:) = distances**this%power - - descriptor_out%x(i_desc)%covariance_cutoff = coordination_function(r_AO_BO, & - this%cutoff,this%cutoff_transition_width) - endif - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(:) = (/ water_monomer_index(:,i),water_monomer_index(:,j) /) - descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,iAO) ! TODO: Have to figure out how to do this. - descriptor_out%x(i_desc)%pos(:,2) = at%pos(:,iAH1) + matmul(at%lattice,shift_AO_AH1) ! TODO: Have to figure out how to do this. - descriptor_out%x(i_desc)%pos(:,3) = at%pos(:,iAH2) + matmul(at%lattice,shift_AO_AH2) ! TODO: Have to figure out how to do this. - descriptor_out%x(i_desc)%pos(:,4) = at%pos(:,iBO) + matmul(at%lattice,shift_AO_BO) ! TODO: Have to figure out how to do this. - descriptor_out%x(i_desc)%pos(:,5) = at%pos(:,iBH1) + matmul(at%lattice,shift_AO_BH1) ! TODO: Have to figure out how to do this. - descriptor_out%x(i_desc)%pos(:,6) = at%pos(:,iBH2) + matmul(at%lattice,shift_AO_BH2) ! TODO: Have to figure out how to do this. - - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - - descriptor_out%x(i_desc)%grad_data(1,:,1) = -diff_AO_BO / r_AO_BO ! 1st descriptor wrt OA - descriptor_out%x(i_desc)%grad_data(1,:,4) = -descriptor_out%x(i_desc)%grad_data(1,:,1) ! 1st descriptor wrt OB - - descriptor_out%x(i_desc)%grad_data(2,:,1) = -diff_AO_AH1 / r_AO_AH1 ! 2nd descriptor wrt OA - descriptor_out%x(i_desc)%grad_data(2,:,2) = -descriptor_out%x(i_desc)%grad_data(2,:,1) ! 2nd descriptor wrt AH1 - descriptor_out%x(i_desc)%grad_data(3,:,1) = -diff_AO_AH2 / r_AO_AH2 ! 3rd descriptor wrt OA - descriptor_out%x(i_desc)%grad_data(3,:,3) = -descriptor_out%x(i_desc)%grad_data(3,:,1) ! 3rd descriptor wrt AH2 - descriptor_out%x(i_desc)%grad_data(4,:,1) = -diff_AO_BH1 / r_AO_BH1 ! 4th descriptor wrt OA - descriptor_out%x(i_desc)%grad_data(4,:,5) = -descriptor_out%x(i_desc)%grad_data(4,:,1) ! 4th descriptor wrt BH1 - descriptor_out%x(i_desc)%grad_data(5,:,1) = -diff_AO_BH2 / r_AO_BH2 ! 5th descriptor wrt OA - descriptor_out%x(i_desc)%grad_data(5,:,6) = -descriptor_out%x(i_desc)%grad_data(5,:,1) ! 5th descriptor wrt BH2 - - descriptor_out%x(i_desc)%grad_data(6,:,4) = -diff_BO_AH1 / r_BO_AH1 ! 6th descriptor wrt OB - descriptor_out%x(i_desc)%grad_data(6,:,2) = -descriptor_out%x(i_desc)%grad_data(6,:,4) ! 6th descriptor wrt AH1 - descriptor_out%x(i_desc)%grad_data(7,:,4) = -diff_BO_AH2 / r_BO_AH2 ! 7th descriptor wrt OB - descriptor_out%x(i_desc)%grad_data(7,:,3) = -descriptor_out%x(i_desc)%grad_data(7,:,4) ! 7th descriptor wrt AH2 - descriptor_out%x(i_desc)%grad_data(8,:,4) = -diff_BO_BH1 / r_BO_BH1 ! 8th descriptor wrt OB - descriptor_out%x(i_desc)%grad_data(8,:,5) = -descriptor_out%x(i_desc)%grad_data(8,:,4) ! 8th descriptor wrt BH1 - descriptor_out%x(i_desc)%grad_data(9,:,4) = -diff_BO_BH2 / r_BO_BH2 ! 9th descriptor wrt OB - descriptor_out%x(i_desc)%grad_data(9,:,6) = -descriptor_out%x(i_desc)%grad_data(9,:,4) ! 9th descriptor wrt BH2 - - descriptor_out%x(i_desc)%grad_data(10,:,2) = -diff_AH1_AH2 / r_AH1_AH2 ! 10th descriptor wrt AH1 - descriptor_out%x(i_desc)%grad_data(10,:,3) = -descriptor_out%x(i_desc)%grad_data(10,:,2) ! 10th descriptor wrt AH2 - descriptor_out%x(i_desc)%grad_data(11,:,2) = -diff_AH1_BH1 / r_AH1_BH1 ! 11th descriptor wrt AH1 - descriptor_out%x(i_desc)%grad_data(11,:,5) = -descriptor_out%x(i_desc)%grad_data(11,:,2) ! 11th descriptor wrt BH1 - descriptor_out%x(i_desc)%grad_data(12,:,2) = -diff_AH1_BH2 / r_AH1_BH2 ! 12th descriptor wrt AH1 - descriptor_out%x(i_desc)%grad_data(12,:,6) = -descriptor_out%x(i_desc)%grad_data(12,:,2) ! 12th descriptor wrt BH2 - - descriptor_out%x(i_desc)%grad_data(13,:,3) = -diff_AH2_BH1 / r_AH2_BH1 ! 13th descriptor wrt AH2 - descriptor_out%x(i_desc)%grad_data(13,:,5) = -descriptor_out%x(i_desc)%grad_data(13,:,3) ! 13th descriptor wrt BH1 - descriptor_out%x(i_desc)%grad_data(14,:,3) = -diff_AH2_BH2 / r_AH2_BH2 ! 14th descriptor wrt AH2 - descriptor_out%x(i_desc)%grad_data(14,:,6) = -descriptor_out%x(i_desc)%grad_data(14,:,3) ! 14th descriptor wrt BH2 - - descriptor_out%x(i_desc)%grad_data(15,:,5) = -diff_BH1_BH2 / r_BH1_BH2 ! 15th descriptor wrt BH1 - descriptor_out%x(i_desc)%grad_data(15,:,6) = -descriptor_out%x(i_desc)%grad_data(15,:,5) ! 15th descriptor wrt BH2 - - do i_distance = 1, 15 - descriptor_out%x(i_desc)%grad_data(i_distance,:,:) = descriptor_out%x(i_desc)%grad_data(i_distance,:,:) * & - distances(i_distance)**(this%power-1.0_dp) * this%power - enddo - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = -dcoordination_function(r_AO_BO,& - this%cutoff,this%cutoff_transition_width) * diff_AO_BO / r_AO_BO - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,4) = -descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) - endif - enddo - enddo - - deallocate(water_monomer_index) - call system_timer('water_dimer_calc') - - endsubroutine water_dimer_calc - - subroutine A2_dimer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(A2_dimer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, n_monomers, i_desc, i, j, & - iA1, iA2, iB1, iB2, n_index - integer, dimension(3) :: shift_A1_A2, shift_A1_B1, shift_A1_B2, shift_A2_B1, shift_A2_B2, shift_B1_B2 - integer, dimension(at%N) :: A2_monomer_index - real(dp) :: r_A1_A2, r_A1_B1, r_A1_B2, r_A2_B1, r_A2_B2, r_B1_B2 - - INIT_ERROR(error) - - call system_timer('A2_dimer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("A2_dimer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='A2_dimer_calc args_str')) then - RAISE_ERROR("A2_dimer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("A2_dimer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("A2_dimer_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = A2_dimer_dimensions(this,error) - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%has_data = .false. - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,4)) - allocate(descriptor_out%x(i)%ii(4)) - allocate(descriptor_out%x(i)%pos(3,4)) - allocate(descriptor_out%x(i)%has_grad_data(4)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,4)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - n_monomers = count(at%Z == this%atomic_number) / 2 - - call find_A2_monomer(at,this%atomic_number, this%monomer_cutoff, A2_monomer_index,error) - - i_desc = 0 - do i = 1, at%N - iA1 = i - iA2 = neighbour(at,i,A2_monomer_index(i),distance=r_A1_A2,shift=shift_A1_A2) - if( iA1 > iA2 ) cycle - - do j = i + 1, at%N - iB1 = j - iB2 = neighbour(at,j,A2_monomer_index(j),distance=r_B1_B2,shift=shift_B1_B2) - if( iB1 > iB2 ) cycle - - r_A1_B1 = distance_min_image(at,iA1,iB1,shift=shift_A1_B1) - r_A1_B2 = distance_min_image(at,iA1,iB2,shift=shift_A1_B2) - - r_A2_B1 = distance_min_image(at,iA2,iB1,shift=shift_A2_B1) - r_A2_B2 = distance_min_image(at,iA2,iB2,shift=shift_A2_B2) - - if( any( (/r_A1_A2,r_B1_B2,r_A1_B1,r_A1_B2,r_A2_B1,r_A2_B2/) >= this%cutoff) ) cycle - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(:) = (/ iA1, iA2, iB1, iB2 /) - descriptor_out%x(i_desc)%has_data = .true. - descriptor_out%x(i_desc)%data(:) = (/ r_A1_A2, r_B1_B2, r_A1_B1, r_A1_B2, r_A2_B1, r_A2_B2/) - endif - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(:) = (/ iA1, iA2, iB1, iB2 /) - descriptor_out%x(i_desc)%pos(:,:) = 0.0_dp ! TODO: Have to figure out how to do this. - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - - descriptor_out%x(i_desc)%grad_data(1,:,1) = -diff(at,iA1,iA2,shift=shift_A1_A2) / r_A1_A2 ! 1st descriptor wrt A1 - descriptor_out%x(i_desc)%grad_data(1,:,2) = -descriptor_out%x(i_desc)%grad_data(1,:,1) ! 1st descriptor wrt A2 - descriptor_out%x(i_desc)%grad_data(2,:,3) = -diff(at,iB1,iB2,shift=shift_B1_B2) / r_B1_B2 ! 2nd descriptor wrt B1 - descriptor_out%x(i_desc)%grad_data(2,:,4) = -descriptor_out%x(i_desc)%grad_data(2,:,3) ! 2nd descriptor wrt B2 - - descriptor_out%x(i_desc)%grad_data(3,:,1) = -diff(at,iA1,iB1,shift=shift_A1_B1) / r_A1_B1 ! 3rd descriptor wrt A1 - descriptor_out%x(i_desc)%grad_data(3,:,3) = -descriptor_out%x(i_desc)%grad_data(3,:,1) ! 3rd descriptor wrt B1 - descriptor_out%x(i_desc)%grad_data(4,:,1) = -diff(at,iA1,iB2,shift=shift_A1_B2) / r_A1_B2 ! 4th descriptor wrt A1 - descriptor_out%x(i_desc)%grad_data(4,:,4) = -descriptor_out%x(i_desc)%grad_data(4,:,1) ! 4th descriptor wrt B2 - - descriptor_out%x(i_desc)%grad_data(5,:,2) = -diff(at,iA2,iB1,shift=shift_A2_B1) / r_A2_B1 ! 5th descriptor wrt A2 - descriptor_out%x(i_desc)%grad_data(5,:,3) = -descriptor_out%x(i_desc)%grad_data(5,:,2) ! 5th descriptor wrt B1 - descriptor_out%x(i_desc)%grad_data(6,:,2) = -diff(at,iA2,iB2,shift=shift_A2_B2) / r_A2_B2 ! 6th descriptor wrt A2 - descriptor_out%x(i_desc)%grad_data(6,:,4) = -descriptor_out%x(i_desc)%grad_data(6,:,2) ! 6th descriptor wrt B2 - - endif - enddo - enddo - - call system_timer('A2_dimer_calc') - - endsubroutine A2_dimer_calc - - subroutine AB_dimer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(AB_dimer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, n_monomers, i_desc, i, j, & - iA1, iA2, iB1, iB2, n_index - integer, dimension(3) :: shift_A1_A2, shift_A1_B1, shift_A1_B2, shift_A2_B1, shift_A2_B2, shift_B1_B2 - integer, dimension(:,:), allocatable :: AB_monomer_index - real(dp) :: r_A1_A2, r_A1_B1, r_A1_B2, r_A2_B1, r_A2_B2, r_B1_B2 - - INIT_ERROR(error) - - call system_timer('AB_dimer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("AB_dimer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='AB_dimer_calc args_str')) then - RAISE_ERROR("AB_dimer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("AB_dimer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("AB_dimer_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = AB_dimer_dimensions(this,error) - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%has_data = .false. - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,4)) - allocate(descriptor_out%x(i)%ii(4)) - allocate(descriptor_out%x(i)%pos(3,4)) - allocate(descriptor_out%x(i)%has_grad_data(4)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,4)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - if( count(at%Z == this%atomic_number1) == count(at%Z == this%atomic_number2) ) then - n_monomers = count(at%Z == this%atomic_number1) - else - RAISE_ERROR("AB_dimer_calc: number of monomer atoms 1 ("//count(at%Z == this%atomic_number1)//") not equal to number of monomer atoms 2 ("//count(at%Z == this%atomic_number1)//")",error) - endif - - allocate(AB_monomer_index(2,n_monomers)) - call find_AB_monomer(at,(/this%atomic_number1,this%atomic_number2/), this%monomer_cutoff, AB_monomer_index,error) - - i_desc = 0 - do i = 1, n_monomers - iA1 = AB_monomer_index(1,i) - iB1 = AB_monomer_index(2,i) - do j = i + 1, n_monomers - iA2 = AB_monomer_index(1,j) - iB2 = AB_monomer_index(2,j) - - - r_A1_B1 = distance_min_image(at,iA1,iB1,shift=shift_A1_B1) - r_A2_B2 = distance_min_image(at,iA2,iB2,shift=shift_A2_B2) - - r_A1_A2 = distance_min_image(at,iA1,iA2,shift=shift_A1_A2) - r_B1_B2 = distance_min_image(at,iB1,iB2,shift=shift_B1_B2) - - r_A1_B2 = distance_min_image(at,iA1,iB2,shift=shift_A1_B2) - r_A2_B1 = distance_min_image(at,iA2,iB1,shift=shift_A2_B1) - - if( any( (/r_A1_A2,r_B1_B2,r_A1_B1,r_A1_B2,r_A2_B1,r_A2_B2/) >= this%cutoff) ) cycle - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(:) = (/ AB_monomer_index(:,i),AB_monomer_index(:,j) /) - descriptor_out%x(i_desc)%has_data = .true. - descriptor_out%x(i_desc)%data(:) = (/ r_A1_B1, r_A2_B2, r_A1_A2, r_B1_B2, r_A1_B2, r_A2_B1 /) - endif - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(:) = (/ AB_monomer_index(:,i),AB_monomer_index(:,j) /) - descriptor_out%x(i_desc)%pos(:,:) = 0.0_dp ! TODO: Have to figure out how to do this. - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - - descriptor_out%x(i_desc)%grad_data(1,:,1) = -diff(at,iA1,iB1,shift=shift_A1_B1) / r_A1_B1 ! 1st descriptor wrt A1 - descriptor_out%x(i_desc)%grad_data(1,:,2) = -descriptor_out%x(i_desc)%grad_data(1,:,1) ! 1st descriptor wrt B1 - descriptor_out%x(i_desc)%grad_data(2,:,3) = -diff(at,iA2,iB2,shift=shift_A2_B2) / r_A2_B2 ! 2nd descriptor wrt A2 - descriptor_out%x(i_desc)%grad_data(2,:,4) = -descriptor_out%x(i_desc)%grad_data(2,:,3) ! 2nd descriptor wrt B2 - - descriptor_out%x(i_desc)%grad_data(3,:,1) = -diff(at,iA1,iA2,shift=shift_A1_A2) / r_A1_A2 ! 1st descriptor wrt A1 - descriptor_out%x(i_desc)%grad_data(3,:,3) = -descriptor_out%x(i_desc)%grad_data(3,:,1) ! 1st descriptor wrt A2 - descriptor_out%x(i_desc)%grad_data(4,:,2) = -diff(at,iB1,iB2,shift=shift_B1_B2) / r_B1_B2 ! 2nd descriptor wrt B1 - descriptor_out%x(i_desc)%grad_data(4,:,4) = -descriptor_out%x(i_desc)%grad_data(4,:,2) ! 2nd descriptor wrt B2 - - descriptor_out%x(i_desc)%grad_data(5,:,1) = -diff(at,iA1,iB2,shift=shift_A1_B2) / r_A1_B2 ! 4th descriptor wrt A1 - descriptor_out%x(i_desc)%grad_data(5,:,4) = -descriptor_out%x(i_desc)%grad_data(5,:,1) ! 4th descriptor wrt B2 - descriptor_out%x(i_desc)%grad_data(6,:,3) = -diff(at,iA2,iB1,shift=shift_A2_B1) / r_A2_B1 ! 5th descriptor wrt A2 - descriptor_out%x(i_desc)%grad_data(6,:,2) = -descriptor_out%x(i_desc)%grad_data(6,:,3) ! 5th descriptor wrt B1 - - endif - enddo - enddo - - deallocate(AB_monomer_index) - call system_timer('AB_dimer_calc') - - endsubroutine AB_dimer_calc - - subroutine bond_real_space_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(bond_real_space), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - type(atoms) :: at_copy - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: n_descriptors, n_cross, i_desc, i, j, n, k, m, m_index, l, & - ij_neighbours, n_index - integer, dimension(3) :: shift_j, shift_k - real(dp) :: r_ij, r_ijk - real(dp) :: atom_i(3), atom_j(3), atom_k(3), bond(3), bond_len - real(dp) :: atom_i_cross_atom_j(3), atom_i_normsq_min_atom_j_normsq - real(dp), allocatable :: r(:,:), z(:), c(:) - real(dp) :: self_overlap - real(dp), allocatable :: dr(:,:,:,:), dz(:,:,:), dc(:,:,:) - real(dp), allocatable :: dself_overlap(:,:) - integer, allocatable :: ii(:) - real(dp), allocatable :: pos(:,:) - real(dp) :: r_m_cross_r_l(3) - - INIT_ERROR(error) - - call system_timer('bond_real_space_calc') - - if(.not. this%initialised) then - RAISE_ERROR("bond_real_space_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='bond_real_space_calc args_str')) then - RAISE_ERROR("bond_real_space_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("bond_real_space_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("bond_real_space_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - - do i = 1, at%N - do n = 1, n_neighbours(at, i) - j = neighbour(at, i, n, shift=shift_j, distance=r_ij, max_dist=this%bond_cutoff) - - if(j == 0) cycle - - i_desc = i_desc + 1 - - atom_i = at%pos(:,i) - atom_j = at%pos(:,j) + matmul(at%lattice, shift_j) - - at_copy = at - call add_atoms(at_copy, 0.5_dp * (atom_i + atom_j), 1) - call calc_connect(at_copy) - - ij_neighbours = 0 - - do m = 1, n_neighbours(at_copy, at%N + 1) - k = neighbour(at_copy, at%N + 1, m, max_dist=this%cutoff) - - if(k == 0) cycle - - if(at_copy%pos(:,k) .feq. at_copy%pos(:,at%N + 1)) cycle - - ij_neighbours = ij_neighbours + 1 - enddo - - if(ij_neighbours > this%max_neighbours) then - RAISE_ERROR("bond_real_space_calc: number of neighbours exceeds max_neighbours", error) - endif - - if(my_do_descriptor .or. my_do_grad_descriptor) then - allocate(r(3,ij_neighbours), z(ij_neighbours), c(ij_neighbours)) - allocate(ii(ij_neighbours), pos(3,ij_neighbours)) - - r = 0.0_dp - z = 0.0_dp - c = 0.0_dp - self_overlap = 0.0_dp - bond = atom_i - atom_j - bond_len = norm(bond) - atom_i_cross_atom_j = atom_i .cross. atom_j - atom_i_normsq_min_atom_j_normsq = normsq(atom_i) - normsq(atom_j) - ii = 0 - pos = 0.0_dp - - if(my_do_grad_descriptor) then - allocate(dr(3,ij_neighbours,3,ij_neighbours), dz(ij_neighbours,3,ij_neighbours), dc(ij_neighbours,3,ij_neighbours), dself_overlap(3,ij_neighbours)) - - dr = 0.0_dp - dz = 0.0_dp - dc = 0.0_dp - dself_overlap = 0.0_dp - endif - - m_index = 2 - - do m = 1, n_neighbours(at_copy, at%N + 1) - k = neighbour(at_copy, at%N + 1, m, shift=shift_k, distance=r_ijk, max_dist=this%cutoff) - - if(k == 0) cycle - - if(at_copy%pos(:,k) .feq. at_copy%pos(:,at%N + 1)) cycle - - atom_k = at_copy%pos(:,k) + matmul(at_copy%lattice, shift_k) - - if(atom_k .feq. atom_i) then - ! r remains zero - z(1) = 0.5_dp * bond_len - c(1) = coordination_function(r_ijk, this%cutoff, this%transition_width) - - ii(1) = k - pos(:,1) = atom_k - - if(my_do_grad_descriptor) then - ! dr remains zero - dz(1,:,1) = 0.5_dp * bond / bond_len - dz(1,:,2) = - dz(1,:,1) - dc(1,:,1) = 0.25_dp * dcoordination_function(r_ijk, this%cutoff, this%transition_width) * bond / r_ijk - dc(1,:,2) = - dc(1,:,1) - endif - elseif(atom_k .feq. atom_j) then - ! r remain zero - z(2) = -0.5_dp * bond_len - c(2) = coordination_function(r_ijk, this%cutoff, this%transition_width) - - ii(2) = k - pos(:,2) = atom_k - - if(my_do_grad_descriptor) then - ! dr remains zero - dz(2,:,1) = -0.5_dp * bond / bond_len - dz(2,:,2) = - dz(2,:,1) - dc(2,:,1) = -0.25_dp * dcoordination_function(r_ijk, this%cutoff, this%transition_width) * bond / r_ijk - dc(2,:,2) = - dc(2,:,1) - endif - else - m_index = m_index + 1 - - r(:,m_index) = ((atom_k .cross. bond) + atom_i_cross_atom_j) / bond_len - z(m_index) = ((atom_k .dot. bond) - 0.5_dp * atom_i_normsq_min_atom_j_normsq) / bond_len - c(m_index) = coordination_function(r_ijk, this%cutoff, this%transition_width) - - ii(m_index) = k - pos(:,m_index) = atom_k - - if(my_do_grad_descriptor) then - dr(:,m_index,1,1) = ((/ 0.0_dp, atom_k(3) - atom_j(3), atom_j(2) - atom_k(2) /) / bond_len) - (r(:,m_index) * bond(1) / bond_len**2) - dr(:,m_index,2,1) = ((/ atom_j(3) - atom_k(3), 0.0_dp, atom_k(1) - atom_j(1) /) / bond_len) - (r(:,m_index) * bond(2) / bond_len**2) - dr(:,m_index,3,1) = ((/ atom_k(2) - atom_j(2), atom_j(1) - atom_k(1), 0.0_dp /) / bond_len) - (r(:,m_index) * bond(3) / bond_len**2) - dz(m_index,:,1) = ((atom_k - atom_i) / bond_len) - (z(m_index) * bond / bond_len**2) - dc(m_index,:,1) = -0.5_dp * dcoordination_function(r_ijk, this%cutoff, this%transition_width) * (atom_k - at_copy%pos(:,at%N + 1)) / r_ijk - - dr(:,m_index,1,2) = - dr(:,m_index,1,1) + ((/ 0.0_dp, bond(3), - bond(2) /) / bond_len) - dr(:,m_index,2,2) = - dr(:,m_index,2,1) + ((/ - bond(3), 0.0_dp, bond(1) /) / bond_len) - dr(:,m_index,3,2) = - dr(:,m_index,3,1) + ((/ bond(2), - bond(1), 0.0_dp /) / bond_len) - dz(m_index,:,2) = - dz(m_index,:,1) - (bond / bond_len) - dc(m_index,:,2) = dc(m_index,:,1) - - dr(:,m_index,1,m_index) = (/ 0.0_dp, - bond(3), bond(2) /) / bond_len - dr(:,m_index,2,m_index) = (/ bond(3), 0.0_dp, - bond(1) /) / bond_len - dr(:,m_index,3,m_index) = (/ - bond(2), bond(1), 0.0_dp /) / bond_len - dz(m_index,:,m_index) = bond / bond_len - dc(m_index,:,m_index) = -2.0_dp * dc(m_index,:,1) - endif - endif - enddo - endif - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(2 + (1 + 2 * this%max_neighbours) * this%max_neighbours)) - allocate(descriptor_out%x(i_desc)%ci(n_index)) - - descriptor_out%x(i_desc)%data = 0.0_dp - - do m = 1, ij_neighbours - self_overlap = self_overlap + c(m)**2 - - if(m == 1) then - descriptor_out%x(i_desc)%data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * m)) = z(m) - elseif(m == 2) then - descriptor_out%x(i_desc)%data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * m)) = z(m) - - self_overlap = self_overlap + 2.0_dp * c(m) * c(m - 1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 ) - else - do l = 3, ij_neighbours - if(l == m) then - descriptor_out%x(i_desc)%data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1) = normsq(r(:,m)) - descriptor_out%x(i_desc)%data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l)) = z(m) - else - descriptor_out%x(i_desc)%data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1) = r(:,m) .dot. r(:,l) - descriptor_out%x(i_desc)%data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l)) = ((r(:,m) .cross. r(:,l)) .dot. bond) / bond_len - endif - - if(l < m) then - self_overlap = self_overlap + 2.0_dp * c(m) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) - endif - enddo - endif - enddo - - descriptor_out%x(i_desc)%data(1) = real(ij_neighbours, dp) - - descriptor_out%x(i_desc)%data(2) = self_overlap - - descriptor_out%x(i_desc)%data(3:ij_neighbours + 2) = c - - descriptor_out%x(i_desc)%covariance_cutoff = coordination_function(r_ij, this%bond_cutoff, this%bond_transition_width) - - descriptor_out%x(i_desc)%ci(:) = (/ i, j /) - descriptor_out%x(i_desc)%has_data = .true. - endif - - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i_desc)%grad_data(2 + (1 + 2 * ij_neighbours) * ij_neighbours,3,ij_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(ij_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,ij_neighbours)) - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,ij_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(ij_neighbours)) - - descriptor_out%x(i_desc)%grad_data = 0.0_dp - - do m = 1, ij_neighbours - dself_overlap(:,1) = dself_overlap(:,1) + 2.0_dp * c(m) * dc(m,:,1) - dself_overlap(:,2) = dself_overlap(:,2) + 2.0_dp * c(m) * dc(m,:,2) - - if(m == 1) then - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * m),:,1) = dz(m,:,1) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * m),:,2) = dz(m,:,2) - elseif(m == 2) then - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * m),:,1) = dz(m,:,1) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * m),:,2) = dz(m,:,2) - - dself_overlap(:,1) = dself_overlap(:,1) + 2.0_dp * dc(m,:,1) * c(m - 1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(m - 1,:,1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 ) \ - + c(m) * c(m - 1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 ) \ - * (pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 - dself_overlap(:,2) = dself_overlap(:,2) + 2.0_dp * dc(m,:,2) * c(m - 1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(m - 1,:,2) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 ) \ - + c(m) * c(m - 1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,m - 1)) / this%atom_sigma**2 ) \ - * (pos(:,m - 1) - pos(:,m)) / this%atom_sigma**2 - else - dself_overlap(:,m) = dself_overlap(:,m) + 2.0_dp * c(m) * dc(m,:,m) - - do l = 3, ij_neighbours - if(l == m) then - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,1,1) = 2.0_dp * (r(:,m) .dot. dr(:,m,1,1)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,2,1) = 2.0_dp * (r(:,m) .dot. dr(:,m,2,1)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,3,1) = 2.0_dp * (r(:,m) .dot. dr(:,m,3,1)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),:,1) = dz(m,:,1) - - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,1,2) = 2.0_dp * (r(:,m) .dot. dr(:,m,1,2)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,2,2) = 2.0_dp * (r(:,m) .dot. dr(:,m,2,2)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,3,2) = 2.0_dp * (r(:,m) .dot. dr(:,m,3,2)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),:,2) = dz(m,:,2) - - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,1,m) = 2.0_dp * (r(:,m) .dot. dr(:,m,1,m)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,2,m) = 2.0_dp * (r(:,m) .dot. dr(:,m,2,m)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,3,m) = 2.0_dp * (r(:,m) .dot. dr(:,m,3,m)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),:,m) = dz(m,:,m) - else - r_m_cross_r_l = r(:,m) .cross. r(:,l) - - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,1,1) = (dr(:,m,1,1) .dot. r(:,l)) + (r(:,m) .dot. dr(:,l,1,1)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,2,1) = (dr(:,m,2,1) .dot. r(:,l)) + (r(:,m) .dot. dr(:,l,2,1)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,3,1) = (dr(:,m,3,1) .dot. r(:,l)) + (r(:,m) .dot. dr(:,l,3,1)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),1,1) = ((((dr(:,m,1,1) .cross. r(:,l)) + (r(:,m) .cross. dr(:,l,1,1))) .dot. bond) + (r_m_cross_r_l .dot. ((/ 1.0_dp, 0.0_dp, 0.0_dp /) - (bond * bond(1) / bond_len**2)))) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),2,1) = ((((dr(:,m,2,1) .cross. r(:,l)) + (r(:,m) .cross. dr(:,l,2,1))) .dot. bond) + (r_m_cross_r_l .dot. ((/ 0.0_dp, 1.0_dp, 0.0_dp /) - (bond * bond(2) / bond_len**2)))) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),3,1) = ((((dr(:,m,3,1) .cross. r(:,l)) + (r(:,m) .cross. dr(:,l,3,1))) .dot. bond) + (r_m_cross_r_l .dot. ((/ 0.0_dp, 0.0_dp, 1.0_dp /) - (bond * bond(3) / bond_len**2)))) / bond_len - - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,1,2) = (dr(:,m,1,2) .dot. r(:,l)) + (r(:,m) .dot. dr(:,l,1,2)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,2,2) = (dr(:,m,2,2) .dot. r(:,l)) + (r(:,m) .dot. dr(:,l,2,2)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,3,2) = (dr(:,m,3,2) .dot. r(:,l)) + (r(:,m) .dot. dr(:,l,3,2)) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),1,2) = ((((dr(:,m,1,2) .cross. r(:,l)) + (r(:,m) .cross. dr(:,l,1,2))) .dot. bond) + (r_m_cross_r_l .dot. ((/ -1.0_dp, 0.0_dp, 0.0_dp /) + (bond * bond(1) / bond_len**2)))) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),2,2) = ((((dr(:,m,2,2) .cross. r(:,l)) + (r(:,m) .cross. dr(:,l,2,2))) .dot. bond) + (r_m_cross_r_l .dot. ((/ 0.0_dp, -1.0_dp, 0.0_dp /) + (bond * bond(2) / bond_len**2)))) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),3,2) = ((((dr(:,m,3,2) .cross. r(:,l)) + (r(:,m) .cross. dr(:,l,3,2))) .dot. bond) + (r_m_cross_r_l .dot. ((/ 0.0_dp, 0.0_dp, -1.0_dp /) + (bond * bond(3) / bond_len**2)))) / bond_len - - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,1,m) = dr(:,m,1,m) .dot. r(:,l) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,2,m) = dr(:,m,2,m) .dot. r(:,l) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,3,m) = dr(:,m,3,m) .dot. r(:,l) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),1,m) = ((dr(:,m,1,m) .cross. r(:,l)) .dot. bond) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),2,m) = ((dr(:,m,2,m) .cross. r(:,l)) .dot. bond) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),3,m) = ((dr(:,m,3,m) .cross. r(:,l)) .dot. bond) / bond_len - - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,1,l) = r(:,m) .dot. dr(:,l,1,l) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,2,l) = r(:,m) .dot. dr(:,l,2,l) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l) - 1,3,l) = r(:,m) .dot. dr(:,l,3,l) - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),1,l) = ((r(:,m) .cross. dr(:,l,1,l)) .dot. bond) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),2,l) = ((r(:,m) .cross. dr(:,l,2,l)) .dot. bond) / bond_len - descriptor_out%x(i_desc)%grad_data(2 + ij_neighbours + (2 * (m - 1) * ij_neighbours) + (2 * l),3,l) = ((r(:,m) .cross. dr(:,l,3,l)) .dot. bond) / bond_len - endif - - if(l < m) then - dself_overlap(:,m) = dself_overlap(:,m) + 2.0_dp * dc(m,:,m) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + c(m) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - * (pos(:,l) - pos(:,m)) / this%atom_sigma**2 - - if(l == 1) then - dself_overlap(:,1) = dself_overlap(:,1) + 2.0_dp * dc(m,:,1) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(l,:,1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + c(m) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - * (pos(:,m) - pos(:,l)) / this%atom_sigma**2 - dself_overlap(:,2) = dself_overlap(:,2) + 2.0_dp * dc(m,:,2) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(l,:,2) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) - elseif(l == 2) then - dself_overlap(:,1) = dself_overlap(:,1) + 2.0_dp * dc(m,:,1) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(l,:,1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) - dself_overlap(:,2) = dself_overlap(:,2) + 2.0_dp * dc(m,:,2) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(l,:,2) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + c(m) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - * (pos(:,m) - pos(:,l)) / this%atom_sigma**2 - else - dself_overlap(:,1) = dself_overlap(:,1) + 2.0_dp * dc(m,:,1) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(l,:,1) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) - dself_overlap(:,2) = dself_overlap(:,2) + 2.0_dp * dc(m,:,2) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + 2.0_dp * c(m) * dc(l,:,2) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) - dself_overlap(:,l) = dself_overlap(:,l) + 2.0_dp * c(m) * dc(l,:,l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - + c(m) * c(l) * exp( -0.25_dp * normsq(pos(:,m) - pos(:,l)) / this%atom_sigma**2 ) \ - * (pos(:,m) - pos(:,l)) / this%atom_sigma**2 - endif - endif - enddo - endif - enddo - - !descriptor_out%x(i_desc)%grad_data(1,:,:) = 0.0_dp - - descriptor_out%x(i_desc)%grad_data(2,:,:) = dself_overlap - - descriptor_out%x(i_desc)%grad_data(3:ij_neighbours + 2,:,:) = dc - - descriptor_out%x(i_desc)%ii = ii - descriptor_out%x(i_desc)%pos = pos - - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = dcoordination_function(r_ij, this%bond_cutoff, this%bond_transition_width) * bond / r_ij - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,2) = - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) - - descriptor_out%x(i_desc)%has_grad_data = .true. - endif - - if(my_do_descriptor .or. my_do_grad_descriptor) then - deallocate(r, z, c) - deallocate(ii, pos) - - if(my_do_grad_descriptor) then - deallocate(dr, dz, dc, dself_overlap) - endif - endif - - call finalise(at_copy) - enddo - enddo - - call system_timer('bond_real_space_calc') - - endsubroutine bond_real_space_calc - - subroutine atom_real_space_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(atom_real_space), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, grad_d, n_descriptors, n_cross, descriptor_mould_size, & - i_desc, i_data, i, j, k, n, l, m, l_n_neighbours, i_n, n_index - - real(dp) :: r - real(dp), dimension(3) :: diff - real(dp), dimension(1) :: descriptor_mould - integer, dimension(3) :: shift - - complex(dp), dimension(:), allocatable :: spherical_harmonics - complex(dp), dimension(:,:), allocatable :: grad_spherical_harmonics - - INIT_ERROR(error) - - call system_timer('atom_real_space_calc') - - if(.not. this%initialised) then - RAISE_ERROR("atom_real_space_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='atom_real_space_calc args_str')) then - RAISE_ERROR("atom_real_space_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("atom_real_space_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("atom_real_space_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - do i = 1, at%N - i_desc = i_desc + 1 - - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - d = ( 2 * (this%l_max+1)**2 + 2 ) * l_n_neighbours - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - grad_d = 2 * (this%l_max+1)**2 + 2 - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,1:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(1:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,1:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(1:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,1:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - allocate(spherical_harmonics(-this%l_max:this%l_max)) - if( my_do_grad_descriptor ) allocate(grad_spherical_harmonics(3,-this%l_max:this%l_max)) - - i_desc = 0 - do i = 1, at%N - i_desc = i_desc + 1 - i_data = 0 - i_n = 0 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - - if(my_do_grad_descriptor) then - !descriptor_out%x(i_desc)%ii(0) = i - !descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - !descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - do n = 1, n_neighbours(at,i) - - j = neighbour(at,i,n,distance = r, diff = diff, shift=shift) - if(r >= this%cutoff) cycle - i_n = i_n + 1 - - i_data = i_data + 1 - if(my_do_descriptor) then - descriptor_out%x(i_desc)%data(i_data) = r - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(i_n) = j - descriptor_out%x(i_desc)%pos(:,i_n) = at%pos(:,j) + matmul(at%lattice,shift) - descriptor_out%x(i_desc)%has_grad_data(i_n) = .true. - descriptor_out%x(i_desc)%grad_data(i_data,:,i_n) = diff / r - endif - - i_data = i_data + 1 - if(my_do_descriptor) descriptor_out%x(i_desc)%data(i_data) = real(i_n,dp) - if(my_do_grad_descriptor) descriptor_out%x(i_desc)%grad_data(i_data,:,i_n) = real(i_n,dp) - - do l = 0, this%l_max - descriptor_mould_size = size(transfer(spherical_harmonics(-l:l),descriptor_mould)) - - do m = -l, l - if(my_do_descriptor) spherical_harmonics(m) = SphericalYCartesian(l,m,diff) - if(my_do_grad_descriptor) grad_spherical_harmonics(:,m) = GradSphericalYCartesian(l,m,diff) - enddo - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%data(i_data+1:i_data+descriptor_mould_size) = transfer(spherical_harmonics(-l:l),descriptor_mould) - endif - - if(my_do_grad_descriptor) then - do k = 1, 3 - descriptor_out%x(i_desc)%grad_data(i_data+1:i_data+descriptor_mould_size,k,i_n) = & - transfer(grad_spherical_harmonics(k,-l:l),descriptor_mould) - enddo - endif - - i_data = i_data + descriptor_mould_size - - enddo - enddo - enddo - - if(allocated(spherical_harmonics)) deallocate(spherical_harmonics) - if(allocated(grad_spherical_harmonics)) deallocate(grad_spherical_harmonics) - - call system_timer('atom_real_space_calc') - - endsubroutine atom_real_space_calc - - subroutine power_so3_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(power_so3), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - type(cplx_1d), dimension(:), allocatable :: SphericalY_ij - type(cplx_1d), dimension(:,:), allocatable :: fourier_so3 - - type(cplx_2d), dimension(:), allocatable :: dSphericalY_ij - type(cplx_2d), dimension(:,:,:), allocatable :: dfourier_so3 - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, i, j, n, a, l, m, i_desc, i_pow, l_n_neighbours, n_i, & - n_descriptors, n_cross, n_index - integer, dimension(3) :: shift_ij - real(dp) :: r_ij - real(dp), dimension(3) :: u_ij, d_ij - real(dp), dimension(:), allocatable :: Rad_ij - real(dp), dimension(:,:), allocatable :: dRad_ij - integer, dimension(total_elements) :: species_map - - INIT_ERROR(error) - - call system_timer('power_so3_calc') - - if(.not. this%initialised) then - RAISE_ERROR("power_so3_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='power_so3_calc args_str')) then - RAISE_ERROR("power_so3_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("power_so3_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - species_map = 0 - do i = 1, size(this%species_Z) - if(this%species_Z(i) == 0) then - species_map = 1 - else - species_map(this%species_Z(i)) = i - endif - enddo - - call finalise(descriptor_out) - - d = power_so3_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross,& - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - descriptor_out%x(i_desc)%has_data = .false. - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - allocate(fourier_so3(0:this%l_max,this%n_max),SphericalY_ij(0:this%l_max),Rad_ij(this%n_max)) - do a = 1, this%n_max - do l = 0, this%l_max - allocate(fourier_so3(l,a)%m(-l:l)) - fourier_so3(l,a)%m(:) = CPLX_ZERO - enddo - enddo - do l = 0, this%l_max - allocate(SphericalY_ij(l)%m(-l:l)) - enddo - - if(my_do_grad_descriptor) then - allocate( dRad_ij(3,this%n_max), dSphericalY_ij(0:this%l_max) ) - do l = 0, this%l_max - allocate(dSphericalY_ij(l)%mm(3,-l:l)) - enddo - endif - - i_desc = 0 - do i = 1, at%N - - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - do a = 1, this%n_max - do l = 0, this%l_max - fourier_so3(l,a)%m(:) = CPLX_ZERO - enddo - enddo - - if(my_do_grad_descriptor) then - allocate( dfourier_so3(0:this%l_max,this%n_max,0:n_neighbours(at,i,max_dist=this%cutoff)) ) - do n = 0, n_neighbours(at,i,max_dist=this%cutoff) - do a = 1, this%n_max - do l = 0, this%l_max - allocate(dfourier_so3(l,a,n)%mm(3,-l:l)) - dfourier_so3(l,a,n)%mm(:,:) = CPLX_ZERO - enddo - enddo - enddo - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines=u_ij, diff=d_ij, shift=shift_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(n_i) = j - descriptor_out%x(i_desc)%pos(:,n_i) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc)%has_grad_data(n_i) = .true. - endif - - do a = 1, this%n_max - Rad_ij(a) = RadialFunction(this%Radial, r_ij, a) - if(my_do_grad_descriptor) dRad_ij(:,a) = GradRadialFunction(this%Radial, r_ij, a) * u_ij - enddo - - do l = 0, this%l_max - do m = -l, l - SphericalY_ij(l)%m(m) = SphericalYCartesian(l,m,d_ij) - if(my_do_grad_descriptor) dSphericalY_ij(l)%mm(:,m) = GradSphericalYCartesian(l,m,d_ij) - enddo - enddo - - do a = 1, this%n_max - do l = 0, this%l_max - do m = -l, l - fourier_so3(l,a)%m(m) = fourier_so3(l,a)%m(m) + Rad_ij(a)*SphericalY_ij(l)%m(m) - if(my_do_grad_descriptor) then - dfourier_so3(l,a,n_i)%mm(:,m) = dfourier_so3(l,a,n_i)%mm(:,m) + & - dRad_ij(:,a) * SphericalY_ij(l)%m(m) + Rad_ij(a)*dSphericalY_ij(l)%mm(:,m) - endif - enddo - enddo - enddo - - enddo ! n - - if(my_do_descriptor) then - i_pow = 0 - do a = 1, this%n_max - do l = 0, this%l_max - i_pow = i_pow + 1 - - descriptor_out%x(i_desc)%data(i_pow) = dot_product(fourier_so3(l,a)%m,fourier_so3(l,a)%m) - enddo - enddo - endif - - if(my_do_grad_descriptor) then - do n = 1, n_neighbours(at,i,max_dist=this%cutoff) - i_pow = 0 - do a = 1, this%n_max - do l = 0, this%l_max - i_pow = i_pow + 1 - - descriptor_out%x(i_desc)%grad_data(i_pow,:,n) = 2.0_dp * matmul(conjg(dfourier_so3(l,a,n)%mm(:,:)),fourier_so3(l,a)%m(:)) - enddo - enddo - descriptor_out%x(i_desc)%grad_data(:,:,0) = descriptor_out%x(i_desc)%grad_data(:,:,0) - descriptor_out%x(i_desc)%grad_data(:,:,n) - enddo - endif - - if(allocated(dfourier_so3)) then - do n = lbound(dfourier_so3,3), ubound(dfourier_so3,3) - do a = lbound(dfourier_so3,2), ubound(dfourier_so3,2) - do l = lbound(dfourier_so3,1), ubound(dfourier_so3,1) - deallocate(dfourier_so3(l,a,n)%mm) - enddo - enddo - enddo - deallocate(dfourier_so3) - endif - - enddo ! i - - if(allocated(Rad_ij)) deallocate(Rad_ij) - if(allocated(dRad_ij)) deallocate(dRad_ij) - - if(allocated(fourier_so3)) then - do a = lbound(fourier_so3,2), ubound(fourier_so3,2) - do l = lbound(fourier_so3,1), ubound(fourier_so3,1) - deallocate(fourier_so3(l,a)%m) - enddo - enddo - deallocate(fourier_so3) - endif - - if(allocated(SphericalY_ij)) then - do l = lbound(SphericalY_ij,1), ubound(SphericalY_ij,1) - deallocate(SphericalY_ij(l)%m) - enddo - deallocate(SphericalY_ij) - endif - - if(allocated(dSphericalY_ij)) then - do l = lbound(dSphericalY_ij,1), ubound(dSphericalY_ij,1) - deallocate(dSphericalY_ij(l)%mm) - enddo - deallocate(dSphericalY_ij) - endif - - call system_timer('power_so3_calc') - - endsubroutine power_so3_calc - - subroutine power_SO4_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(power_SO4), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(cplx_2d), dimension(:), allocatable :: U - type(cplx_3d), dimension(:,:), allocatable :: dU - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - real(dp), dimension(3) :: diff, u_ij - real(dp) :: r - integer :: i, n, n_i, ji, jn, k, j, i_desc, i_bisp, d, & - n_descriptors, n_cross, l_n_neighbours, n_index - integer, dimension(3) :: shift - integer, dimension(total_elements) :: species_map - logical :: my_do_descriptor, my_do_grad_descriptor - - INIT_ERROR(error) - - call system_timer('power_SO4_calc') - - if(.not. this%initialised) then - RAISE_ERROR("power_SO4_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - species_map = 0 - do i = 1, size(this%species_Z) - if(this%species_Z(i) == 0) then - species_map = 1 - else - species_map(this%species_Z(i)) = i - endif - enddo - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='power_SO4_calc args_str')) then - RAISE_ERROR("power_SO4_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("power_SO4_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("power_SO4_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = power_SO4_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - - i_desc = i_desc + 1 - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - descriptor_out%x(i_desc)%has_data = .false. - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - - enddo - - i_desc = 0 - do i = 1, at%N - - if( associated(atom_mask_pointer) ) then - if( .not. atom_mask_pointer(i) ) cycle - endif - - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - ji = neighbour(at, i, n, jn=jn, distance=r, diff=diff, cosines=u_ij,shift=shift) - if( r >= this%cutoff ) cycle - - n_i = n_i + 1 - - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(n_i) = ji - descriptor_out%x(i_desc)%pos(:,n_i) = at%pos(:,ji) + matmul(at%lattice,shift) - descriptor_out%x(i_desc)%has_grad_data(n_i) = .true. - endif - enddo - - if(my_do_grad_descriptor) then - call fourier_SO4_calc(this%fourier_SO4,at,i,U,dU,args_str,error=error) - else - call fourier_SO4_calc(this%fourier_SO4,at,i,U,args_str=args_str,error=error) - endif - - if(my_do_descriptor) then - - i_bisp = 0 - do j = 0, this%j_max - i_bisp = i_bisp + 1 - descriptor_out%x(i_desc)%data(i_bisp) = sum( conjg(U(j)%mm)*U(j)%mm ) - enddo - endif - - if(my_do_grad_descriptor) then - n_i = 0 - do n = 1, n_neighbours(at,i) - ji = neighbour(at, i, n, distance=r) - if( r >= this%cutoff ) cycle - n_i = n_i + 1 - i_bisp = 0 - do j = 0, this%j_max - i_bisp = i_bisp + 1 - do k = 1, 3 - descriptor_out%x(i_desc)%grad_data(i_bisp,k,n_i) = 2.0_dp * sum( conjg(U(j)%mm)*dU(j,n_i)%mm(k,:,:) ) - enddo - enddo - enddo - descriptor_out%x(i_desc)%grad_data(:,:,0) = -sum(descriptor_out%x(i_desc)%grad_data(:,:,:), dim=3) - endif - - call finalise(dU) - enddo ! i - - ! clear U from the memory - call finalise(U) - - call system_timer('power_SO4_calc') - - endsubroutine power_SO4_calc - - subroutine soap_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type real_2d_array - type(real_2d), dimension(:,:,:), allocatable :: x - endtype real_2d_array - - type(soap), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - type(cplx_1d), dimension(:), allocatable, save :: SphericalY_ij - type(cplx_2d), dimension(:), allocatable, save :: grad_SphericalY_ij - - !SPEED type(cplx_1d), dimension(:,:,:), allocatable :: fourier_so3 - !SPEED type(cplx_2d), dimension(:,:,:), allocatable :: grad_fourier_so3 - type(real_1d), dimension(:,:,:), allocatable, save :: fourier_so3_r, fourier_so3_i, global_fourier_so3_r, global_fourier_so3_i - type(real_2d), dimension(:,:,:), allocatable :: grad_fourier_so3_r, grad_fourier_so3_i - real(dp), allocatable :: t_g_r(:,:), t_g_i(:,:), t_f_r(:,:), t_f_i(:,:), t_g_f_rr(:,:), t_g_f_ii(:,:) - integer :: alpha - - logical :: my_do_descriptor, my_do_grad_descriptor, do_two_l_plus_one - integer :: d, i, j, n, a, b, k, l, m, i_pow, i_coeff, l_n_neighbours, n_i, & - n_descriptors, n_cross, i_species, j_species, ia, jb, i_desc_i, & - xml_version, sum_l_n_neighbours, i_pair, i_pair_i, n_index - integer, dimension(3) :: shift_ij - integer, dimension(:), allocatable :: i_desc - integer, dimension(:,:), allocatable :: rs_index - real(dp) :: r_ij, arg_bess, mo_spher_bess_fi_ki_l, mo_spher_bess_fi_ki_lm, mo_spher_bess_fi_ki_lmm, mo_spher_bess_fi_ki_lp, & - exp_p, exp_m, f_cut, df_cut, norm_descriptor_i, radial_decay, dradial_decay, norm_radial_decay - real(dp), dimension(3) :: u_ij, d_ij - real(dp), dimension(:,:), allocatable, save :: radial_fun, radial_coefficient, grad_radial_fun, grad_radial_coefficient, grad_descriptor_i - real(dp), dimension(:), allocatable, save :: descriptor_i - real(dp), dimension(:), allocatable :: global_fourier_so3_r_array, global_fourier_so3_i_array - type(real_2d_array), dimension(:), allocatable :: global_grad_fourier_so3_r_array, global_grad_fourier_so3_i_array - integer, dimension(total_elements) :: species_map -!$omp threadprivate(radial_fun, radial_coefficient, grad_radial_fun, grad_radial_coefficient) -!$omp threadprivate(fourier_so3_r, fourier_so3_i) -!$omp threadprivate(SphericalY_ij,grad_SphericalY_ij) -!$omp threadprivate(descriptor_i, grad_descriptor_i) - - INIT_ERROR(error) - - call system_timer('soap_calc') - - if(.not. this%initialised) then - RAISE_ERROR("soap_calc: descriptor object not initialised", error) - endif - - species_map = 0 - do i_species = 1, this%n_species - if(this%species_Z(i_species) == 0) then - species_map = 1 - else - species_map(this%species_Z(i_species)) = i_species - endif - enddo - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - has_atom_mask_name = .false. - atom_mask_pointer => null() - xml_version = 1423143769 - - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is " // & - "true, descriptors are calculated.") - - call param_register(params, 'xml_version', '1423143769', xml_version, & - help_string="Version of GAP the XML potential file was created") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='soap_calc args_str')) then - RAISE_ERROR("soap_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("soap_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - if( this%cutoff_dexp > 0 ) then - if( this%cutoff_rate == 0.0_dp ) then - norm_radial_decay = 1.0_dp - else - norm_radial_decay = this%cutoff_rate / ( 1.0_dp + this%cutoff_rate ) - endif - else - norm_radial_decay = 1.0_dp - endif - - do_two_l_plus_one = (xml_version >= 1423143769) - - allocate(rs_index(2,this%n_max*this%n_species)) - i = 0 - do i_species = 1, this%n_species - do a = 1, this%n_max - i = i + 1 - rs_index(:,i) = (/a,i_species/) - enddo - enddo - - call finalise(descriptor_out) - - d = soap_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - allocate(i_desc(at%N)) - -!$omp parallel default(none) shared(this,my_do_grad_descriptor,d) private(i_species, a, l) - allocate(descriptor_i(d)) - if(my_do_grad_descriptor) allocate(grad_descriptor_i(d,3)) - - allocate(radial_fun(0:this%l_max, this%n_max), radial_coefficient(0:this%l_max, this%n_max)) - !SPEED allocate(fourier_so3(0:this%l_max,this%n_max,this%n_species), SphericalY_ij(0:this%l_max)) - allocate(fourier_so3_r(0:this%l_max,this%n_max,this%n_species), fourier_so3_i(0:this%l_max,this%n_max,this%n_species), SphericalY_ij(0:this%l_max)) - - if(my_do_grad_descriptor) then - allocate(grad_radial_fun(0:this%l_max, this%n_max), grad_radial_coefficient(0:this%l_max, this%n_max)) - allocate(grad_SphericalY_ij(0:this%l_max)) - endif - - do i_species = 1, this%n_species - do a = 1, this%n_max - do l = 0, this%l_max - !SPEED allocate(fourier_so3(l,a,i_species)%m(-l:l)) - !SPEED fourier_so3(l,a,i_species)%m(:) = CPLX_ZERO - allocate(fourier_so3_r(l,a,i_species)%m(-l:l)) - allocate(fourier_so3_i(l,a,i_species)%m(-l:l)) - fourier_so3_r(l,a,i_species)%m(:) = 0.0_dp - fourier_so3_i(l,a,i_species)%m(:) = 0.0_dp - enddo - enddo - enddo - - do l = 0, this%l_max - allocate(SphericalY_ij(l)%m(-l:l)) - if(my_do_grad_descriptor) allocate(grad_SphericalY_ij(l)%mm(3,-l:l)) - enddo -!$omp end parallel - - i_desc = 0 - i_desc_i = 0 - do i = 1, at%N - - if( .not. any( at%Z(i) == this%Z ) .and. .not. any(this%Z == 0) ) cycle - - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - - i_desc_i = i_desc_i + 1 - i_desc(i) = i_desc_i - - if(.not. this%global) then ! atomic SOAP - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc_i)%data(d)) - !slow, no need - !descriptor_out%x(i_desc_i)%data = 0.0_dp - allocate(descriptor_out%x(i_desc_i)%ci(n_index)) - descriptor_out%x(i_desc_i)%has_data = .false. - descriptor_out%x(i_desc_i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc_i)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc_i)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc_i)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc_i)%has_grad_data(0:l_n_neighbours)) - ! slow, no need - ! descriptor_out%x(i_desc_i)%grad_data = 0.0_dp - descriptor_out%x(i_desc_i)%grad_data(:,:,0) = 0.0_dp - descriptor_out%x(i_desc_i)%ii = 0 - descriptor_out%x(i_desc_i)%pos = 0.0_dp - descriptor_out%x(i_desc_i)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc_i)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc_i)%grad_covariance_cutoff = 0.0_dp - endif - endif - enddo - - allocate( & - global_fourier_so3_r_array((this%l_max+1)**2 * this%n_max * this%n_species), & - global_fourier_so3_i_array((this%l_max+1)**2 * this%n_max * this%n_species), & - global_grad_fourier_so3_r_array( count(i_desc/=0) ), & - global_grad_fourier_so3_i_array( count(i_desc/=0) ) ) - - - - if(this%global) then - if(my_do_descriptor) then - allocate(descriptor_out%x(1)%data(d)) - allocate(descriptor_out%x(1)%ci(n_index)) - if( any(this%Z == 0) ) then - descriptor_out%x(1)%ci(:) = (/ (i, i=1, at%N) /) - else - forall(i=1:at%N, any(at%Z(i) == this%Z)) descriptor_out%x(1)%ci(i_desc(i)) = i - endif - descriptor_out%x(1)%has_data = .true. - descriptor_out%x(1)%covariance_cutoff = 1.0_dp - endif ! my_do_descriptor - if(my_do_grad_descriptor) then - sum_l_n_neighbours = 0 - do i = 1, at%N - - if(i_desc(i) == 0) then - cycle - else - i_desc_i = i_desc(i) - endif - - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - sum_l_n_neighbours = sum_l_n_neighbours + l_n_neighbours + 1 ! include central atom as well! - - allocate( & - global_grad_fourier_so3_r_array(i_desc_i)%x(0:this%l_max,this%n_max,l_n_neighbours), & - global_grad_fourier_so3_i_array(i_desc_i)%x(0:this%l_max,this%n_max,l_n_neighbours) ) - - do n_i = 1, l_n_neighbours - do a = 1, this%n_max - do l = 0, this%l_max - allocate( & - global_grad_fourier_so3_r_array(i_desc_i)%x(l,a,n_i)%mm(3,-l:l), & - global_grad_fourier_so3_i_array(i_desc_i)%x(l,a,n_i)%mm(3,-l:l) ) - global_grad_fourier_so3_r_array(i_desc_i)%x(l,a,n_i)%mm = 0.0_dp - global_grad_fourier_so3_i_array(i_desc_i)%x(l,a,n_i)%mm = 0.0_dp - enddo ! l - enddo ! a - enddo ! n_i - enddo ! i - - allocate(descriptor_out%x(1)%grad_data(d,3,sum_l_n_neighbours)) - allocate(descriptor_out%x(1)%ii(sum_l_n_neighbours)) - allocate(descriptor_out%x(1)%pos(3,sum_l_n_neighbours)) - allocate(descriptor_out%x(1)%has_grad_data(sum_l_n_neighbours)) - - allocate(descriptor_out%x(1)%grad_covariance_cutoff(3,sum_l_n_neighbours)) - descriptor_out%x(1)%grad_covariance_cutoff = 0.0_dp - endif ! my_do_grad_descriptor - - global_fourier_so3_r_array = 0.0_dp - global_fourier_so3_i_array = 0.0_dp - endif ! this%global - -!$omp parallel do schedule(dynamic) default(none) shared(this, at, descriptor_out, my_do_descriptor, my_do_grad_descriptor, d, i_desc, species_map, rs_index, do_two_l_plus_one) & -!$omp shared(global_grad_fourier_so3_r_array, global_grad_fourier_so3_i_array, norm_radial_decay) & -!$omp private(i, j, i_species, j_species, a, b, l, m, n, n_i, r_ij, u_ij, d_ij, shift_ij, i_pow, i_coeff, ia, jb, alpha, i_desc_i) & -!$omp private(grad_fourier_so3_r,grad_fourier_so3_i,t_g_r, t_g_i, t_f_r, t_f_i, t_g_f_rr, t_g_f_ii) & -!$omp private(f_cut, df_cut, arg_bess, exp_p, exp_m, mo_spher_bess_fi_ki_l, mo_spher_bess_fi_ki_lp, mo_spher_bess_fi_ki_lm, mo_spher_bess_fi_ki_lmm, norm_descriptor_i) & -!$omp private(radial_decay, dradial_decay) & -!$omp reduction(+:global_fourier_so3_r_array,global_fourier_so3_i_array) - do i = 1, at%N - - if(i_desc(i) == 0) then - cycle - else - i_desc_i = i_desc(i) - endif - - if(.not.this%global) then - if(my_do_descriptor) then - descriptor_out%x(i_desc_i)%ci(1) = i - descriptor_out%x(i_desc_i)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc_i)%ii(0) = i - descriptor_out%x(i_desc_i)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc_i)%has_grad_data(0) = .true. - endif - endif - if(my_do_grad_descriptor) then - !SPEED allocate( grad_fourier_so3(0:this%l_max,this%n_max,n_neighbours(at,i,max_dist=this%cutoff)) ) - allocate( grad_fourier_so3_r(0:this%l_max,this%n_max,n_neighbours(at,i,max_dist=this%cutoff)) ) - allocate( grad_fourier_so3_i(0:this%l_max,this%n_max,n_neighbours(at,i,max_dist=this%cutoff)) ) - endif - - !do a = 1, this%n_max - ! radial_fun(0,a) = exp( -this%alpha * this%r_basis(a)**2 ) !* this%r_basis(a) - !enddo - !radial_coefficient(0,:) = matmul( radial_fun(0,:), this%transform_basis ) - radial_fun(0,:) = 0.0_dp - radial_fun(0,1) = 1.0_dp - radial_coefficient(0,:) = matmul( radial_fun(0,:), this%cholesky_overlap_basis) - - do i_species = 1, this%n_species - do a = 1, this%n_max - !SPEED fourier_so3(0,a,i_species)%m(0) = radial_coefficient(0,a) * SphericalYCartesian(0,0,(/0.0_dp, 0.0_dp, 0.0_dp/)) - if( this%central_reference_all_species .or. this%species_Z(i_species) == at%Z(i) .or. this%species_Z(i_species) == 0 ) then - fourier_so3_r(0,a,i_species)%m(0) = this%central_weight * real(radial_coefficient(0,a) * SphericalYCartesian(0,0,(/0.0_dp, 0.0_dp, 0.0_dp/)), dp) - fourier_so3_i(0,a,i_species)%m(0) = this%central_weight * aimag(radial_coefficient(0,a) * SphericalYCartesian(0,0,(/0.0_dp, 0.0_dp, 0.0_dp/))) - else - fourier_so3_i(0,a,i_species)%m(0) = 0.0_dp - fourier_so3_r(0,a,i_species)%m(0) = 0.0_dp - endif - - do l = 1, this%l_max - !SPEED fourier_so3(l,a,i_species)%m(:) = CPLX_ZERO - fourier_so3_r(l,a,i_species)%m(:) = 0.0_dp - fourier_so3_i(l,a,i_species)%m(:) = 0.0_dp - enddo - enddo - enddo - -! soap_calc 20 takes 0.0052 s -! call system_timer("soap_calc 20") - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines=u_ij, diff=d_ij, shift=shift_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - - i_species = species_map(at%Z(j)) - if( i_species == 0 ) cycle - - if(.not. this%global .and. my_do_grad_descriptor) then - descriptor_out%x(i_desc_i)%ii(n_i) = j - descriptor_out%x(i_desc_i)%pos(:,n_i) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc_i)%has_grad_data(n_i) = .true. - endif - - f_cut = coordination_function(r_ij,this%cutoff, this%cutoff_transition_width) - radial_decay = ( 1.0_dp + this%cutoff_rate ) / ( this%cutoff_rate + ( r_ij / this%cutoff_scale )**this%cutoff_dexp ) - radial_decay = norm_radial_decay * radial_decay - - if(my_do_grad_descriptor) then - df_cut = dcoordination_function(r_ij,this%cutoff, this%cutoff_transition_width) - dradial_decay = - this%cutoff_dexp * ( 1.0_dp + this%cutoff_rate ) * ( r_ij / this%cutoff_scale )**this%cutoff_dexp / & - ( r_ij * ( this%cutoff_rate + ( r_ij / this%cutoff_scale )**this%cutoff_dexp )**2 ) - dradial_decay = norm_radial_decay * dradial_decay - - df_cut = df_cut * radial_decay + f_cut * dradial_decay - do a = 1, this%n_max - do l = 0, this%l_max - !SPEED allocate(grad_fourier_so3(l,a,n_i)%mm(3,-l:l)) - !SPEED grad_fourier_so3(l,a,n_i)%mm(:,:) = CPLX_ZERO - allocate(grad_fourier_so3_r(l,a,n_i)%mm(3,-l:l)) - allocate(grad_fourier_so3_i(l,a,n_i)%mm(3,-l:l)) - grad_fourier_so3_r(l,a,n_i)%mm(:,:) = 0.0_dp - grad_fourier_so3_i(l,a,n_i)%mm(:,:) = 0.0_dp - enddo - enddo - endif - f_cut = f_cut * radial_decay - - do a = 1, this%n_max - arg_bess = 2.0_dp * this%alpha * r_ij * this%r_basis(a) - exp_p = exp( -this%alpha*( r_ij + this%r_basis(a) )**2 ) - exp_m = exp( -this%alpha*( r_ij - this%r_basis(a) )**2 ) - - do l = 0, this%l_max - if( l == 0 ) then - if(arg_bess == 0.0_dp) then - !mo_spher_bess_fi_ki_l = 1.0_dp - mo_spher_bess_fi_ki_l = exp( -this%alpha * (this%r_basis(a)**2 + r_ij**2) ) - if(my_do_grad_descriptor) mo_spher_bess_fi_ki_lp = 0.0_dp - else - !mo_spher_bess_fi_ki_lm = cosh(arg_bess)/arg_bess - !mo_spher_bess_fi_ki_l = sinh(arg_bess)/arg_bess - mo_spher_bess_fi_ki_lm = 0.5_dp * (exp_m + exp_p) / arg_bess - mo_spher_bess_fi_ki_l = 0.5_dp * (exp_m - exp_p) / arg_bess - if(my_do_grad_descriptor) mo_spher_bess_fi_ki_lp = mo_spher_bess_fi_ki_lm - (2*l+1)*mo_spher_bess_fi_ki_l / arg_bess - endif - else - if(arg_bess == 0.0_dp) then - mo_spher_bess_fi_ki_l = 0.0_dp - if(my_do_grad_descriptor) mo_spher_bess_fi_ki_lp = 0.0_dp - else - mo_spher_bess_fi_ki_lmm = mo_spher_bess_fi_ki_lm - mo_spher_bess_fi_ki_lm = mo_spher_bess_fi_ki_l - if(my_do_grad_descriptor) then - mo_spher_bess_fi_ki_l = mo_spher_bess_fi_ki_lp - mo_spher_bess_fi_ki_lp = mo_spher_bess_fi_ki_lm - (2*l+1)*mo_spher_bess_fi_ki_l / arg_bess - else - mo_spher_bess_fi_ki_l = mo_spher_bess_fi_ki_lmm - (2*l-1)*mo_spher_bess_fi_ki_lm / arg_bess - endif - endif - endif - - !radial_fun(l,a) = exp( -this%alpha * (this%r_basis(a)**2 + r_ij**2) ) * mo_spher_bess_fi_ki_l !* this%r_basis(a) - radial_fun(l,a) = mo_spher_bess_fi_ki_l !* this%r_basis(a) - if(my_do_grad_descriptor) grad_radial_fun(l,a) = -2.0_dp * this%alpha * r_ij * mo_spher_bess_fi_ki_l + & - l*mo_spher_bess_fi_ki_l / r_ij + mo_spher_bess_fi_ki_lp * 2.0_dp * this%alpha * this%r_basis(a) - - enddo - enddo - - radial_coefficient = matmul( radial_fun, this%transform_basis ) - if(my_do_grad_descriptor) grad_radial_coefficient = matmul( grad_radial_fun, this%transform_basis ) * f_cut + radial_coefficient * df_cut - radial_coefficient = radial_coefficient * f_cut - - do l = 0, this%l_max - do m = -l, l - SphericalY_ij(l)%m(m) = SphericalYCartesian(l,m,d_ij) - if(my_do_grad_descriptor) grad_SphericalY_ij(l)%mm(:,m) = GradSphericalYCartesian(l,m,d_ij) - enddo - enddo - - do a = 1, this%n_max - do l = 0, this%l_max - do m = -l, l - !SPEED fourier_so3(l,a,i_species)%m(m) = fourier_so3(l,a,i_species)%m(m) + radial_coefficient(l,a) * SphericalY_ij(l)%m(m) - !SPEED if(my_do_grad_descriptor) grad_fourier_so3(l,a,n_i)%mm(:,m) = grad_fourier_so3(l,a,n_i)%mm(:,m) + & - !SPEED grad_radial_coefficient(l,a) * SphericalY_ij(l)%m(m) * u_ij + radial_coefficient(l,a) * grad_SphericalY_ij(l)%mm(:,m) - fourier_so3_r(l,a,i_species)%m(m) = fourier_so3_r(l,a,i_species)%m(m) + real(radial_coefficient(l,a) * SphericalY_ij(l)%m(m), dp) - fourier_so3_i(l,a,i_species)%m(m) = fourier_so3_i(l,a,i_species)%m(m) + aimag(radial_coefficient(l,a) * SphericalY_ij(l)%m(m)) - if(my_do_grad_descriptor) then - grad_fourier_so3_r(l,a,n_i)%mm(:,m) = grad_fourier_so3_r(l,a,n_i)%mm(:,m) + & - real(grad_radial_coefficient(l,a) * SphericalY_ij(l)%m(m) * u_ij + radial_coefficient(l,a) * grad_SphericalY_ij(l)%mm(:,m), dp) - grad_fourier_so3_i(l,a,n_i)%mm(:,m) = grad_fourier_so3_i(l,a,n_i)%mm(:,m) + & - aimag(grad_radial_coefficient(l,a) * SphericalY_ij(l)%m(m) * u_ij + radial_coefficient(l,a) * grad_SphericalY_ij(l)%mm(:,m)) - endif ! my_do_grad_descriptor - enddo ! m - enddo ! l - enddo ! a - - enddo ! n -! call system_timer("soap_calc 20") - - if(this%global .and. my_do_grad_descriptor) then - global_grad_fourier_so3_r_array(i_desc_i)%x = grad_fourier_so3_r - global_grad_fourier_so3_i_array(i_desc_i)%x = grad_fourier_so3_i - !do n_i = lbound(grad_fourier_so3_r,3), ubound(grad_fourier_so3_r,3) - ! do a = lbound(grad_fourier_so3_r,2), ubound(grad_fourier_so3_r,2) - ! do l = lbound(grad_fourier_so3_r,1), ubound(grad_fourier_so3_r,1) - ! global_grad_fourier_so3_r_array(i_desc_i)%x(l,a,n_i)%mm = grad_fourier_so3_r(l,a,n_i)%mm - ! global_grad_fourier_so3_i_array(i_desc_i)%x(l,a,n_i)%mm = grad_fourier_so3_i(l,a,n_i)%mm - ! enddo ! l - ! enddo ! a - !enddo ! n_i - endif - - if(this%global) then - i_coeff = 0 - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - do l = 0, this%l_max - global_fourier_so3_r_array(i_coeff+1:i_coeff+2*l+1) = global_fourier_so3_r_array(i_coeff+1:i_coeff+2*l+1) + fourier_so3_r(l,a,i_species)%m(:) - global_fourier_so3_i_array(i_coeff+1:i_coeff+2*l+1) = global_fourier_so3_i_array(i_coeff+1:i_coeff+2*l+1) + fourier_so3_i(l,a,i_species)%m(:) - i_coeff = i_coeff + 2*l+1 - enddo - enddo - endif - - i_pow = 0 - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - do jb = 1, ia - b = rs_index(1,jb) - j_species = rs_index(2,jb) - - if(this%diagonal_radial .and. a /= b) cycle - - do l = 0, this%l_max - i_pow = i_pow + 1 - !SPEED descriptor_i(i_pow) = real( dot_product(fourier_so3(l,a,i_species)%m, fourier_so3(l,b,j_species)%m) ) - descriptor_i(i_pow) = dot_product(fourier_so3_r(l,a,i_species)%m, fourier_so3_r(l,b,j_species)%m) + dot_product(fourier_so3_i(l,a,i_species)%m, fourier_so3_i(l,b,j_species)%m) - if(do_two_l_plus_one) descriptor_i(i_pow) = descriptor_i(i_pow) / sqrt(2.0_dp * l + 1.0_dp) - if( ia /= jb ) descriptor_i(i_pow) = descriptor_i(i_pow) * SQRT_TWO - enddo !l - enddo !jb - enddo !ia - - descriptor_i(d) = 0.0_dp - norm_descriptor_i = sqrt(dot_product(descriptor_i,descriptor_i)) - - if(.not. this%global .and. my_do_descriptor) then - if(this%normalise) then - descriptor_out%x(i_desc_i)%data = descriptor_i / norm_descriptor_i - else - descriptor_out%x(i_desc_i)%data = descriptor_i - endif - - descriptor_out%x(i_desc_i)%data(d) = this%covariance_sigma0 - endif - - if(my_do_grad_descriptor) then -! soap_calc 33 takes 0.047 s -! call system_timer("soap_calc 33") - allocate(t_g_r(this%n_max*3, 2*this%l_max+1), t_g_i(this%n_max*3, 2*this%l_max+1)) - allocate(t_f_r(this%n_max*this%n_species, 2*this%l_max+1), t_f_i(this%n_max*this%n_species, 2*this%l_max+1)) - allocate(t_g_f_rr(this%n_max*3, this%n_max*this%n_species), t_g_f_ii(this%n_max*3, this%n_max*this%n_species)) - !do n_i = 1, n_neighbours(at,i,max_dist=this%cutoff) - - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - - if( species_map(at%Z(j)) == 0 ) cycle - - i_pow = 0 - grad_descriptor_i = 0.0_dp - - !SPEED do ia = 1, this%n_species*this%n_max - !SPEED a = rs_index(1,ia) - !SPEED i_species = rs_index(2,ia) - !SPEED do jb = 1, ia - !SPEED b = rs_index(1,jb) - !SPEED j_species = rs_index(2,jb) - !SPEED do l = 0, this%l_max - !SPEED i_pow = i_pow + 1 - !SPEED if(at%Z(j) == this%species_Z(i_species) .or. this%species_Z(i_species)==0) grad_descriptor_i(i_pow,:) = grad_descriptor_i(i_pow,:) + real( matmul(conjg(grad_fourier_so3(l,a,n_i)%mm),fourier_so3(l,b,j_species)%m) ) - !SPEED if(at%Z(j) == this%species_Z(j_species) .or. this%species_Z(j_species)==0) grad_descriptor_i(i_pow,:) = grad_descriptor_i(i_pow,:) + real( matmul(grad_fourier_so3(l,b,n_i)%mm,conjg(fourier_so3(l,a,i_species)%m)) ) - !SPEED !grad_descriptor_i(i_pow,:) = real( matmul(conjg(grad_fourier_so3(l,a,n_i)%mm),fourier_so3(l,b)%m) + matmul(grad_fourier_so3(l,b,n_i)%mm,conjg(fourier_so3(l,a)%m)) ) - !SPEED if( ia /= jb ) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) * SQRT_TWO - !SPEED enddo !l - !SPEED enddo !jb - !SPEED enddo !ia - - !SPEED do ia = 1, this%n_species*this%n_max - !SPEED a = rs_index(1,ia) - !SPEED i_species = rs_index(2,ia) - !SPEED do jb = 1, ia - !SPEED b = rs_index(1,jb) - !SPEED j_species = rs_index(2,jb) - !SPEED do l = 0, this%l_max - !SPEED i_pow = i_pow + 1 - !SPEED if(at%Z(j) == this%species_Z(i_species) .or. this%species_Z(i_species)==0) grad_descriptor_i(i_pow,:) = grad_descriptor_i(i_pow,:) + & - !SPEED matmul(grad_fourier_so3_r(l,a,n_i)%mm,fourier_so3_r(l,b,j_species)%m) + matmul(grad_fourier_so3_i(l,a,n_i)%mm,fourier_so3_i(l,b,j_species)%m) - !SPEED if(at%Z(j) == this%species_Z(j_species) .or. this%species_Z(j_species)==0) grad_descriptor_i(i_pow,:) = grad_descriptor_i(i_pow,:) + & - !SPEED matmul(grad_fourier_so3_r(l,b,n_i)%mm,fourier_so3_r(l,a,i_species)%m) + matmul(grad_fourier_so3_i(l,b,n_i)%mm,fourier_so3_i(l,a,i_species)%m) - !SPEED if( ia /= jb ) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) * SQRT_TWO - !SPEED enddo !l - !SPEED enddo !jb - !SPEED enddo !ia - - do l=0, this%l_max - do a = 1, this%n_max - do alpha=1, 3 - t_g_r(3*(a-1)+alpha, 1:2*l+1) = grad_fourier_so3_r(l,a,n_i)%mm(alpha,-l:l) - t_g_i(3*(a-1)+alpha, 1:2*l+1) = grad_fourier_so3_i(l,a,n_i)%mm(alpha,-l:l) - enddo - enddo - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - - t_f_r(ia, 1:2*l+1) = fourier_so3_r(l,a,i_species)%m(-l:l) - t_f_i(ia, 1:2*l+1) = fourier_so3_i(l,a,i_species)%m(-l:l) - enddo - call dgemm('N','T',this%n_max*3, this%n_max*this%n_species, 2*l+1, 1.0_dp, & - t_g_r(1,1), size(t_g_r,1), t_f_r(1,1), size(t_f_r,1), 0.0_dp, t_g_f_rr(1,1), size(t_g_f_rr, 1)) - call dgemm('N','T',this%n_max*3, this%n_max*this%n_species, 2*l+1, 1.0_dp, & - t_g_i(1,1), size(t_g_i,1), t_f_i(1,1), size(t_f_i,1), 0.0_dp, t_g_f_ii(1,1), size(t_g_f_ii, 1)) - !t_g_f_rr = matmul(t_g_r,transpose(t_f_r)) - !t_g_f_ii = matmul(t_g_i,transpose(t_f_i)) - - i_pow = l+1 - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - do jb = 1, ia !this%n_species*this%n_max !ia - b = rs_index(1,jb) - j_species = rs_index(2,jb) - - if(this%diagonal_radial .and. a /= b) cycle - - if(at%Z(j) == this%species_Z(i_species) .or. this%species_Z(i_species)==0) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) + t_g_f_rr(3*(a-1)+1:3*a,jb) + t_g_f_ii(3*(a-1)+1:3*a,jb) - if(at%Z(j) == this%species_Z(j_species) .or. this%species_Z(j_species)==0) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) + t_g_f_rr(3*(b-1)+1:3*b,ia) + t_g_f_ii(3*(b-1)+1:3*b,ia) - - - if(do_two_l_plus_one) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) / sqrt(2.0_dp * l + 1.0_dp) - if( ia /= jb ) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) * SQRT_TWO - i_pow = i_pow + this%l_max+1 - enddo - enddo - - !do a = 1, this%n_max - ! do b = 1, a - ! grad_descriptor_i(i_pow, 1:3) = t_g_f_rr(3*(a-1)+1:3*a,b) + t_g_f_ii(3*(a-1)+1:3*a,b) + & - ! t_g_f_rr(3*(b-1)+1:3*b,a) + t_g_f_ii(3*(b-1)+1:3*b,a) - ! if( a /= b ) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) * SQRT_TWO - ! i_pow = i_pow + this%l_max+1 - ! end do - !end do - - end do !l - - grad_descriptor_i(d, 1:3) = 0.0_dp - - if(.not. this%global) then - if( this%normalise ) then - descriptor_out%x(i_desc_i)%grad_data(:,:,n_i) = grad_descriptor_i / norm_descriptor_i - do k = 1, 3 - descriptor_out%x(i_desc_i)%grad_data(:,k,n_i) = descriptor_out%x(i_desc_i)%grad_data(:,k,n_i) - descriptor_i * dot_product(descriptor_i,grad_descriptor_i(:,k)) / norm_descriptor_i**3 - enddo - else - descriptor_out%x(i_desc_i)%grad_data(:,:,n_i) = grad_descriptor_i - endif - - descriptor_out%x(i_desc_i)%grad_data(:,:,0) = descriptor_out%x(i_desc_i)%grad_data(:,:,0) - descriptor_out%x(i_desc_i)%grad_data(:,:,n_i) - endif - enddo !ni - deallocate(t_f_r, t_f_i) - deallocate(t_g_r, t_g_i) - deallocate(t_g_f_rr, t_g_f_ii) -! call system_timer("soap_calc 33") - - do n_i = 1, n_neighbours(at,i,max_dist=this%cutoff) - do a = 1, this%n_max - do l = 0, this%l_max - !SPEED deallocate(grad_fourier_so3(l,a,n_i)%mm) - if(allocated(grad_fourier_so3_r(l,a,n_i)%mm)) deallocate(grad_fourier_so3_r(l,a,n_i)%mm) - if(allocated(grad_fourier_so3_i(l,a,n_i)%mm)) deallocate(grad_fourier_so3_i(l,a,n_i)%mm) - enddo - enddo - enddo - !SPEED deallocate(grad_fourier_so3) - deallocate(grad_fourier_so3_r) - deallocate(grad_fourier_so3_i) - endif - - enddo ! i -!$omp end parallel do - - !SPEED if(allocated(fourier_so3)) then - !SPEED do i_species = 1, this%n_species - !SPEED do a = lbound(fourier_so3,2), ubound(fourier_so3,2) - !SPEED do l = lbound(fourier_so3,1), ubound(fourier_so3,1) - !SPEED deallocate(fourier_so3(l,a,i_species)%m) - !SPEED enddo - !SPEED enddo - !SPEED enddo - !SPEED deallocate(fourier_so3) - !SPEED endif - -!$omp parallel default(none) private(i_species, a, l) - if(allocated(fourier_so3_r)) then - do i_species = lbound(fourier_so3_r,3), ubound(fourier_so3_r,3) - do a = lbound(fourier_so3_r,2), ubound(fourier_so3_r,2) - do l = lbound(fourier_so3_r,1), ubound(fourier_so3_r,1) - deallocate(fourier_so3_r(l,a,i_species)%m) - enddo - enddo - enddo - deallocate(fourier_so3_r) - endif - if(allocated(fourier_so3_i)) then - do i_species = lbound(fourier_so3_i,3), ubound(fourier_so3_i,3) - do a = lbound(fourier_so3_i,2), ubound(fourier_so3_i,2) - do l = lbound(fourier_so3_i,1), ubound(fourier_so3_i,1) - deallocate(fourier_so3_i(l,a,i_species)%m) - enddo - enddo - enddo - deallocate(fourier_so3_i) - endif - - if(allocated(SphericalY_ij)) then - do l = lbound(SphericalY_ij,1), ubound(SphericalY_ij,1) - deallocate(SphericalY_ij(l)%m) - enddo - deallocate(SphericalY_ij) - endif - - if(allocated(grad_SphericalY_ij)) then - do l = lbound(grad_SphericalY_ij,1), ubound(grad_SphericalY_ij,1) - deallocate(grad_SphericalY_ij(l)%mm) - enddo - deallocate(grad_SphericalY_ij) - endif - - if(allocated(radial_fun)) deallocate(radial_fun) - if(allocated(radial_coefficient)) deallocate(radial_coefficient) - if(allocated(grad_radial_fun)) deallocate(grad_radial_fun) - if(allocated(grad_radial_coefficient)) deallocate(grad_radial_coefficient) - if(allocated(descriptor_i)) deallocate(descriptor_i) - if(allocated(grad_descriptor_i)) deallocate(grad_descriptor_i) -!$omp end parallel - - if(this%global) then - allocate(global_fourier_so3_r(0:this%l_max,this%n_max,this%n_species), global_fourier_so3_i(0:this%l_max,this%n_max,this%n_species), & - descriptor_i(d) ) - - i_coeff = 0 - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - do l = 0, this%l_max - allocate(global_fourier_so3_r(l,a,i_species)%m(-l:l)) - allocate(global_fourier_so3_i(l,a,i_species)%m(-l:l)) - global_fourier_so3_r(l,a,i_species)%m(:) = global_fourier_so3_r_array(i_coeff+1:i_coeff+2*l+1) - global_fourier_so3_i(l,a,i_species)%m(:) = global_fourier_so3_i_array(i_coeff+1:i_coeff+2*l+1) - i_coeff = i_coeff + 2*l+1 - enddo - enddo - - i_pow = 0 - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - do jb = 1, ia - b = rs_index(1,jb) - j_species = rs_index(2,jb) - - if(this%diagonal_radial .and. a /= b) cycle - - do l = 0, this%l_max - i_pow = i_pow + 1 - descriptor_i(i_pow) = & - dot_product(global_fourier_so3_r(l,a,i_species)%m, global_fourier_so3_r(l,b,j_species)%m) + & - dot_product(global_fourier_so3_i(l,a,i_species)%m, global_fourier_so3_i(l,b,j_species)%m) - - !if( ia /= jb ) descriptor_out%global_data(i_pow) = descriptor_out%global_data(i_pow) * SQRT_TWO - if(do_two_l_plus_one) descriptor_i(i_pow) = descriptor_i(i_pow) / sqrt(2.0_dp * l + 1.0_dp) - if( ia /= jb ) descriptor_i(i_pow) = descriptor_i(i_pow) * SQRT_TWO - enddo !l - - enddo !jb - enddo !ia - descriptor_i(d) = 0.0_dp - norm_descriptor_i = sqrt(dot_product(descriptor_i,descriptor_i)) - if(my_do_descriptor) then - if(this%normalise) then - descriptor_out%x(1)%data = descriptor_i / norm_descriptor_i - else - descriptor_out%x(1)%data = descriptor_i - endif - descriptor_out%x(1)%data(d) = this%covariance_sigma0 - endif - - if(my_do_grad_descriptor) then - allocate(t_g_r(this%n_max*3, 2*this%l_max+1), t_g_i(this%n_max*3, 2*this%l_max+1)) - allocate(t_f_r(this%n_max*this%n_species, 2*this%l_max+1), t_f_i(this%n_max*this%n_species, 2*this%l_max+1)) - allocate(t_g_f_rr(this%n_max*3, this%n_max*this%n_species), t_g_f_ii(this%n_max*3, this%n_max*this%n_species)) - allocate(grad_descriptor_i(d,3)) - - i_pair = 0 - do i = 1, at%N - - if(i_desc(i) == 0) then - cycle - else - i_desc_i = i_desc(i) - endif - - i_pair = i_pair + 1 - i_pair_i = i_pair ! accumulates \frac{ \partial p^{(j)} }{ \partial r_{ji\alpha} } - - descriptor_out%x(1)%ii(i_pair_i) = i - descriptor_out%x(1)%pos(:,i_pair_i) = 0.0_dp - descriptor_out%x(1)%has_grad_data(i_pair_i) = .true. - descriptor_out%x(1)%grad_data(:,:,i_pair_i) = 0.0_dp - - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, diff = d_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - i_pair = i_pair + 1 ! \frac{ \partial p^{(i)} }{ \partial r_{ij\alpha} } - - descriptor_out%x(1)%ii(i_pair) = j - descriptor_out%x(1)%pos(:,i_pair) = d_ij - descriptor_out%x(1)%has_grad_data(i_pair) = .true. - - i_pow = 0 - grad_descriptor_i = 0.0_dp - - do l=0, this%l_max - do a = 1, this%n_max - do alpha=1, 3 - t_g_r(3*(a-1)+alpha, 1:2*l+1) = global_grad_fourier_so3_r_array(i_desc_i)%x(l,a,n_i)%mm(alpha,-l:l) - t_g_i(3*(a-1)+alpha, 1:2*l+1) = global_grad_fourier_so3_i_array(i_desc_i)%x(l,a,n_i)%mm(alpha,-l:l) - enddo - enddo - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - - t_f_r(ia, 1:2*l+1) = global_fourier_so3_r(l,a,i_species)%m(-l:l) - t_f_i(ia, 1:2*l+1) = global_fourier_so3_i(l,a,i_species)%m(-l:l) - enddo - call dgemm('N','T',this%n_max*3, this%n_max*this%n_species, 2*l+1, 1.0_dp, & - t_g_r(1,1), size(t_g_r,1), t_f_r(1,1), size(t_f_r,1), 0.0_dp, t_g_f_rr(1,1), size(t_g_f_rr, 1)) - call dgemm('N','T',this%n_max*3, this%n_max*this%n_species, 2*l+1, 1.0_dp, & - t_g_i(1,1), size(t_g_i,1), t_f_i(1,1), size(t_f_i,1), 0.0_dp, t_g_f_ii(1,1), size(t_g_f_ii, 1)) - !t_g_f_rr = matmul(t_g_r,transpose(t_f_r)) - !t_g_f_ii = matmul(t_g_i,transpose(t_f_i)) - - i_pow = l+1 - do ia = 1, this%n_species*this%n_max - a = rs_index(1,ia) - i_species = rs_index(2,ia) - do jb = 1, ia !this%n_species*this%n_max !ia - b = rs_index(1,jb) - j_species = rs_index(2,jb) - - if(this%diagonal_radial .and. a /= b) cycle - - if(at%Z(j) == this%species_Z(i_species) .or. this%species_Z(i_species)==0) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) + t_g_f_rr(3*(a-1)+1:3*a,jb) + t_g_f_ii(3*(a-1)+1:3*a,jb) - if(at%Z(j) == this%species_Z(j_species) .or. this%species_Z(j_species)==0) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) + t_g_f_rr(3*(b-1)+1:3*b,ia) + t_g_f_ii(3*(b-1)+1:3*b,ia) - - - if(do_two_l_plus_one) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) / sqrt(2.0_dp * l + 1.0_dp) - if( ia /= jb ) grad_descriptor_i(i_pow, 1:3) = grad_descriptor_i(i_pow, 1:3) * SQRT_TWO - i_pow = i_pow + this%l_max+1 - enddo - enddo - - end do !l - - grad_descriptor_i(d, 1:3) = 0.0_dp - if( this%normalise ) then - descriptor_out%x(1)%grad_data(:,:,i_pair) = grad_descriptor_i / norm_descriptor_i - do k = 1, 3 - descriptor_out%x(1)%grad_data(:,k,i_pair) = descriptor_out%x(1)%grad_data(:,k,i_pair) - descriptor_i * dot_product(descriptor_i,grad_descriptor_i(:,k)) / norm_descriptor_i**3 - enddo - else - descriptor_out%x(1)%grad_data(:,:,i_pair) = grad_descriptor_i - endif - - descriptor_out%x(1)%grad_data(:,:,i_pair_i) = descriptor_out%x(1)%grad_data(:,:,i_pair_i) - descriptor_out%x(1)%grad_data(:,:,i_pair) - enddo ! n/n_i - - enddo ! i - - deallocate(grad_descriptor_i) - deallocate(t_f_r, t_f_i) - deallocate(t_g_r, t_g_i) - deallocate(t_g_f_rr, t_g_f_ii) - endif ! my_do_grad_descriptor - - do i_species = lbound(global_fourier_so3_r,3), ubound(global_fourier_so3_r,3) - do a = lbound(global_fourier_so3_r,2), ubound(global_fourier_so3_r,2) - do l = lbound(global_fourier_so3_r,1), ubound(global_fourier_so3_r,1) - deallocate(global_fourier_so3_r(l,a,i_species)%m) - enddo - enddo - enddo - deallocate(global_fourier_so3_r) - do i_species = lbound(global_fourier_so3_i,3), ubound(global_fourier_so3_i,3) - do a = lbound(global_fourier_so3_i,2), ubound(global_fourier_so3_i,2) - do l = lbound(global_fourier_so3_i,1), ubound(global_fourier_so3_i,1) - deallocate(global_fourier_so3_i(l,a,i_species)%m) - enddo - enddo - enddo - deallocate(global_fourier_so3_i) - - if(allocated(descriptor_i)) deallocate(descriptor_i) - endif ! this%global - - if(allocated(global_fourier_so3_r_array)) deallocate(global_fourier_so3_r_array) - if(allocated(global_fourier_so3_i_array)) deallocate(global_fourier_so3_i_array) - - if(allocated(global_grad_fourier_so3_r_array)) then - do i_desc_i = lbound(global_grad_fourier_so3_r_array,1), ubound(global_grad_fourier_so3_r_array,1) - if(allocated(global_grad_fourier_so3_r_array(i_desc_i)%x)) then - do n_i = lbound(global_grad_fourier_so3_r_array(i_desc_i)%x,3), ubound(global_grad_fourier_so3_r_array(i_desc_i)%x,3) - do a = lbound(global_grad_fourier_so3_r_array(i_desc_i)%x,2), ubound(global_grad_fourier_so3_r_array(i_desc_i)%x,2) - do l = lbound(global_grad_fourier_so3_r_array(i_desc_i)%x,1), ubound(global_grad_fourier_so3_r_array(i_desc_i)%x,1) - if(allocated(global_grad_fourier_so3_r_array(i_desc_i)%x(l,a,n_i)%mm)) deallocate(global_grad_fourier_so3_r_array(i_desc_i)%x(l,a,n_i)%mm) - enddo ! l - enddo ! a - enddo ! n_i - deallocate(global_grad_fourier_so3_r_array(i_desc_i)%x) - endif - enddo ! i_desc_i - deallocate(global_grad_fourier_so3_r_array) - endif - if(allocated(global_grad_fourier_so3_i_array)) then - do i_desc_i = lbound(global_grad_fourier_so3_i_array,1), ubound(global_grad_fourier_so3_i_array,1) - if(allocated(global_grad_fourier_so3_i_array(i_desc_i)%x)) then - do n_i = lbound(global_grad_fourier_so3_i_array(i_desc_i)%x,3), ubound(global_grad_fourier_so3_i_array(i_desc_i)%x,3) - do a = lbound(global_grad_fourier_so3_i_array(i_desc_i)%x,2), ubound(global_grad_fourier_so3_i_array(i_desc_i)%x,2) - do l = lbound(global_grad_fourier_so3_i_array(i_desc_i)%x,1), ubound(global_grad_fourier_so3_i_array(i_desc_i)%x,1) - if(allocated(global_grad_fourier_so3_i_array(i_desc_i)%x(l,a,n_i)%mm)) deallocate(global_grad_fourier_so3_i_array(i_desc_i)%x(l,a,n_i)%mm) - enddo ! l - enddo ! a - enddo ! n_i - deallocate(global_grad_fourier_so3_i_array(i_desc_i)%x) - endif - enddo ! i_desc_i - deallocate(global_grad_fourier_so3_i_array) - endif - - if(allocated(rs_index)) deallocate(rs_index) - if(allocated(i_desc)) deallocate(i_desc) - - call system_timer('soap_calc') - - endsubroutine soap_calc - - subroutine AN_monomer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(AN_monomer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, i_desc, i, j, k, n, m, n_index - integer, dimension(3) :: shift_ij, shift_ik - real(dp) :: r_ij, r_ik, r_jk - real(dp), dimension(3) :: d_ij, d_ik, d_jk, u_ij, u_jk - - INIT_ERROR(error) - - call system_timer('AN_monomer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("AN_monomer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='AN_monomer_calc args_str')) then - RAISE_ERROR("AN_monomer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("AN_monomer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("AN_monomer_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = AN_monomer_dimensions(this,error) - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,0:this%N-1)) - allocate(descriptor_out%x(i)%ii(0:this%N-1)) - allocate(descriptor_out%x(i)%pos(3,0:this%N-1)) - allocate(descriptor_out%x(i)%has_grad_data(0:this%N-1)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,0:this%N-1)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - if(at%N /= this%N) then - RAISE_ERROR("AN_monomer_calc: number of atoms is "//at%N//" instead of "//this%N,error) - endif - - do i = 1, at%N - - i_desc = 0 - - if(my_do_descriptor) then - if(this%do_atomic) then - descriptor_out%x(i)%ci(1) = i - else - descriptor_out%x(i)%ci(:) = (/(m,m=1,this%N)/) - endif - endif - - if(my_do_grad_descriptor) then - descriptor_out%x(i)%ii(0) = i - descriptor_out%x(i)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i)%has_grad_data(:) = .true. - endif - - do n = 1, n_neighbours(at,i) - j = neighbour(at,i,n,distance=r_ij, cosines=u_ij, shift=shift_ij) - - i_desc = i_desc + 1 - if(my_do_descriptor) then - descriptor_out%x(i)%has_data = .true. - descriptor_out%x(i)%data(i_desc) = r_ij - endif - - if(my_do_grad_descriptor) then - descriptor_out%x(i)%ii(n) = j - descriptor_out%x(i)%pos(:,n) = at%pos(:,j) + matmul(at%lattice,shift_ij) - - descriptor_out%x(i)%grad_data(i_desc,:,n) = u_ij - descriptor_out%x(i)%grad_data(i_desc,:,0) = -u_ij - endif - - do m = 1, n_neighbours(at,i) - if(n >= m) cycle - - k = neighbour(at,i,m,distance=r_ik, shift=shift_ik) - - d_jk = ( at%pos(:,j) + matmul(at%lattice,shift_ij) ) - ( at%pos(:,k) + matmul(at%lattice,shift_ik) ) - r_jk = norm(d_jk) - u_jk = d_jk / r_jk - - i_desc = i_desc + 1 - if(my_do_descriptor) then - descriptor_out%x(i)%has_data = .true. - descriptor_out%x(i)%data(i_desc) = r_jk - endif - - if(my_do_grad_descriptor) then - descriptor_out%x(i)%grad_data(i_desc,:,n) = u_jk - descriptor_out%x(i)%grad_data(i_desc,:,m) = -u_jk - endif - - enddo - enddo - - if(.not. this%do_atomic) exit - - enddo - - call system_timer('AN_monomer_calc') - - endsubroutine AN_monomer_calc - - subroutine general_monomer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(general_monomer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor!, use_smooth_cutoff - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, monomer_size, i, & - i_atomic, j_atomic, k, start, finish, n_index - integer, dimension(3) :: temp_shift - real(dp), dimension(:), allocatable :: dist_vec - real(dp), dimension(:,:), allocatable :: interatomic_distances - real(dp), dimension(:,:,:), allocatable :: interatomic_vectors - integer, dimension(:), allocatable :: atomic_index - integer, dimension(:,:), allocatable :: monomer_index, shifts - logical, dimension(:), allocatable :: associated_to_monomer - - - INIT_ERROR(error) - - call system_timer('general_monomer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("general_monomer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='general_monomer_calc args_str')) then - RAISE_ERROR("general_monomer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("general_monomer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - monomer_size=size(this%signature) - d = general_monomer_dimensions(this,error) - - allocate(shifts(monomer_size,3)) - allocate(dist_vec(d)) - allocate(atomic_index(monomer_size)) - allocate(associated_to_monomer(at%N)) - allocate(interatomic_vectors(monomer_size,monomer_size,3)) - allocate(interatomic_distances(monomer_size,monomer_size)) - interatomic_vectors = 0.0_dp - interatomic_distances = 0.0_dp - associated_to_monomer=.False. - - call find_general_monomer(at,monomer_index,this%signature,associated_to_monomer,this%cutoff,this%atom_ordercheck,error) - if(.not. all(associated_to_monomer)) then - !RAISE_ERROR("general_monomer_calc: not all atoms assigned to a monomer", error) - call print("Not all atoms can be assigned to a monomer with atomic numbers "//this%signature) - endif - n_descriptors = size(monomer_index,2) - call print("found "//n_descriptors//" monomers", PRINT_VERBOSE) - n_index = size(this%signature) - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%data = 0.0_dp - descriptor_out%x(i)%ci = 0 - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,monomer_size)) - allocate(descriptor_out%x(i)%ii(monomer_size)) - allocate(descriptor_out%x(i)%pos(3,monomer_size)) - allocate(descriptor_out%x(i)%has_grad_data(monomer_size)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,monomer_size)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - - do i = 1, n_descriptors - - atomic_index = monomer_index(:,i) !stores the indices of atoms in this monomer -!write(*,*) "THE ATOMS IN THE MONOMER ARE : "// atomic_index - - if(associated(atom_mask_pointer)) then - if(.not. any(atom_mask_pointer(atomic_index))) then - cycle - else - if(.not. all(atom_mask_pointer(atomic_index))) then - RAISE_ERROR("general_monomer_calc: atom mask has to encompass either all or none of the atoms of a monomer",error) - endif - endif - endif - - !calc all positions relative to atom 1 - do i_atomic=2,monomer_size - temp_shift=0 - interatomic_vectors(1,i_atomic,:) = diff_min_image(at,atomic_index(1),atomic_index(i_atomic),shift=temp_shift) - shifts(i_atomic,:) = temp_shift - end do - - !find other relative positions through vector addition - do j_atomic=2,monomer_size - do i_atomic=2,j_atomic-1 - interatomic_vectors(i_atomic,j_atomic,:) = interatomic_vectors(1,j_atomic,:) -interatomic_vectors(1,i_atomic,:) - end do - end do - - !Now convert vectors to scalar distances - do i_atomic=1,monomer_size - do j_atomic=i_atomic+1,monomer_size - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do -!!$do i_atomic=1,size(interatomic_distances,1) -!!$ write(*,'(6F12.8)') interatomic_distances(i_atomic,:) -!!$end do - !and convert this NxN matrix into the required vector length N(N-1)/2 - start = 1 - finish = monomer_size-1 - do i_atomic=1,monomer_size-1 - dist_vec(start:finish) = interatomic_distances(i_atomic,i_atomic+1:monomer_size) - start = finish+1 - finish=finish + monomer_size-i_atomic-1 - end do - - if(my_do_descriptor) then - descriptor_out%x(i)%ci(:) = atomic_index - descriptor_out%x(i)%has_data = .true. - descriptor_out%x(i)%data = dist_vec**this%power - endif - call print("distances: "//dist_vec, PRINT_VERBOSE) - if(my_do_grad_descriptor) then -!!$write(*,*) "doing grad descriptor" - descriptor_out%x(i)%ii(:) = atomic_index -!!$do i_atomic=1,at%N -!!$write(*,*) at%pos(:,atomic_index(i_atomic)) -!!$end do - descriptor_out%x(i)%pos(:,1) = at%pos(:,atomic_index(1)) - do i_atomic =2,monomer_size - descriptor_out%x(i)%pos(:,i_atomic) = at%pos(:,atomic_index(i_atomic)) + matmul(at%lattice,shifts(i_atomic,:)) - end do - - !build the grad_data matrix - descriptor_out%x(i)%has_grad_data(:) = .true. - do k=1,d - !find the pair of atoms contributing to this descriptor - do i_atomic=1,monomer_size - do j_atomic=i_atomic+1,monomer_size - if (interatomic_distances(i_atomic,j_atomic)==dist_vec(k)) then - descriptor_out%x(i)%grad_data(k,:,i_atomic) = -this%power * dist_vec(k)**(this%power-1.0_dp) * interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic) ! kth descriptor wrt atom i_atomic - descriptor_out%x(i)%grad_data(k,:,j_atomic) = -descriptor_out%x(i)%grad_data(k,:,i_atomic) ! kth descriptor wrt j_atomic -!write(*,*) "descriptor dimension "//k//" wrt atoms "//atomic_index(i_atomic)//" and "//atomic_index(j_atomic) - end if - end do - end do - end do - - - endif - - enddo - - deallocate(shifts) - deallocate(dist_vec) - deallocate(atomic_index) - deallocate(associated_to_monomer) - deallocate(interatomic_vectors) - deallocate(interatomic_distances) - deallocate(monomer_index) - call system_timer('general_monomer_calc') - - endsubroutine general_monomer_calc - - subroutine com_dimer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - - type(com_dimer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor!, use_smooth_cutoff - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor, use_smooth_cutoff, double_count - integer :: n_descriptors, dimer_size, i, j, i_atomic, j_atomic, i_desc, n_index - integer :: monomer_one_size, monomer_two_size, n_monomer_one, n_monomer_two, this_pair, diff_loc - integer, dimension(1) :: unit_array - real(dp), dimension(3) :: diff_one_two, com_pos_one, com_pos_two, transdirvec - real(dp) :: dist, primitive_cutoff, primitive_cutoff_grad - real(dp), dimension(:,:), allocatable :: diffs_one, diffs_two, com_pos_diffs - real(dp), dimension(:), allocatable :: weight_one, weight_two - integer, dimension(:), allocatable :: atomic_index, atomic_index_one, atomic_index_two, pairs_diffs_map - integer, dimension(:,:), allocatable :: monomer_one_index, monomer_two_index, monomer_pairs - logical, dimension(:), allocatable :: associated_to_monomer - - - INIT_ERROR(error) - use_smooth_cutoff = .false. - double_count = .false. - call system_timer('com_dimer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("com_dimer_calc: descriptor object not initialised", error) - endif - - if (.not. has_property(at, 'mass')) then - RAISE_ERROR('com_dimer_calc: Atoms has no mass property', error) - end if - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - monomer_one_size =size(this%signature_one) - monomer_two_size =size(this%signature_two) - dimer_size = monomer_one_size + monomer_two_size - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='com_dimer_calc args_str')) then - RAISE_ERROR("com_dimer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("com_dimer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - allocate(atomic_index(dimer_size)) - allocate(atomic_index_one(monomer_one_size)) - allocate(atomic_index_two(monomer_two_size)) - allocate(diffs_one(3,monomer_one_size)) - allocate(diffs_two(3,monomer_two_size)) - allocate(weight_one(monomer_one_size)) - allocate(weight_two(monomer_two_size)) - - allocate(associated_to_monomer(at%N)) - associated_to_monomer=.false. - - call find_general_monomer(at,monomer_one_index,this%signature_one,associated_to_monomer,this%monomer_one_cutoff,this%atom_ordercheck,error) - if (this%monomers_identical) then - allocate(monomer_two_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_two_index = monomer_one_index - else - call find_general_monomer(at,monomer_two_index,this%signature_two,associated_to_monomer,this%monomer_two_cutoff,this%atom_ordercheck,error) - end if - - if(.not. all(associated_to_monomer)) then - call print("WARNING: com_dimer_calc: not all atoms assigned to a monomer, if you have molecules present other than the following, this is OK") - call print("signature of molecule 1 ") - call print(this%signature_one) - call print("signature of molecule 2 ") - call print(this%signature_two) - endif - - n_monomer_one = size(monomer_one_index,2) - n_monomer_two = size(monomer_two_index,2) - if (n_monomer_one < 1 .or. n_monomer_two < 1) then - if ( this%strict ) then - RAISE_ERROR("com_dimer_calc failed to find at least one of the monomer types, try increasing monomer cutoffs", error) - else - call print("WARNING: com_dimer_calc failed to find at least one of the monomer types, try increasing monomer cutoffs") - end if - end if - - call system_timer('com_dimer_calc: find_monomer_pairs') - if (this%mpifind) then - call print("Using find_monomer_pairs_MPI", PRINT_NERD) - if(associated(atom_mask_pointer)) then - call find_monomer_pairs_MPI(at,monomer_pairs,com_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,double_count,this%cutoff,error=error,use_com=.true.,atom_mask=atom_mask_pointer) - else - call find_monomer_pairs_MPI(at,monomer_pairs,com_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,double_count,this%cutoff,error=error,use_com=.true.) - end if - else - call find_monomer_pairs (at,monomer_pairs,com_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,double_count,this%cutoff,use_com=.true.,error=error) - end if - call system_timer('com_dimer_calc: find_monomer_pairs') - - if ( size(pairs_diffs_map) < 1) then - if ( this%strict ) then - RAISE_ERROR("com_dimer_calc did not find any monomer pairs to make a dimer", error) - else - call print("WARNING: com_dimer_calc did not find any monomer pairs to make a dimer") - end if - end if - - n_descriptors = size(pairs_diffs_map) - n_index = size(this%signature_one) + size(this%signature_two) - - call print("ready to construct "//n_descriptors //" descriptors",PRINT_NERD) - allocate(descriptor_out%x(n_descriptors)) - loop_descriptor_init: do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(1)) - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%data = 0.0_dp - descriptor_out%x(i)%ci = 0 - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(1,3,dimer_size)) - allocate(descriptor_out%x(i)%ii(dimer_size)) - allocate(descriptor_out%x(i)%pos(3,dimer_size)) - allocate(descriptor_out%x(i)%has_grad_data(dimer_size)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,dimer_size)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo loop_descriptor_init - - if (n_descriptors > 0) then ! only loop over monomers if we actually found any dimers - i_desc = 0 - loop_monomer_one: do i = 1, n_monomer_one - if (.not. any(monomer_pairs(1,:) .eq. i)) cycle - - !get indices of monomer and calc internal distances - atomic_index_one = monomer_one_index(:,i) - - if(associated(atom_mask_pointer)) then - if(.not. any(atom_mask_pointer(atomic_index_one))) then - cycle - else - if(.not. all(atom_mask_pointer(atomic_index_one))) then - RAISE_ERROR("com_dimer_calc: atom mask has to encompass either all or none of the atoms of monomer one",error) - endif - endif - endif - - do i_atomic=1,monomer_one_size - weight_one(i_atomic) = at%mass(atomic_index_one(i_atomic)) - call print("weight_one("//i_atomic//") = "//weight_one(i_atomic), PRINT_NERD) - end do - call print("weight_one = "//weight_one, PRINT_NERD) - call print("sum(weight_one) = "//sum(weight_one), PRINT_NERD) - weight_one = weight_one / sum(weight_one) - call print("weight_one = "//weight_one, PRINT_NERD) - - com_pos_one = centre_of_mass(at, index_list=atomic_index_one) - !calc atomic positions and shifts relative to mean pos for monomer one, and also distances wrt atom 1 - do i_atomic=1,monomer_one_size - diffs_one(:,i_atomic) = diff_min_image(at,at%pos(:,atomic_index_one(i_atomic)),com_pos_one) - end do - - ! Loop through monomers paired with this one to make dimers - loop_monomer_pairs: do - unit_array = maxloc(monomer_pairs(2,:), monomer_pairs(1,:) .eq. i) ! find a monomer paired with i - this_pair = unit_array(1) - - if (this_pair == 0) exit - - !get indices of monomer two - j = monomer_pairs(2,this_pair) - monomer_pairs(:,this_pair) = 0 ! make sure this pair isn't found again - atomic_index_two = monomer_two_index(:,j) - atomic_index=(/atomic_index_one,atomic_index_two/) - - do i_atomic=1,monomer_two_size - weight_two(i_atomic) = at%mass(atomic_index_two(i_atomic)) - end do - weight_two = weight_two / sum(weight_two) - call print("weight_two="//weight_two, PRINT_NERD) - - com_pos_two = centre_of_mass(at, index_list=atomic_index_two) - - ! calc distances and shifts wrt to mean pos this monomer, and distances wrt its first atom - do j_atomic=1,monomer_two_size - diffs_two(:,j_atomic) = diff_min_image(at,at%pos(:,atomic_index_two(j_atomic)),com_pos_two) - end do - - loop_different_shifts: do - unit_array = maxloc(pairs_diffs_map, pairs_diffs_map .eq. this_pair) ! find repeats of this pair with different shifts - diff_loc = unit_array(1) - if (diff_loc == 0) exit - - i_desc = i_desc + 1 - - diff_one_two = com_pos_diffs(:,diff_loc) ! shift between mean positions of these two monomers - pairs_diffs_map(diff_loc) = 0 ! make sure this shifted pair isn't found again - - ! calculate distance - dist = norm(diff_one_two) - call print("COM distance = "//dist, PRINT_NERD) - - calc_descriptor: if(my_do_descriptor) then - descriptor_out%x(i_desc)%has_data = .true. - if(this%transfer_parameters%do_transfer) then - descriptor_out%x(i_desc)%data = transferfunction(dist, this%transfer_parameters) - else - descriptor_out%x(i_desc)%data = dist - end if - descriptor_out%x(i_desc)%ci(:) = atomic_index - descriptor_out%x(i_desc)%covariance_cutoff = 0.0_dp - primitive_cutoff = coordination_function(dist,this%cutoff,this%cutoff_transition_width) - descriptor_out%x(i_desc)%covariance_cutoff = primitive_cutoff - end if calc_descriptor - - calc_grad_descriptor: if(my_do_grad_descriptor) then !calc grads and update - - descriptor_out%x(i_desc)%ii(:) = atomic_index - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - - primitive_cutoff_grad = dcoordination_function(dist,this%cutoff,this%cutoff_transition_width) - - if(this%transfer_parameters%do_transfer) then - transdirvec = transferfunction_grad(dist, this%transfer_parameters) * diff_one_two / dist - else - transdirvec = diff_one_two / dist - endif - - do i_atomic=1,monomer_one_size - descriptor_out%x(i_desc)%pos(:,i_atomic) = com_pos_one - diffs_one(:,i_atomic) - descriptor_out%x(i_desc)%grad_data(1,:,i_atomic) = - weight_one(i_atomic) * transdirvec ! descriptor wrt atom i_atomic - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) = primitive_cutoff_grad * descriptor_out%x(i_desc)%grad_data(1,:,i_atomic) - end do - - do j_atomic=1,monomer_two_size - descriptor_out%x(i_desc)%pos(:,monomer_one_size+j_atomic) = com_pos_one + diff_one_two - diffs_two(:,j_atomic) - descriptor_out%x(i_desc)%grad_data(1,:,monomer_one_size+j_atomic) = weight_two(j_atomic) * transdirvec ! descriptor wrt atom j_atomic - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,monomer_one_size+j_atomic) = primitive_cutoff_grad * descriptor_out%x(i_desc)%grad_data(1,:,monomer_one_size+j_atomic) - end do - - endif calc_grad_descriptor - enddo loop_different_shifts - enddo loop_monomer_pairs - enddo loop_monomer_one - endif ! (n_descriptors > 0) ... still need to deallocate if no dimers were found: - - - deallocate(monomer_one_index) - deallocate(monomer_two_index) - deallocate(monomer_pairs) - deallocate(com_pos_diffs) - deallocate(pairs_diffs_map) - - deallocate(atomic_index) - deallocate(atomic_index_one) - deallocate(atomic_index_two) - deallocate(weight_one) - deallocate(weight_two) - deallocate(diffs_one) - deallocate(diffs_two) - - deallocate(associated_to_monomer) - - call system_timer('com_dimer_calc') - - endsubroutine com_dimer_calc - - subroutine general_dimer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - - type(general_dimer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor!, use_smooth_cutoff - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - real(dp) :: r_one_two - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - character(STRING_LENGTH) :: pers_idces_name - logical :: has_atom_mask_name = .false. - logical :: has_pers_idces_name = .false. - logical :: called_from_lammps = .false. - logical, dimension(:), pointer :: atom_mask_pointer - integer, dimension(:), pointer :: pers_idces_pointer - type(atoms), pointer :: at_ordered - - real(dp) :: com_dist, cutoff_grad - logical :: my_do_descriptor, my_do_grad_descriptor, monomers_identical, use_smooth_cutoff, compound_cutoff - integer :: d, n_descriptors, n_cross, dimer_size, i, j, k, n, m, & - i_atomic, j_atomic, start, finish, i_desc, cutoff_pos, n_index - integer :: monomer_one_size, monomer_two_size, n_monomer_one, n_monomer_two, n_products, this_pair, this_shift,diff_loc - integer, dimension(1) :: unit_array - real(dp), dimension(3) :: diff_one_two, temp_diff, mean_pos_one, mean_pos_two, transdirvec - real(dp), dimension(:), allocatable :: dist_vec, primitive_cutoffs, primitive_cutoff_grads - real(dp), dimension(:,:), allocatable :: interatomic_distances, diffs_one, diffs_two, mean_pos_diffs - real(dp), dimension(:,:,:), allocatable :: interatomic_vectors - integer, dimension(3) :: temp_shift, shift_one_two - real(dp), dimension(:), allocatable :: weight_one, weight_two - integer, dimension(:), allocatable :: atomic_index, atomic_index_one, atomic_index_two, pairs_diffs_map - integer, dimension(:,:), allocatable :: monomer_one_index, monomer_two_index, monomer_pairs - logical, dimension(:), allocatable :: associated_to_monomer - - - INIT_ERROR(error) - use_smooth_cutoff = .false. - call system_timer('general_dimer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("general_dimer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - monomer_one_size =size(this%signature_one) - monomer_two_size =size(this%signature_two) - dimer_size = monomer_one_size + monomer_two_size - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - call param_register(params, 'pers_idces_name', 'NONE', pers_idces_name, has_value_target=has_pers_idces_name, & - help_string="Name of a property in the atoms object giving persistent indices, i.e. atom IDs that do not change from frame to frame") - call param_register(params, 'lammps', 'F', called_from_lammps, help_string="True if the potential was called from LAMMPS") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='general_dimer_calc args_str')) then - RAISE_ERROR("general_dimer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if (called_from_lammps .and. .not. (has_atom_mask_name .and. has_pers_idces_name)) then - RAISE_ERROR("general_dimer_calc needs BOTH atom mask and persistent indices if called from LAMMPS", error) - endif - - endif - - if (this%use_com .and. .not. has_property(at, 'mass')) then - RAISE_ERROR('general_dimer_calc: Atoms has no mass property', error) - end if - - d = general_dimer_dimensions(this,error) - - compound_cutoff=.True. - if (count(this%cutoff_contributor) == 1) then - compound_cutoff=.False. - else if (count(this%cutoff_contributor) == 0) then - RAISE_ERROR("general_dimer_calc, initialisation of general dimer did not find a pair of heavy atoms to use for calculating cutoff", error) - end if - - allocate(dist_vec(d)) - allocate(primitive_cutoffs(d)) - allocate(primitive_cutoff_grads(d)) - - allocate(atomic_index(dimer_size)) - allocate(atomic_index_one(monomer_one_size)) - allocate(atomic_index_two(monomer_two_size)) - allocate(diffs_one(3,monomer_one_size)) - allocate(diffs_two(3,monomer_two_size)) - allocate(weight_one(monomer_one_size)) - allocate(weight_two(monomer_two_size)) - - allocate(interatomic_vectors(dimer_size,dimer_size,3)) - allocate(interatomic_distances(dimer_size,dimer_size)) - allocate(associated_to_monomer(at%N)) - interatomic_vectors = 0.0_dp - interatomic_distances = 0.0_dp - associated_to_monomer=.false. - - ! Warning: Potential performance hog! (but descriptors expect the atoms object not to be modified...) - ! Should we waste time sorting if we're not finding monomers based on index alone? - ! (i.e. if atom_ordercheck=T)? - if (called_from_lammps) then - allocate(at_ordered) - at_ordered = at - at_ordered%own_this = .true. - call sort(at_ordered, pers_idces_name, error=error) - else - call shallowcopy(at_ordered, at) - endif - - if( has_atom_mask_name ) then - call assign_property_pointer(at_ordered, trim(atom_mask_name), atom_mask_pointer, error) - else - atom_mask_pointer => null() - endif - - call find_general_monomer(at_ordered,monomer_one_index,this%signature_one,associated_to_monomer,this%monomer_one_cutoff,this%atom_ordercheck,error) - if (this%monomers_identical) then - allocate(monomer_two_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_two_index = monomer_one_index - else - call find_general_monomer(at_ordered,monomer_two_index,this%signature_two,associated_to_monomer,this%monomer_two_cutoff,this%atom_ordercheck,error) - end if - - if(.not. all(associated_to_monomer)) then - call print("WARNING: general_dimer_calc: not all atoms assigned to a monomer, if you have molecules present other than the following, this is OK") - call print("signature of molecule 1 ") - call print(this%signature_one) - call print("signature of molecule 2 ") - call print(this%signature_two) - endif - - n_monomer_one = size(monomer_one_index,2) - n_monomer_two = size(monomer_two_index,2) - if (n_monomer_one < 1 .or. n_monomer_two < 1) then - if ( this%strict ) then - RAISE_ERROR("general_dimer_calc,failed to find at least one of the monomer types, try increasing monomer cutoffs", error) - else - call print("WARNING: general_dimer_calc failed to find at least one of the monomer types, try increasing monomer cutoffs") - end if - end if - - call system_timer('general_dimer_calc: find_monomer_pairs') - if (this%mpifind) then - call print("Using find_monomer_pairs_MPI", PRINT_NERD) - if(associated(atom_mask_pointer)) then - call find_monomer_pairs_MPI(at_ordered,monomer_pairs,mean_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,this%double_count,this%cutoff,error=error,use_com=this%use_com,atom_mask=atom_mask_pointer) - else - call find_monomer_pairs_MPI(at_ordered,monomer_pairs,mean_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,this%double_count,this%cutoff,error=error,use_com=this%use_com) - end if - else - call find_monomer_pairs(at_ordered,monomer_pairs,mean_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,this%double_count,this%cutoff,error=error,use_com=this%use_com) - end if - call system_timer('general_dimer_calc: find_monomer_pairs') - - if ( size(pairs_diffs_map) < 1) then - if ( this%strict ) then - RAISE_ERROR("general_dimer_calc did not find any monomer pairs to make a dimer", error) - else - call print("WARNING: general_dimer_calc did not find any monomer pairs to make a dimer") - end if - end if - - n_descriptors = size(pairs_diffs_map) - call print("ready to construct "//n_descriptors //" descriptors",PRINT_NERD) - - n_index = size(this%signature_one) + size(this%signature_two) - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if (my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%data = 0.0_dp - descriptor_out%x(i)%ci = 0 - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - end if - if (my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,dimer_size)) - allocate(descriptor_out%x(i)%ii(dimer_size)) - allocate(descriptor_out%x(i)%pos(3,dimer_size)) - allocate(descriptor_out%x(i)%has_grad_data(dimer_size)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,dimer_size)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - end if - end do - - has_descriptors: if (n_descriptors > 0) then ! only loop over monomers if we actually found any dimers - i_desc = 0 - loop_monomer_one: do i = 1, n_monomer_one - if (.not. any(monomer_pairs(1,:) .eq. i)) cycle - - !get indices of monomer and calc internal distances - atomic_index_one = monomer_one_index(:,i) - - if (associated(atom_mask_pointer)) then - if (.not. any(atom_mask_pointer(atomic_index_one))) then - cycle - else - if (.not. all(atom_mask_pointer(atomic_index_one))) then - if (this%strict_mask) then - RAISE_ERROR("general_dimer_calc: atom mask has to encompass either all or none of the atoms of monomer one",error) - else - call print("general_dimer_calc: atom mask encompasses only part of a monomer; deciding based on first atom.", PRINT_NERD) - if (.not. atom_mask_pointer(atomic_index_one(1))) then - cycle - end if - end if - end if - end if - end if - - if (this%use_com) then - do i_atomic=1,monomer_one_size - weight_one(i_atomic) = at_ordered%mass(atomic_index_one(i_atomic)) - end do - weight_one = weight_one / sum(weight_one) - mean_pos_one = centre_of_mass(at_ordered, index_list=atomic_index_one) - else - weight_one = 1.0_dp / monomer_one_size - mean_pos_one = calc_mean_pos(at_ordered,atomic_index_one) - end if - - - !calc atomic positions and shifts relative to mean pos for monomer one, and also distances wrt atom 1 - do i_atomic=1,monomer_one_size - diffs_one(:,i_atomic) = diff_min_image(at_ordered,at_ordered%pos(:,atomic_index_one(i_atomic)),mean_pos_one) - interatomic_vectors(1,i_atomic,:) = diff_min_image(at_ordered,atomic_index_one(1),atomic_index_one(i_atomic)) - end do - - !find other relative positions through vector addition - do j_atomic=2,monomer_one_size - do i_atomic=2,j_atomic-1 - interatomic_vectors(i_atomic,j_atomic,:) = interatomic_vectors(1,j_atomic,:) -interatomic_vectors(1,i_atomic,:) - end do - end do - - !And convert vectors to scalar distances - do i_atomic=1,monomer_one_size - do j_atomic=i_atomic+1,monomer_one_size - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - ! Loop through monomers paired with this one to make dimers - loop_monomer_pairs: do - unit_array = maxloc(monomer_pairs(2,:), monomer_pairs(1,:) .eq. i) ! find a monomer paired with i - this_pair = unit_array(1) - - if (this_pair == 0) exit - - !get indices of monomer two - j = monomer_pairs(2,this_pair) - monomer_pairs(:,this_pair) = 0 ! make sure this pair isn't found again - atomic_index_two = monomer_two_index(:,j) - atomic_index=(/atomic_index_one,atomic_index_two/) - - if (this%use_com) then - do j_atomic=1,monomer_two_size - weight_two(j_atomic) = at_ordered%mass(atomic_index_two(j_atomic)) - end do - weight_two = weight_two / sum(weight_two) - mean_pos_two = centre_of_mass(at_ordered, index_list=atomic_index_two) - else - weight_two = 1.0_dp / monomer_two_size - mean_pos_two = calc_mean_pos(at_ordered,atomic_index_two) - end if - - ! calc distances and shifts wrt to mean pos this monomer, and distances wrt its first atom - do i_atomic=1,monomer_two_size - diffs_two(:,i_atomic) = diff_min_image(at_ordered,at_ordered%pos(:,atomic_index_two(i_atomic)),mean_pos_two) - interatomic_vectors(monomer_one_size+1,monomer_one_size+i_atomic,:) = diff_min_image(at_ordered,atomic_index_two(1),atomic_index_two(i_atomic)) - end do - - !find other relative positions through vector addition - do j_atomic=monomer_one_size+2,dimer_size - do i_atomic=monomer_one_size+2,j_atomic-1 - interatomic_vectors(i_atomic,j_atomic,:) = interatomic_vectors(monomer_one_size+1,j_atomic,:) - interatomic_vectors(monomer_one_size+1,i_atomic,:) - end do - end do - - !And convert vectors to scalar distances - do i_atomic=monomer_one_size+1,dimer_size - do j_atomic=i_atomic+1,dimer_size - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - loop_pair_shifts: do - unit_array = maxloc(pairs_diffs_map, pairs_diffs_map .eq. this_pair) ! find repeats of this pair with different shifts - diff_loc = unit_array(1) - if (diff_loc == 0) exit - - i_desc = i_desc + 1 - - diff_one_two = mean_pos_diffs(:,diff_loc) ! shift between mean positions of these two monomers - pairs_diffs_map(diff_loc) = 0 ! make sure this shifted pair isn't found again - - com_dist = norm(diff_one_two) - - ! calculate intermolecular distances, also by vector addition - do i_atomic=1,monomer_one_size - do j_atomic = monomer_one_size+1,dimer_size - interatomic_vectors(i_atomic,j_atomic,:) = diffs_one(:,i_atomic) + diff_one_two - diffs_two(:,j_atomic-monomer_one_size) - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - !Now take the whole matrix of scalar distances and combine into 1D array - start = 1 - finish = dimer_size-1 - do i_atomic=1,dimer_size-1 - dist_vec(start:finish) = interatomic_distances(i_atomic,i_atomic+1:dimer_size) - start = finish+1 - finish=finish + dimer_size-i_atomic-1 - end do - call print("dist vec "//dist_vec,PRINT_NERD) - - primitive_cutoffs=1.0_dp - do k=1,d - if (this%cutoff_contributor(k)) then - primitive_cutoffs(k) = coordination_function(dist_vec(k),this%cutoff,this%cutoff_transition_width) - end if - end do - - calc_descriptor: if (my_do_descriptor) then - descriptor_out%x(i_desc)%has_data = .true. - - do_transfer: if (this%transfer_parameters%do_transfer) then - do k=1,d - if (this%is_intermolecular(k)) then - descriptor_out%x(i_desc)%data(k) = transferfunction(dist_vec(k), this%transfer_parameters) - else - ! don't apply transfer function to intra-molecular (bond) distances - descriptor_out%x(i_desc)%data(k) = dist_vec(k) - end if - end do - else - descriptor_out%x(i_desc)%data = dist_vec**this%power - end if do_transfer - - descriptor_out%x(i_desc)%ci(:) = atomic_index - descriptor_out%x(i_desc)%covariance_cutoff = 0.0_dp - - is_com_cutoff: if (this%mpifind) then - descriptor_out%x(i_desc)%covariance_cutoff = coordination_function(com_dist, this%cutoff, this%cutoff_transition_width) - else - - is_compound_cutoff: if (compound_cutoff) then - ! Covariance cutoff is sum of pairwise products of primitive cutoffs for all *inter*molecular distances excluding H atoms - - n_products=0 - do k=1,d - if (this%cutoff_contributor(k)) then - do m=k+1,d - if (this%cutoff_contributor(m)) then - n_products = n_products + 1 - descriptor_out%x(i_desc)%covariance_cutoff = descriptor_out%x(i_desc)%covariance_cutoff + primitive_cutoffs(k)*primitive_cutoffs(m) - end if - end do - end if - end do - ! normalise - descriptor_out%x(i_desc)%covariance_cutoff = descriptor_out%x(i_desc)%covariance_cutoff / n_products - - else ! Covariance cutoff is primitive cutoff, i.e. there is only one pair of heavy atoms - - do k=1,d - if (this%cutoff_contributor(k)) then - cutoff_pos=k - descriptor_out%x(i_desc)%covariance_cutoff = coordination_function(dist_vec(k),this%cutoff,this%cutoff_transition_width) - exit - end if - end do - - end if is_compound_cutoff - - end if is_com_cutoff - end if calc_descriptor - - calc_grad_descriptor: if (my_do_grad_descriptor) then !calc grads and update - - descriptor_out%x(i_desc)%ii(:) = atomic_index - - do i_atomic=1,monomer_one_size - descriptor_out%x(i_desc)%pos(:,i_atomic) = mean_pos_one - diffs_one(:,i_atomic) - end do - do i_atomic=1,monomer_two_size - descriptor_out%x(i_desc)%pos(:,monomer_one_size+i_atomic) = mean_pos_one + diff_one_two - diffs_two(:,i_atomic) - end do - - call print("DIMER "//dimer_size,PRINT_NERD) - call print('DIMER Lattice="10.0000000 0.00000000 0.00000000 0.00000000 10.0000000 0.00000000 0.00000000 0.00000000 10.00000" Properties=Z:I:1:pos:R:3',PRINT_NERD) - do i_atomic=1,dimer_size - call print("DIMER "//at_ordered%Z(atomic_index(i_atomic))//" "//descriptor_out%x(i_desc)%pos(:,i_atomic),PRINT_NERD) - end do - - !build the grad_data matrix - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - - do k=1,d - !get pair of atoms contributing to this component - i_atomic = this%component_atoms(k,1) - j_atomic = this%component_atoms(k,2) - do_transfer_grad: if (this%transfer_parameters%do_transfer .and. this%is_intermolecular(k)) then - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = -transferfunction_grad(dist_vec(k), this%transfer_parameters) * interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic) ! descriptor wrt atom i_atomic, using a transfer function - else - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = - this%power * dist_vec(k)**(this%power-1.0_dp) * & - interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic) ! descriptor wrt atom i_atomic - end if do_transfer_grad - descriptor_out%x(i_desc)%grad_data(k,:,j_atomic) = -descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) ! descriptor wrt j_atomic - end do - - is_com_cutoff_grad: if (this%mpifind) then - transdirvec = diff_one_two / com_dist - - cutoff_grad = dcoordination_function(com_dist, this%cutoff, this%cutoff_transition_width) - do i_atomic=1,monomer_one_size - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) = - weight_one(i_atomic) * transdirvec * cutoff_grad - end do - do j_atomic=1,monomer_two_size - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,monomer_one_size+j_atomic) = weight_two(j_atomic) * transdirvec * cutoff_grad - end do - else - - primitive_cutoff_grads=0.0_dp - do k=1,d - if (this%cutoff_contributor(k)) then - primitive_cutoff_grads(k) = dcoordination_function(dist_vec(k),this%cutoff,this%cutoff_transition_width) - end if - end do - - is_compound_cutoff_grad: if (compound_cutoff) then - - do i_atomic=1,dimer_size ! for each atom in the dimer... - do k=1,d ! ...iterate over all distance... - if (this%cutoff_contributor(k)) then - do m=k+1,d ! ...pairs involved... - if (this%cutoff_contributor(m)) then - if (any(this%component_atoms(k,:) == i_atomic) .or. any(this%component_atoms(m,:) == i_atomic)) then - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) & - = descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) & - + primitive_cutoffs(m)*primitive_cutoff_grads(k)*descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) & - + primitive_cutoffs(k)*primitive_cutoff_grads(m)*descriptor_out%x(i_desc)%grad_data(m,:,i_atomic) - end if - end if - end do - end if - end do - end do - !normalisation factor - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,:) = descriptor_out%x(i_desc)%grad_covariance_cutoff(:,:) / n_products - - else - - i_atomic = this%component_atoms(cutoff_pos,1) - j_atomic = this%component_atoms(cutoff_pos,2) - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) = primitive_cutoff_grads(cutoff_pos) *descriptor_out%x(i_desc)%grad_data(cutoff_pos,:,i_atomic) - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,j_atomic) = primitive_cutoff_grads(cutoff_pos) *descriptor_out%x(i_desc)%grad_data(cutoff_pos,:,j_atomic) - - end if is_compound_cutoff_grad - - end if is_com_cutoff_grad - end if calc_grad_descriptor - - end do loop_pair_shifts - end do loop_monomer_pairs - end do loop_monomer_one - end if has_descriptors ! (n_descriptors > 0) ... still need to deallocate if no dimers were found: - - call finalise_ptr(at_ordered) - - deallocate(monomer_one_index) - deallocate(monomer_two_index) - deallocate(monomer_pairs) - deallocate(mean_pos_diffs) - deallocate(pairs_diffs_map) - - deallocate(dist_vec) - deallocate(primitive_cutoffs) - deallocate(primitive_cutoff_grads) - - deallocate(atomic_index) - deallocate(atomic_index_one) - deallocate(atomic_index_two) - deallocate(weight_one) - deallocate(weight_two) - deallocate(diffs_one) - deallocate(diffs_two) - - deallocate(interatomic_vectors) - deallocate(interatomic_distances) - deallocate(associated_to_monomer) - - call system_timer('general_dimer_calc') - - end subroutine general_dimer_calc - - subroutine general_trimer_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - - type(general_trimer), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor!, use_smooth_cutoff - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - real(dp) :: r_one_two - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor, one_two_identical, one_three_identical, two_three_identical, & - use_smooth_cutoff, done_this_monomer, done_this_dimer - - integer :: d, n_descriptors, n_cross, dimer_size, trimer_size, & - i, j, k, n, m, i_atomic, j_atomic, start, finish, i_desc,this_diff,triplet_pos, & - n_index - integer :: monomer_one_size, monomer_two_size, monomer_three_size, n_monomer_one, n_monomer_two, n_monomer_three, n_products, contributor_loc - integer, dimension(1) :: unit_array - real(dp) :: temp_dist - real(dp), dimension(3) :: diff_one_two, diff_one_three,diff_two_three,mean_pos_one,mean_pos_two,mean_pos_three - real(dp), dimension(3) :: grad_cut_one, grad_cut_two, grad_cut_three - real(dp) :: dist_one_two, dist_one_three, dist_two_three, cut12, cut13, cut23, dcut12, dcut13, dcut23 - real(dp), dimension(:), allocatable :: dist_vec, primitive_cutoffs, primitive_cutoff_grads - real(dp), dimension(:,:), allocatable :: interatomic_distances,diffs_one,diffs_two,diffs_three,triplets_diffs - real(dp), dimension(:,:,:), allocatable :: interatomic_vectors - integer, dimension(3) :: temp_shift, shift_one_two,shift_one_three - integer, dimension(:), allocatable :: atomic_index_dimer, atomic_index_trimer, atomic_index_one, atomic_index_two, atomic_index_three,triplets_diffs_map - integer, dimension(:,:), allocatable :: monomer_one_index, monomer_two_index, monomer_three_index, monomer_triplets - logical, dimension(:), allocatable :: associated_to_monomer - logical :: double_count - - INIT_ERROR(error) - use_smooth_cutoff = .false. - double_count=.false. - call system_timer('general_trimer_calc') - - if(.not. this%initialised) then - RAISE_ERROR("general_trimer_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - monomer_one_size =size(this%signature_one) - monomer_two_size =size(this%signature_two) - monomer_three_size =size(this%signature_three) - dimer_size = monomer_one_size + monomer_two_size - trimer_size = monomer_one_size + monomer_two_size + monomer_three_size - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='general_trimer_calc args_str')) then - RAISE_ERROR("general_trimer_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("general_trimer_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - d = general_trimer_dimensions(this,error) - - allocate(dist_vec(d)) - allocate(primitive_cutoffs(d)) - allocate(primitive_cutoff_grads(d)) - - allocate(atomic_index_one(monomer_one_size)) - allocate(atomic_index_two(monomer_two_size)) - allocate(atomic_index_three(monomer_three_size)) - allocate(atomic_index_dimer(dimer_size)) - allocate(atomic_index_trimer(trimer_size)) - - allocate(interatomic_vectors(trimer_size,trimer_size,3)) - allocate(interatomic_distances(trimer_size,trimer_size)) - allocate(associated_to_monomer(at%N)) - allocate(diffs_one(3,monomer_one_size)) - allocate(diffs_two(3,monomer_two_size)) - allocate(diffs_three(3,monomer_three_size)) - - interatomic_vectors = 0.0_dp - interatomic_distances = 0.0_dp - associated_to_monomer=.false. - - call find_general_monomer(at,monomer_one_index,this%signature_one,associated_to_monomer,this%monomer_one_cutoff,this%atom_ordercheck,error) - if (this%one_two_identical) then - allocate(monomer_two_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_two_index = monomer_one_index - else - call find_general_monomer(at,monomer_two_index,this%signature_two,associated_to_monomer,this%monomer_two_cutoff,this%atom_ordercheck,error) - end if - if (this%one_three_identical) then - allocate(monomer_three_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_three_index = monomer_one_index - else if (this%two_three_identical) then - allocate(monomer_three_index(size(monomer_two_index,1),size(monomer_two_index,2))) - monomer_three_index = monomer_two_index - else - call find_general_monomer(at,monomer_three_index,this%signature_three,associated_to_monomer,this%monomer_three_cutoff,this%atom_ordercheck,error) - end if - - if(.not. all(associated_to_monomer)) then - if(this%strict) then - RAISE_ERROR("general_trimer_calc: not all atoms assigned to a monomer", error) - else - call print("WARNING: general_trimer_calc: not all atoms assigned to a monomer") - endif - endif - - n_monomer_one = size(monomer_one_index,2) - n_monomer_two = size(monomer_two_index,2) - n_monomer_three = size(monomer_three_index,2) - - if (this%use_com) then - RAISE_ERROR("general_trimer_calc: use_com=T not implemented yet", error) - end if - call system_timer('general_trimer_calc: find_monomer_triplets') - if(this%mpifind) then - call print("Using find_monomer_triplets_MPI", PRINT_NERD) - if(associated(atom_mask_pointer)) then - call find_monomer_triplets_MPI(at,monomer_triplets,triplets_diffs,triplets_diffs_map,monomer_one_index,monomer_two_index,monomer_three_index,this%one_two_identical,this%one_three_identical,this%two_three_identical,this%cutoff,error,use_com=.false.,atom_mask=atom_mask_pointer) - else - call find_monomer_triplets_MPI(at,monomer_triplets,triplets_diffs,triplets_diffs_map,monomer_one_index,monomer_two_index,monomer_three_index,this%one_two_identical,this%one_three_identical,this%two_three_identical,this%cutoff,error,use_com=.false.) - end if - else - call find_monomer_triplets(at,monomer_triplets,triplets_diffs,triplets_diffs_map,monomer_one_index,monomer_two_index,monomer_three_index,this%one_two_identical,this%one_three_identical,this%two_three_identical,this%cutoff,error) - end if - call system_timer('general_trimer_calc: find_monomer_triplets') - call print("monomer_triplets("//size(monomer_triplets,1)//", "//size(monomer_triplets,2)//")", PRINT_NERD) - call print("triplets_diffs("//size(triplets_diffs,1)//", "//size(triplets_diffs,2)//")", PRINT_NERD) - call print("triplets_diffs_map("//size(triplets_diffs_map)//")", PRINT_NERD) - - !call print("monomer_triplets",PRINT_NERD) - !call print(monomer_triplets,PRINT_NERD) - - n_descriptors = size(triplets_diffs_map) - n_index = size(this%signature_one) + size(this%signature_two) + size(this%signature_three) - - call print("ready to construct "//n_descriptors //" descriptors",PRINT_NERD) - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%data = 0.0_dp - descriptor_out%x(i)%ci = 0 - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,trimer_size)) - allocate(descriptor_out%x(i)%ii(trimer_size)) - allocate(descriptor_out%x(i)%pos(3,trimer_size)) - allocate(descriptor_out%x(i)%has_grad_data(trimer_size)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,trimer_size)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - has_descriptors: if (n_descriptors > 0) then ! only loop over monomers if we actually found any trimers - i_desc = 0 - loop_monomer_one: do i = 1, n_monomer_one - if (.not. any(monomer_triplets(1,:) .eq. i)) cycle - done_this_monomer = .false. - !get indices of monomer and calc internal distances - atomic_index_one = monomer_one_index(:,i) !store the indices of atoms in this monomer - - if(associated(atom_mask_pointer)) then - if(.not. any(atom_mask_pointer(atomic_index_one))) then - cycle - else - if(.not. all(atom_mask_pointer(atomic_index_one))) then - RAISE_ERROR("general_trimer_calc: atom mask has to encompass either all or none of the atoms of monomer one",error) - endif - endif - endif - - mean_pos_one = calc_mean_pos(at,atomic_index_one) - - !calc atomic positions and shifts relative to mean pos for monomer one, and also distances wrt atom 1 - do i_atomic=1,monomer_one_size - diffs_one(:,i_atomic) = diff_min_image(at,at%pos(:,atomic_index_one(i_atomic)),mean_pos_one) - interatomic_vectors(1,i_atomic,:) = diff_min_image(at,atomic_index_one(1),atomic_index_one(i_atomic)) - end do - - !find other relative positions through vector addition - do j_atomic=2,monomer_one_size - do i_atomic=2,j_atomic-1 - interatomic_vectors(i_atomic,j_atomic,:) = interatomic_vectors(1,j_atomic,:) -interatomic_vectors(1,i_atomic,:) - end do - end do - - !Now convert vectors to scalar distances - do i_atomic=1,monomer_one_size - do j_atomic=i_atomic+1,monomer_one_size - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - ! Loop through monomers paired with this one to make dimers - loop_monomer_pairs: do - unit_array = maxloc(monomer_triplets(2,:), monomer_triplets(1,:) .eq. i) ! find a monomer paired with i - if (all(unit_array .eq. 0)) exit - - !get indices of monomer two - j = monomer_triplets(2,unit_array(1)) - atomic_index_two = monomer_two_index(:,j) - atomic_index_dimer=(/atomic_index_one,atomic_index_two/) - - mean_pos_two = calc_mean_pos(at,atomic_index_two) - - ! calc distances and shifts wrt to mean pos this monomer, and distances wrt its first atom - do i_atomic=1,monomer_two_size - diffs_two(:,i_atomic) = diff_min_image(at,at%pos(:,atomic_index_two(i_atomic)),mean_pos_two) - interatomic_vectors(monomer_one_size+1,monomer_one_size+i_atomic,:) = diff_min_image(at,atomic_index_two(1),atomic_index_two(i_atomic)) - end do - - !find other relative positions through vector addition - do j_atomic=monomer_one_size+2,dimer_size - do i_atomic=monomer_one_size+2,j_atomic-1 - interatomic_vectors(i_atomic,j_atomic,:) = interatomic_vectors(monomer_one_size+1,j_atomic,:) - interatomic_vectors(monomer_one_size+1,i_atomic,:) - end do - end do - - !And convert vectors to scalar distances - do i_atomic=monomer_one_size+1,dimer_size - do j_atomic=i_atomic+1,dimer_size - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - !! Now make trimers based on this dimer - loop_triplets: do - unit_array = maxloc(monomer_triplets(3,:), monomer_triplets(1,:) .eq. i .and. monomer_triplets(2,:) .eq. j ) ! look for a triplet - if (all(unit_array .eq. 0)) exit - triplet_pos=unit_array(1) - - !get indices of monomer three - k = monomer_triplets(3,triplet_pos) - monomer_triplets(:,triplet_pos) = 0 ! make sure this triplet isn't found again - atomic_index_three = monomer_three_index(:,k) - atomic_index_trimer=(/atomic_index_dimer,atomic_index_three/) - mean_pos_three = calc_mean_pos(at,atomic_index_three) - ! calc distances and shifts wrt to mean pos this monomer, and distances wrt its first atom - do i_atomic=1,monomer_three_size - diffs_three(:,i_atomic) = diff_min_image(at,at%pos(:,atomic_index_three(i_atomic)),mean_pos_three) - interatomic_vectors(dimer_size+1,dimer_size+i_atomic,:) = diff_min_image(at,atomic_index_three(1),atomic_index_three(i_atomic)) - end do - - !find other relative positions through vector addition - do j_atomic=dimer_size+2,trimer_size - do i_atomic=dimer_size+2,j_atomic-1 - interatomic_vectors(i_atomic,j_atomic,:) = interatomic_vectors(dimer_size+1,j_atomic,:) -interatomic_vectors(dimer_size+1,i_atomic,:) - end do - end do - - !Now convert vectors to scalar distances - do i_atomic=dimer_size+1,trimer_size - do j_atomic=i_atomic+1,trimer_size - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - ! Loop over all trimers which can be created from these three monomers, i.e. with different periodic images - loop_trimers: do - unit_array = maxloc(triplets_diffs_map, triplets_diffs_map .eq. triplet_pos) - this_diff = unit_array(1) - if (this_diff == 0) exit - - i_desc = i_desc + 1 - - diff_one_two = triplets_diffs(1:3,this_diff) - diff_one_three = triplets_diffs(4:6,this_diff) - diff_two_three = diff_one_three - diff_one_two - triplets_diffs_map(this_diff)=0 ! make sure this shifted triplet isn't found again - dist_one_two = norm(diff_one_two) - dist_one_three = norm(diff_one_three) - dist_two_three = norm(diff_two_three) - - ! calculate intermolecular distances, monomers one and two - do i_atomic=1,monomer_one_size - do j_atomic = monomer_one_size+1,dimer_size - interatomic_vectors(i_atomic,j_atomic,:) = diffs_one(:,i_atomic) + diff_one_two - diffs_two(:,j_atomic-monomer_one_size) - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - ! calculate intermolecular distances, monomers one and three - do i_atomic=1,monomer_one_size - do j_atomic = dimer_size+1,trimer_size - interatomic_vectors(i_atomic,j_atomic,:) = diffs_one(:,i_atomic) + diff_one_three - diffs_three(:,j_atomic-dimer_size) - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - ! calculate intermolecular distances, monomers two and three - do i_atomic=monomer_one_size+1,dimer_size - do j_atomic = dimer_size+1,trimer_size - interatomic_vectors(i_atomic,j_atomic,:) = diffs_two(:,i_atomic-monomer_one_size) + diff_two_three - diffs_three(:,j_atomic-dimer_size) - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - !Now take the whole matrix of scalar distances and combine into 1D array - start = 1 - finish=trimer_size-1 - do i_atomic=1,trimer_size-1 - dist_vec(start:finish) = interatomic_distances(i_atomic,i_atomic+1:trimer_size) - start = finish+1 - finish=finish + trimer_size-i_atomic-1 - end do - call print( "list of distances: "//dist_vec,PRINT_NERD) - - calc_descriptor: if(my_do_descriptor) then - descriptor_out%x(i_desc)%has_data = .true. - descriptor_out%x(i_desc)%data = dist_vec**this%power - descriptor_out%x(i_desc)%ci(:) = atomic_index_trimer - - cutoff_type: if (this%mpifind) then - cut12 = coordination_function(dist_one_two, this%cutoff, this%cutoff_transition_width) - cut13 = coordination_function(dist_one_three, this%cutoff, this%cutoff_transition_width) - cut23 = coordination_function(dist_two_three, this%cutoff, this%cutoff_transition_width) - descriptor_out%x(i_desc)%covariance_cutoff = (cut12*cut13 + cut12*cut23 + cut13*cut23)/3.0_dp - else - descriptor_out%x(i_desc)%covariance_cutoff = 0.0_dp - - primitive_cutoffs=1.0_dp - - do k=1,d - if (this%cutoff_contributor(k)) then - primitive_cutoffs(k) = coordination_function(dist_vec(k),this%cutoff,this%cutoff_transition_width) - end if - end do - n_products=0 - do k=1,d - if (this%cutoff_contributor(k)) then - do m=k+1,d - if (this%cutoff_contributor(m)) then - n_products=n_products+1 - descriptor_out%x(i_desc)%covariance_cutoff = descriptor_out%x(i_desc)%covariance_cutoff + primitive_cutoffs(k)*primitive_cutoffs(m) - end if - end do - end if - end do - ! normalise - descriptor_out%x(i_desc)%covariance_cutoff = descriptor_out%x(i_desc)%covariance_cutoff / n_products - end if cutoff_type - end if calc_descriptor - - calc_grad_descriptor: if(my_do_grad_descriptor) then !calc grads and update - - descriptor_out%x(i_desc)%ii(:) = atomic_index_trimer - do i_atomic=1,monomer_one_size - descriptor_out%x(i_desc)%pos(:,i_atomic) = mean_pos_one - diffs_one(:,i_atomic) - end do - do i_atomic=1,monomer_two_size - descriptor_out%x(i_desc)%pos(:,monomer_one_size+i_atomic) = mean_pos_one + diff_one_two - diffs_two(:,i_atomic) - end do - do i_atomic=1,monomer_three_size - descriptor_out%x(i_desc)%pos(:,dimer_size+i_atomic) = mean_pos_one + diff_one_three - diffs_three(:,i_atomic) - end do - - call print("TRIM 9",PRINT_NERD) - call print('TRIM Lattice="10.0000000 0.00000000 0.00000000 0.00000000 10.0000000 0.00000000 0.00000000 0.00000000 10.00000" Properties=Z:I:1:pos:R:3',PRINT_NERD) - do i_atomic=1,trimer_size - call print("TRIM "//at%Z(atomic_index_trimer(i_atomic))//" "//descriptor_out%x(i_desc)%pos(:,i_atomic),PRINT_NERD) - end do - - !build the grad_data matrix - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - do k=1,d - i_atomic = this%component_atoms(k,1) - j_atomic = this%component_atoms(k,2) - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = - this%power * dist_vec(k)**(this%power-1.0_dp) * interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic) ! descriptor wrt atom i_atomic - descriptor_out%x(i_desc)%grad_data(k,:,j_atomic) = -descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) ! descriptor wrt j_atomic - end do - - cutoff_type_grad: if (this%mpifind) then - cut12 = coordination_function(dist_one_two, this%cutoff, this%cutoff_transition_width) - cut13 = coordination_function(dist_one_three, this%cutoff, this%cutoff_transition_width) - cut23 = coordination_function(dist_two_three, this%cutoff, this%cutoff_transition_width) - dcut12 = dcoordination_function(dist_one_two, this%cutoff, this%cutoff_transition_width) - dcut13 = dcoordination_function(dist_one_three, this%cutoff, this%cutoff_transition_width) - dcut23 = dcoordination_function(dist_two_three, this%cutoff, this%cutoff_transition_width) - !descriptor_out%x(i_desc)%covariance_cutoff = (cut12*cut13 + cut12*cut23 + cut13*cut23)/3.0_dp - grad_cut_one = (- diff_one_two / dist_one_two * dcut12 * (cut13+cut23) & - - diff_one_three / dist_one_three * dcut13 * (cut12+cut23)) / 3.0_dp - grad_cut_two = ( diff_one_two / dist_one_two * dcut12 * (cut13+cut23) & - - diff_two_three / dist_two_three * dcut23 * (cut12+cut13)) / 3.0_dp - grad_cut_three = (diff_one_three / dist_one_three * dcut13 * (cut12+cut23) & - + diff_two_three / dist_two_three * dcut23 * (cut12+cut13)) / 3.0_dp - do i_atomic=1,monomer_one_size - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) = grad_cut_one / real(monomer_one_size,dp) - end do - do i_atomic=1,monomer_two_size - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,monomer_one_size+i_atomic) = grad_cut_two / real(monomer_two_size,dp) - end do - do i_atomic=1,monomer_three_size - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,dimer_size+i_atomic) = grad_cut_three / real(monomer_three_size,dp) - end do - else - primitive_cutoff_grads=0.0_dp - do k=1,d - if (this%cutoff_contributor(k)) then - primitive_cutoff_grads(k) = dcoordination_function(dist_vec(k),this%cutoff,this%cutoff_transition_width) - end if - end do - - do i_atomic=1,trimer_size - do k=1,d - if (this%cutoff_contributor(k) ) then - do m=k+1,d - if (this%cutoff_contributor(m)) then - if (any(this%component_atoms(k,:) == i_atomic) .or. any(this%component_atoms(m,:) == i_atomic)) then - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) = descriptor_out%x(i_desc)%grad_covariance_cutoff(:,i_atomic) & - + primitive_cutoffs(m)*primitive_cutoff_grads(k)*descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) & - + primitive_cutoffs(k)*primitive_cutoff_grads(m)*descriptor_out%x(i_desc)%grad_data(m,:,i_atomic) - end if - end if - end do - end if - end do - end do - - !normalisation factor - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,:) = descriptor_out%x(i_desc)%grad_covariance_cutoff(:,:) / n_products - end if cutoff_type_grad - - end if calc_grad_descriptor - - end do loop_trimers - end do loop_triplets - end do loop_monomer_pairs - end do loop_monomer_one - end if has_descriptors ! (n_descriptors > 0) ... still need to deallocate if no trimers were found: - - deallocate(monomer_one_index) - deallocate(monomer_two_index) - deallocate(monomer_three_index) - if (allocated(monomer_triplets)) deallocate(monomer_triplets) - if (allocated(triplets_diffs)) deallocate(triplets_diffs) - if (allocated(triplets_diffs_map)) deallocate(triplets_diffs_map) - - deallocate(dist_vec) - deallocate(primitive_cutoffs) - deallocate(primitive_cutoff_grads) - - deallocate(atomic_index_dimer) - deallocate(atomic_index_trimer) - deallocate(atomic_index_one) - deallocate(atomic_index_two) - deallocate(atomic_index_three) - deallocate(diffs_one) - deallocate(diffs_two) - deallocate(diffs_three) - - deallocate(interatomic_vectors) - deallocate(interatomic_distances) - deallocate(associated_to_monomer) - - - call system_timer('general_trimer_calc') - - endsubroutine general_trimer_calc - - subroutine rdf_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(rdf), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, i, j, n, i_n, l_n_neighbours, i_desc, n_descriptors, n_cross, n_index - integer, dimension(3) :: shift - real(dp) :: r_ij, f_cut, df_cut - real(dp), dimension(3) :: u_ij - real(dp), dimension(:), allocatable :: rdf_ij - - INIT_ERROR(error) - - call system_timer('rdf_calc') - - if(.not. this%initialised) then - RAISE_ERROR("rdf_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='rdf_calc args_str')) then - RAISE_ERROR("rdf_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("rdf_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - call finalise(descriptor_out) - - d = rdf_dimensions(this,error) - allocate(rdf_ij(d)) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - - i_desc = i_desc + 1 - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - i_desc = 0 - do i = 1, at%N - - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - i_n = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, shift=shift) - - if( r_ij >= this%cutoff ) cycle - i_n = i_n + 1 - - rdf_ij = exp( -0.5_dp * (r_ij - this%r_gauss)**2 / this%w_gauss**2 ) - f_cut = coordination_function(r_ij,this%cutoff,this%transition_width) - - if(my_do_descriptor) & - descriptor_out%x(i_desc)%data = descriptor_out%x(i_desc)%data + rdf_ij * f_cut - - if(my_do_grad_descriptor) then - df_cut = dcoordination_function(r_ij,this%cutoff,this%transition_width) - - descriptor_out%x(i_desc)%ii(i_n) = j - descriptor_out%x(i_desc)%pos(:,i_n) = at%pos(:,j) + matmul(at%lattice,shift) - descriptor_out%x(i_desc)%has_grad_data(i_n) = .true. - - descriptor_out%x(i_desc)%grad_data(:,:,i_n) = ( - ( rdf_ij * (r_ij - this%r_gauss) / this%w_gauss**2 ) * f_cut + rdf_ij * df_cut ) .outer. u_ij - descriptor_out%x(i_desc)%grad_data(:,:,0) = descriptor_out%x(i_desc)%grad_data(:,:,0) - descriptor_out%x(i_desc)%grad_data(:,:,i_n) - endif - enddo - enddo - - if(allocated(rdf_ij)) deallocate(rdf_ij) - - call system_timer('rdf_calc') - - endsubroutine rdf_calc - - subroutine as_distance_2b_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(as_distance_2b), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(descriptor) :: my_coordination - type(descriptor_data) :: descriptor_coordination - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor, Zi1, Zi2, Zj1, Zj2 - integer :: d, n_descriptors, n_cross, i_desc, i, j, k, n, m, & - n_neighbours_coordination_i, n_neighbours_coordination_ij, n_index - integer, dimension(3) :: shift - real(dp) :: r_ij, r_ik, r_jk, cos_ijk, cos_jik, f_cut_i, f_cut_j, f_cut_ij, f_cut_ik, f_cut_jk, f_cut_as_i, f_cut_as_j, rho_i, rho_j - real(dp), dimension(3) :: u_ij, u_ik, u_jk - - INIT_ERROR(error) - call system_timer('as_distance_2b_calc') - - if(.not. this%initialised) then - RAISE_ERROR("as_distance_2b_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='as_distance_2b_calc args_str')) then - RAISE_ERROR("as_distance_2b_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("as_distance_2b_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("as_distance_2b_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - d = as_distance_2b_dimensions(this,error) - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - - - allocate(descriptor_out%x(n_descriptors)) - i_desc = 0 - do i = 1, at%N - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance=r_ij, cosines=u_ij) - - if(r_ij > this%max_cutoff .or. r_ij < this%min_cutoff) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - rho_i = 0.0_dp - f_cut_i = 0.0_dp - - do m = 1, n_neighbours(at,i) - k = neighbour(at, i, m, distance=r_ik, cosines=u_ik) - - if(r_ik > this%coordination_cutoff) cycle - - cos_ijk = dot_product(u_ij,u_ik) - f_cut_ik = coordination_function(r_ik,this%coordination_cutoff,this%coordination_transition_width) - - f_cut_i = f_cut_i + f_cut_ik - rho_i = rho_i + 0.5_dp * ( erf(cos_ijk/this%overlap_alpha) + 1.0_dp ) * f_cut_ik**2 - enddo - - rho_i = rho_i / f_cut_i - - if(rho_i > this%as_cutoff) cycle - - rho_j = 0.0_dp - f_cut_j = 0.0_dp - - do m = 1, n_neighbours(at,j) - k = neighbour(at, j, m, distance=r_jk, cosines=u_jk) - - if(r_jk > this%coordination_cutoff) cycle - - cos_jik = dot_product(-u_ij,u_jk) - f_cut_jk = coordination_function(r_jk,this%coordination_cutoff,this%coordination_transition_width) - - f_cut_j = f_cut_j + f_cut_jk - rho_j = rho_j + 0.5_dp * ( erf(cos_jik/this%overlap_alpha) + 1.0_dp ) * f_cut_jk**2 - enddo - - if(rho_j > this%as_cutoff) cycle - ! all three conditions fulfilled: pair within lower and upper cutoff, asymmetricity lower than threshold - - i_desc = i_desc + 1 - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - endif - - if(my_do_grad_descriptor) then - n_neighbours_coordination_ij = n_neighbours(at,i,max_dist=this%coordination_cutoff) + & - n_neighbours(at,j,max_dist=this%coordination_cutoff) + 2 - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:1+n_neighbours_coordination_ij)) - allocate(descriptor_out%x(i_desc)%ii(0:1+n_neighbours_coordination_ij)) - allocate(descriptor_out%x(i_desc)%pos(3,0:1+n_neighbours_coordination_ij)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:1+n_neighbours_coordination_ij)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:1+n_neighbours_coordination_ij)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - enddo - - i_desc = 0 - do i = 1, at%N - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, shift=shift) - - if(r_ij > this%max_cutoff .or. r_ij < this%min_cutoff) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - rho_i = 0.0_dp - f_cut_i = 0.0_dp - - do m = 1, n_neighbours(at,i) - k = neighbour(at, i, m, distance=r_ik, cosines=u_ik) - - if(r_ik > this%coordination_cutoff) cycle - - cos_ijk = dot_product(u_ij,u_ik) - f_cut_ik = coordination_function(r_ik,this%coordination_cutoff,this%coordination_transition_width) - - f_cut_i = f_cut_i + f_cut_ik - rho_i = rho_i + 0.5_dp * ( erf(cos_ijk/this%overlap_alpha) + 1.0_dp ) * f_cut_ik**2 - enddo - - rho_i = rho_i / f_cut_i - - if(rho_i > this%as_cutoff) cycle - - rho_j = 0.0_dp - f_cut_j = 0.0_dp - - do m = 1, n_neighbours(at,j) - k = neighbour(at, j, m, distance=r_jk, cosines=u_jk) - - if(r_jk > this%coordination_cutoff) cycle - - cos_jik = dot_product(-u_ij,u_jk) - f_cut_jk = coordination_function(r_jk,this%coordination_cutoff,this%coordination_transition_width) - - f_cut_j = f_cut_j + f_cut_jk - rho_j = rho_j + 0.5_dp * ( erf(cos_jik/this%overlap_alpha) + 1.0_dp ) * f_cut_jk**2 - enddo - - if(rho_j > this%as_cutoff) cycle - ! all three conditions fulfilled: pair within lower and upper cutoff, asymmetricity lower than threshold - - i_desc = i_desc + 1 - - f_cut_ij = coordination_function(r_ij,this%max_cutoff,this%max_transition_width,this%min_cutoff,this%min_transition_width) - f_cut_as_i = coordination_function(rho_i,this%as_cutoff, this%as_transition_width) - f_cut_as_j = coordination_function(rho_j,this%as_cutoff, this%as_transition_width) - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1:2) = (/i,j/) - - descriptor_out%x(i_desc)%has_data = .true. - - descriptor_out%x(i_desc)%data(1) = r_ij - descriptor_out%x(i_desc)%data(2) = f_cut_i + f_cut_j - descriptor_out%x(i_desc)%data(3) = (f_cut_i - f_cut_j)**2 - - descriptor_out%x(i_desc)%covariance_cutoff = f_cut_ij * f_cut_as_i * f_cut_as_j - endif - if(my_do_grad_descriptor) then - n_neighbours_coordination_i = n_neighbours(at,i,max_dist=this%coordination_cutoff) - - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - descriptor_out%x(i_desc)%grad_data(1,:,0) = -u_ij(:) - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) = -dcoordination_function(r_ij,this%coordination_cutoff,this%coordination_transition_width)*u_ij - - !descriptor_out%x(i_desc)%ii(1) = j - !descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,j) + matmul(at%lattice,shift) - !descriptor_out%x(i_desc)%has_grad_data(1) = .true. - !descriptor_out%x(i_desc)%grad_data(1,:,1) = u_ij(:) - !descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = -descriptor_out%x(i_desc)%grad_covariance_cutoff(:,0) - - !descriptor_out%x(i_desc)%ii(2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%ii(:) - !descriptor_out%x(i_desc)%pos(:,2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%pos(:,:) - !descriptor_out%x(i_desc)%has_grad_data(2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%has_grad_data(:) - !descriptor_out%x(i_desc)%grad_data(2,:,2:n_neighbours_coordination_i+2) = descriptor_coordination%x(i)%grad_data(1,:,:) - !descriptor_out%x(i_desc)%grad_data(3,:,2:n_neighbours_coordination_i+2) = 2.0_dp*(descriptor_coordination%x(i)%data(1) - descriptor_coordination%x(j)%data(1))*& - ! descriptor_coordination%x(i)%grad_data(1,:,:) - - !descriptor_out%x(i_desc)%ii(n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%ii(:) - !descriptor_out%x(i_desc)%pos(:,n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%pos(:,:) - !descriptor_out%x(i_desc)%has_grad_data(n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%has_grad_data(:) - !descriptor_out%x(i_desc)%grad_data(2,:,n_neighbours_coordination_i+3:) = descriptor_coordination%x(j)%grad_data(1,:,:) - !descriptor_out%x(i_desc)%grad_data(3,:,n_neighbours_coordination_i+3:) = -2.0_dp*(descriptor_coordination%x(i)%data(1) - descriptor_coordination%x(j)%data(1))*& - ! descriptor_coordination%x(j)%grad_data(1,:,:) - - endif - enddo - enddo - - call finalise(my_coordination) - call finalise(descriptor_coordination) - - call system_timer('as_distance_2b_calc') - - endsubroutine as_distance_2b_calc - - subroutine molecule_lo_d_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(molecule_lo_d), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor!, use_smooth_cutoff - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, i, j, i_atomic, j_atomic, k, & - start, finish, molecule_size, i_component, i_desc, n_index - integer, dimension(3) :: temp_shift - real(dp), dimension(:), allocatable :: dist_vec - real(dp), dimension(:,:), allocatable :: interatomic_distances - real(dp), dimension(:,:,:), allocatable :: interatomic_vectors - integer, dimension(:), allocatable :: atomic_index - integer, dimension(:,:), allocatable :: shifts - logical, dimension(:), allocatable :: associated_to_molecule - - - INIT_ERROR(error) - - call system_timer('molecule_lo_d_calc') - - if(.not. this%initialised) then - RAISE_ERROR("molecule_lo_d_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='molecule_lo_d_calc args_str')) then - RAISE_ERROR("molecule_lo_d_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("molecule_lo_d_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - RAISE_ERROR("molecule_lo_d_calc cannot use atom masks yet.",error) - else - atom_mask_pointer => null() - endif - - endif - - molecule_size=this%n_atoms - d = molecule_lo_d_dimensions(this,error) - - if (this%atom_ordercheck) then - do i=1,molecule_size - if (this%template_atoms%Z(i) /= at%Z(i)) then - call print("atoms in all input frams should be in the same order as this template : ") - call print(this%template_atoms) - RAISE_ERROR("molecule_lo_d_calc: atoms not in same order as in template used to teach", error) - end if - end do - end if - - allocate(shifts(molecule_size,3)) - allocate(dist_vec(this%max_dimension)) - allocate(atomic_index(molecule_size)) - - allocate(associated_to_molecule(at%N)) - associated_to_molecule=.True. - do i=1,molecule_size - atomic_index(i) = i - end do - - - allocate(interatomic_vectors(molecule_size,molecule_size,3)) - allocate(interatomic_distances(molecule_size,molecule_size)) - interatomic_vectors = 0.0_dp - interatomic_distances = 0.0_dp - -! call find_general_monomer(at,monomer_index,this%signature,associated_to_monomer,this%cutoff,this%atom_ordercheck,error) - - if(.not. all(associated_to_molecule)) then - RAISE_ERROR("molecule_lo_d_calc: not all atoms assigned to a monomer", error) - endif - - n_descriptors = 1 ! currently no support for finding multiple nontrivial molecules, otherwise would be size(monomer_index,2) - n_index = this%n_atoms - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%data = 0.0_dp - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,molecule_size)) - allocate(descriptor_out%x(i)%ii(molecule_size)) - allocate(descriptor_out%x(i)%pos(3,molecule_size)) - allocate(descriptor_out%x(i)%has_grad_data(molecule_size)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,molecule_size)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - - do i_desc = 1, n_descriptors - - !atomic_index = monomer_index(:,i) !for fixed ordering don't need this - ! for now calculate all O(N^2) distances and just pick out the ones we want - - !calc all positions relative to atom 1 - do i_atomic=2,molecule_size - temp_shift=0 - interatomic_vectors(1,i_atomic,:) = diff_min_image(at,atomic_index(1),atomic_index(i_atomic),shift=temp_shift) - shifts(i_atomic,:) = temp_shift - end do - - !find other relative positions through vector addition - do j_atomic=2,molecule_size - do i_atomic=2,j_atomic-1 - interatomic_vectors(i_atomic,j_atomic,:) = interatomic_vectors(1,j_atomic,:) -interatomic_vectors(1,i_atomic,:) - end do - end do - - !Now convert vectors to scalar distances - do i_atomic=1,molecule_size - do j_atomic=i_atomic+1,molecule_size - interatomic_distances(i_atomic,j_atomic) = norm(interatomic_vectors(i_atomic,j_atomic,:)) - end do - end do - - !and convert this NxN matrix into the required vector length N(N-1)/2 - start = 1 - finish = molecule_size-1 - do i_atomic=1,molecule_size-1 - dist_vec(start:finish) = interatomic_distances(i_atomic,i_atomic+1:molecule_size) - start = finish+1 - finish=finish + molecule_size-i_atomic-1 - end do - - - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(:) = atomic_index - descriptor_out%x(i_desc)%has_data = .true. - if(this%desctype == 0) then - descriptor_out%x(i_desc)%data = dist_vec(this%included_components) ! here's where we pick out the components - else if(this%desctype == 1) then - descriptor_out%x(i_desc)%data = 1.0_dp / dist_vec(this%included_components) - else if(this%desctype == 2) then - do k=1,d - i_component = this%included_components(k) - i_atomic = this%component_atoms(i_component,1) - j_atomic = this%component_atoms(i_component,2) - descriptor_out%x(i_desc)%data(k) = real(at%Z(i_atomic)*at%Z(j_atomic)) / dist_vec(i_component) - end do - else if(this%desctype == 3) then - descriptor_out%x(i_desc)%data = exp(-dist_vec(this%included_components)) - else if(this%desctype < 0) then - descriptor_out%x(i_desc)%data = dist_vec(this%included_components)**this%desctype - else - RAISE_ERROR("molecule_lo_d_calc: not implemented descriptor type",error) - end if - endif -!call print(descriptor_out%x(i_desc)%data) -!!$ -!!$ do i_component=1,d -!!$ write(*,*) 'component : ',i_component -!!$ write(*,*) 'value : ',descriptor_out%x(i_desc)%data(i_component) -!!$ write(*,*) 'atoms : ',this%component_atoms(this%included_components(i_component),:) -!!$ write(*,*) 'dist_mat_entry : ',interatomic_distances(this%component_atoms(this%included_components(i_component),1),this%component_atoms(this%included_components(i_component),2)) -!!$ end do - - if(my_do_grad_descriptor) then - - descriptor_out%x(i_desc)%ii(:) = atomic_index - descriptor_out%x(i_desc)%pos(:,1) = at%pos(:,atomic_index(1)) - do i_atomic =2,molecule_size - descriptor_out%x(i_desc)%pos(:,i_atomic) = at%pos(:,atomic_index(i_atomic)) + matmul(at%lattice,shifts(i_atomic,:)) - end do - - !build the grad_data matrix - descriptor_out%x(i_desc)%has_grad_data(:) = .true. - - - do k=1,d - !get pair of atoms contributing to this component - i_component = this%included_components(k) - i_atomic = this%component_atoms(i_component,1) - j_atomic = this%component_atoms(i_component,2) - - if(this%desctype == 0) then - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = -interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic) ! descriptor wrt atom i_atomic - else if(this%desctype == 1) then - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic)**3 ! descriptor wrt atom i_atomic - else if(this%desctype == 2) then - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = real(at%Z(i_atomic)*at%Z(j_atomic)) * interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic)**3 ! descriptor wrt atom i_atomic - else if(this%desctype == 3) then - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = interatomic_vectors(i_atomic,j_atomic,:) / interatomic_distances(i_atomic,j_atomic) * exp(-interatomic_distances(i_atomic,j_atomic)) - else if(this%desctype < 0) then - descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) = -real(this%desctype) * interatomic_vectors(i_atomic,j_atomic,:) * interatomic_distances(i_atomic,j_atomic)**(this%desctype-2) - else - RAISE_ERROR("molecule_lo_d_calc: not implemented descriptortype",error) - end if - descriptor_out%x(i_desc)%grad_data(k,:,j_atomic) = -descriptor_out%x(i_desc)%grad_data(k,:,i_atomic) ! descriptor wrt j_atomic -! call print(descriptor_out%x(i_desc)%grad_data(k,:,:)) - end do - - endif - - enddo - deallocate(shifts) - deallocate(dist_vec) - deallocate(atomic_index) - deallocate(interatomic_vectors) - deallocate(interatomic_distances) - - call system_timer('molecule_lo_d_calc') - - endsubroutine molecule_lo_d_calc - - subroutine alex_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(alex), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - - integer :: i, j, n, d, p, q, r, a, b, c, n_i, n_radial, pp, i_desc, & - l_n_neighbours, desc_index, n_cross, n_descriptors, n_index - integer, dimension(3) :: shift_ij - real(dp) :: r_ij - real(dp), dimension(3) :: d_ij - real(dp), dimension(:), allocatable :: neighbour_dists - real(dp), dimension(:,:), allocatable :: neighbour_vecs - integer, dimension(total_elements) :: species_map - real(dp), allocatable :: S0(:), S1(:,:), S2(:,:,:), S0der(:,:,:), S1der(:,:,:,:), S2der(:,:,:,:,:) - - INIT_ERROR(error) - - call system_timer('alex_calc') - - if(.not. this%initialised) then - RAISE_ERROR("alex_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='alex_calc args_str')) then - RAISE_ERROR("alex_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("alex_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - species_map = 0 - do i = 1, size(this%species_Z) - if(this%species_Z(i) == 0) then - species_map = 1 - else - species_map(this%species_Z(i)) = i - endif - enddo - - call finalise(descriptor_out) - - d = alex_dimensions(this,error) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - - n_radial = this%power_max-this%power_min+1 - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - - i_desc = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - ! number of neighbours for the current atom within the descriptor cutoff - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - allocate(neighbour_vecs(3,l_n_neighbours), neighbour_dists(l_n_neighbours)) - allocate(S0(n_radial), S1(3,n_radial), S2(3,3,n_radial)) - if(my_do_grad_descriptor) then - allocate( & - S0der(n_radial,l_n_neighbours,3), & - S1der(3,n_radial,l_n_neighbours,3), & - S2der(3,3,n_radial,l_n_neighbours,3) ) - endif - - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, diff=d_ij) - if( r_ij >= this%cutoff ) cycle - n_i = n_i + 1 - neighbour_vecs(:,n_i) = d_ij - neighbour_dists(n_i) = r_ij - end do - - do p = 1,n_radial - pp = -(p+this%power_min-1) - S0(p) = sum(neighbour_dists**pp) - if(my_do_grad_descriptor) then - do n_i = 1, l_n_neighbours - S0der(p,n_i,:) = pp * neighbour_dists(n_i)**(pp-2) * neighbour_vecs(:,n_i) - enddo - endif - - S1(:,p) = matmul(neighbour_vecs, neighbour_dists**pp) - !do a = 1,3 - !S1(a, p) = sum(neighbour_vecs(a,:)*neighbour_dists**pp) - !end do - if(my_do_grad_descriptor) then - do n_i = 1, l_n_neighbours - do a = 1,3 - S1der(a,p,n_i,:) = pp * neighbour_dists(n_i)**(pp-2) * neighbour_vecs(a,n_i) * neighbour_vecs(:,n_i) - S1der(a,p,n_i,a) = S1der(a,p,n_i,a) + neighbour_dists(n_i)**pp - end do - enddo - endif - - !do a=1,3 - do b=1,3 - S2(:,b,p) = matmul(neighbour_vecs, neighbour_vecs(b,:)*neighbour_dists**pp) - !S2(a,b,p) = sum(neighbour_vecs(a,:)*neighbour_vecs(b,:)*neighbour_dists**pp) - end do - !end do - - if(my_do_grad_descriptor) then - do n_i = 1, l_n_neighbours - do a = 1,3 - do b = 1,3 - S2der(a,b,p,n_i,:) = pp * neighbour_dists(n_i)**(pp-2) * neighbour_vecs(a,n_i) * neighbour_vecs(b,n_i) * neighbour_vecs(:,n_i) - end do - end do - - do a = 1,3 - do b = 1,3 - S2der(a,b,p,n_i,b) = S2der(a,b,p,n_i,b) + neighbour_dists(n_i)**pp * neighbour_vecs(a,n_i) - S2der(a,b,p,n_i,a) = S2der(a,b,p,n_i,a) + neighbour_dists(n_i)**pp * neighbour_vecs(b,n_i) - end do - end do - enddo - endif - end do - - descriptor_out%x(i_desc)%data(1:n_radial) = S0 - descriptor_out%x(i_desc)%data(n_radial+1:n_radial+n_radial**2) = reshape(matmul(transpose(S1), S1), (/n_radial**2/)) - desc_index = n_radial+n_radial**2+1 - do p = 1,n_radial - do q = 1,n_radial - descriptor_out%x(i_desc)%data(desc_index) = sum(S2(:,:,p) * S2(:,:,q)) - desc_index = desc_index + 1 - end do - end do - - do p = 1,n_radial - do q = 1,n_radial - do r = 1,n_radial - descriptor_out%x(i_desc)%data(desc_index) = dot_product(S1(:,p), matmul(S2(:,:,q), S1(:,r))) - desc_index = desc_index + 1 - end do - end do - end do - - if(my_do_grad_descriptor) then - n_i = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, shift=shift_ij) - if( r_ij >= this%cutoff ) cycle - - n_i = n_i + 1 - - descriptor_out%x(i_desc)%ii(n_i) = j - descriptor_out%x(i_desc)%pos(:,n_i) = at%pos(:,j) + matmul(at%lattice,shift_ij) - descriptor_out%x(i_desc)%has_grad_data(n_i) = .true. - - descriptor_out%x(i_desc)%grad_data(1:n_radial,:,n_i) = S0der(:,n_i,:) - - desc_index = n_radial + 1 - do p = 1,n_radial - do q = 1,n_radial - do a = 1, 3 - do c = 1, 3 - descriptor_out%x(i_desc)%grad_data(desc_index,c,n_i) = descriptor_out%x(i_desc)%grad_data(desc_index,c,n_i) + & - S1der(a,p,n_i,c)*S1(a,q) + S1(a,p)*S1der(a,q,n_i,c) - enddo - enddo - desc_index = desc_index + 1 - enddo - enddo - - do p = 1, n_radial - do q = 1, n_radial - do a = 1, 3 - do b = 1, 3 - do c = 1, 3 - descriptor_out%x(i_desc)%grad_data(desc_index,c,n_i) = descriptor_out%x(i_desc)%grad_data(desc_index,c,n_i) + & - S2der(a,b,p,n_i,c)*S2(a,b,q) + S2(a,b,p)*S2der(a,b,q,n_i,c) - enddo - enddo - enddo - desc_index = desc_index + 1 - enddo - enddo - - do p = 1, n_radial - do q = 1, n_radial - do r = 1, n_radial - do a = 1, 3 - do b = 1, 3 - do c = 1, 3 - descriptor_out%x(i_desc)%grad_data(desc_index,c,n_i) = descriptor_out%x(i_desc)%grad_data(desc_index,c,n_i) + & - S1der(a,p,n_i,c) * S2(a,b,q) * S1(b,r) + & - S1(a,p) * S2der(a,b,q,n_i,c) * S1(b,r) + & - S1(a,p) * S2(a,b,q) * S1der(b,r,n_i,c) - enddo - enddo - enddo - desc_index = desc_index + 1 - enddo - enddo - enddo - enddo - - descriptor_out%x(i_desc)%grad_data(:,:,0) = descriptor_out%x(i_desc)%grad_data(:,:,0) - descriptor_out%x(i_desc)%grad_data(:,:,n_i) - deallocate(S0der, S1der, S2der) - endif - - deallocate(neighbour_vecs, neighbour_dists, S0, S1, S2) - enddo - - - call system_timer('alex_calc') - - endsubroutine alex_calc - - subroutine distance_Nb_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(distance_Nb), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: d, n_descriptors, n_cross, i_desc, i_data, i, j, ii, jj, kk, ll, & - iConnectivity, n_index - integer, dimension(3) :: s_i, s_j - real(dp) :: r_ij, fcut_connectivity - real(dp), dimension(3) :: dfcut_connectivity - real(dp), dimension(3) :: d_ij - integer, dimension(:,:,:), allocatable :: atoms_in_descriptors - real(dp), dimension(:,:), allocatable :: fcut_pair, dfcut_pair - real(dp), dimension(:,:,:), allocatable :: directions - - logical, dimension(:), pointer :: atom_mask_pointer => null() - - INIT_ERROR(error) - - call system_timer('distance_Nb_calc') - - if(.not. this%initialised) then - RAISE_ERROR("distance_Nb_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - call finalise(descriptor_out) - - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='distance_Nb_calc args_str')) then - RAISE_ERROR("distance_Nb_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - atom_mask_pointer => null() - - if( has_atom_mask_name ) then - if( .not. this%compact_clusters ) then - RAISE_ERROR("distance_Nb_calc: MPI ready only for compact_clusters=T type of distance_Nb.", error) - endif - - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("distance_Nb_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - d = distance_Nb_dimensions(this,error) - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask = atom_mask_pointer,n_index=n_index, error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - do i = 1, n_descriptors - if(my_do_descriptor) then - allocate(descriptor_out%x(i)%data(d)) - descriptor_out%x(i)%data = 0.0_dp - allocate(descriptor_out%x(i)%ci(n_index)) - descriptor_out%x(i)%ci = 0 - descriptor_out%x(i)%has_data = .false. - descriptor_out%x(i)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - allocate(descriptor_out%x(i)%grad_data(d,3,this%order)) - allocate(descriptor_out%x(i)%ii(this%order)) - allocate(descriptor_out%x(i)%pos(3,this%order)) - allocate(descriptor_out%x(i)%has_grad_data(this%order)) - descriptor_out%x(i)%grad_data = 0.0_dp - descriptor_out%x(i)%ii = 0 - descriptor_out%x(i)%pos = 0.0_dp - descriptor_out%x(i)%has_grad_data = .false. - - allocate(descriptor_out%x(i)%grad_covariance_cutoff(3,this%order)) - descriptor_out%x(i)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - if(associated(atom_mask_pointer)) then - call distance_Nb_calc_get_clusters(this,at,atoms_in_descriptors=atoms_in_descriptors,mask=atom_mask_pointer,error=error) - else - call distance_Nb_calc_get_clusters(this,at,atoms_in_descriptors=atoms_in_descriptors,error=error) - endif - - allocate(fcut_pair(this%order,this%order)) - if( my_do_grad_descriptor ) then - allocate(dfcut_pair(this%order,this%order), directions(3,this%order,this%order)) - endif - - do i_desc = 1, n_descriptors - if( this%order == 1 ) then - descriptor_out%x(i_desc)%data = 0.0_dp - if( my_do_grad_descriptor ) descriptor_out%x(i_desc)%grad_data = 0.0_dp - else - i_data = 0 - do ii = 1, this%order - i = atoms_in_descriptors(1,ii,i_desc) - s_i = atoms_in_descriptors(2:4,ii,i_desc) - do jj = ii+1, this%order - i_data = i_data + 1 - j = atoms_in_descriptors(1,jj,i_desc) - s_j = atoms_in_descriptors(2:4,jj,i_desc) - d_ij = at%pos(:,j) - at%pos(:,i) + matmul(at%lattice,s_j-s_i) - r_ij = sqrt(sum(d_ij**2)) - - fcut_pair(jj,ii) = coordination_function(r_ij,this%cutoff,this%cutoff_transition_width) - fcut_pair(ii,jj) = fcut_pair(jj,ii) - - descriptor_out%x(i_desc)%data(i_data) = r_ij - if( my_do_grad_descriptor ) then - dfcut_pair(ii,jj) = dcoordination_function(r_ij,this%cutoff,this%cutoff_transition_width) - dfcut_pair(jj,ii) = dfcut_pair(ii,jj) - - directions(:,ii,jj) = d_ij / r_ij - directions(:,jj,ii) = - directions(:,ii,jj) - descriptor_out%x(i_desc)%grad_data(i_data,:,jj) = directions(:,ii,jj) - descriptor_out%x(i_desc)%grad_data(i_data,:,ii) = & - - descriptor_out%x(i_desc)%grad_data(i_data,:,jj) - endif - enddo - enddo - - descriptor_out%x(i_desc)%covariance_cutoff = 0.0_dp - if ( this%compact_clusters ) then - - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - - do jj = 2, this%order - descriptor_out%x(i_desc)%covariance_cutoff = descriptor_out%x(i_desc)%covariance_cutoff * fcut_pair(jj,1) - enddo - - if( my_do_grad_descriptor ) then - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = 0.0_dp - do kk = 2, this%order - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) = 1.0_dp - do jj = 2, this%order - if( jj == kk ) then - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) = & - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) * dfcut_pair(jj,1) * (-directions(:,jj,1)) - else - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) = & - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) * fcut_pair(jj,1) - endif - enddo - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) = & - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,1) - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) - enddo - endif - - - else - do iConnectivity = 1, size(this%monomerConnectivities,3) - - fcut_connectivity = 1.0_dp - - do ii = 1, this%order - do jj = ii+1, this%order - if( this%monomerConnectivities(jj,ii,iConnectivity) ) then - fcut_connectivity = fcut_connectivity * fcut_pair(jj,ii) - else - fcut_connectivity = fcut_connectivity * ( 1.0_dp - fcut_pair(jj,ii) ) - endif - enddo - enddo - descriptor_out%x(i_desc)%covariance_cutoff = descriptor_out%x(i_desc)%covariance_cutoff + fcut_connectivity - - if( my_do_grad_descriptor ) then - do kk = 1, this%order - do ll = kk+1, this%order - dfcut_connectivity = 1.0_dp - do ii = 1, this%order - do jj = ii+1, this%order - if( this%monomerConnectivities(jj,ii,iConnectivity) ) then - if( kk == ii .and. ll == jj ) then - dfcut_connectivity = dfcut_connectivity * dfcut_pair(jj,ii) * directions(:,ll,kk) - elseif( kk == jj .and. ll == ii ) then - dfcut_connectivity = dfcut_connectivity * dfcut_pair(jj,ii) * directions(:,ll,kk) - else - dfcut_connectivity = dfcut_connectivity * fcut_pair(jj,ii) - endif - else - if( kk == ii .and. ll == jj ) then - dfcut_connectivity = - dfcut_connectivity * dfcut_pair(jj,ii) * directions(:,ll,kk) - elseif( kk == jj .and. ll == ii) then - dfcut_connectivity = - dfcut_connectivity * dfcut_pair(jj,ii) * directions(:,ll,kk) - else - dfcut_connectivity = dfcut_connectivity * ( 1.0_dp - fcut_pair(jj,ii) ) - endif - endif - enddo !jj - enddo !ii - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) = descriptor_out%x(i_desc)%grad_covariance_cutoff(:,kk) + & - dfcut_connectivity - descriptor_out%x(i_desc)%grad_covariance_cutoff(:,ll) = descriptor_out%x(i_desc)%grad_covariance_cutoff(:,ll) - & - dfcut_connectivity - enddo !ll - enddo !kk - endif - - enddo - endif - - endif - - descriptor_out%x(i_desc)%ci = atoms_in_descriptors(1,:,i_desc) - descriptor_out%x(i_desc)%has_data = .true. - if( my_do_grad_descriptor ) then - descriptor_out%x(i_desc)%ii = descriptor_out%x(i_desc)%ci - descriptor_out%x(i_desc)%pos = at%pos(:,descriptor_out%x(i_desc)%ii) + & - matmul(at%lattice,atoms_in_descriptors(2:4,:,i_desc)) - descriptor_out%x(i_desc)%has_grad_data = .true. - endif - - enddo - - if(allocated(atoms_in_descriptors)) deallocate(atoms_in_descriptors) - if(allocated(fcut_pair)) deallocate(fcut_pair) - if(allocated(dfcut_pair)) deallocate(dfcut_pair) - if(allocated(directions)) deallocate(directions) - - call system_timer('distance_Nb_calc') - - endsubroutine distance_Nb_calc - - subroutine distance_Nb_calc_get_clusters(this,at,atoms_in_descriptors,n_descriptors,mask,error) - - type(distance_Nb), intent(in) :: this - type(atoms), intent(in) :: at - integer, dimension(:,:,:), intent(out), allocatable, optional :: atoms_in_descriptors - integer, intent(out), optional :: n_descriptors - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: error - - integer, dimension(:,:,:), allocatable :: my_atoms_in_descriptors - - if( present(atoms_in_descriptors) ) then - call distance_Nb_calc_neighbour_loop(this,at,atoms_in_descriptors,n_descriptors=n_descriptors,mask=mask,error=error) - else - call distance_Nb_calc_neighbour_loop(this,at,my_atoms_in_descriptors,n_descriptors=n_descriptors,mask=mask,error=error) - if(allocated(my_atoms_in_descriptors)) deallocate(my_atoms_in_descriptors) - endif - - endsubroutine distance_Nb_calc_get_clusters - -! recursive subroutine distance_Nb_calc_neighbour_loop(this,at,atoms_in_descriptors,n_descriptors,error) -! -! type(distance_Nb), intent(in) :: this -! type(atoms), intent(in) :: at -! integer, dimension(:,:,:), intent(inout), allocatable :: atoms_in_descriptors -! integer, intent(out), optional :: n_descriptors -! integer, intent(out), optional :: error -! -! integer, save :: current_order = 0 -! integer :: i, j, n, order, i_desc, d -! real(dp) :: r_ij -! integer, dimension(3) :: shift_i, shift_j, shift_ij -! integer, dimension(:,:), allocatable :: current_descriptor -! -! type(LinkedList_i2d), pointer :: LL_atoms_in_descriptors => null() -! -! INIT_ERROR(error) -! -! current_order = current_order + 1 -! -! if( current_order == 1 ) then -! allocate(current_descriptor(4,1)) -! -! do i = 1, at%N -! if( any( at%Z(i) == this%Z ) .or. any( 0 == this%Z ) ) then -! current_descriptor(:,1) = (/i,0,0,0/) -! call append(LL_atoms_in_descriptors,current_descriptor,error) -! endif -! enddo -! -! deallocate(current_descriptor) -! call retrieve(LL_atoms_in_descriptors,atoms_in_descriptors) -! call finalise(LL_atoms_in_descriptors) -! if( this%order > 1 ) & -! call distance_Nb_calc_neighbour_loop(this,at,atoms_in_descriptors = atoms_in_descriptors,n_descriptors=n_descriptors,error=error) -! -! if( present(n_descriptors) ) n_descriptors = size(atoms_in_descriptors,3) -! else -! if( .not. allocated(atoms_in_descriptors) ) then -! RAISE_ERROR("distance_Nb_calc_neighbour_loop: atoms_in_descriptors must be allocated",error) -! endif -! -! allocate(current_descriptor(4,current_order)) -! do i_desc = 1, size(atoms_in_descriptors,3) -! do order = 1, size(atoms_in_descriptors,2) -! i = atoms_in_descriptors(1,order,i_desc) -! shift_i = atoms_in_descriptors(2:4,order,i_desc) -! loop_n: do n = 1, n_neighbours(at,i) -! j = neighbour(at,i,n,distance = r_ij, shift = shift_ij) -! -! if( r_ij > this%cutoff ) cycle -! if( .not. is_subset(this%Z, at%Z( (/j,atoms_in_descriptors(1,:,i_desc)/) ), error) .and. all(this%Z /= 0) ) cycle -! -! shift_j = shift_ij + shift_i -! -! current_descriptor(:,1:current_order-1) = atoms_in_descriptors(:,:,i_desc) -! current_descriptor(:,current_order) = (/j, shift_j/) -! if( order_and_check_for_duplicates(current_descriptor,at) ) then -! do d = current_order, 1, -1 -! current_descriptor(2:4,d) = current_descriptor(2:4,d) - current_descriptor(2:4,1) -! enddo -! if( .not. is_in_LinkedList(LL_atoms_in_descriptors,current_descriptor,error) ) & -! call append(LL_atoms_in_descriptors,current_descriptor,error) -! endif -! enddo loop_n -! enddo -! enddo -! -! deallocate(current_descriptor) -! call retrieve(LL_atoms_in_descriptors,atoms_in_descriptors) -! call finalise(LL_atoms_in_descriptors) -! if( current_order < this%order ) & -! call distance_Nb_calc_neighbour_loop(this,at,atoms_in_descriptors = atoms_in_descriptors,n_descriptors=n_descriptors,error=error) -! endif -! -! current_order = current_order - 1 -! -! endsubroutine distance_Nb_calc_neighbour_loop - - recursive subroutine distance_Nb_calc_neighbour_loop(this,at,atoms_in_descriptors,n_descriptors,mask,error) - - type(distance_Nb), intent(in) :: this - type(atoms), intent(in) :: at - integer, dimension(:,:,:), intent(inout), allocatable :: atoms_in_descriptors - integer, intent(out), optional :: n_descriptors - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: error - - integer, save :: current_order = 0 - integer :: i, j, n, order, i_desc, d - real(dp) :: r_ij - integer, dimension(3) :: shift_i, shift_j, shift_ij - integer, dimension(:,:), allocatable :: current_descriptor - - type(Table) :: Table_atoms_in_descriptors, Table_atoms_in_descriptors_uniq - - INIT_ERROR(error) - - current_order = current_order + 1 - - if( current_order == 1 ) then - call initialise(Table_atoms_in_descriptors, Nint = 4*current_order, Nreal = 0, Nstr = 0, Nlogical = 0, error=error) - allocate(current_descriptor(4,1)) - - do i = 1, at%N - if( any( at%Z(i) == this%Z ) .or. any( 0 == this%Z ) ) then - if( present(mask) ) then - if( .not. mask(i) ) cycle - endif - - current_descriptor(:,1) = (/i,0,0,0/) - call append(Table_atoms_in_descriptors,current_descriptor(:,1)) - endif - enddo - - deallocate(current_descriptor) - - allocate(atoms_in_descriptors(4,1,Table_atoms_in_descriptors%N)) - atoms_in_descriptors = reshape(Table_atoms_in_descriptors%int(:,1:Table_atoms_in_descriptors%N),(/4,1,Table_atoms_in_descriptors%N/)) - - call finalise(Table_atoms_in_descriptors) - - if( this%order > 1 ) & - call distance_Nb_calc_neighbour_loop(this,at,atoms_in_descriptors = atoms_in_descriptors,n_descriptors=n_descriptors,error=error) - - if( present(n_descriptors) ) n_descriptors = size(atoms_in_descriptors,3) - else - if( .not. allocated(atoms_in_descriptors) ) then - RAISE_ERROR("distance_Nb_calc_neighbour_loop: atoms_in_descriptors must be allocated",error) - endif - - call initialise(Table_atoms_in_descriptors, Nint = 4*current_order, Nreal = 0, Nstr = 0, Nlogical = 0, error=error) - allocate(current_descriptor(4,current_order)) - - do i_desc = 1, size(atoms_in_descriptors,3) - do order = 1, merge(1,size(atoms_in_descriptors,2),this%compact_clusters) !size(atoms_in_descriptors,2) - ! if compact_clusters == T, only neighbours of the first (central) atom is considered - i = atoms_in_descriptors(1,order,i_desc) - shift_i = atoms_in_descriptors(2:4,order,i_desc) - loop_n: do n = 1, n_neighbours(at,i) - j = neighbour(at,i,n,distance = r_ij, shift = shift_ij) - - if( r_ij > this%cutoff ) cycle - if( .not. is_subset(this%Z, at%Z( (/j,atoms_in_descriptors(1,:,i_desc)/) ), error) .and. all(this%Z /= 0) ) cycle - - shift_j = shift_ij + shift_i - - current_descriptor(:,1:current_order-1) = atoms_in_descriptors(:,:,i_desc) - current_descriptor(:,current_order) = (/j, shift_j/) - if( order_and_check_for_duplicates(current_descriptor(:,merge(2,1,this%compact_clusters):),at) ) then - ! if compact_clusters == T, leave first atom alone - do d = current_order, 1, -1 - current_descriptor(2:4,d) = current_descriptor(2:4,d) - current_descriptor(2:4,1) - enddo - call append(Table_atoms_in_descriptors,reshape(current_descriptor,(/4*current_order/))) - - !if( .not. is_in_LinkedList(LL_atoms_in_descriptors,current_descriptor,error) ) & - ! call append(LL_atoms_in_descriptors,current_descriptor,error) - endif - enddo loop_n - enddo - enddo - - deallocate(current_descriptor,atoms_in_descriptors) - call initialise(Table_atoms_in_descriptors_uniq, Nint = 4*current_order, Nreal = 0, Nstr = 0, Nlogical = 0, error=error) - - if( Table_atoms_in_descriptors%N > 0 ) then - call heap_sort(Table_atoms_in_descriptors%int(:,1:Table_atoms_in_descriptors%N)) - call append(Table_atoms_in_descriptors_uniq,Table_atoms_in_descriptors%int(:,1)) - do i_desc = 2, Table_atoms_in_descriptors%N - if( .not. all( Table_atoms_in_descriptors%int(:,i_desc) == Table_atoms_in_descriptors%int(:,i_desc-1) ) ) & - call append(Table_atoms_in_descriptors_uniq,Table_atoms_in_descriptors%int(:,i_desc)) - enddo - endif - - allocate(atoms_in_descriptors(4,current_order,Table_atoms_in_descriptors_uniq%N)) - atoms_in_descriptors = reshape(Table_atoms_in_descriptors_uniq%int(:,1:Table_atoms_in_descriptors_uniq%N),(/4,current_order,Table_atoms_in_descriptors_uniq%N/)) - - call finalise(Table_atoms_in_descriptors) - call finalise(Table_atoms_in_descriptors_uniq) - - if( current_order < this%order ) & - call distance_Nb_calc_neighbour_loop(this,at,atoms_in_descriptors = atoms_in_descriptors,n_descriptors=n_descriptors,error=error) - endif - - current_order = current_order - 1 - - endsubroutine distance_Nb_calc_neighbour_loop - - function order_and_check_for_duplicates(array,at) - integer, dimension(:,:), intent(inout) :: array - type(atoms), intent(in) :: at - logical :: order_and_check_for_duplicates - - integer :: ii, jj, n - integer, dimension(size(array,1)) :: tmp - logical :: do_swap - - integer, dimension(size(array,1)+1,size(array,2)) :: Z_array - - Z_array(1,:) = at%Z(array(1,:)) - Z_array(2:,:) = array(:,:) - - call heap_sort(Z_array) - - do ii = 2, size(Z_array,2) - if( all( Z_array(:,ii-1) == Z_array(:,ii) ) ) then - order_and_check_for_duplicates = .false. - return - endif - enddo - - array(:,:) = Z_array(2:,:) - - order_and_check_for_duplicates = .true. - - endfunction order_and_check_for_duplicates - - function is_subset(set,subset,error) - logical :: is_subset - integer, dimension(:), intent(in) :: set, subset - integer, optional, intent(out) :: error - - logical, dimension(size(set)) :: found - integer :: i, j - - INIT_ERROR(error) - if( size(set) < size(subset) ) then - RAISE_ERROR("is_subset: size of set must be greater than or equal to the size of subset",error) - endif - - found = .false. - loop_i: do i = 1, size(subset) - do j = 1, size(set) - if(set(j) == subset(i) .and. .not. found(j)) then - found(j) = .true. - cycle loop_i - endif - enddo - enddo loop_i - - is_subset = ( count(found) == size(subset) ) - - endfunction is_subset - - subroutine soap_express_calc(this,at,descriptor_out,do_descriptor,do_grad_descriptor,args_str,error) - type(soap_express), intent(in) :: this - type(atoms), intent(in) :: at - type(descriptor_data), intent(out) :: descriptor_out - logical, intent(in), optional :: do_descriptor, do_grad_descriptor - character(len=*), intent(in), optional :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - character(STRING_LENGTH) :: atom_mask_name - logical :: has_atom_mask_name - logical, dimension(:), pointer :: atom_mask_pointer - - logical :: my_do_descriptor, my_do_grad_descriptor - integer :: a, b, d, i, j, k, l, m, n, i_n, l_n_neighbours, & - i_desc, n_descriptors, n_cross, n_index, i_pow - integer, dimension(3) :: shift - real(dp) :: r_ij, phi, atom_sigma_angular, atom_sigma_radial, r_ij_sigma_angular_scaled, & - amplitude, multiplicity, atom_sigma_radial_scaled - real(dp), dimension(3) :: u_ij - real(dp), dimension(this%angular_array_size) :: plm_array - complex(dp), dimension(this%angular_array_size) :: eimphi - complex(dp), dimension(:,:), allocatable :: exp_coeff_angular - real(dp), dimension(:,:), allocatable :: exp_coeff_radial - real(dp), dimension(:), allocatable :: descriptor_i - complex(dp), dimension(:,:), allocatable :: fourier_so3_array - - INIT_ERROR(error) - - call system_timer('soap_express_calc') - - if(.not. this%initialised) then - RAISE_ERROR("soap_express_calc: descriptor object not initialised", error) - endif - - my_do_descriptor = optional_default(.false., do_descriptor) - my_do_grad_descriptor = optional_default(.false., do_grad_descriptor) - - if( .not. my_do_descriptor .and. .not. my_do_grad_descriptor ) return - - atom_mask_pointer => null() - if(present(args_str)) then - call initialise(params) - - call param_register(params, 'atom_mask_name', 'NONE', atom_mask_name, has_value_target=has_atom_mask_name, & - help_string="Name of a logical property in the atoms object. For atoms where this property is true descriptors are " // & - "calculated.") - - if (.not. param_read_line(params,args_str,ignore_unknown=.true.,task='coordination_calc args_str')) then - RAISE_ERROR("soap_express_calc failed to parse args_str='"//trim(args_str)//"'", error) - endif - - call finalise(params) - - if( has_atom_mask_name ) then - if (.not. assign_pointer(at, trim(atom_mask_name), atom_mask_pointer)) then - RAISE_ERROR("soap_express_calc did not find "//trim(atom_mask_name)//" property in the atoms object.", error) - endif - else - atom_mask_pointer => null() - endif - - endif - - call finalise(descriptor_out) - - d = soap_express_dimensions(this,error) - - allocate(fourier_so3_array(this%angular_array_size,this%n_max)) - allocate(descriptor_i(d)) - - if(associated(atom_mask_pointer)) then - call descriptor_sizes(this,at,n_descriptors,n_cross, & - mask=atom_mask_pointer,n_index=n_index,error=error) - else - call descriptor_sizes(this,at,n_descriptors,n_cross,n_index=n_index,error=error) - endif - - allocate(descriptor_out%x(n_descriptors)) - i_desc = 0 - do i = 1, at%N - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - - i_desc = i_desc + 1 - if(my_do_descriptor) then - allocate(descriptor_out%x(i_desc)%data(d)) - descriptor_out%x(i_desc)%data = 0.0_dp - allocate(descriptor_out%x(i_desc)%ci(n_index)) - descriptor_out%x(i_desc)%has_data = .false. - - descriptor_out%x(i_desc)%covariance_cutoff = 1.0_dp - endif - if(my_do_grad_descriptor) then - l_n_neighbours = n_neighbours(at,i,max_dist=this%cutoff) - - allocate(descriptor_out%x(i_desc)%grad_data(d,3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%ii(0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%pos(3,0:l_n_neighbours)) - allocate(descriptor_out%x(i_desc)%has_grad_data(0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_data = 0.0_dp - descriptor_out%x(i_desc)%ii = 0 - descriptor_out%x(i_desc)%pos = 0.0_dp - descriptor_out%x(i_desc)%has_grad_data = .false. - - allocate(descriptor_out%x(i_desc)%grad_covariance_cutoff(3,0:l_n_neighbours)) - descriptor_out%x(i_desc)%grad_covariance_cutoff = 0.0_dp - endif - enddo - - i_desc = 0 - do i = 1, at%N - - if(associated(atom_mask_pointer)) then - if(.not. atom_mask_pointer(i)) cycle - endif - i_desc = i_desc + 1 - - if(my_do_descriptor) then - descriptor_out%x(i_desc)%ci(1) = i - descriptor_out%x(i_desc)%has_data = .true. - endif - if(my_do_grad_descriptor) then - descriptor_out%x(i_desc)%ii(0) = i - descriptor_out%x(i_desc)%pos(:,0) = at%pos(:,i) - descriptor_out%x(i_desc)%has_grad_data(0) = .true. - endif - - allocate(exp_coeff_angular(this%angular_array_size, n_neighbours(at,i, max_dist = this%cutoff))) - allocate(exp_coeff_radial(this%n_max, n_neighbours(at,i, max_dist = this%cutoff))) - - i_n = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij, cosines = u_ij, shift=shift) - - if( r_ij >= this%cutoff ) cycle - i_n = i_n + 1 - - phi = atan2( u_ij(2), u_ij(1) ) - - atom_sigma_angular = this%atom_sigma_angular + this%atom_sigma_scaling_angular * r_ij - - r_ij_sigma_angular_scaled = r_ij / atom_sigma_angular - - call descriptor_soap_express_get_eimphi_conjg( this, phi, r_ij_sigma_angular_scaled, eimphi ) - call descriptor_soap_express_get_plm_array(this, u_ij(3), plm_array) - - exp_coeff_angular(:,i_n) = this%Y_lm_prefactor * plm_array * eimphi / atom_sigma_angular**2 - call descriptor_soap_express_get_radial_array( this, r_ij, exp_coeff_radial(:,i_n) ) - - if( .false. )then -! This is the "inverse power" scaling originally proposed - amplitude = 1.0_dp / (1.0_dp + this%amplitude_scaling*r_ij) - else -! This is the polynomial scaling that made it to the paper - if( this%amplitude_scaling == 0 )then - amplitude = 1.0_dp - else if( this%amplitude_scaling == 1 )then - amplitude = 1.0_dp * ( 1.d0 + 2.d0*(r_ij/this%cutoff)**3 - 3.d0*(r_ij/this%cutoff)**2 ) - else - amplitude = 1.0_dp * ( 1.d0 + 2.d0*(r_ij/this%cutoff)**3 - 3.d0*(r_ij/this%cutoff)**2 )**this%amplitude_scaling - end if - endif - atom_sigma_radial_scaled = this%atom_sigma_radial + this%atom_sigma_scaling_radial*r_ij - exp_coeff_radial(1:this%n_max, i_n) = exp_coeff_radial(1:this%n_max, i_n) * amplitude / atom_sigma_radial_scaled - enddo - -! This results from the change of variable in the -! overlap integrals. We only need this if we want to -! know the actual value of the expansion coefficients. -! Since this is a global factor, once we have normalized -! the SOAP vectors it does not have an effect anymore. - exp_coeff_radial = exp_coeff_radial * sqrt(this%cutoff) - - fourier_so3_array = CPLX_ZERO - i_n = 0 - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij) - - if( r_ij >= this%cutoff ) cycle - i_n = i_n + 1 - - do a = 1, this%n_max - do l = 0, this%l_max - do m = 0, l - - k = 1 + (l*(l+1)) / 2 + m -! It is messy with the prefactor in spherical harmonics but we need to be sure because of the central atom below - fourier_so3_array(k, a) = fourier_so3_array(k, a) + 4.0_dp*PI * exp_coeff_radial(a, i_n) * & - exp_coeff_angular(k,i_n) - - enddo - enddo - enddo - enddo - if( this%central_weight > 0.0_dp )then - fourier_so3_array(1, 1:this%n_max) = fourier_so3_array(1, 1:this%n_max) + this%central_weight / sqrt(4.0_dp*PI) * & - PI**0.25_dp * sqrt(this%atom_sigma_radial / 2.0_dp) * & - matmul(this%basis_transformation_coefficients, this%overlap(1:this%n_max, this%n_max)) - end if - - if(my_do_descriptor) then - descriptor_i = 0.0_dp - i_pow = 0 - do a = 1, this%n_max - do b = a, this%n_max - do l = 0, this%l_max - i_pow = i_pow+1 - do m = 0, l - k = 1 + (l*(l+1))/2 + m - multiplicity = 1.0_dp - if( a /= b ) multiplicity = multiplicity * SQRT_TWO - if( m > 0 ) multiplicity = multiplicity * 2.0_dp - descriptor_i(i_pow) = descriptor_i(i_pow) + multiplicity * real(fourier_so3_array(k, a) * & - conjg(fourier_so3_array(k, b))) - enddo - enddo - enddo - enddo - descriptor_out%x(i_desc)%data = descriptor_i / sqrt(dot_product(descriptor_i, descriptor_i)) - endif - - if(my_do_grad_descriptor) then - RAISE_ERROR("soap_express_calc: derivatives not implemented yet.", error) - endif - - deallocate(exp_coeff_angular) - deallocate(exp_coeff_radial) - - enddo - - deallocate(fourier_so3_array) - deallocate(descriptor_i) - - call system_timer('soap_express_calc') - - endsubroutine soap_express_calc - - function descriptor_dimensions(this,error) - type(descriptor), intent(in) :: this - integer, optional, intent(out) :: error - integer :: descriptor_dimensions - - INIT_ERROR(error) - - selectcase(this%descriptor_type) - case(DT_BISPECTRUM_SO4) - descriptor_dimensions = bispectrum_SO4_dimensions(this%descriptor_bispectrum_SO4,error) - case(DT_BISPECTRUM_SO3) - descriptor_dimensions = bispectrum_SO3_dimensions(this%descriptor_bispectrum_SO3,error) - case(DT_BEHLER) - descriptor_dimensions = behler_dimensions(this%descriptor_behler,error) - case(DT_DISTANCE_2b) - descriptor_dimensions = distance_2b_dimensions(this%descriptor_distance_2b,error) - case(DT_COORDINATION) - descriptor_dimensions = coordination_dimensions(this%descriptor_coordination,error) - case(DT_ANGLE_3B) - descriptor_dimensions = angle_3b_dimensions(this%descriptor_angle_3b,error) - case(DT_CO_ANGLE_3B) - descriptor_dimensions = co_angle_3b_dimensions(this%descriptor_co_angle_3b,error) - case(DT_CO_DISTANCE_2b) - descriptor_dimensions = co_distance_2b_dimensions(this%descriptor_co_distance_2b,error) - case(DT_COSNX) - descriptor_dimensions = cosnx_dimensions(this%descriptor_cosnx,error) - case(DT_TRIHIS) - descriptor_dimensions = trihis_dimensions(this%descriptor_trihis,error) - case(DT_WATER_MONOMER) - descriptor_dimensions = water_monomer_dimensions(this%descriptor_water_monomer,error) - case(DT_WATER_DIMER) - descriptor_dimensions = water_dimer_dimensions(this%descriptor_water_dimer,error) - case(DT_A2_DIMER) - descriptor_dimensions = A2_dimer_dimensions(this%descriptor_A2_dimer,error) - case(DT_AB_DIMER) - descriptor_dimensions = AB_dimer_dimensions(this%descriptor_AB_dimer,error) - case(DT_BOND_REAL_SPACE) - descriptor_dimensions = bond_real_space_dimensions(this%descriptor_bond_real_space,error) - case(DT_ATOM_REAL_SPACE) - descriptor_dimensions = atom_real_space_dimensions(this%descriptor_atom_real_space,error) - case(DT_POWER_SO3) - descriptor_dimensions = power_so3_dimensions(this%descriptor_power_so3,error) - case(DT_POWER_SO4) - descriptor_dimensions = power_so4_dimensions(this%descriptor_power_so4,error) - case(DT_SOAP) - descriptor_dimensions = soap_dimensions(this%descriptor_soap,error) - case(DT_AN_MONOMER) - descriptor_dimensions = AN_monomer_dimensions(this%descriptor_AN_monomer,error) - case(DT_GENERAL_MONOMER) - descriptor_dimensions = general_monomer_dimensions(this%descriptor_general_monomer,error) - case(DT_GENERAL_DIMER) - descriptor_dimensions = general_dimer_dimensions(this%descriptor_general_dimer,error) - case(DT_GENERAL_TRIMER) - descriptor_dimensions = general_trimer_dimensions(this%descriptor_general_trimer,error) - case(DT_RDF) - descriptor_dimensions = rdf_dimensions(this%descriptor_rdf,error) - case(DT_AS_DISTANCE_2b) - descriptor_dimensions = as_distance_2b_dimensions(this%descriptor_as_distance_2b,error) - case(DT_MOLECULE_LO_D) - descriptor_dimensions = molecule_lo_d_dimensions(this%descriptor_molecule_lo_d,error) - case(DT_ALEX) - descriptor_dimensions = alex_dimensions(this%descriptor_alex,error) - case(DT_COM_DIMER) - descriptor_dimensions = com_dimer_dimensions(this%descriptor_com_dimer,error) - case(DT_DISTANCE_Nb) - descriptor_dimensions = distance_Nb_dimensions(this%descriptor_distance_Nb,error) - case(DT_SOAP_EXPRESS) - descriptor_dimensions = soap_express_dimensions(this%descriptor_soap_express,error) - case default - RAISE_ERROR("descriptor_dimensions: unknown descriptor type "//this%descriptor_type,error) - endselect - - endfunction descriptor_dimensions - - function bispectrum_SO4_dimensions(this,error) result(i) - type(bispectrum_SO4), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - integer :: j, j1, j2 - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_SO4_dimensions: descriptor object not initialised", error) - endif - - i = 0 - do j1 = 0, this%j_max - j2 = j1 - !do j2 = 0, this%j_max - do j = abs(j1-j2), min(this%j_max,j1+j2) - if( mod(j1+j2+j,2) == 1 ) cycle - i = i + 1 - enddo - !enddo - enddo - - endfunction bispectrum_SO4_dimensions - - function bispectrum_SO3_dimensions(this,error) result(i) - type(bispectrum_SO3), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - integer :: a, l1, l2, l - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_SO3_dimensions: descriptor object not initialised", error) - endif - - i = 0 - do a = 1, this%n_max - do l1 = 0, this%l_max - l2 = l1 - !do l2 = 0, this%l_max - do l = abs(l1-l2), min(this%l_max,l1+l2) - if( mod(l1,2)==1 .and. mod(l2,2)==1 .and. mod(l,2)==1 ) cycle - i = i + 1 - enddo - !enddo - enddo - enddo - - endfunction bispectrum_SO3_dimensions - - function behler_dimensions(this,error) result(i) - type(behler), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("behler_dimensions: descriptor object not initialised", error) - endif - - i = this%n_g2 + this%n_g3 - - endfunction behler_dimensions - - function distance_2b_dimensions(this,error) result(i) - type(distance_2b), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("distance_2b_dimensions: descriptor object not initialised", error) - endif - - i = this%n_exponents - - endfunction distance_2b_dimensions - - function coordination_dimensions(this,error) result(i) - type(coordination), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("coordination_dimensions: descriptor object not initialised", error) - endif - - i = 1 - - endfunction coordination_dimensions - - function angle_3b_dimensions(this,error) result(i) - type(angle_3b), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("angle_3b_dimensions: descriptor object not initialised", error) - endif - - i = 3 - - endfunction angle_3b_dimensions - - function co_angle_3b_dimensions(this,error) result(i) - type(co_angle_3b), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("co_angle_3b_dimensions: descriptor object not initialised", error) - endif - - i = 4 - - endfunction co_angle_3b_dimensions - - function co_distance_2b_dimensions(this,error) result(i) - type(co_distance_2b), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("co_distance_2b_dimensions: descriptor object not initialised", error) - endif - - i = 3 - - endfunction co_distance_2b_dimensions - - function cosnx_dimensions(this,error) result(i) - type(cosnx), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("cosnx_dimensions: descriptor object not initialised", error) - endif - - i = this%n_max*(this%l_max+1) - - endfunction cosnx_dimensions - - function trihis_dimensions(this,error) result(i) - type(trihis), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("trihis_dimensions: descriptor object not initialised", error) - endif - - i = this%n_gauss - - endfunction trihis_dimensions - - function water_monomer_dimensions(this,error) result(i) - type(water_monomer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("water_monomer_dimensions: descriptor object not initialised", error) - endif - - i = 3 - - endfunction water_monomer_dimensions - - function water_dimer_dimensions(this,error) result(i) - type(water_dimer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("water_dimer_dimensions: descriptor object not initialised", error) - endif - - i = 15 - - endfunction water_dimer_dimensions - - function A2_dimer_dimensions(this,error) result(i) - type(A2_dimer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("A2_dimer_dimensions: descriptor object not initialised", error) - endif - - i = 6 - - endfunction A2_dimer_dimensions - - function AB_dimer_dimensions(this,error) result(i) - type(AB_dimer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("AB_dimer_dimensions: descriptor object not initialised", error) - endif - - i = 6 - - endfunction AB_dimer_dimensions - - function bond_real_space_dimensions(this,error) result(i) - type(bond_real_space), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bond_real_space_dimensions: descriptor object not initialised", error) - endif - - i = 2 + (1 + 2 * this%max_neighbours) * this%max_neighbours - - endfunction bond_real_space_dimensions - - function atom_real_space_dimensions(this,error) result(i) - type(atom_real_space), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("atom_real_space_dimensions: descriptor object not initialised", error) - endif - - i = 2 * (this%l_max+1)**2 + 2 - - endfunction atom_real_space_dimensions - - function power_so3_dimensions(this,error) result(i) - type(power_so3), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("power_so3_dimensions: descriptor object not initialised", error) - endif - - i = this%n_max*(this%l_max+1) - - endfunction power_so3_dimensions - - function power_SO4_dimensions(this,error) result(i) - type(power_SO4), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("power_SO4_dimensions: descriptor object not initialised", error) - endif - - i = this%j_max + 1 - - endfunction power_SO4_dimensions - - function soap_dimensions(this,error) result(i) - type(soap), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("soap_dimensions: descriptor object not initialised", error) - endif - - !i = (this%l_max+1) * ( this%n_max * (this%n_max+1) / 2 ) * ( this%n_species * (this%n_species+1) / 2 ) + 1 - !i = (this%l_max+1) * this%n_max**2 * this%n_species**2 + 1 - if(this%diagonal_radial) then - i = (this%l_max+1) * this%n_max * this%n_species * (this%n_species+1) / 2 + 1 - else - i = (this%l_max+1) * ( (this%n_max*this%n_species)*(this%n_max*this%n_species+1) ) / 2 + 1 - endif - - endfunction soap_dimensions - - function AN_monomer_dimensions(this,error) result(i) - type(AN_monomer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("AN_monomer_dimensions: descriptor object not initialised", error) - endif - - i = this%N * (this%N - 1) / 2 - - endfunction AN_monomer_dimensions - - function general_monomer_dimensions(this,error) result(i) - type(general_monomer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_monomer_dimensions: descriptor object not initialised", error) - endif - if(.not. this%permutation_data%initialised) then - RAISE_ERROR("general_monomer_dimensions: descriptor object's permutation data not initialised", error) - endif - - i = size(this%permutation_data%dist_vec) - - endfunction general_monomer_dimensions - - function com_dimer_dimensions(this,error) result(i) - type(com_dimer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("com_dimer_dimensions: descriptor object not initialised", error) - endif - - i = 1 - - endfunction com_dimer_dimensions - - function general_dimer_dimensions(this,error) result(i) - type(general_dimer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_dimer_dimensions: descriptor object not initialised", error) - endif - if(.not. this%permutation_data%initialised) then - RAISE_ERROR("general_dimer_dimensions: descriptor object's permutation data not initialised", error) - endif - - i = size(this%permutation_data%dist_vec) - - endfunction general_dimer_dimensions - - - function general_trimer_dimensions(this,error) result(i) - type(general_trimer), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_trimer_dimensions: descriptor object not initialised", error) - endif - if(.not. this%permutation_data%initialised) then - RAISE_ERROR("general_trimer_dimensions: descriptor object's permutation data not initialised", error) - endif - - i = size(this%permutation_data%dist_vec) - - endfunction general_trimer_dimensions - - function rdf_dimensions(this,error) result(i) - type(rdf), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("rdf_dimensions: descriptor object not initialised", error) - endif - - i = this%n_gauss - - endfunction rdf_dimensions - - function as_distance_2b_dimensions(this,error) result(i) - type(as_distance_2b), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("as_distance_2b_dimensions: descriptor object not initialised", error) - endif - - i = 3 - - endfunction as_distance_2b_dimensions - - function molecule_lo_d_dimensions(this,error) result(i) - type(molecule_lo_d), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("molecule_lo_d_dimensions: descriptor object not initialised", error) - endif - if(.not. this%permutation_data%initialised) then - RAISE_ERROR("molecule_lo_d_dimensions: descriptor object's permutation data not initialised", error) - endif - - i = size(this%included_components) - - endfunction molecule_lo_d_dimensions - - function alex_dimensions(this,error) result(i) - type(alex), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i, nradial - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("alex_dimensions: descriptor object not initialised", error) - endif - - nradial = this%power_max-this%power_min + 1 - i = nradial+2*nradial**2+nradial**3 - - endfunction alex_dimensions - - function distance_Nb_dimensions(this,error) result(i) - type(distance_Nb), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("distance_Nb_dimensions: descriptor object not initialised", error) - endif - - i = max(1,this%order * ( this%order - 1 ) / 2) - - endfunction distance_Nb_dimensions - - function soap_express_dimensions(this,error) result(i) - type(soap_express), intent(in) :: this - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("soap_express_dimensions: descriptor object not initialised", error) - endif - - i = ( this%l_max+1 ) * ( this%n_max*(this%n_max+1) ) / 2 + 1 - - endfunction soap_express_dimensions - - function descriptor_cutoff(this,error) - type(descriptor), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: descriptor_cutoff - - INIT_ERROR(error) - - selectcase(this%descriptor_type) - case(DT_BISPECTRUM_SO4) - descriptor_cutoff = cutoff(this%descriptor_bispectrum_SO4,error) - case(DT_BISPECTRUM_SO3) - descriptor_cutoff = cutoff(this%descriptor_bispectrum_SO3,error) - case(DT_BEHLER) - descriptor_cutoff = cutoff(this%descriptor_behler,error) - case(DT_DISTANCE_2b) - descriptor_cutoff = cutoff(this%descriptor_distance_2b,error) - case(DT_COORDINATION) - descriptor_cutoff = cutoff(this%descriptor_coordination,error) - case(DT_ANGLE_3B) - descriptor_cutoff = cutoff(this%descriptor_angle_3b,error) - case(DT_CO_ANGLE_3B) - descriptor_cutoff = cutoff(this%descriptor_co_angle_3b,error) - case(DT_CO_DISTANCE_2b) - descriptor_cutoff = cutoff(this%descriptor_co_distance_2b,error) - case(DT_COSNX) - descriptor_cutoff = cutoff(this%descriptor_cosnx,error) - case(DT_TRIHIS) - descriptor_cutoff = cutoff(this%descriptor_trihis,error) - case(DT_WATER_MONOMER) - descriptor_cutoff = cutoff(this%descriptor_water_monomer,error) - case(DT_WATER_DIMER) - descriptor_cutoff = cutoff(this%descriptor_water_dimer,error) - case(DT_A2_DIMER) - descriptor_cutoff = cutoff(this%descriptor_A2_dimer,error) - case(DT_AB_DIMER) - descriptor_cutoff = cutoff(this%descriptor_AB_dimer,error) - case(DT_BOND_REAL_SPACE) - descriptor_cutoff = cutoff(this%descriptor_bond_real_space,error) - case(DT_ATOM_REAL_SPACE) - descriptor_cutoff = cutoff(this%descriptor_atom_real_space,error) - case(DT_POWER_SO3) - descriptor_cutoff = cutoff(this%descriptor_power_so3,error) - case(DT_POWER_SO4) - descriptor_cutoff = cutoff(this%descriptor_power_so4,error) - case(DT_SOAP) - descriptor_cutoff = cutoff(this%descriptor_soap,error) - case(DT_AN_MONOMER) - descriptor_cutoff = cutoff(this%descriptor_AN_monomer,error) - case(DT_GENERAL_MONOMER) - descriptor_cutoff = cutoff(this%descriptor_general_monomer,error) - case(DT_GENERAL_DIMER) - descriptor_cutoff = cutoff(this%descriptor_general_dimer,error) - case(DT_GENERAL_TRIMER) - descriptor_cutoff = cutoff(this%descriptor_general_trimer,error) - case(DT_RDF) - descriptor_cutoff = cutoff(this%descriptor_rdf,error) - case(DT_MOLECULE_LO_D) - descriptor_cutoff = cutoff(this%descriptor_molecule_lo_d,error) - case(DT_ALEX) - descriptor_cutoff = cutoff(this%descriptor_alex,error) - case(DT_COM_DIMER) - descriptor_cutoff = cutoff(this%descriptor_com_dimer,error) - case(DT_DISTANCE_Nb) - descriptor_cutoff = cutoff(this%descriptor_distance_Nb,error) - case(DT_SOAP_EXPRESS) - descriptor_cutoff = cutoff(this%descriptor_soap_express,error) - case default - RAISE_ERROR("descriptor_cutoff: unknown descriptor type "//this%descriptor_type,error) - endselect - - endfunction descriptor_cutoff - - function bispectrum_SO4_cutoff(this,error) - type(bispectrum_SO4), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: bispectrum_SO4_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_SO4_cutoff: descriptor object not initialised", error) - endif - - bispectrum_SO4_cutoff = this%cutoff - - endfunction bispectrum_SO4_cutoff - - function bispectrum_SO3_cutoff(this,error) - type(bispectrum_SO3), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: bispectrum_SO3_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_SO3_cutoff: descriptor object not initialised", error) - endif - - bispectrum_SO3_cutoff = this%cutoff - - endfunction bispectrum_SO3_cutoff - - function behler_cutoff(this,error) - type(behler), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: behler_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("behler_cutoff: descriptor object not initialised", error) - endif - - behler_cutoff = this%cutoff - - endfunction behler_cutoff - - function distance_2b_cutoff(this,error) - type(distance_2b), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: distance_2b_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("distance_2b_cutoff: descriptor object not initialised", error) - endif - - distance_2b_cutoff = this%cutoff - - endfunction distance_2b_cutoff - - function co_distance_2b_cutoff(this,error) - type(co_distance_2b), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: co_distance_2b_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("co_distance_2b_cutoff: descriptor object not initialised", error) - endif - - co_distance_2b_cutoff = this%cutoff - - endfunction co_distance_2b_cutoff - - function coordination_cutoff(this,error) - type(coordination), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: coordination_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("coordination_cutoff: descriptor object not initialised", error) - endif - - coordination_cutoff = this%cutoff - - endfunction coordination_cutoff - - function angle_3b_cutoff(this,error) - type(angle_3b), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: angle_3b_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("angle_3b_cutoff: descriptor object not initialised", error) - endif - - angle_3b_cutoff = this%cutoff - - endfunction angle_3b_cutoff - - function co_angle_3b_cutoff(this,error) - type(co_angle_3b), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: co_angle_3b_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("co_angle_3b_cutoff: descriptor object not initialised", error) - endif - - co_angle_3b_cutoff = this%cutoff - - endfunction co_angle_3b_cutoff - - function cosnx_cutoff(this,error) - type(cosnx), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: cosnx_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("cosnx_cutoff: descriptor object not initialised", error) - endif - - cosnx_cutoff = this%cutoff - - endfunction cosnx_cutoff - - function trihis_cutoff(this,error) - type(trihis), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: trihis_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("trihis_cutoff: descriptor object not initialised", error) - endif - - trihis_cutoff = this%cutoff - - endfunction trihis_cutoff - - function water_monomer_cutoff(this,error) - type(water_monomer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: water_monomer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("water_monomer_cutoff: descriptor object not initialised", error) - endif - - water_monomer_cutoff = this%cutoff - - endfunction water_monomer_cutoff - - function water_dimer_cutoff(this,error) - type(water_dimer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: water_dimer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("water_dimer_cutoff: descriptor object not initialised", error) - endif - - water_dimer_cutoff = this%cutoff - - endfunction water_dimer_cutoff - - function A2_dimer_cutoff(this,error) - type(A2_dimer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: A2_dimer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("A2_dimer_cutoff: descriptor object not initialised", error) - endif - - A2_dimer_cutoff = this%cutoff - - endfunction A2_dimer_cutoff - - function AB_dimer_cutoff(this,error) - type(AB_dimer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: AB_dimer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("AB_dimer_cutoff: descriptor object not initialised", error) - endif - - AB_dimer_cutoff = this%cutoff - - endfunction AB_dimer_cutoff - - function bond_real_space_cutoff(this,error) - type(bond_real_space), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: bond_real_space_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bond_real_space_cutoff: descriptor object not initialised", error) - endif - - bond_real_space_cutoff = max(this%cutoff, this%bond_cutoff) - - endfunction bond_real_space_cutoff - - function atom_real_space_cutoff(this,error) - type(atom_real_space), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: atom_real_space_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("atom_real_space_cutoff: descriptor object not initialised", error) - endif - - atom_real_space_cutoff = this%cutoff - - endfunction atom_real_space_cutoff - - function power_so3_cutoff(this,error) - type(power_so3), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: power_so3_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("power_so3_cutoff: descriptor object not initialised", error) - endif - - power_so3_cutoff = this%cutoff - - endfunction power_so3_cutoff - - function power_so4_cutoff(this,error) - type(power_so4), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: power_so4_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("power_so4_cutoff: descriptor object not initialised", error) - endif - - power_so4_cutoff = this%cutoff - - endfunction power_so4_cutoff - - function soap_cutoff(this,error) - type(soap), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: soap_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("soap_cutoff: descriptor object not initialised", error) - endif - - soap_cutoff = this%cutoff - - endfunction soap_cutoff - - function AN_monomer_cutoff(this,error) - type(AN_monomer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: AN_monomer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("AN_monomer_cutoff: descriptor object not initialised", error) - endif - - AN_monomer_cutoff = this%cutoff - - endfunction AN_monomer_cutoff - - function general_monomer_cutoff(this,error) - type(general_monomer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: general_monomer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_monomer_cutoff: descriptor object not initialised", error) - endif - - general_monomer_cutoff = this%cutoff - - endfunction general_monomer_cutoff - - function com_dimer_cutoff(this,error) - type(com_dimer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: com_dimer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("com_dimer_cutoff: descriptor object not initialised", error) - endif - - com_dimer_cutoff = this%cutoff - - endfunction com_dimer_cutoff - - function general_dimer_cutoff(this,error) - type(general_dimer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: general_dimer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_dimer_cutoff: descriptor object not initialised", error) - endif - - general_dimer_cutoff = this%cutoff - - endfunction general_dimer_cutoff - - function general_trimer_cutoff(this,error) - type(general_trimer), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: general_trimer_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_trimer_cutoff: descriptor object not initialised", error) - endif - - general_trimer_cutoff = this%cutoff - - endfunction general_trimer_cutoff - - function rdf_cutoff(this,error) - type(rdf), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: rdf_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("rdf_cutoff: descriptor object not initialised", error) - endif - - rdf_cutoff = this%cutoff - - endfunction rdf_cutoff - - function as_distance_2b_cutoff(this,error) - type(as_distance_2b), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: as_distance_2b_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("as_distance_2b_cutoff: descriptor object not initialised", error) - endif - - as_distance_2b_cutoff = this%max_cutoff - - endfunction as_distance_2b_cutoff - - function molecule_lo_d_cutoff(this,error) - type(molecule_lo_d), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: molecule_lo_d_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("molecule_lo_d_cutoff: descriptor object not initialised", error) - endif - - molecule_lo_d_cutoff = this%cutoff - - endfunction molecule_lo_d_cutoff - - function alex_cutoff(this,error) - type(alex), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: alex_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("alex_cutoff: descriptor object not initialised", error) - endif - - alex_cutoff = this%cutoff - - endfunction alex_cutoff - - function distance_Nb_cutoff(this,error) - type(distance_Nb), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: distance_Nb_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("distance_Nb_cutoff: descriptor object not initialised", error) - endif - - distance_Nb_cutoff = this%cutoff - - endfunction distance_Nb_cutoff - - function soap_express_cutoff(this,error) - type(soap_express), intent(in) :: this - integer, optional, intent(out) :: error - real(dp) :: soap_express_cutoff - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("soap_express_cutoff: descriptor object not initialised", error) - endif - - soap_express_cutoff = this%cutoff - - endfunction soap_express_cutoff - - subroutine descriptor_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(descriptor), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - INIT_ERROR(error) - - selectcase(this%descriptor_type) - case(DT_BISPECTRUM_SO4) - call bispectrum_SO4_sizes(this%descriptor_bispectrum_SO4,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_BISPECTRUM_SO3) - call bispectrum_SO3_sizes(this%descriptor_bispectrum_SO3,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_BEHLER) - call behler_sizes(this%descriptor_behler,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_DISTANCE_2b) - call distance_2b_sizes(this%descriptor_distance_2b,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_COORDINATION) - call coordination_sizes(this%descriptor_coordination,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_ANGLE_3B) - call angle_3b_sizes(this%descriptor_angle_3b,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_CO_ANGLE_3B) - call co_angle_3b_sizes(this%descriptor_co_angle_3b,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_CO_DISTANCE_2b) - call co_distance_2b_sizes(this%descriptor_co_distance_2b,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_COSNX) - call cosnx_sizes(this%descriptor_cosnx,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_TRIHIS) - call trihis_sizes(this%descriptor_trihis,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_WATER_MONOMER) - call water_monomer_sizes(this%descriptor_water_monomer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_WATER_DIMER) - call water_dimer_sizes(this%descriptor_water_dimer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_A2_DIMER) - call A2_dimer_sizes(this%descriptor_A2_dimer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_AB_DIMER) - call AB_dimer_sizes(this%descriptor_AB_dimer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_BOND_REAL_SPACE) - call bond_real_space_sizes(this%descriptor_bond_real_space,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_ATOM_REAL_SPACE) - call atom_real_space_sizes(this%descriptor_atom_real_space,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_POWER_SO3) - call power_so3_sizes(this%descriptor_power_so3,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_POWER_SO4) - call power_so4_sizes(this%descriptor_power_so4,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_SOAP) - call soap_sizes(this%descriptor_soap,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_AN_MONOMER) - call AN_monomer_sizes(this%descriptor_AN_monomer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_GENERAL_MONOMER) - call general_monomer_sizes(this%descriptor_general_monomer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_GENERAL_DIMER) - call general_dimer_sizes(this%descriptor_general_dimer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_GENERAL_TRIMER) - call general_trimer_sizes(this%descriptor_general_trimer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_RDF) - call rdf_sizes(this%descriptor_rdf,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_AS_DISTANCE_2b) - call as_distance_2b_sizes(this%descriptor_as_distance_2b,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_MOLECULE_LO_D) - call molecule_lo_d_sizes(this%descriptor_molecule_lo_d,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_ALEX) - call alex_sizes(this%descriptor_alex,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_COM_DIMER) - call com_dimer_sizes(this%descriptor_com_dimer,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_DISTANCE_Nb) - call distance_Nb_sizes(this%descriptor_distance_Nb,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case(DT_SOAP_EXPRESS) - call soap_express_sizes(this%descriptor_soap_express,at, & - n_descriptors,n_cross,mask=mask,n_index=n_index,error=error) - case default - RAISE_ERROR("descriptor_sizes: unknown descriptor type "//this%descriptor_type,error) - endselect - - endsubroutine descriptor_sizes - - subroutine bispectrum_SO4_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(bispectrum_SO4), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_SO4_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine bispectrum_SO4_sizes - - subroutine bispectrum_SO3_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(bispectrum_SO3), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bispectrum_SO3_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine bispectrum_SO3_sizes - - subroutine behler_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(behler), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("behler_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - do i = 1, at%N - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine behler_sizes - - subroutine distance_2b_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(distance_2b), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i, j, n - logical :: Zi1, Zi2, Zj1, Zj2 - real(dp) :: r_ij - - logical :: needs_resid - integer, dimension(:), pointer :: resid_pointer - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("distance_2b_sizes: descriptor object not initialised", error) - endif - - needs_resid = this%only_intra .or. this%only_inter - if (needs_resid) then - if (.not. assign_pointer(at, trim(this%resid_name), resid_pointer)) then - RAISE_ERROR("distance_2b_sizes did not find "//trim(this%resid_name)//" property (residue id) in the atoms object.", error) - end if - else - resid_pointer => null() - end if - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if(present(mask)) then - if(.not. mask(i)) cycle - endif - - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance=r_ij) - if(r_ij >= this%cutoff) cycle -!if(r_ij < 3.5_dp) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - if (needs_resid) then - if (this%only_intra .and. resid_pointer(i) /= resid_pointer(j)) cycle - if (this%only_inter .and. resid_pointer(i) == resid_pointer(j)) cycle - end if - - n_descriptors = n_descriptors + 1 - enddo - enddo - - n_cross = n_descriptors*2 - - if( present(n_index) ) n_index = 2 - - endsubroutine distance_2b_sizes - - subroutine coordination_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(coordination), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("coordination_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine coordination_sizes - - subroutine angle_3b_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(angle_3b), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i, j, k, n, m - real(dp) :: r_ij, r_ik - logical :: Zk1, Zk2, Zj1, Zj2 - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("angle_3b_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( (this%Z /=0) .and. (at%Z(i) /= this%Z) ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij) - if( r_ij >= this%cutoff ) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - - do m = 1, n_neighbours(at,i) - if( n == m ) cycle - - k = neighbour(at, i, m, distance = r_ik) - if( r_ik >= this%cutoff ) cycle - - Zk1 = (this%Z1 == 0) .or. (at%Z(k) == this%Z1) - Zk2 = (this%Z2 == 0) .or. (at%Z(k) == this%Z2) - if( .not. ( ( Zk1 .and. Zj2 ) .or. ( Zk2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - n_descriptors = n_descriptors + 1 - enddo - enddo - enddo - n_cross = n_descriptors * 3 - - if( present(n_index) ) n_index = 1 - - endsubroutine angle_3b_sizes - - subroutine co_angle_3b_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(co_angle_3b), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i, j, k, n, m, n_neighbours_coordination - real(dp) :: r_ij, r_ik - logical :: Zk1, Zk2, Zj1, Zj2 - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("co_angle_3b_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( (this%Z /=0) .and. (at%Z(i) /= this%Z) ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - - n_neighbours_coordination = n_neighbours(at,i,max_dist=this%coordination_cutoff) - - do n = 1, n_neighbours(at,i) - j = neighbour(at, i, n, distance = r_ij) - if( r_ij >= this%cutoff ) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - - do m = 1, n_neighbours(at,i) - if( n == m ) cycle - k = neighbour(at, i, m, distance = r_ik) - if( r_ik >= this%cutoff ) cycle - - Zk1 = (this%Z1 == 0) .or. (at%Z(k) == this%Z1) - Zk2 = (this%Z2 == 0) .or. (at%Z(k) == this%Z2) - if( .not. ( ( Zk1 .and. Zj2 ) .or. ( Zk2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - n_descriptors = n_descriptors + 1 - n_cross = n_cross + 3 + n_neighbours_coordination - enddo - enddo - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine co_angle_3b_sizes - - subroutine co_distance_2b_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(co_distance_2b), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - real(dp) :: r_ij - integer :: i, j, n - logical :: Zi1, Zi2, Zj1, Zj2 - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("co_distance_2b_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if(present(mask)) then - if(.not. mask(i)) cycle - endif - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at,i,n,distance=r_ij) - if( r_ij >= this%cutoff ) cycle -!if( r_ij < 3.5_dp ) cycle - - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - n_descriptors = n_descriptors + 1 - n_cross = n_cross + 4 + n_neighbours(at,i,max_dist=this%coordination_cutoff) + n_neighbours(at,j,max_dist=this%coordination_cutoff) - enddo - enddo - - if( present(n_index) ) n_index = 2 - - endsubroutine co_distance_2b_sizes - - subroutine cosnx_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(cosnx), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("cosnx_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine cosnx_sizes - - subroutine trihis_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(trihis), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("trihis_sizes: descriptor object not initialised", error) - endif - - n_descriptors = at%N - - n_cross = 0 - - do i = 1, at%N - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_cross = n_cross + n_neighbours(at,i) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine trihis_sizes - - subroutine water_monomer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(water_monomer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("water_monomer_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if(at%Z(i) == 8) then - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + 3 - endif - enddo - - if( present(n_index) ) n_index = 3 - - endsubroutine water_monomer_sizes - - subroutine water_dimer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(water_dimer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i, j, n - real(dp) :: r_ij - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("water_dimer_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 -call print("mask present ? "//present(mask)) - do i = 1, at%N - if(at%Z(i) == 8) then - if(present(mask)) then - if(.not. mask(i)) cycle - endif - do n = 1, n_neighbours(at,i) - j = neighbour(at,i,n,distance=r_ij) - if(at%Z(j) == 8 .and. r_ij < this%cutoff) then - n_descriptors = n_descriptors + 1 - n_cross = n_cross + 6 - endif - enddo - endif - enddo - - if( present(n_index) ) n_index = 6 - - endsubroutine water_dimer_sizes - - subroutine A2_dimer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(A2_dimer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i, j, iA1, iA2, iB1, iB2 - integer, dimension(at%N) :: A2_monomer_index - real(dp) :: r_A1_A2, r_B1_B2, r_A1_B1, r_A1_B2, r_A2_B1, r_A2_B2 - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("A2_dimer_sizes: descriptor object not initialised", error) - endif - - call find_A2_monomer(at,this%atomic_number, this%monomer_cutoff, A2_monomer_index) - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - iA1 = i - iA2 = neighbour(at,i,A2_monomer_index(i),distance=r_A1_A2) - if( iA1 > iA2 ) cycle - - do j = i + 1, at%N - iB1 = j - iB2 = neighbour(at,j,A2_monomer_index(j),distance=r_B1_B2) - if( iB1 > iB2 ) cycle - - r_A1_B1 = distance_min_image(at,iA1,iB1) - r_A1_B2 = distance_min_image(at,iA1,iB2) - - r_A2_B1 = distance_min_image(at,iA2,iB1) - r_A2_B2 = distance_min_image(at,iA2,iB2) - - if( all( (/r_A1_A2,r_B1_B2,r_A1_B1,r_A1_B2,r_A2_B1,r_A2_B2/) < this%cutoff) ) then - n_descriptors = n_descriptors + 1 - n_cross = n_cross + 4 - endif - enddo - enddo - - if( present(n_index) ) n_index = 4 - - endsubroutine A2_dimer_sizes - - subroutine AB_dimer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(AB_dimer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i, j, n_monomers, iA1, iA2, iB1, iB2 - integer, dimension(:,:), allocatable :: AB_monomer_index - real(dp) :: r_A1_A2, r_B1_B2, r_A1_B1, r_A1_B2, r_A2_B1, r_A2_B2 - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("A2_dimer_sizes: descriptor object not initialised", error) - endif - - if( count(at%Z == this%atomic_number1) == count(at%Z == this%atomic_number2) ) then - n_monomers = count(at%Z == this%atomic_number1) - else - RAISE_ERROR("AB_dimer_sizes: number of monomer atoms 1 ("//count(at%Z == this%atomic_number1)//") not equal to number of monomer atoms 2 ("//count(at%Z == this%atomic_number1)//")",error) - endif - - allocate(AB_monomer_index(2,n_monomers)) - call find_AB_monomer(at,(/this%atomic_number1,this%atomic_number2/), this%monomer_cutoff, AB_monomer_index) - - n_descriptors = 0 - n_cross = 0 - - do i = 1, n_monomers - iA1 = AB_monomer_index(1,i) - iB1 = AB_monomer_index(2,i) - do j = i + 1, n_monomers - iA2 = AB_monomer_index(1,j) - iB2 = AB_monomer_index(2,j) - - r_A1_B1 = distance_min_image(at,iA1,iB1) - r_A2_B2 = distance_min_image(at,iA2,iB2) - - r_A1_A2 = distance_min_image(at,iA1,iA2) - r_B1_B2 = distance_min_image(at,iB1,iB2) - - r_A1_B2 = distance_min_image(at,iA1,iB2) - r_A2_B1 = distance_min_image(at,iA2,iB1) - - if( all( (/r_A1_A2,r_B1_B2,r_A1_B1,r_A1_B2,r_A2_B1,r_A2_B2/) < this%cutoff) ) then - n_descriptors = n_descriptors + 1 - n_cross = n_cross + 4 - endif - enddo - enddo - - deallocate(AB_monomer_index) - - if( present(n_index) ) n_index = 4 - - endsubroutine AB_dimer_sizes - - subroutine bond_real_space_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(bond_real_space), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - type(atoms) :: at_copy - integer :: i, j, k, n, m, shift_j(3) - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("bond_real_space_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - n_descriptors = n_descriptors + n_neighbours(at, i, max_dist=this%bond_cutoff) - - do n = 1, n_neighbours(at, i) - j = neighbour(at, i, n, shift=shift_j, max_dist=this%bond_cutoff) - - if(j == 0) cycle - - at_copy = at - call add_atoms(at_copy, 0.5_dp * (at%pos(:,i) + at%pos(:,j) + matmul(at%lattice,shift_j)), 1) - call calc_connect(at_copy) - - do m = 1, n_neighbours(at_copy, at%N + 1) - k = neighbour(at_copy, at%N + 1, m, max_dist=this%cutoff) - - if(k == 0) cycle - - if(at_copy%pos(:,k) .feq. at_copy%pos(:,at%N + 1)) cycle - - n_cross = n_cross + 1 - enddo - - call finalise(at_copy) - enddo - enddo - - if( present(n_index) ) n_index = 2 - - endsubroutine bond_real_space_sizes - - subroutine atom_real_space_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(atom_real_space), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("atom_real_space_sizes: descriptor object not initialised", error) - endif - - n_descriptors = at%N - n_cross = 0 - - do i = 1, at%N - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff)*2 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine atom_real_space_sizes - - subroutine power_so3_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(power_so3), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("power_so3_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine power_so3_sizes - - subroutine power_SO4_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(power_SO4), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("power_SO4_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine power_SO4_sizes - - subroutine soap_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(soap), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("soap_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( .not. any( at%Z(i) == this%Z ) .and. .not. any(this%Z == 0) ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if(this%global) then - n_descriptors = 1 - if( present(n_index) ) then - if( any(this%Z == 0) ) then - n_index = at%N - else - n_index = count( (/(any(at%Z(i)==this%Z),i=1,at%N)/) ) - endif - endif - else - if( present(n_index) ) n_index = 1 - endif - - endsubroutine soap_sizes - - subroutine AN_monomer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(AN_monomer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("AN_monomer: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - n_descriptors = n_descriptors + 1 - n_cross = n_cross + this%N - if(.not.this%do_atomic) exit - enddo - - if(this%do_atomic) then - if( present(n_index) ) n_index = 1 - else - if( present(n_index) ) n_index = this%N - endif - - endsubroutine AN_monomer_sizes - - subroutine general_monomer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(general_monomer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer, dimension(:,:), allocatable :: monomer_index - integer :: i - logical, dimension(:), allocatable :: associated_to_monomer - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_monomer_sizes: descriptor object not initialised", error) - endif - - allocate(associated_to_monomer(at%N)) - associated_to_monomer=.false. - - call find_general_monomer(at,monomer_index,this%signature,associated_to_monomer,this%cutoff,this%atom_ordercheck,error) - n_descriptors = size(monomer_index,2) - n_cross=size(monomer_index) - if(.not. all(associated_to_monomer)) then - if(this%strict) then - RAISE_ERROR("general_monomer_sizes: not all atoms assigned to a monomer", error) - else - call print("WARNING: general_monomer_sizes: not all atoms assigned to a monomer") - endif - endif - - deallocate(monomer_index) - deallocate(associated_to_monomer) - - if( present(n_index) ) n_index = size(this%signature) - - endsubroutine general_monomer_sizes - - subroutine com_dimer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - - type(com_dimer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer, dimension(:,:), allocatable :: monomer_one_index, monomer_two_index, monomer_pairs - integer, dimension(:), allocatable :: pairs_diffs_map - real(dp), dimension(:,:), allocatable :: mean_pos_diffs - logical, dimension(:), allocatable :: associated_to_monomer - logical :: double_count - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("com_dimer_sizes: descriptor object not initialised", error) - endif - - double_count = .false. - - allocate(associated_to_monomer(at%N)) - associated_to_monomer=.false. - - call find_general_monomer(at,monomer_one_index,this%signature_one,associated_to_monomer,this%monomer_one_cutoff,this%atom_ordercheck,error) - if (this%monomers_identical) then - allocate(monomer_two_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_two_index = monomer_one_index - else - call find_general_monomer(at,monomer_two_index,this%signature_two,associated_to_monomer,this%monomer_two_cutoff,this%atom_ordercheck,error) - end if - - if(.not. all(associated_to_monomer)) then - if(this%strict) then - RAISE_ERROR("com_dimer_sizes: not all atoms assigned to a monomer", error) - else - call print("WARNING: com_dimer_sizes: not all atoms assigned to a monomer") - endif - endif - - if (this%mpifind) then - call print("Using find_monomer_pairs_MPI", PRINT_NERD) - call find_monomer_pairs_MPI(at,monomer_pairs,mean_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,double_count,this%cutoff,error=error,use_com=.true.) - else - call find_monomer_pairs(at,monomer_pairs,mean_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,double_count,this%cutoff,error=error,use_com=.true.) - end if - n_descriptors = size(pairs_diffs_map) - n_cross=n_descriptors*(size(this%signature_one)+size(this%signature_two)) - - deallocate(associated_to_monomer) - deallocate(pairs_diffs_map) - deallocate(monomer_pairs) - deallocate(monomer_one_index) - deallocate(monomer_two_index) - deallocate(mean_pos_diffs) - - if( present(n_index) ) n_index = size(this%signature_one) + size(this%signature_two) - - endsubroutine com_dimer_sizes - - subroutine general_dimer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - - type(general_dimer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer, dimension(:,:), allocatable :: monomer_one_index, monomer_two_index, monomer_pairs - integer, dimension(:), allocatable :: pairs_diffs_map - real(dp), dimension(:,:), allocatable :: mean_pos_diffs - logical, dimension(:), allocatable :: associated_to_monomer - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_dimer_sizes: descriptor object not initialised", error) - endif - - allocate(associated_to_monomer(at%N)) - associated_to_monomer=.false. - - call find_general_monomer(at,monomer_one_index,this%signature_one,associated_to_monomer,this%monomer_one_cutoff,this%atom_ordercheck,error) - if (this%monomers_identical) then - allocate(monomer_two_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_two_index = monomer_one_index - else - call find_general_monomer(at,monomer_two_index,this%signature_two,associated_to_monomer,this%monomer_two_cutoff,this%atom_ordercheck,error) - end if - - if(.not. all(associated_to_monomer)) then - if(this%strict) then - RAISE_ERROR("general_dimer_sizes: not all atoms assigned to a monomer", error) - else - call print("WARNING: general_dimer_sizes: not all atoms assigned to a monomer") - endif - endif - - if (this%mpifind) then - call print("Using find_monomer_pairs_MPI", PRINT_NERD) - call find_monomer_pairs_MPI(at,monomer_pairs,mean_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,this%double_count,this%cutoff,error=error,use_com=this%use_com) - else - call find_monomer_pairs(at,monomer_pairs,mean_pos_diffs,pairs_diffs_map,monomer_one_index,monomer_two_index,this%monomers_identical,this%double_count,this%cutoff,error=error,use_com=this%use_com) - end if - n_descriptors = size(pairs_diffs_map) - n_cross=n_descriptors*(size(this%signature_one)+size(this%signature_two)) - - deallocate(associated_to_monomer) - deallocate(pairs_diffs_map) - deallocate(monomer_pairs) - deallocate(monomer_one_index) - deallocate(monomer_two_index) - deallocate(mean_pos_diffs) - - if( present(n_index) ) n_index = size(this%signature_one) + size(this%signature_two) - endsubroutine general_dimer_sizes - - subroutine general_trimer_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - - type(general_trimer), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer, dimension(:), allocatable :: triplets_diffs_map - integer, dimension(:,:), allocatable :: monomer_one_index, monomer_two_index, monomer_three_index, monomer_triplets - real(dp), dimension(:,:), allocatable :: triplets_diffs - logical, dimension(:), allocatable :: associated_to_monomer - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("general_trimer_sizes: descriptor object not initialised", error) - endif - - allocate(associated_to_monomer(at%N)) - associated_to_monomer=.false. - - call find_general_monomer(at,monomer_one_index,this%signature_one,associated_to_monomer,this%monomer_one_cutoff,this%atom_ordercheck,error) - if (this%one_two_identical) then - allocate(monomer_two_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_two_index = monomer_one_index - else - call find_general_monomer(at,monomer_two_index,this%signature_two,associated_to_monomer,this%monomer_two_cutoff,this%atom_ordercheck,error) - end if - if (this%one_three_identical) then - allocate(monomer_three_index(size(monomer_one_index,1),size(monomer_one_index,2))) - monomer_three_index = monomer_one_index - else if (this%two_three_identical) then - allocate(monomer_three_index(size(monomer_two_index,1),size(monomer_two_index,2))) - monomer_three_index = monomer_two_index - else - call find_general_monomer(at,monomer_three_index,this%signature_three,associated_to_monomer,this%monomer_three_cutoff,this%atom_ordercheck,error) - end if - - if(.not. all(associated_to_monomer)) then - if(this%strict) then - RAISE_ERROR("general_trimer_sizes: not all atoms assigned to a monomer", error) - else - call print("WARNING: general_trimer_sizes: not all atoms assigned to a monomer") - endif - endif - - if (this%use_com) then - RAISE_ERROR("general_trimer_calc: use_com=T not implemented yet", error) - end if - if(this%mpifind) then - call print("Using find_monomer_triplets_MPI", PRINT_NERD) - call find_monomer_triplets_MPI(at,monomer_triplets,triplets_diffs,triplets_diffs_map,monomer_one_index,monomer_two_index,monomer_three_index,this%one_two_identical,this%one_three_identical,this%two_three_identical,this%cutoff,error,use_com=.false.) - else - call find_monomer_triplets(at,monomer_triplets,triplets_diffs,triplets_diffs_map,monomer_one_index,monomer_two_index,monomer_three_index,this%one_two_identical,this%one_three_identical,this%two_three_identical,this%cutoff,error) - end if - n_descriptors = size(triplets_diffs_map) - n_cross=n_descriptors*(size(this%signature_one)+size(this%signature_two)+size(this%signature_three)) - - deallocate(monomer_one_index) - deallocate(monomer_two_index) - deallocate(monomer_three_index) - if(allocated(monomer_triplets)) deallocate(monomer_triplets) - if(allocated(triplets_diffs)) deallocate(triplets_diffs) - deallocate(associated_to_monomer) - - if( present(n_index) ) n_index = size(this%signature_one) + & - size(this%signature_two) + size(this%signature_three) - - endsubroutine general_trimer_sizes - - subroutine rdf_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(rdf), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("rdf_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine rdf_sizes - - subroutine as_distance_2b_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(as_distance_2b), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - real(dp) :: r_ij - integer :: i, j, n - logical :: Zi1, Zi2, Zj1, Zj2 - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("as_distance_2b_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - Zi1 = (this%Z1 == 0) .or. (at%Z(i) == this%Z1) - Zi2 = (this%Z2 == 0) .or. (at%Z(i) == this%Z2) - do n = 1, n_neighbours(at,i) - j = neighbour(at,i,n,distance=r_ij) - if( r_ij > this%max_cutoff ) cycle - - Zj1 = (this%Z1 == 0) .or. (at%Z(j) == this%Z1) - Zj2 = (this%Z2 == 0) .or. (at%Z(j) == this%Z2) - if( .not. ( ( Zi1 .and. Zj2 ) .or. ( Zi2 .and. Zj1 ) ) ) cycle ! this pair doesn't belong to the descriptor type - - n_descriptors = n_descriptors + 1 - n_cross = n_cross + 4 + n_neighbours(at,i,max_dist=this%coordination_cutoff) + n_neighbours(at,j,max_dist=this%coordination_cutoff) - enddo - enddo - - if( present(n_index) ) n_index = 2 - - endsubroutine as_distance_2b_sizes - - subroutine molecule_lo_d_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(molecule_lo_d), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer, dimension(:,:), allocatable :: monomer_index - integer :: i - logical, dimension(:), allocatable :: associated_to_monomer - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("molecule_lo_d_sizes: descriptor object not initialised", error) - endif - - n_descriptors=1 - n_cross = this%n_atoms - -!!$ allocate(associated_to_monomer(at%N)) -!!$ associated_to_monomer=.false. -!!$ -!!$ call find_general_monomer(at,monomer_index,this%signature,associated_to_monomer,this%cutoff,this%atom_ordercheck,error) -!!$ n_descriptors = size(monomer_index,2) -!!$ n_cross=size(monomer_index) - - if( present(n_index) ) n_index = this%n_atoms - - endsubroutine molecule_lo_d_sizes - - subroutine alex_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(alex), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("alex_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if( at%Z(i) /= this%Z .and. this%Z /=0 ) cycle - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine alex_sizes - - subroutine distance_Nb_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(distance_Nb), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i, j, n - logical :: Zi1, Zi2, Zj1, Zj2 - real(dp) :: r_ij - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("distance_Nb_sizes: descriptor object not initialised", error) - endif - - call distance_Nb_calc_get_clusters(this,at,n_descriptors=n_descriptors,mask=mask,error=error) - n_cross = n_descriptors * this%order - - if( present(n_index) ) n_index = this%order - - endsubroutine distance_Nb_sizes - - subroutine soap_express_sizes(this,at,n_descriptors,n_cross,mask,n_index,error) - type(soap_express), intent(in) :: this - type(atoms), intent(in) :: at - integer, intent(out) :: n_descriptors, n_cross - logical, dimension(:), intent(in), optional :: mask - integer, intent(out), optional :: n_index - integer, optional, intent(out) :: error - - integer :: i - - INIT_ERROR(error) - - if(.not. this%initialised) then - RAISE_ERROR("soap_express_sizes: descriptor object not initialised", error) - endif - - n_descriptors = 0 - n_cross = 0 - - do i = 1, at%N - if(present(mask)) then - if(.not. mask(i)) cycle - endif - n_descriptors = n_descriptors + 1 - n_cross = n_cross + n_neighbours(at,i,max_dist=this%cutoff) + 1 - enddo - - if( present(n_index) ) n_index = 1 - - endsubroutine soap_express_sizes - - function descriptor_n_permutations(this,error) - type(descriptor), intent(in) :: this - integer, optional, intent(out) :: error - - integer :: descriptor_n_permutations, i - - INIT_ERROR(error) - - selectcase(this%descriptor_type) - case(DT_BISPECTRUM_SO4,DT_BISPECTRUM_SO3,DT_BEHLER,DT_DISTANCE_2b,DT_COORDINATION, & - DT_ANGLE_3B,DT_CO_ANGLE_3B,DT_CO_DISTANCE_2b,DT_COSNX,DT_TRIHIS,DT_WATER_MONOMER,DT_BOND_REAL_SPACE,& - DT_ATOM_REAL_SPACE,DT_POWER_SO3,DT_POWER_SO4,DT_SOAP,DT_RDF, DT_ALEX, DT_COM_DIMER,DT_SOAP_EXPRESS) - - descriptor_n_permutations = 1 - - case(DT_WATER_DIMER) - descriptor_n_permutations = NP_WATER_DIMER - case(DT_A2_DIMER) - descriptor_n_permutations = NP_A2_DIMER - case(DT_AB_DIMER) - descriptor_n_permutations = NP_AB_DIMER - case(DT_AN_MONOMER) - if(this%descriptor_AN_monomer%do_atomic) then - descriptor_n_permutations = factorial(this%descriptor_AN_monomer%N-1) - else - descriptor_n_permutations = factorial(this%descriptor_AN_monomer%N) - endif - case(DT_GENERAL_MONOMER) - if (.not. this%descriptor_general_monomer%permutation_data%initialised)then - RAISE_ERROR("descriptor_n_permutations: permutation_data not initialised "//this%descriptor_type,error) - end if - descriptor_n_permutations = this%descriptor_general_monomer%permutation_data%n_perms - case(DT_GENERAL_DIMER) - if (.not. this%descriptor_general_dimer%permutation_data%initialised)then - RAISE_ERROR("descriptor_n_permutations: permutation_data not initialised "//this%descriptor_type,error) - end if - descriptor_n_permutations = this%descriptor_general_dimer%permutation_data%n_perms - case(DT_GENERAL_TRIMER) - if (.not. this%descriptor_general_trimer%permutation_data%initialised)then - RAISE_ERROR("descriptor_n_permutations: permutation_data not initialised "//this%descriptor_type,error) - end if - descriptor_n_permutations = this%descriptor_general_trimer%permutation_data%n_perms - case(DT_MOLECULE_LO_D) - if (.not. this%descriptor_molecule_lo_d%permutation_data%initialised)then - RAISE_ERROR("descriptor_n_permutations: permutation_data not initialised "//this%descriptor_type,error) - end if - descriptor_n_permutations = this%descriptor_molecule_lo_d%permutation_data%n_perms - case(DT_DISTANCE_NB) - descriptor_n_permutations = this%descriptor_distance_Nb%n_permutations - case default - RAISE_ERROR("descriptor_n_permutations: unknown descriptor type "//this%descriptor_type,error) - endselect - - endfunction descriptor_n_permutations - - subroutine descriptor_permutations(this,permutations,error) - type(descriptor), intent(in) :: this - type(permutation_data_type) :: my_permutation_data - integer, dimension(:,:), intent(out) :: permutations - integer, optional, intent(out) :: error - - integer :: i, d, np, n, m, ip, j - integer,dimension(1) :: unit_vec - integer, dimension(:), allocatable :: this_perm - integer, dimension(:,:), allocatable :: distance_matrix, atom_permutations, sliced_permutations - - INIT_ERROR(error) - - d = descriptor_dimensions(this,error) - np = descriptor_n_permutations(this,error) - call check_size('permutations',permutations, (/d,np/),'descriptor_permutations',error) - - selectcase(this%descriptor_type) - case(DT_BISPECTRUM_SO4,DT_BISPECTRUM_SO3,DT_BEHLER,DT_DISTANCE_2b,DT_COORDINATION, & - DT_ANGLE_3B,DT_CO_ANGLE_3B,DT_CO_DISTANCE_2b,DT_COSNX,DT_TRIHIS,DT_WATER_MONOMER,DT_BOND_REAL_SPACE,& - DT_ATOM_REAL_SPACE,DT_POWER_SO3,DT_POWER_SO4,DT_SOAP,DT_RDF, DT_ALEX, DT_COM_DIMER,DT_SOAP_EXPRESS) - - permutations(:,1) = (/ (i, i = 1, size(permutations,1)) /) - case(DT_WATER_DIMER) - permutations(:,1) = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15/) ! original order - permutations(:,2) = (/1, 3, 2, 4, 5, 7, 6, 8, 9, 10, 13, 14, 11, 12, 15/) ! swap Hs on monomer A - permutations(:,3) = (/1, 2, 3, 5, 4, 6, 7, 9, 8, 10, 12, 11, 14, 13, 15/) ! swap Hs on monomer B - permutations(:,4) = (/1, 3, 2, 5, 4, 7, 6, 9, 8, 10, 14, 13, 12, 11, 15/) ! swap Hs on both monomers - permutations(:,5) = (/1, 8, 9, 6, 7, 4, 5, 2, 3, 15, 11, 13, 12, 14, 10/) ! swap monomers A and B - permutations(:,6) = (/1, 9, 8, 6, 7, 5, 4, 2, 3, 15, 12, 14, 11, 13, 10/) ! swap monomers and Hs on monomer A - permutations(:,7) = (/1, 8, 9, 7, 6, 4, 5, 3, 2, 15, 13, 11, 14, 12, 10/) ! swap monomers and Hs on monomer B - permutations(:,8) = (/1, 9, 8, 7, 6, 5, 4, 3, 2, 15, 14, 12, 13, 11, 10/) ! swap monomers and Hs on both monomers - - case(DT_A2_DIMER) - permutations(:,1) = (/1, 2, 3, 4, 5, 6/) ! original order - permutations(:,2) = (/1, 2, 5, 6, 3, 4/) ! swap atoms on monomer A - permutations(:,3) = (/1, 2, 4, 3, 6, 5/) ! swap atoms on monomer B - permutations(:,4) = (/1, 2, 6, 5, 4, 3/) ! swap atoms on both monomers - permutations(:,5) = (/2, 1, 3, 5, 4, 6/) ! swap monomers A and B - permutations(:,6) = (/2, 1, 5, 3, 6, 4/) ! swap monomers and atoms on monomer A - permutations(:,7) = (/2, 1, 4, 6, 3, 5/) ! swap monomers and atoms on monomer B - permutations(:,8) = (/2, 1, 6, 4, 5, 3/) ! swap monomers and atoms on both monomers - - case(DT_AB_DIMER) - permutations(:,1) = (/1, 2, 3, 4, 5, 6/) ! original order - permutations(:,2) = (/2, 1, 3, 4, 6, 5/) ! swap monomers - - case(DT_AN_MONOMER) - allocate(distance_matrix(this%descriptor_AN_monomer%N,this%descriptor_AN_monomer%N), atom_permutations(this%descriptor_AN_monomer%N,np)) - - if(this%descriptor_AN_monomer%do_atomic) then - atom_permutations(1,:) = 0 - call generate_AN_permutations(atom_permutations(2:this%descriptor_AN_monomer%N,:)) - atom_permutations = atom_permutations + 1 - else - call generate_AN_permutations(atom_permutations(:,:)) - endif - - i = 0 - distance_matrix = 0 - do n = 2, this%descriptor_AN_monomer%N - i = i + 1 - distance_matrix(1,n) = i - distance_matrix(n,1) = i - do m = n+1, this%descriptor_AN_monomer%N - i = i + 1 - distance_matrix(m,n) = i - distance_matrix(n,m) = i - enddo - enddo - - do ip = 1, np - i = 0 - do n = 2, this%descriptor_AN_monomer%N - i = i + 1 - permutations(i,ip) = distance_matrix(atom_permutations(1,ip),atom_permutations(n,ip)) - do m = n+1, this%descriptor_AN_monomer%N - i = i + 1 - permutations(i,ip) = distance_matrix(atom_permutations(m,ip),atom_permutations(n,ip)) - enddo - enddo - enddo - deallocate(distance_matrix,atom_permutations) - - - case(DT_GENERAL_MONOMER) - if (.not. this%descriptor_general_monomer%permutation_data%initialised) then - RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error) - else if (this%descriptor_general_monomer%permutation_data%perm_number /= 1) then - RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error) - end if - - call permutation_data_copy(my_permutation_data, this%descriptor_general_monomer%permutation_data) - - if (my_permutation_data%n_perms > 1) then - call next(my_permutation_data, 1) - end if - - permutations=my_permutation_data%dist_vec_permutations - - case(DT_GENERAL_DIMER) - if (.not. this%descriptor_general_dimer%permutation_data%initialised)then - RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error) - else if (this%descriptor_general_dimer%permutation_data%perm_number /= 1) then - RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error) - end if - - call permutation_data_copy(my_permutation_data, this%descriptor_general_dimer%permutation_data) - - if (my_permutation_data%n_perms > 1) then - call next(my_permutation_data, 1) - end if - - permutations=my_permutation_data%dist_vec_permutations - - case(DT_GENERAL_TRIMER) - if (.not. this%descriptor_general_trimer%permutation_data%initialised)then - RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error) - else if (this%descriptor_general_trimer%permutation_data%perm_number /= 1) then - RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error) - end if - - call permutation_data_copy(my_permutation_data, this%descriptor_general_trimer%permutation_data) - - if (my_permutation_data%n_perms > 1) then - call next(my_permutation_data, 1) - end if - - permutations=my_permutation_data%dist_vec_permutations - - case(DT_MOLECULE_LO_D) - if (.not. this%descriptor_molecule_lo_d%permutation_data%initialised) then - RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error) - else if (this%descriptor_molecule_lo_d%permutation_data%perm_number /= 1) then - RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error) - end if - - call permutation_data_copy(my_permutation_data, this%descriptor_molecule_lo_d%permutation_data) - - if (my_permutation_data%n_perms > 1) then - call next(my_permutation_data, 1) - end if - - allocate(sliced_permutations(size(this%descriptor_molecule_lo_d%included_components),my_permutation_data%n_perms)) - allocate(this_perm(size(this%descriptor_molecule_lo_d%included_components))) - sliced_permutations =my_permutation_data%dist_vec_permutations(this%descriptor_molecule_lo_d%included_components,:) - - do j=1,my_permutation_data%n_perms - this_perm=sliced_permutations(:,j) - do i=1,size(this%descriptor_molecule_lo_d%included_components) - unit_vec=maxloc(this%descriptor_molecule_lo_d%included_components, mask=this%descriptor_molecule_lo_d%included_components .eq. this_perm(i)) - if (unit_vec(1) == 0) then - RAISE_ERROR("descriptor_permutations: you have specified symmetries between atoms with different connectivity",error) - end if - permutations(i,j) =unit_vec(1) - end do - end do -! begin brau - if(size(this%descriptor_molecule_lo_d%included_components) > maxval(this%descriptor_molecule_lo_d%included_components)) then - permutations=my_permutation_data%dist_vec_permutations - end if -! end brau - case(DT_DISTANCE_NB) - permutations = this%descriptor_distance_Nb%permutations - case default - RAISE_ERROR("descriptor_permutations: unknown descriptor type "//this%descriptor_type,error) - endselect - - endsubroutine descriptor_permutations - - subroutine generate_AN_permutations(this,list,error) - integer, dimension(:,:), intent(out) :: this - integer, dimension(:), intent(in), optional :: list - integer, optional, intent(out) :: error - - integer, dimension(:), allocatable :: my_list, my_list_uniq - integer :: i, n, m, p, np, min_tail, min_tail_i, tmp_i - - INIT_ERROR(error) - - if(present(list)) then - n = size(list) - allocate(my_list(n)) - my_list = list - else - n = size(this,1) - allocate(my_list(n)) - my_list = (/(i, i = 1, n)/) - endif - - call uniq(my_list, my_list_uniq) - - np = factorial(size(my_list_uniq)) - - call check_size('this', this, (/n,np/), 'generate_permutations',error) - - call sort_array(my_list) - - this(:,1) = my_list - - do p = 2, np - ! Find longest tail that is ordered in decreasing order. - do m = n - 1, 1, -1 - if(my_list(m) < my_list(m+1)) exit - enddo - - min_tail = my_list(m+1) - min_tail_i = m+1 - ! Find the smallest number bigger than my_list(m) in the tail - do i = m + 1, n - if(min_tail > my_list(i) .and. my_list(m) < my_list(i)) then - min_tail = my_list(i) - min_tail_i = i - endif - enddo - - ! swap - tmp_i = my_list(m) - my_list(m) = my_list(min_tail_i) - my_list(min_tail_i) = tmp_i - - - ! reverse tail - my_list(m+1:n) = my_list(n:m+1:-1) - - this(:,p) = my_list - enddo - - endsubroutine generate_AN_permutations - - subroutine real_space_fourier_coefficients(at,l_max,atom_coefficient) - type(atoms), intent(in) :: at - integer, intent(in) :: l_max - type(neighbour_type), dimension(:), allocatable :: atom_coefficient - - integer :: i, j, n, l, m - real(dp) :: r - real(dp), dimension(3) :: d - - if(.not.allocated(atom_coefficient)) allocate(atom_coefficient(at%N)) - - do i = 1, at%N - if(.not. allocated(atom_coefficient(i)%neighbour)) allocate(atom_coefficient(i)%neighbour(n_neighbours(at,i))) - do n = 1, n_neighbours(at,i) - - j = neighbour(at,i,n,distance = r, diff = d) - atom_coefficient(i)%neighbour(n)%r = r - atom_coefficient(i)%neighbour(n)%u = d / r - - if(.not. allocated(atom_coefficient(i)%neighbour(n)%spherical_harmonics)) allocate( atom_coefficient(i)%neighbour(n)%spherical_harmonics(0:l_max), & - atom_coefficient(i)%neighbour(n)%grad_spherical_harmonics(0:l_max) ) - do l = 0, l_max - if(.not. allocated(atom_coefficient(i)%neighbour(n)%spherical_harmonics(l)%m)) & - allocate(atom_coefficient(i)%neighbour(n)%spherical_harmonics(l)%m(-l:l)) - if(.not. allocated(atom_coefficient(i)%neighbour(n)%grad_spherical_harmonics(l)%mm)) & - allocate(atom_coefficient(i)%neighbour(n)%grad_spherical_harmonics(l)%mm(3,-l:l)) - - atom_coefficient(i)%neighbour(n)%spherical_harmonics(l)%m = CPLX_ZERO - atom_coefficient(i)%neighbour(n)%grad_spherical_harmonics(l)%mm = CPLX_ZERO - - do m = -l, l - atom_coefficient(i)%neighbour(n)%spherical_harmonics(l)%m(m) = SphericalYCartesian(l,m,d) - atom_coefficient(i)%neighbour(n)%grad_spherical_harmonics(l)%mm(:,m) = GradSphericalYCartesian(l,m,d) - enddo - enddo - enddo - enddo - - endsubroutine real_space_fourier_coefficients - - function real_space_covariance_coefficient(anc1,anc2,i1,i2,alpha,l_max,f1,f2) - type(neighbour_type), dimension(:), intent(in) :: anc1, anc2 - real(dp), intent(in) :: alpha - integer, intent(in) :: i1, i2, l_max - real(dp), dimension(:,:), intent(out), optional :: f1, f2 - - real(dp) :: real_space_covariance_coefficient - - complex(dp) :: real_space_covariance_in, I_lm1m2 - integer :: n1, n2, l, m1, m2, k - real(dp) :: r1, r2, arg_bess, fac_exp, mo_spher_bess_fi_ki_l, mo_spher_bess_fi_ki_lm, mo_spher_bess_fi_ki_lmm, mo_spher_bess_fi_ki_lp, grad_mo_spher_bess_fi_ki_l - real(dp), dimension(3) :: u1, u2, grad_arg_bess1, grad_fac_exp1, grad_arg_bess2, grad_fac_exp2 - type(cplx_2d), dimension(:), allocatable :: integral_r - type(grad_spherical_harmonics_overlap_type), dimension(:), allocatable :: grad_integral_r1, grad_integral_r2 - - logical :: do_derivative - - do_derivative = (present(f1) .or. present(f2)) - - real_space_covariance_in = CPLX_ZERO - - allocate(integral_r(0:l_max)) - do l = 0, l_max - allocate(integral_r(l)%mm(-l:l,-l:l)) - integral_r(l)%mm = CPLX_ZERO - enddo - - if(present(f1)) then - allocate(grad_integral_r1(0:size(anc1(i1)%neighbour))) - do n1 = 0, size(anc1(i1)%neighbour) - allocate(grad_integral_r1(n1)%grad_integral(0:l_max)) - do l = 0, l_max - allocate(grad_integral_r1(n1)%grad_integral(l)%mm(3,-l:l,-l:l)) - grad_integral_r1(n1)%grad_integral(l)%mm = CPLX_ZERO - enddo - enddo - endif - - if(present(f2)) then - allocate(grad_integral_r2(0:size(anc2(i2)%neighbour))) - do n2 = 0, size(anc2(i2)%neighbour) - allocate(grad_integral_r2(n2)%grad_integral(0:l_max)) - do l = 0, l_max - allocate(grad_integral_r2(n2)%grad_integral(l)%mm(3,-l:l,-l:l)) - grad_integral_r2(n2)%grad_integral(l)%mm = CPLX_ZERO - enddo - enddo - endif - do n1 = 1, size(anc1(i1)%neighbour) - r1 = anc1(i1)%neighbour(n1)%r - u1 = anc1(i1)%neighbour(n1)%u - do n2 = 1, size(anc2(i2)%neighbour) - r2 = anc2(i2)%neighbour(n2)%r - - u2 = anc2(i2)%neighbour(n2)%u - - arg_bess = alpha*r1*r2 - fac_exp = exp(-0.5_dp*alpha*(r1**2+r2**2)) - - if(present(f1)) then - grad_arg_bess1 = alpha*r2*u1 - grad_fac_exp1 = -fac_exp*alpha*r1*u1 - endif - - if(present(f2)) then - grad_arg_bess2 = alpha*r1*u2 - grad_fac_exp2 = -fac_exp*alpha*r2*u2 - endif - - do l = 0, l_max - if( l == 0 ) then - mo_spher_bess_fi_ki_lm = cosh(arg_bess)/arg_bess - mo_spher_bess_fi_ki_l = sinh(arg_bess)/arg_bess - if(do_derivative) mo_spher_bess_fi_ki_lp = mo_spher_bess_fi_ki_lm - (2*l+1)*mo_spher_bess_fi_ki_l / arg_bess - else - mo_spher_bess_fi_ki_lmm = mo_spher_bess_fi_ki_lm - mo_spher_bess_fi_ki_lm = mo_spher_bess_fi_ki_l - if(do_derivative) then - mo_spher_bess_fi_ki_l = mo_spher_bess_fi_ki_lp - mo_spher_bess_fi_ki_lp = mo_spher_bess_fi_ki_lm - (2*l+1)*mo_spher_bess_fi_ki_l / arg_bess - else - mo_spher_bess_fi_ki_l = mo_spher_bess_fi_ki_lmm - (2*l-1)*mo_spher_bess_fi_ki_lm / arg_bess - endif - - endif - - - if(do_derivative) grad_mo_spher_bess_fi_ki_l = 0.5_dp * (mo_spher_bess_fi_ki_lp - mo_spher_bess_fi_ki_l / arg_bess + mo_spher_bess_fi_ki_lm) - - do m1 = -l, l - do m2 = -l, l - I_lm1m2 = conjg(anc1(i1)%neighbour(n1)%spherical_harmonics(l)%m(m1)) * anc2(i2)%neighbour(n2)%spherical_harmonics(l)%m(m2) * mo_spher_bess_fi_ki_l*fac_exp - integral_r(l)%mm(m2,m1) = integral_r(l)%mm(m2,m1) + I_lm1m2 - if(present(f1)) then - grad_integral_r1(n1)%grad_integral(l)%mm(:,m2,m1) = grad_integral_r1(n1)%grad_integral(l)%mm(:,m2,m1) + & - anc2(i2)%neighbour(n2)%spherical_harmonics(l)%m(m2) * & - ( conjg(anc1(i1)%neighbour(n1)%grad_spherical_harmonics(l)%mm(:,m1)) * mo_spher_bess_fi_ki_l*fac_exp + & - conjg(anc1(i1)%neighbour(n1)%spherical_harmonics(l)%m(m1)) * ( grad_mo_spher_bess_fi_ki_l * grad_arg_bess1 * fac_exp + mo_spher_bess_fi_ki_l * grad_fac_exp1 ) ) - endif - - if(present(f2)) then - grad_integral_r2(n2)%grad_integral(l)%mm(:,m2,m1) = grad_integral_r2(n2)%grad_integral(l)%mm(:,m2,m1) + & - conjg(anc1(i1)%neighbour(n1)%spherical_harmonics(l)%m(m1)) * & - ( anc2(i2)%neighbour(n2)%grad_spherical_harmonics(l)%mm(:,m2) * mo_spher_bess_fi_ki_l*fac_exp + & - anc2(i2)%neighbour(n2)%spherical_harmonics(l)%m(m2) * ( grad_mo_spher_bess_fi_ki_l * grad_arg_bess2 * fac_exp + mo_spher_bess_fi_ki_l * grad_fac_exp2 ) ) - endif - - enddo - enddo - enddo - enddo - enddo - - if(present(f1)) then - f1 = 0.0_dp - do n1 = 0, size(anc1(i1)%neighbour) - do l = 0, l_max - do k = 1, 3 - f1(k,n1+1) = f1(k,n1+1) + real(sum(conjg(grad_integral_r1(n1)%grad_integral(l)%mm(k,:,:))*integral_r(l)%mm(:,:))) - enddo - enddo - enddo - f1 = 2.0_dp * f1 - endif - - if(present(f2)) then - f2 = 0.0_dp - do n2 = 0, size(anc2(i2)%neighbour) - do l = 0, l_max - do k = 1, 3 - f2(k,n2+1) = f2(k,n2+1) + real(sum(conjg(grad_integral_r2(n2)%grad_integral(l)%mm(k,:,:))*integral_r(l)%mm(:,:))) - enddo - enddo - enddo - f2 = 2.0_dp * f2 - endif - - do l = 0, l_max - real_space_covariance_in = real_space_covariance_in + sum(conjg(integral_r(l)%mm) * integral_r(l)%mm) - enddo - real_space_covariance_coefficient = real(real_space_covariance_in) - - do l = 0, l_max - deallocate(integral_r(l)%mm) - enddo - deallocate(integral_r) - - if(present(f1)) then - do n1 = 0, size(anc1(i1)%neighbour) - do l = 0, l_max - deallocate(grad_integral_r1(n1)%grad_integral(l)%mm) - enddo - deallocate(grad_integral_r1(n1)%grad_integral) - enddo - deallocate(grad_integral_r1) - endif - - if(present(f2)) then - do n2 = 0, size(anc2(i2)%neighbour) - do l = 0, l_max - deallocate(grad_integral_r2(n2)%grad_integral(l)%mm) - enddo - deallocate(grad_integral_r2(n2)%grad_integral) - enddo - deallocate(grad_integral_r2) - endif - - endfunction real_space_covariance_coefficient - - function real_space_covariance(at1,at2,i1,i2,alpha,l_max,f1,f2) - type(atoms), intent(in) :: at1, at2 - real(dp), intent(in) :: alpha - integer, intent(in) :: i1, i2, l_max - real(dp), dimension(:,:), intent(inout), optional :: f1, f2 - - real(dp) :: real_space_covariance - - complex(dp) :: real_space_covariance_in, I_lm1m2 - integer :: j1, j2, n1, n2, l, m1, m2 - real(dp) :: r1, r2, arg_bess, fac_exp, mo_spher_bess_fi_ki_l, mo_spher_bess_fi_ki_lm, mo_spher_bess_fi_ki_lmm - real(dp), dimension(3) :: d1, d2 - type(cplx_2d), dimension(:), allocatable :: integral_r - - logical :: do_derivative - - do_derivative = (present(f1) .or. present(f2)) - - real_space_covariance_in = CPLX_ZERO - - allocate(integral_r(0:l_max)) - do l = 0, l_max - allocate(integral_r(l)%mm(-l:l,-l:l)) - integral_r(l)%mm = CPLX_ZERO - enddo - - do n1 = 1, n_neighbours(at1,i1) - j1 = neighbour(at1,i1,n1,distance = r1, diff = d1) - do n2 = 1, n_neighbours(at2,i2) - j2 = neighbour(at2,i2,n2,distance = r2, diff = d2) - - arg_bess = alpha*r1*r2 - fac_exp = exp(-0.5_dp*alpha*(r1**2+r2**2)) - - do l = 0, l_max - if( l == 0 ) then - mo_spher_bess_fi_ki_lmm = sinh(arg_bess)/arg_bess - mo_spher_bess_fi_ki_l = mo_spher_bess_fi_ki_lmm - elseif( l == 1 ) then - mo_spher_bess_fi_ki_lm = ( arg_bess*cosh(arg_bess) - sinh(arg_bess) ) / arg_bess**2 - mo_spher_bess_fi_ki_l = mo_spher_bess_fi_ki_lm - else - mo_spher_bess_fi_ki_l = mo_spher_bess_fi_ki_lmm - (2*l+1)*mo_spher_bess_fi_ki_lm / arg_bess - mo_spher_bess_fi_ki_lm = mo_spher_bess_fi_ki_l - mo_spher_bess_fi_ki_lmm = mo_spher_bess_fi_ki_lm - endif - - do m1 = -l, l - do m2 = -l, l - I_lm1m2 = conjg(SphericalYCartesian(l,m1,d1)) * SphericalYCartesian(l,m2,d2)*mo_spher_bess_fi_ki_l*fac_exp - integral_r(l)%mm(m2,m1) = integral_r(l)%mm(m2,m1) + I_lm1m2 - enddo - enddo - enddo - enddo - enddo - - do l = 0, l_max - real_space_covariance_in = real_space_covariance_in + sum(conjg(integral_r(l)%mm) * integral_r(l)%mm) - enddo - real_space_covariance = real(real_space_covariance_in) - - do l = 0, l_max - deallocate(integral_r(l)%mm) - enddo - deallocate(integral_r) - - endfunction real_space_covariance - - function RadialFunction(this,r,i) - type(RadialFunction_type), intent(in) :: this - real(dp), intent(in) :: r - integer, intent(in) :: i - - real(dp) :: RadialFunction - - real(dp), dimension(this%n_max) :: h - integer :: j - - if( r < this%cutoff ) then - do j = 1, this%n_max - h(j) = (this%cutoff-r)**(j+2) / this%NormFunction(j) - enddo - RadialFunction = dot_product(this%RadialTransform(:,i),h) - else - RadialFunction = 0.0_dp - endif - - endfunction RadialFunction - - function GradRadialFunction(this,r,i) - type(RadialFunction_type), intent(in) :: this - real(dp), intent(in) :: r - integer, intent(in) :: i - - real(dp) :: GradRadialFunction - - real(dp), dimension(this%n_max) :: h - integer :: j - - if( r < this%cutoff ) then - do j = 1, this%n_max - h(j) = - (j+2) * (this%cutoff-r)**(j+1) / this%NormFunction(j) - enddo - GradRadialFunction = dot_product(this%RadialTransform(:,i),h) - else - GradRadialFunction = 0.0_dp - endif - - endfunction GradRadialFunction - - subroutine bond_list_next_layer(shallow_list,deep_list,error) - ! shallow list contains all pairs of bonded atoms, whereas deep_list will include pairs of atoms - ! separated by two bonds, three bonds, etc., depending on how many times this function has been called - type(Table), intent(in):: shallow_list - type(Table), intent(inout):: deep_list - type(Table) :: connected, pairs_containing_atom_i, pairs_containing_atom_j, deep_list_input, bonded_to_atom_i, bonded_to_atom_j - integer :: i, atom_i, atom_j, j, atom_k, k, N_input,N_deep,N_shallow - integer, intent(inout), optional :: error - logical, dimension(:), allocatable :: mask_deep, mask_shallow - logical :: i_k_present, k_i_present, j_k_present, k_j_present - - allocate(mask_deep(deep_list%N)) - allocate(mask_shallow(shallow_list%N)) - mask_deep = .False. - mask_shallow = .False. - - ! make a copy of the deep list input - deep_list_input = deep_list - N_input=deep_list_input%N - N_shallow=shallow_list%N - - ! loop over pairs in deep_list_input - do i=1,N_input - - - atom_i = deep_list_input%int(1,i) - atom_j = deep_list_input%int(2,i) - - ! select the 1st neighbours of atom_i and atom_j - mask_shallow = shallow_list%int(1,:N_shallow) .eq. atom_i .or. shallow_list%int(2,:N_shallow) .eq. atom_i - call select(bonded_to_atom_i, shallow_list, row_mask=mask_shallow) - - mask_shallow = shallow_list%int(1,:N_shallow) .eq. atom_j .or. shallow_list%int(2,:N_shallow) .eq. atom_j - call select(bonded_to_atom_j, shallow_list, row_mask=mask_shallow) - - - do k=1,bonded_to_atom_j%N ! and append distances between atom i and all atoms bonded to atom j - N_deep = deep_list%N - atom_k = bonded_to_atom_j%int(1,k) - if (atom_k .eq. atom_j) then - atom_k = bonded_to_atom_j%int(2,k) - end if - if (atom_k .eq. atom_i) cycle - - i_k_present = any(deep_list%int(1,:N_deep) .eq. atom_i .and. (deep_list%int(2,:N_deep) .eq. atom_k)) - k_i_present = any(deep_list%int(1,:N_deep) .eq. atom_k .and. (deep_list%int(2,:N_deep) .eq. atom_i)) - - if (.not. k_i_present .and. .not. i_k_present) then - call append(deep_list,(/atom_i,atom_k/)) - end if - end do - - do k=1,bonded_to_atom_i%N ! and append distances between atom i and all atoms bonded to atom j - N_deep = deep_list%N - atom_k = bonded_to_atom_i%int(1,k) - if (atom_k .eq. atom_i) then - atom_k = bonded_to_atom_i%int(2,k) - end if - if (atom_k .eq. atom_j) cycle - - j_k_present = any(deep_list%int(1,:N_deep) .eq. atom_j .and. (deep_list%int(2,:N_deep) .eq. atom_k)) - k_j_present = any(deep_list%int(1,:N_deep) .eq. atom_k .and. (deep_list%int(2,:N_deep) .eq. atom_j)) - - if (.not. k_j_present .and. .not. j_k_present) then - call append(deep_list,(/atom_j,atom_k/)) - end if - end do - - end do - - - end subroutine bond_list_next_layer - - subroutine transfer_initialise(this, args_str, error) - type(transfer_parameters_type), intent(inout) :: this - character(len=*), intent(in) :: args_str - integer, optional, intent(out) :: error - - type(Dictionary) :: params - - INIT_ERROR(error) - call initialise(params) - call param_register(params, 'do_transfer', 'false', this%do_transfer, help_string="Enable transfer function") - call param_register(params, 'transfer_factor', '5.0', this%factor, help_string="Transfer function: stretch factor") - call param_register(params, 'transfer_width', '1.0', this%width, help_string="Transfer function: transition width") - call param_register(params, 'transfer_r0', '3.0', this%r0, help_string="Transfer function: transition distance") - - if (.not. param_read_line(params, args_str, ignore_unknown=.true., task='transfer_initialise args_str')) then - RAISE_ERROR("transfer_initialise failed to parse args_str='"//trim(args_str)//"'", error) - endif - call finalise(params) - - if (this%do_transfer) then - call print("Using transfer function with factor="//this%factor//", r0="//this%r0//", width="//this%width) - endif - end subroutine transfer_initialise - - function transferfunction_grad(x, params) result(td) - real(dp), intent(in) :: x - type(transfer_parameters_type), intent(in) :: params - real(dp) :: td - ! for x << r0 - width: params%factor - ! for x >> r0 + width: 1 - td = (params%factor - 1.0_dp) * 0.5_dp*(tanh((params%r0 - x)/params%width) + 1.0_dp) + 1.0_dp - end function transferfunction_grad - - function transferfunction(x, params) result(t) - real(dp), intent(in) :: x - type(transfer_parameters_type), intent(in) :: params - real(dp) :: t - ! for x >> r0 - width: identity (slope=1) - ! for x << r0 + width: linear slope = factor - t = (1.0_dp - params%factor) * 0.5_dp*(params%width*(log(2.0_dp*cosh((x - params%r0)/params%width))) + x + params%r0) + params%factor * x - end function transferfunction - - function graphIsConnected(connectivityMatrix,error) - - logical, dimension(:,:), intent(in) :: connectivityMatrix - integer, intent(out), optional :: error - logical :: graphIsConnected - - logical, dimension(:), allocatable :: visitedVertices - - INIT_ERROR(error) - - if( .not. is_square(connectivityMatrix) ) then - RAISE_ERROR("graphIsConnected: not square matrix",error) - endif - - allocate(visitedVertices(size(connectivityMatrix,1))) - - call graphBFS(connectivityMatrix,1,visitedVertices=visitedVertices,error=error) - graphIsConnected = all(visitedVertices) - - deallocate(visitedVertices) - - endfunction graphIsConnected - - subroutine graphBFS(connectivityMatrix,startVertex,visitedVertices,tree,error) - - logical, dimension(:,:), intent(in) :: connectivityMatrix - integer, intent(in) :: startVertex - logical, dimension(:), target, intent(out), optional :: visitedVertices - integer, dimension(:,:), allocatable, intent(out), optional :: tree - integer, intent(out), optional :: error - - type(LinkedList_i1d), pointer :: LL_edges => null(), LL_remove => null(), LL_tree => null() - - logical, dimension(:), pointer :: my_visitedVertices - integer, dimension(:), pointer :: edge - integer, dimension(2) :: vw - - INIT_ERROR(error) - - if( .not. is_square(connectivityMatrix) ) then - RAISE_ERROR("graphBFS: not square matrix",error) - endif - - if( present( visitedVertices ) ) then - my_visitedVertices => visitedVertices - else - allocate(my_visitedVertices(size(connectivityMatrix,1))) - endif - - my_visitedVertices = .false. - call graphSearch(connectivityMatrix,startVertex,LL_edges,my_visitedVertices,error) - do while( associated(LL_edges) ) - LL_remove => LL_edges - edge => retrieve_node(LL_remove) - vw = edge - call delete_node(LL_edges,LL_remove) - if( .not. my_visitedVertices(vw(2)) ) then - - if(present(tree)) call append(LL_tree,vw) - call graphSearch(connectivityMatrix, vw(2), LL_edges, my_visitedVertices,error) - endif - enddo - - if( .not. present( visitedVertices ) ) deallocate(my_visitedVertices) - - if (present(tree)) then - call retrieve(LL_tree,tree) - call finalise(LL_tree) - endif - - endsubroutine graphBFS - - subroutine graphSearch(connectivityMatrix, vertex, LL_edges, visitedVertices,error) - logical, dimension(:,:), intent(in) :: connectivityMatrix - integer, intent(in) :: vertex - type(LinkedList_i1d), pointer, intent(inout) :: LL_edges - logical, dimension(:), intent(inout) :: visitedVertices - integer, intent(out), optional :: error - - integer :: i - - INIT_ERROR(error) - - if( .not. is_square(connectivityMatrix) ) then - RAISE_ERROR("graphSearch: not square matrix",error) - endif - - visitedVertices(vertex) = .true. - - do i = 1, size(connectivityMatrix,1) - if( connectivityMatrix(i,vertex) ) call append(LL_edges,(/vertex,i/)) - enddo - - endsubroutine graphSearch - - subroutine descriptor_soap_express_get_eimphi_conjg(this, phi, r_ij_sigma_angular_scaled, eimphi) - type(soap_express), intent(in) :: this - real(dp), intent(in) :: phi, r_ij_sigma_angular_scaled - complex(dp), dimension(:), intent(out) :: eimphi - - real(dp) :: cosm2, sinm2, cosm1, sinm1, cos0, sin0, cosphi2 - integer :: l, m, k - - real(dp), dimension(0:this%l_max) :: prefactor_l - complex(dp), dimension(0:this%l_max) :: prefactor_m - - call descriptor_soap_express_get_ilexp(this,r_ij_sigma_angular_scaled,prefactor_l) -! Complex exponential using Euler's formula and Chebyshev recursion - cosm2 = cos(phi) - cosphi2 = 2.0_dp * cosm2 - sinm2 = -sin(phi) - cosm1 = 1.0_dp - sinm1 = 0.0_dp - prefactor_m(0) = CPLX_ONE - do l = 1, this%l_max - cos0 = cosphi2 * cosm1 - cosm2 - sin0 = cosphi2 * sinm1 - sinm2 - cosm2 = cosm1 - sinm2 = sinm1 - cosm1 = cos0 - sinm1 = sin0 - prefactor_m(l) = cmplx(cos0,-sin0,kind=dp) - end do - k = 1 - do l = 0, this%l_max - eimphi(k:k+l) = prefactor_l(l) * prefactor_m(0:l) - k = k + l + 1 - end do - end subroutine - - subroutine descriptor_soap_express_get_ilexp(this, x, prefactor_l) - type(soap_express), intent(in) :: this - real(dp), intent(in) :: x - real(dp), dimension(0:this%l_max), intent(out) :: prefactor_l - - real(dp) :: x2, x4, fl, flm1, flm2 - integer :: l - - real(dp), parameter :: small_number = 1.0e-7_dp, small_number2 = 1.0e-4_dp - - x2 = x**2 - x4 = x**4 - -! Full calculation. This is numerically unstable for small x, that's why -! we have cases below - flm2 = abs( (1.0_dp - exp(-2.0_dp * x2)) / 2.0_dp / x2 ) - flm1 = abs( (x2 - 1.0_dp + exp(-2.0_dp * x2)*(x2+1.0_dp)) / 2.0_dp / x4 ) - - do l = 0, this%l_max - if( l == 0 ) then - if( x < small_number )then - prefactor_l(0) = 1.0_dp - x2 - else - prefactor_l(0) = flm2 - endif - elseif( l == 1 )then - if( x2 < small_number2 ) then - prefactor_l(1) = (x2 - x4)/ this%semifactorial_table(1) - else - prefactor_l(1) = flm1 - endif - else - if( x2**l / this%semifactorial_table(l) * l < small_number )then - fl = x2**l / this%semifactorial_table(l) - else - fl = abs( flm2 - (2.0_dp*l - 1.0_dp)/x2 * flm1 ) - end if - flm2 = flm1 - flm1 = fl - prefactor_l(l) = fl - endif - end do - - endsubroutine - - subroutine descriptor_soap_express_get_plm_array(this,x,plm_array) -! Returns an array with the Associated Legendre polynomials Plm(x), where Pll(x) -! is the highest order in the series, together with, all the lower order ones. -! l is > 0, m can take values from 0 to l and x is within the [-1, 1] domain -! -! plm_array is a 1D array with modified index lm -> k, where -! k = 1 + l*(l+1)/2 + m - type(soap_express), intent(in) :: this - real(dp), intent(in) :: x - real(dp), dimension(this%angular_array_size), intent(out) :: plm_array - - integer :: l, m, i, lmax, k - real(dp), parameter :: small_number = 1.0e-5_dp - - if( abs(x) > 1.0_dp ) then - call system_abort("Bad argument for associated Legendre polynomial") - end if - -! We need these 5 polynomials to initialize the recursion series -! P_00(x), k = 1 - plm_array(1) = 1.0_dp - if( this%l_max > 0 )then -! P_10(x), k = 2 - plm_array(2) = x -! P_11(x), k = 3 - plm_array(3) = -sqrt(1.0_dp-x**2) - if( this%l_max > 1 )then -! P_20(x), k = 4 - plm_array(4) = 1.5_dp*x**2 - 0.5_dp -! P_21(x), k = 5 - plm_array(5) = -3.0_dp * x * sqrt(1.0_dp-x**2) -! P_22(x), k = 6 - plm_array(6) = 3.0_dp - 3.0_dp*x**2 - else - return - end if - else - return - end if - - if( this%l_max == 2 )then - return - else - do l = 3, this%l_max -! First we need to obtain Pl0 and Pl1 with the recursion formula on l: -! Plm(x) = ( (2l-1)*x*Pl-1m(x) - (l-1+m)*Pl-2m(x) ) / (l-m) -! -! m = 0 - k = 1 + l*(l+1)/2 - plm_array(k) = ((2*l-1)*x*plm_array(k-l) + (1-l)*plm_array(k-2*l+1)) / real(l,kind=dp) -! m = 1 - k = k + 1 - plm_array(k) = ((2*l-1)*x*plm_array(k-l) - (l)*plm_array(k-2*l+1)) / real(l-1,kind=dp) - do m = 2, l -! Now we get all the Plm for greater m using a recursion formula on m: -! Plm(x) = - 2(m-1)x/sqrt(1-x**2)Plm-1(x) - (l+m-1)*(l-m+2)*Plm-2(x) -! - k = k + 1 -! If x ~ +-1, we set everything to 0., which is the result when x is exactly +-1 -! We need to do this because our recursion formula diverges for x = +-1 - if( abs(abs(x)-1.0_dp) < small_number )then - plm_array(k) = 0.0_dp - else - plm_array(k) = - 2.0_dp*(m-1)*x/sqrt(1.0_dp-x**2)*plm_array(k-1) - (l+m-1)*(l-m+2)*plm_array(k-2) - end if - end do - end do - end if - - end subroutine - - subroutine descriptor_soap_express_get_radial_array(this,r_ij,coeff) - type(soap_express), intent(in) :: this - real(dp), intent(in) :: r_ij - real(dp), dimension(this%n_max), intent(out) :: coeff - - real(dp) :: r_ij_radial_scaled, cutoff_hard_scaled, cutoff_soft_scaled, atom_sigma_radial_scaled, & - atom_sigma_radial_scaled_2, I_n, N_n, N_np1, I_np1, C1, C2, atom_sigma_radial_filtered, & - r_ij_radial_scaled_filtered, atom_sigma_radial_filtered_2, N_np2, I_np2, & - cutoff_decay_scaled, atom_sigma_radial0_scaled, n_gauss - - integer :: a - - cutoff_decay_scaled = this%cutoff_transition_width / this%cutoff - atom_sigma_radial0_scaled = this%atom_sigma_radial/this%cutoff - n_gauss = sqrt(2.0_dp / atom_sigma_radial0_scaled) / PI**0.25_dp - - r_ij_radial_scaled = r_ij / this%cutoff - cutoff_hard_scaled = 1.0_dp - cutoff_soft_scaled = (this%cutoff - this%cutoff_transition_width) / this%cutoff - - atom_sigma_radial_scaled = ( this%atom_sigma_radial + this%atom_sigma_scaling_radial*r_ij ) / this%cutoff - atom_sigma_radial_scaled_2 = atom_sigma_radial_scaled**2 - -! We have the recursion series starting at n = 0, which means alpha = -2 -! However, we only need to save the expansion coefficients for alpha >= 1 -! This is I_-1 - I_n = 0.0_dp - N_n = 1.0_dp -! This is I_0 - N_np1 = descriptor_soap_express_N_a(cutoff_hard_scaled, -2) - I_np1 = sqrt(PI/2.0_dp) * atom_sigma_radial_scaled * ( & - erf( (cutoff_soft_scaled-r_ij_radial_scaled)/SQRT_TWO/atom_sigma_radial_scaled ) - & - erf( (-r_ij_radial_scaled)/SQRT_TWO/atom_sigma_radial_scaled ) ) / N_np1 -! Speed up the computation of these coefficients - if( this%cutoff_transition_width .feq. 0.0_dp )then - C1 = 0.0_dp - else - C1 = atom_sigma_radial_scaled_2 / cutoff_decay_scaled * & - exp(-0.5_dp * (cutoff_soft_scaled - r_ij_radial_scaled)**2 / atom_sigma_radial_scaled_2) - endif - C2 = atom_sigma_radial_scaled_2 / cutoff_hard_scaled * exp(-0.5_dp * r_ij_radial_scaled**2 / atom_sigma_radial_scaled_2) -! This is different wrt the regular polynomial basis, we only go up to alpha_max-1 - do a = -1, this%n_max-1 - C1 = C1 * cutoff_decay_scaled - C2 = C2 * cutoff_hard_scaled - N_np2 = descriptor_soap_express_N_a(cutoff_hard_scaled, a) -! This is I_alpha - I_np2 = atom_sigma_radial_scaled_2 * (a+1) * N_n/ N_np2 * I_n & - - N_np1 * (r_ij_radial_scaled - cutoff_hard_scaled) / N_np2 * I_np1 & - + C1 / N_np2 & - - C2 / N_np2 - if(a > 0) coeff(a) = I_np2 - N_n = N_np1 - N_np1 = N_np2 - I_n = I_np1 - I_np1 = I_np2 - enddo -! If the atom is less than 3 standard deviations away from the soft cutoff, we add -! also this correction to the integrals. This corresponds to a Gaussian filter. We -! integrate between rcut_soft and rcut_hard in this case - - if( (cutoff_soft_scaled - r_ij_radial_scaled) < 3.0_dp*atom_sigma_radial_scaled )then - atom_sigma_radial_filtered = atom_sigma_radial_scaled * cutoff_decay_scaled / & - this%cutoff_decay_rate / sqrt(atom_sigma_radial_scaled_2 + & - cutoff_decay_scaled**2/this%cutoff_decay_rate**2) - - r_ij_radial_scaled_filtered = (atom_sigma_radial_scaled_2 * cutoff_soft_scaled + & - cutoff_decay_scaled**2/this%cutoff_decay_rate**2*r_ij_radial_scaled) / & - sqrt(atom_sigma_radial_scaled_2 + cutoff_decay_scaled**2/this%cutoff_decay_rate**2) - -! We leave this here because in the future atom_sigma will be rj dependent - atom_sigma_radial_filtered_2 = atom_sigma_radial_filtered**2 -! We have the recursion series starting at n = 0, which means alpha = -2 -! However, we only need to save the expansion coefficients for alpha >= 1 -! This is I_-1 - I_n = 0.0_dp - N_n = 1.0_dp -! This is I_0 - N_np1 = descriptor_soap_express_N_a(cutoff_hard_scaled, -2) - I_np1 = sqrt(PI/2.0_dp) * atom_sigma_radial_filtered * & - ( erf( (cutoff_hard_scaled-r_ij_radial_scaled_filtered)/SQRT_TWO/atom_sigma_radial_filtered ) - & - erf( (cutoff_soft_scaled-r_ij_radial_scaled_filtered)/SQRT_TWO/atom_sigma_radial_filtered ) ) / N_np1 -! Speed up the computation of these coefficients - C2 = atom_sigma_radial_filtered_2 / cutoff_decay_scaled * & - exp(-0.5_dp * (cutoff_soft_scaled - r_ij_radial_scaled_filtered)**2 / atom_sigma_radial_filtered_2) -! This is different wrt the regular polynomial basis, we only go up to this%n_max-1 - do a = -1, this%n_max-1 - C2 = C2 * cutoff_decay_scaled - N_np2 = descriptor_soap_express_N_a(cutoff_hard_scaled, a) -! This is I_alpha - I_np2 = atom_sigma_radial_scaled_2 * (a+1) * N_n/ N_np2 * I_n & - - N_np1 * (r_ij_radial_scaled_filtered - cutoff_hard_scaled) / N_np2 * I_np1 & - - C2 / N_np2 - if(a > 0)then - coeff(a) = coeff(a) + I_np2 - end if - N_n = N_np1 - N_np1 = N_np2 - I_n = I_np1 - I_np1 = I_np2 - end do - endif -! Now we obtain the overlap integral between the atomic density and the Gaussian centered at the origin -! We assume that at the soft cutoff the Gaussian basis function is approx. zero, and we only -! compute the overlap coefficient if the atom is close to the origin -! Note that the sigma for the Gaussian basis function and the atom's sigma are not the same if there is -! sigma scaling - if( r_ij_radial_scaled < 3.0_dp*(atom_sigma_radial0_scaled + atom_sigma_radial_scaled) ) then - coeff(this%n_max) = exp(-0.5_dp * r_ij_radial_scaled**2 / & - (atom_sigma_radial_scaled_2 + atom_sigma_radial0_scaled**2) ) * sqrt(PI/2.0_dp) * & - atom_sigma_radial_scaled*atom_sigma_radial0_scaled / & - sqrt( atom_sigma_radial_scaled_2 + atom_sigma_radial0_scaled**2 ) * & - ( 1.0_dp + erf(atom_sigma_radial0_scaled/atom_sigma_radial_scaled*r_ij_radial_scaled/SQRT_TWO/& - sqrt( atom_sigma_radial_scaled_2 + atom_sigma_radial0_scaled**2 )) ) / n_gauss - endif -! Transform from g_alpha to g_n (the orthonormal basis) - coeff(1:this%n_max) = matmul( this%basis_transformation_coefficients, coeff(1:this%n_max) ) -! Add filter if necessary (only for inverse scaling mode, which applies a global 1/(1+a*rj) filter -! plus this one inside the buffer zone; the polynomial scaling mode applies a global filter only -! which appropriately goes to zero at rcut_hard) - if( r_ij_radial_scaled > cutoff_soft_scaled .and. .false.)then - coeff(1:this%n_max) = coeff(1:this%n_max) * exp(-0.5_dp*(r_ij_radial_scaled - & - cutoff_soft_scaled)**2 / cutoff_decay_scaled**2 * this%cutoff_decay_rate**2) - endif - - endsubroutine descriptor_soap_express_get_radial_array - - function descriptor_soap_express_N_a(cutoff, a) - - integer, intent(in) :: a - real(dp), intent(in) :: cutoff - real(dp) :: descriptor_soap_express_N_a - - integer :: b - - b = 2*a + 5 - -! **************** New basis ****************** -! N_a = dsqrt( rcut**b / dfloat(b) ) - descriptor_soap_express_N_a = sqrt( cutoff / real(b,kind=dp) ) -! ********************************************* - - endfunction descriptor_soap_express_N_a - -endmodule descriptors_module diff --git a/3rdparty/gap/descriptors_wrapper.f95 b/3rdparty/gap/descriptors_wrapper.f95 deleted file mode 100644 index a66b4729eba67c16ff5881c1a8694131d9a0a776..0000000000000000000000000000000000000000 --- a/3rdparty/gap/descriptors_wrapper.f95 +++ /dev/null @@ -1,577 +0,0 @@ -! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -! HND X -! HND X libAtoms+QUIP: atomistic simulation library -! HND X -! HND X Portions of this code were written by -! HND X Albert Bartok-Partay, Silvia Cereda, Gabor Csanyi, James Kermode, -! HND X Ivan Solt, Wojciech Szlachta, Csilla Varnai, Steven Winfield. -! HND X -! HND X Copyright 2006-2010. -! HND X -! HND X Not for distribution -! HND X -! HND X Portions of this code were written by Noam Bernstein as part of -! HND X his employment for the U.S. Government, and are not subject -! HND X to copyright in the USA. -! HND X -! HND X When using this software, please cite the following reference: -! HND X -! HND X http://www.libatoms.org -! HND X -! HND X Additional contributions by -! HND X Alessio Comisso, Chiara Gattinoni, and Gianpietro Moras -! HND X -! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -!XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -!X -!X descriptors_wrapper subroutine -!X -!XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -!XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -subroutine descriptors_wrapper_distances(N,lattice,symbol,coord,descriptor_str,descriptor_str_len, & - calc_args_str,calc_args_str_len,i,fractional,previous_accepted,distances) - - use system_module - use linearalgebra_module - use dictionary_module - use periodictable_module - use atoms_types_module - use connection_module - use atoms_module - - use descriptors_module - - - implicit none - - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - integer, intent(in) :: descriptor_str_len - character(len=descriptor_str_len) :: descriptor_str - integer, intent(in) :: calc_args_str_len - character(len=calc_args_str_len) :: calc_args_str - real(dp), dimension(3,N), intent(in) :: coord - integer, intent(in) :: i - logical, intent(in) :: fractional, previous_accepted - real(dp), dimension(N,N), intent(out) :: distances - - type(atoms), save :: at - type(Connection), save :: at_connect_last_accepted, at_connect_previous - type(descriptor), save :: desc - type(descriptor_data) :: desc_data - real(dp), dimension(:,:), allocatable, save :: desc_array_last_accepted, distances_in_last_accepted, desc_array_previous, distances_in_previous - logical, dimension(:), pointer :: desc_mask - - integer, save :: d - integer :: j, k, l, n_i - - logical, save :: first_run = .true. - logical :: recalculate - - recalculate = .false. - - if( first_run ) then - call system_initialise(verbosity=PRINT_SILENT) - call initialise(desc,trim(descriptor_str)) - call initialise(at,N,lattice) - call add_property(at,'desc_mask',.true.,ptr=desc_mask) - - d = descriptor_dimensions(desc) - allocate(desc_array_previous(d,N), desc_array_last_accepted(d,N)) - allocate(distances_in_previous(N,N), distances_in_last_accepted(N,N)) - - recalculate = .true. - endif - - if( .not. first_run .and. (N /= at%N) ) then - call finalise(at) - call initialise(at,N,lattice) - call add_property(at,'desc_mask',.true.,ptr=desc_mask) - - if(allocated(desc_array_previous)) deallocate(desc_array_previous) - allocate(desc_array_previous(d,N)) - if(allocated(desc_array_last_accepted)) deallocate(desc_array_last_accepted) - allocate(desc_array_last_accepted(d,N)) - - if(allocated(distances_in_previous)) deallocate(distances_in_previous) - allocate(distances_in_previous(N,N)) - if(allocated(distances_in_last_accepted)) deallocate(distances_in_last_accepted) - allocate(distances_in_last_accepted(N,N)) - - recalculate = .true. - endif - - if( .not. first_run ) then - if( previous_accepted ) then - at_connect_last_accepted = at_connect_previous - desc_array_last_accepted = desc_array_previous - distances_in_last_accepted = distances_in_previous - else - at_connect_previous = at_connect_last_accepted - desc_array_previous = desc_array_last_accepted - distances_in_previous = distances_in_last_accepted - endif - endif - - if( at%lattice .fne. lattice ) then - call set_lattice(at,lattice, scale_positions=.false.) - recalculate = .true. - endif - - do k = 1, at%N - at%Z(k) = atomic_number_from_symbol(symbol(k)) - enddo - - if( i > 0 .and. previous_accepted .and. .not. recalculate ) then - if( fractional ) then - at%pos(:,i) = matmul(at%lattice,coord(:,i)) - else - at%pos(:,i) = coord(:,i) - endif - else - if( fractional ) then - at%pos = matmul(at%lattice,coord) - else - at%pos = coord - endif - endif - - call set_cutoff(at,cutoff(desc)+0.5_dp) - call calc_connect(at) - - if( .not. assign_pointer(at,'desc_mask',desc_mask) ) call system_abort("descriptors_wrapper: could not assign pointer desc_mask") - - if( i > 0 .and. .not. recalculate ) then - - if( i > at%N ) call system_abort("descriptors_wrapper: argument i = "//i//" greater than number of atoms "//at%N) - - desc_mask = .false. - - desc_mask(i) = .true. - - if( at_connect_previous%initialised ) then - do n_i = 1, n_neighbours(at,i,alt_connect=at_connect_previous) - desc_mask(neighbour(at,i,n_i,alt_connect=at_connect_previous)) = .true. - enddo - endif - - do n_i = 1, n_neighbours(at,i) - desc_mask(neighbour(at,i,n_i)) = .true. - enddo - - call calc(desc,at,desc_data,do_descriptor=.true.,do_grad_descriptor=.false.,args_str="atom_mask_name=desc_mask "//trim(calc_args_str)) - - do k = 1, count(desc_mask) - j = desc_data%x(k)%ci(1) - desc_array_previous(:,j) = desc_data%x(k)%data(:) - enddo - - do k = 1, count(desc_mask) - j = desc_data%x(k)%ci(1) - do l = 1, at%N - distances_in_previous(l,j) = sum( desc_array_previous(:,l) * desc_array_previous(:,j) ) - distances_in_previous(j,l) = distances_in_previous(l,j) - enddo - enddo - desc_mask = .true. - at_connect_previous = at%connect - else - call calc(desc,at,desc_data,do_descriptor=.true.,do_grad_descriptor=.false.,args_str=trim(calc_args_str)) - do j = 1, at%N - desc_array_previous(:,j) = desc_data%x(j)%data - enddo - - distances_in_previous = matmul(transpose(desc_array_previous),desc_array_previous) - - at_connect_previous = at%connect - endif - - distances = -log(distances_in_previous) - - call finalise(desc_data) - - if( first_run ) then - at_connect_last_accepted = at_connect_previous - desc_array_last_accepted = desc_array_previous - distances_in_last_accepted = distances_in_previous - endif - first_run = .false. - -endsubroutine descriptors_wrapper_distances - -module descriptors_wrapper_module - -use system_module -use periodictable_module, only : atomic_number_from_symbol -use atoms_module -use linearalgebra_module -use descriptors_module, only : descriptor, initialise, finalise, cutoff, calc, descriptor_sizes, descriptor_dimensions - -implicit none - -#ifdef HAVE_GAP -type(descriptor), save :: desc -#endif - -logical :: first_run = .true. - -contains - - subroutine descriptors_wrapper_initialise(descriptor_str) - - character(len=*) :: descriptor_str - -#ifdef HAVE_GAP - if( first_run ) then - call system_initialise(verbosity=PRINT_SILENT) - call initialise(desc,trim(descriptor_str)) - else - call finalise(desc) - call initialise(desc,trim(descriptor_str)) - endif - first_run = .false. -#endif - endsubroutine descriptors_wrapper_initialise - - subroutine descriptors_wrapper_initialise_C(descriptor_str,n_descriptor_str) bind(c) - - use iso_c_binding, only: c_int, c_char - - character(kind=c_char), intent(in) :: descriptor_str(n_descriptor_str) - integer(kind=c_int), intent(in) :: n_descriptor_str - - call descriptors_wrapper_initialise(trim(a2s(descriptor_str))) - - endsubroutine descriptors_wrapper_initialise_C - - function descriptors_wrapper_dimensions() - integer :: descriptors_wrapper_dimensions - - if(.not. first_run) then - descriptors_wrapper_dimensions = descriptor_dimensions(desc) - else - call system_abort("descriptors_wrapper_dimensions: initialise with calling descriptors_wrapper_initialise() first.") - endif - - endfunction descriptors_wrapper_dimensions - - function descriptors_wrapper_dimensions_C() bind(c) - use iso_c_binding, only: c_int - integer(kind=c_int) :: descriptors_wrapper_dimensions_C - - descriptors_wrapper_dimensions_C = descriptors_wrapper_dimensions() - endfunction descriptors_wrapper_dimensions_C - - function descriptors_wrapper_size(N,lattice,symbol,coord,fractional) - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - real(dp), dimension(3,N), intent(in) :: coord - logical, intent(in) :: fractional - - integer :: descriptors_wrapper_size - - type(atoms), save :: at - integer :: n_descriptors,n_cross - - call copy_data_to_atoms(at,N,lattice,symbol,coord,fractional) - call descriptor_sizes(desc,at,n_descriptors,n_cross) - - descriptors_wrapper_size = n_descriptors - - endfunction descriptors_wrapper_size - - function descriptors_wrapper_size_C(N,lattice,symbol,coord,fractional) bind(c) - - use iso_c_binding, only: c_double, c_int, c_bool, c_char - - integer(kind=c_int), intent(in) :: N - real(kind=c_double), dimension(3,3), intent(inout) :: lattice - character(kind=c_char), dimension(3,N), intent(in) :: symbol - real(kind=c_double), dimension(3,N), intent(in) :: coord - logical(kind=c_bool), intent(in) :: fractional - - integer(kind=c_int) :: descriptors_wrapper_size_C - - character(len=3), dimension(N) :: my_symbol - integer :: i - logical :: my_fractional - - do i = 1, N - my_symbol(i) = a2s(symbol(:,i)) - enddo - - my_fractional = logical(fractional,kind=kind(my_fractional)) - descriptors_wrapper_size_C = descriptors_wrapper_size(N,lattice,my_symbol,coord,my_fractional) - - endfunction descriptors_wrapper_size_C - - function descriptors_wrapper_gradient_size(N,lattice,symbol,coord,fractional) - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - real(dp), dimension(3,N), intent(in) :: coord - logical, intent(in) :: fractional - - integer :: descriptors_wrapper_gradient_size - - type(atoms), save :: at - integer :: n_descriptors,n_cross - - call copy_data_to_atoms(at,N,lattice,symbol,coord,fractional) - call descriptor_sizes(desc,at,n_descriptors,n_cross) - - descriptors_wrapper_gradient_size = n_cross - - endfunction descriptors_wrapper_gradient_size - - function descriptors_wrapper_gradient_size_C(N,lattice,symbol,coord,fractional) bind(c) - - use iso_c_binding, only: c_double, c_int, c_bool, c_char - - integer(kind=c_int), intent(in) :: N - real(kind=c_double), dimension(3,3), intent(inout) :: lattice - character(kind=c_char), dimension(3,N), intent(in) :: symbol - real(kind=c_double), dimension(3,N), intent(in) :: coord - logical(kind=c_bool), intent(in) :: fractional - - integer(kind=c_int) :: descriptors_wrapper_gradient_size_C - - character(len=3), dimension(N) :: my_symbol - integer :: i - logical :: my_fractional - - do i = 1, N - my_symbol(i) = a2s(symbol(:,i)) - enddo - - my_fractional = logical(fractional,kind=kind(my_fractional)) - descriptors_wrapper_gradient_size_C = descriptors_wrapper_gradient_size(N,lattice,my_symbol,coord,my_fractional) - - endfunction descriptors_wrapper_gradient_size_C - - subroutine descriptors_wrapper_both_sizes(N,lattice,symbol,coord,fractional,n_descriptors,n_cross) - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - real(dp), dimension(3,N), intent(in) :: coord - logical, intent(in) :: fractional - integer, intent(out) :: n_descriptors, n_cross - - type(atoms), save :: at - - call copy_data_to_atoms(at,N,lattice,symbol,coord,fractional) - call descriptor_sizes(desc,at,n_descriptors,n_cross) - - endsubroutine descriptors_wrapper_both_sizes - - subroutine descriptors_wrapper_both_sizes_C(N,lattice,symbol,coord,fractional,n_descriptors,n_cross) bind(c) - - use iso_c_binding, only: c_double, c_int, c_bool, c_char - - integer(kind=c_int), intent(in) :: N - real(kind=c_double), dimension(3,3), intent(inout) :: lattice - character(kind=c_char), dimension(3,N), intent(in) :: symbol - real(kind=c_double), dimension(3,N), intent(in) :: coord - logical(kind=c_bool), intent(in) :: fractional - integer(kind=c_int), intent(out) :: n_descriptors, n_cross - - character(len=3), dimension(N) :: my_symbol - integer :: i - logical :: my_fractional - - do i = 1, N - my_symbol(i) = a2s(symbol(:,i)) - enddo - - my_fractional = logical(fractional,kind=kind(my_fractional)) - call descriptors_wrapper_both_sizes(N,lattice,my_symbol,coord,my_fractional,n_descriptors,n_cross) - - endsubroutine descriptors_wrapper_both_sizes_C - - subroutine descriptors_wrapper_array(N,lattice,symbol,coord,fractional,descriptor_array,d_descriptor, n_descriptor) - - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - real(dp), dimension(3,N), intent(in) :: coord - logical, intent(in) :: fractional - integer, intent(in) :: d_descriptor, n_descriptor - real(dp), dimension(d_descriptor,n_descriptor), intent(out):: descriptor_array - - type(atoms), save :: at - - if( first_run ) then - call system_abort("descriptors_wrapper_array: initialise with calling descriptors_wrapper_initialise() first.") - endif - - call copy_data_to_atoms(at,N,lattice,symbol,coord,fractional) - call calc(desc,at,descriptor_array) - - endsubroutine descriptors_wrapper_array - - subroutine descriptors_wrapper_array_C(N,lattice,symbol,coord,fractional,descriptor_array,d_descriptor, n_descriptor) bind(c) - - use iso_c_binding, only: c_double, c_int, c_bool, c_char - - integer(kind=c_int), intent(in) :: N - real(kind=c_double), dimension(3,3), intent(inout) :: lattice - character(kind=c_char), dimension(3,N), intent(in) :: symbol - real(kind=c_double), dimension(3,N), intent(in) :: coord - logical(kind=c_bool), intent(in) :: fractional - integer(kind=c_int), intent(in) :: d_descriptor, n_descriptor - real(kind=c_double), dimension(d_descriptor,n_descriptor), intent(out):: descriptor_array - - character(len=3), dimension(N) :: my_symbol - integer :: i - logical :: my_fractional - - do i = 1, N - my_symbol(i) = a2s(symbol(:,i)) - enddo - - my_fractional = logical(fractional,kind=kind(my_fractional)) - call descriptors_wrapper_array(N,lattice,my_symbol,coord,my_fractional,descriptor_array,d_descriptor,n_descriptor) - - endsubroutine descriptors_wrapper_array_C - - subroutine descriptors_wrapper_gradient_array(N,lattice,symbol,coord,fractional,grad_descriptor_array,grad_descriptor_index,grad_descriptor_pos,d_descriptor,n_cross) - - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - real(dp), dimension(3,N), intent(in) :: coord - logical, intent(in) :: fractional - integer, intent(in) :: d_descriptor, n_cross - real(dp), dimension(d_descriptor,3,n_cross), intent(out):: grad_descriptor_array - integer, dimension(2,n_cross), intent(out):: grad_descriptor_index - real(dp), dimension(3,n_cross), intent(out):: grad_descriptor_pos - - type(atoms), save :: at - - if( first_run ) then - call system_abort("descriptors_wrapper_gradient_array: initialise with calling descriptors_wrapper_initialise() first.") - endif - - call copy_data_to_atoms(at,N,lattice,symbol,coord,fractional) - call calc(desc,at,grad_descriptor_out=grad_descriptor_array,grad_descriptor_index=grad_descriptor_index,grad_descriptor_pos=grad_descriptor_pos) - - endsubroutine descriptors_wrapper_gradient_array - - subroutine descriptors_wrapper_gradient_array_C(N,lattice,symbol,coord,fractional,grad_descriptor_array,grad_descriptor_index,grad_descriptor_pos,d_descriptor,n_cross) bind(c) - - use iso_c_binding, only: c_double, c_int, c_bool, c_char - - integer(kind=c_int), intent(in) :: N - real(kind=c_double), dimension(3,3), intent(inout) :: lattice - character(kind=c_char), dimension(3,N), intent(in) :: symbol - real(kind=c_double), dimension(3,N), intent(in) :: coord - logical(kind=c_bool), intent(in) :: fractional - integer(kind=c_int), intent(in) :: d_descriptor, n_cross - real(kind=c_double), dimension(d_descriptor,3,n_cross), intent(out):: grad_descriptor_array - integer(kind=c_int), dimension(2,n_cross), intent(out):: grad_descriptor_index - real(kind=c_double), dimension(3,n_cross), intent(out):: grad_descriptor_pos - - character(len=3), dimension(N) :: my_symbol - integer :: i - logical :: my_fractional - - do i = 1, N - my_symbol(i) = a2s(symbol(:,i)) - enddo - - my_fractional = logical(fractional,kind=kind(my_fractional)) - call descriptors_wrapper_gradient_array(N,lattice,my_symbol,coord,my_fractional,grad_descriptor_array,grad_descriptor_index,grad_descriptor_pos,d_descriptor,n_cross) - - endsubroutine descriptors_wrapper_gradient_array_C - - subroutine descriptors_wrapper_both_arrays(N,lattice,symbol,coord,fractional,descriptor_array,grad_descriptor_array,grad_descriptor_index,grad_descriptor_pos,d_descriptor,n_descriptor,n_cross) - - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - real(dp), dimension(3,N), intent(in) :: coord - logical, intent(in) :: fractional - integer, intent(in) :: d_descriptor, n_descriptor, n_cross - real(dp), dimension(d_descriptor,n_descriptor), intent(out):: descriptor_array - real(dp), dimension(d_descriptor,3,n_cross), intent(out):: grad_descriptor_array - integer, dimension(2,n_cross), intent(out):: grad_descriptor_index - real(dp), dimension(3,n_cross), intent(out):: grad_descriptor_pos - - type(atoms), save :: at - - if( first_run ) then - call system_abort("descriptors_wrapper_both_arrays: initialise with calling descriptors_wrapper_initialise() first.") - endif - - call copy_data_to_atoms(at,N,lattice,symbol,coord,fractional) - call calc(desc,at,descriptor_out=descriptor_array,grad_descriptor_out=grad_descriptor_array,grad_descriptor_index=grad_descriptor_index,grad_descriptor_pos=grad_descriptor_pos) - - endsubroutine descriptors_wrapper_both_arrays - - subroutine descriptors_wrapper_both_arrays_C(N,lattice,symbol,coord,fractional,descriptor_array,grad_descriptor_array,grad_descriptor_index,grad_descriptor_pos,d_descriptor,n_descriptor,n_cross) bind(c) - - use iso_c_binding, only: c_double, c_int, c_bool, c_char - - integer(kind=c_int), intent(in) :: N - real(kind=c_double), dimension(3,3), intent(inout) :: lattice - character(kind=c_char), dimension(3,N), intent(in) :: symbol - real(kind=c_double), dimension(3,N), intent(in) :: coord - logical(kind=c_bool), intent(in) :: fractional - integer(kind=c_int), intent(in) :: d_descriptor, n_descriptor, n_cross - real(kind=c_double), dimension(d_descriptor,n_descriptor), intent(out):: descriptor_array - real(kind=c_double), dimension(d_descriptor,3,n_cross), intent(out):: grad_descriptor_array - integer(kind=c_int), dimension(2,n_cross), intent(out):: grad_descriptor_index - real(kind=c_double), dimension(3,n_cross), intent(out):: grad_descriptor_pos - - character(len=3), dimension(N) :: my_symbol - integer :: i - logical :: my_fractional - - do i = 1, N - my_symbol(i) = a2s(symbol(:,i)) - enddo - - my_fractional = logical(fractional,kind=kind(my_fractional)) - call descriptors_wrapper_both_arrays(N,lattice,my_symbol,coord,my_fractional,descriptor_array,grad_descriptor_array,grad_descriptor_index,grad_descriptor_pos,d_descriptor,n_descriptor,n_cross) - - endsubroutine descriptors_wrapper_both_arrays_C - - subroutine copy_data_to_atoms(at,N,lattice,symbol,coord,fractional) - type(atoms), intent(inout) :: at - integer, intent(in) :: N - real(dp), dimension(3,3), intent(inout) :: lattice - character(len=3), dimension(N), intent(in) :: symbol - real(dp), dimension(3,N), intent(in) :: coord - logical, intent(in) :: fractional - - integer :: k - - if( N /= at%N ) then - call finalise(at) - call initialise(at,N,lattice) - endif - - if( at%lattice .fne. lattice ) then - call set_lattice(at,lattice, scale_positions=.false.) - endif - - do k = 1, at%N - at%Z(k) = atomic_number_from_symbol(symbol(k)) - enddo - - if( fractional ) then - at%pos = matmul(at%lattice,coord) - else - at%pos = coord - endif - - call set_cutoff(at,cutoff(desc)+0.5_dp) - call calc_connect(at) - - endsubroutine copy_data_to_atoms - -endmodule descriptors_wrapper_module diff --git a/3rdparty/gap/doc/Makefile b/3rdparty/gap/doc/Makefile deleted file mode 100644 index d2c2b4b4563b2f0d240bb1191c7e188d360b46e5..0000000000000000000000000000000000000000 --- a/3rdparty/gap/doc/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = gap -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/3rdparty/gap/doc/conf.py b/3rdparty/gap/doc/conf.py deleted file mode 100644 index 3e62291c14d98cdac54c14565fb87d2d5db73005..0000000000000000000000000000000000000000 --- a/3rdparty/gap/doc/conf.py +++ /dev/null @@ -1,343 +0,0 @@ -# -*- coding: utf-8 -*- -# HQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -# HQ X -# HQ X quippy: Python interface to QUIP atomistic simulation library -# HQ X -# HQ X Copyright James Kermode 2010 -# HQ X -# HQ X These portions of the source code are released under the GNU General -# HQ X Public License, version 2, http://www.gnu.org/copyleft/gpl.html -# HQ X -# HQ X If you would like to license the source code under different terms, -# HQ X please contact James Kermode, james.kermode@gmail.com -# HQ X -# HQ X When using this software, please cite the following reference: -# HQ X -# HQ X http://www.jrkermode.co.uk/quippy -# HQ X -# HQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -# -# quippy documentation build configuration file, created by -# sphinx-quickstart on Wed Sep 2 14:17:01 2009. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import glob -import os -import sys - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. - -sys.path.insert(1, os.path.abspath('..')) -sys.path.insert(1, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -needs_sphinx = '1.4' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.mathjax', - 'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', - 'sphinx.ext.githubpages', - 'nbsphinx', - 'modcontents', - 'numpydoc'] - -# This list was generated by grep as a workaround to -# https://github.com/sphinx-doc/sphinx/issues/2485 -autosummary_generate = [ - 'gap_fit.rst', -] - -nbsphinx_allow_errors = True - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = ['.rst', '.ipynb'] - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'gap' -copyright = u'20010-2019, Gabor Csanyi' -author= u'Gabor Csanyi' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -import os -version=os.popen('cat ../GIT_VERSON 2> /dev/null|| ' - 'git describe --always --tags --dirty=+ 2> /dev/null || ' - 'echo ').read().strip() -# The full version, including alpha/beta/rc tags. -release = version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['quippy.'] - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -html_theme_options = { -} - -html_logo = 'hybrid.png' -html_favicon = 'favicon.ico' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Fix for RTD tables -html_context = { - 'css_files': [ - '_static/theme_overrides.css', - ], -} - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = 'gapdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'gap.tex', u'GAP Documentation', - u'Gabor Csanyi', 'manual'), -] - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'gap', u'GAP Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'gap', u'GAP Documentation', - author, 'gap', 'One line description of project.', - 'Miscellaneous'), -] - - -intersphinx_mapping = {'python': ('https://docs.python.org/2.7', None), - 'ase': ('https://wiki.fysik.dtu.dk/ase/', None), - 'numpy': ('https://docs.scipy.org/doc/numpy/', None), - 'matplotlib': ('https://matplotlib.org/', None)} - -### -### sphinx-quickstart output ends here -### - -from quippy.oo_fortran import FortranDerivedType, FortranDerivedTypes - -import re - -# classnames = [v.__name__ for v in FortranDerivedTypes.values()] -# classname_re = re.compile('(^|\s+)((``)?)('+'|'.join([v.__name__ for v in FortranDerivedTypes.values()])+r')\2(?=[\s\W]+)') -# method_or_attr_re = re.compile('(^|\s+)((``)?)('+'|'.join([v.__name__+'\.([a-zA-Z][a-zA-Z0-9_]*)' for v in FortranDerivedTypes.values()])+r')\2(?=[\s\W]+)') - -# doc_subs = [(classname_re, r'\1:class:`~.\4`'), -# (method_or_attr_re, r'\1:meth:`.\4`')] - -global_options = None - -def process_docstring(app, what, name, obj, options, lines): - global global_options - global_options = options - -def process_signature(app, what, name, obj, options, signature, return_annotation): - return (signature, return_annotation) - -def maybe_skip_member(app, what, name, obj, skip, options): - if hasattr(FortranDerivedType, name) and options.inherited_members: - return True - return skip - -from docutils import nodes, utils -from docutils.parsers.rst.roles import set_classes - -def get_github_url(view, path): - return 'https://github.com/{project}/{view}/{branch}/{path}'.format( - project='libAtoms/GAP', - view=view, - branch='public', - path=path) - -def github_role(role, rawtext, text, lineno, inliner, options={}, content=[]): - if text[-1] == '>': - i = text.index('<') - name = text[:i - 1] - text = text[i + 1:-1] - else: - name = text - ref = get_github_url('blob', name) - set_classes(options) - node = nodes.reference(rawtext, name, refuri=ref, **options) - return [node], [] - -def mol_role(role, rawtext, text, lineno, inliner, options={}, content=[]): - n = [] - t = '' - while text: - if text[0] == '_': - n.append(nodes.Text(t)) - t = '' - n.append(nodes.subscript(text=text[1])) - text = text[2:] - else: - t += text[0] - text = text[1:] - n.append(nodes.Text(t)) - return n, [] - -def setup(app): - app.connect('autodoc-process-docstring', process_docstring) - app.connect('autodoc-process-signature', process_signature) - app.connect('autodoc-skip-member', maybe_skip_member) - - app.add_role('git', github_role) - app.add_role('mol', mol_role) - - -autodoc_member_order = 'groupwise' -#autoclass_content = 'both' - -def add_line(self, line, source, *lineno): - """Append one line of generated reST to the output.""" - sys.stdout.write(self.indent + line + '\n') - self.directive.result.append(self.indent + line, source, *lineno) - -# Uncomment two lines below to debug autodoc rst output - -#import sphinx.ext.autodoc -#sphinx.ext.autodoc.Documenter.add_line = add_line - -# Monkey patch numpydoc to exclude methods and attributes inherited -# from base classes, and to include attributes wrapped by Python -# properties - -numpydoc_show_class_members = True - -import inspect -import pydoc -import numpydoc -import numpydoc.docscrape - -@property -def methods(self): - if self._cls is None: - return [] - do_inherited = False - if global_options is not None and hasattr(global_options, 'inherited_members'): - do_inherited = global_options.inherited_members - - return [name for name,func in inspect.getmembers(self._cls) - if not name.startswith('_') and callable(func) and - (do_inherited or not any(hasattr(base, name) for base in self._cls.__bases__)) and - pydoc.getdoc(getattr(self._cls, name))] - -@property -def properties(self): - if self._cls is None: - return [] - do_inherited = False - if global_options is not None and hasattr(global_options, 'inherited_members'): - do_inherited = global_options.inherited_members - return [name for name,func in inspect.getmembers(self._cls) if not name.startswith('_') and - (do_inherited or not any(hasattr(base, name) for base in self._cls.__bases__)) and - (func is None or isinstance(getattr(self._cls, name), property)) and - pydoc.getdoc(getattr(self._cls, name))] - -numpydoc.docscrape.ClassDoc.methods = methods -numpydoc.docscrape.ClassDoc.properties = properties - -# generate .rst versions of any .ipynb notebooks under Examples/ -# Superseded by nbsphinx extension -# for notebook in glob.glob(os.path.join('Examples/*.ipynb')): - # cmd = 'jupyter nbconvert --to rst {0}'.format(notebook) - # print cmd - # os.system(cmd) - diff --git a/3rdparty/gap/doc/gap_fit.rst b/3rdparty/gap/doc/gap_fit.rst deleted file mode 100644 index 42f7046fc3a2d2e5182f6fc87a3688181a2f36e5..0000000000000000000000000000000000000000 --- a/3rdparty/gap/doc/gap_fit.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. HQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -.. HQ X -.. HQ X quippy: Python interface to QUIP atomistic simulation library -.. HQ X -.. HQ X Copyright James Kermode 2010 -.. HQ X -.. HQ X These portions of the source code are released under the GNU General -.. HQ X Public License, version 2, http://www.gnu.org/copyleft/gpl.html -.. HQ X -.. HQ X If you would like to license the source code under different terms, -.. HQ X please contact James Kermode, james.kermode@gmail.com -.. HQ X -.. HQ X When using this software, please cite the following reference: -.. HQ X -.. HQ X http://www.jrkermode.co.uk/quippy -.. HQ X -.. HQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -The gap_fit program -===================================================== - -Main options ------------- -.. autofunction:: quippy.gap_fit_parse_command_line - -GAP options ------------ -.. autofunction:: quippy.gap_fit_parse_gap_str - -`sparse_method` options are: - - RANDOM: default, chooses n_sparse random datapoints - - PIVOT: based on the full covariance matrix finds the n_sparse "pivoting" points - - CLUSTER: based on the full covariance matrix performs a k-medoid clustering into n_sparse clusters, returning the medoids - - UNIFORM: makes a histogram of the data based on n_sparse and returns a data point from each bin - - KMEANS: k-means clustering based on the data points - - COVARIANCE: greedy data point selection based on the sparse covariance matrix, to minimise the GP variance of all datapoints - - UNIQ: selects unique datapoints from the dataset - - FUZZY: fuzzy k-means clustering - - FILE: reads sparse points from a file - - INDEX_FILE: reads indices of sparse points from a file - - CUR_COVARIANCE: CUR, based on the full covariance matrix - - CUR_POINTS: CUR, based on the datapoints diff --git a/3rdparty/gap/doc/gap_fitting_tutorial.ipynb b/3rdparty/gap/doc/gap_fitting_tutorial.ipynb deleted file mode 100644 index d7e40a97712e6937225727b9c2a19ab849470499..0000000000000000000000000000000000000000 --- a/3rdparty/gap/doc/gap_fitting_tutorial.ipynb +++ /dev/null @@ -1,956 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Training a GAP model\n", - "\n", - "## steps\n", - " 1. generate a small dataset of water structures \n", - " - use CP2K if you havea access to it\n", - " - otherwise: use any simple potential implemented in ASE, just for trying this out I have used EMT here\n", - " 1. generate e0 values\n", - " 1. separate a training and a validation dataset\n", - " 1. **train the model**\n", - " 1. look at the outcome of the model\n", - " \n", - "## here we will fit twice, to see the difference between a 2b-only and a 2b+3b fit" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-08T08:48:40.136302Z", - "start_time": "2018-10-08T08:48:40.130282Z" - } - }, - "outputs": [], - "source": [ - "# general imports \n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from copy import deepcopy as cp\n", - "\n", - "# ase imports\n", - "import ase.io\n", - "from ase import Atoms, Atom\n", - "from ase import units\n", - "from ase.build import molecule\n", - "# for MD\n", - "from ase.md.langevin import Langevin\n", - "from ase.io.trajectory import Trajectory" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:07:43.520727Z", - "start_time": "2018-10-07T17:07:43.460810Z" - } - }, - "outputs": [], - "source": [ - "# helper functions\n", - "def make_water(density, super_cell=[3, 3, 3]):\n", - " \"\"\" Geenrates a supercell of water molecules with a desired density.\n", - " Density in g/cm^3!!!\"\"\"\n", - " h2o = molecule('H2O')\n", - " a = np.cbrt((sum(h2o.get_masses()) * units.m ** 3 * 1E-6 ) / (density * units.mol))\n", - " h2o.set_cell((a, a, a))\n", - " h2o.set_pbc((True, True, True))\n", - " #return cp(h2o.repeat(super_cell))\n", - " return h2o.repeat(super_cell)\n", - "\n", - "def rms_dict(x_ref, x_pred):\n", - " \"\"\" Takes two datasets of the same shape and returns a dictionary containing RMS error data\"\"\"\n", - "\n", - " x_ref = np.array(x_ref)\n", - " x_pred = np.array(x_pred)\n", - "\n", - " if np.shape(x_pred) != np.shape(x_ref):\n", - " raise ValueError('WARNING: not matching shapes in rms')\n", - "\n", - " error_2 = (x_ref - x_pred) ** 2\n", - "\n", - " average = np.sqrt(np.average(error_2))\n", - " std_ = np.sqrt(np.var(error_2))\n", - "\n", - " return {'rmse': average, 'std': std_}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## generating data only with ASE, using the EMT calculator\n", - "\n", - "This is only for the demonstration of how to do it, this run is will be done very fast. There is no practical use of the data beyond lerning the use teach_sparse, quip, etc. with it." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:10:50.964994Z", - "start_time": "2018-10-07T17:07:43.523997Z" - }, - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Energy per atom: Epot = 0.885eV Ekin = 0.000eV (T= 0K) Etot = 0.885eV\n", - "Energy per atom: Epot = 0.820eV Ekin = 0.053eV (T=413K) Etot = 0.874eV\n", - "Energy per atom: Epot = 0.660eV Ekin = 0.208eV (T=1611K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.632eV Ekin = 0.224eV (T=1736K) Etot = 0.857eV\n", - "Energy per atom: Epot = 0.869eV Ekin = 0.005eV (T= 39K) Etot = 0.874eV\n", - "Energy per atom: Epot = 0.781eV Ekin = 0.088eV (T=682K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.756eV Ekin = 0.117eV (T=903K) Etot = 0.872eV\n", - "Energy per atom: Epot = 0.706eV Ekin = 0.164eV (T=1270K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.703eV Ekin = 0.158eV (T=1223K) Etot = 0.861eV\n", - "Energy per atom: Epot = 0.867eV Ekin = 0.006eV (T= 47K) Etot = 0.873eV\n", - "Energy per atom: Epot = 0.704eV Ekin = 0.160eV (T=1241K) Etot = 0.864eV\n", - "Energy per atom: Epot = 0.679eV Ekin = 0.190eV (T=1468K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.822eV Ekin = 0.051eV (T=391K) Etot = 0.872eV\n", - "Energy per atom: Epot = 0.782eV Ekin = 0.088eV (T=677K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.775eV Ekin = 0.096eV (T=739K) Etot = 0.871eV\n", - "Energy per atom: Epot = 0.751eV Ekin = 0.119eV (T=920K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.752eV Ekin = 0.117eV (T=904K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.764eV Ekin = 0.107eV (T=825K) Etot = 0.871eV\n", - "Energy per atom: Epot = 0.752eV Ekin = 0.120eV (T=927K) Etot = 0.871eV\n", - "Energy per atom: Epot = 0.747eV Ekin = 0.123eV (T=953K) Etot = 0.871eV\n", - "Energy per atom: Epot = 0.724eV Ekin = 0.145eV (T=1119K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.703eV Ekin = 0.166eV (T=1281K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.738eV Ekin = 0.132eV (T=1022K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.703eV Ekin = 0.165eV (T=1280K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.697eV Ekin = 0.172eV (T=1331K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.731eV Ekin = 0.140eV (T=1083K) Etot = 0.871eV\n", - "Energy per atom: Epot = 0.716eV Ekin = 0.155eV (T=1199K) Etot = 0.871eV\n", - "Energy per atom: Epot = 0.706eV Ekin = 0.163eV (T=1257K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.721eV Ekin = 0.149eV (T=1151K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.685eV Ekin = 0.184eV (T=1424K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.706eV Ekin = 0.164eV (T=1271K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.721eV Ekin = 0.150eV (T=1161K) Etot = 0.871eV\n", - "Energy per atom: Epot = 0.690eV Ekin = 0.180eV (T=1396K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.663eV Ekin = 0.207eV (T=1601K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.623eV Ekin = 0.247eV (T=1907K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.609eV Ekin = 0.261eV (T=2022K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.576eV Ekin = 0.294eV (T=2271K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.563eV Ekin = 0.306eV (T=2370K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.571eV Ekin = 0.298eV (T=2307K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.605eV Ekin = 0.265eV (T=2050K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.566eV Ekin = 0.302eV (T=2338K) Etot = 0.869eV\n", - "Energy per atom: Epot = 0.579eV Ekin = 0.290eV (T=2242K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.580eV Ekin = 0.289eV (T=2234K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.557eV Ekin = 0.311eV (T=2406K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.605eV Ekin = 0.264eV (T=2046K) Etot = 0.870eV\n", - "Energy per atom: Epot = 0.541eV Ekin = 0.327eV (T=2530K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.521eV Ekin = 0.347eV (T=2686K) Etot = 0.868eV\n", - "Energy per atom: Epot = 0.535eV Ekin = 0.339eV (T=2622K) Etot = 0.874eV\n", - "Energy per atom: Epot = 0.540eV Ekin = 0.333eV (T=2574K) Etot = 0.873eV\n", - "Energy per atom: Epot = 0.597eV Ekin = 0.278eV (T=2151K) Etot = 0.875eV\n", - "Energy per atom: Epot = 0.534eV Ekin = 0.340eV (T=2631K) Etot = 0.874eV\n", - "Energy per atom: Epot = 0.490eV Ekin = 0.384eV (T=2968K) Etot = 0.874eV\n", - "Energy per atom: Epot = 0.496eV Ekin = 0.378eV (T=2928K) Etot = 0.875eV\n", - "Energy per atom: Epot = 0.495eV Ekin = 0.384eV (T=2970K) Etot = 0.879eV\n", - "Energy per atom: Epot = 0.456eV Ekin = 0.424eV (T=3277K) Etot = 0.880eV\n", - "Energy per atom: Epot = 0.454eV Ekin = 0.430eV (T=3325K) Etot = 0.883eV\n", - "Energy per atom: Epot = 0.527eV Ekin = 0.358eV (T=2767K) Etot = 0.884eV\n", - "Energy per atom: Epot = 0.493eV Ekin = 0.391eV (T=3027K) Etot = 0.885eV\n", - "Energy per atom: Epot = 0.496eV Ekin = 0.389eV (T=3011K) Etot = 0.885eV\n", - "Energy per atom: Epot = 0.500eV Ekin = 0.384eV (T=2971K) Etot = 0.884eV\n", - "Energy per atom: Epot = 0.508eV Ekin = 0.374eV (T=2895K) Etot = 0.882eV\n", - "Energy per atom: Epot = 0.470eV Ekin = 0.411eV (T=3176K) Etot = 0.881eV\n", - "Energy per atom: Epot = 0.430eV Ekin = 0.451eV (T=3489K) Etot = 0.881eV\n", - "Energy per atom: Epot = 0.404eV Ekin = 0.478eV (T=3696K) Etot = 0.882eV\n", - "Energy per atom: Epot = 0.410eV Ekin = 0.471eV (T=3645K) Etot = 0.881eV\n", - "Energy per atom: Epot = 0.473eV Ekin = 0.408eV (T=3160K) Etot = 0.881eV\n", - "Energy per atom: Epot = 0.429eV Ekin = 0.451eV (T=3490K) Etot = 0.880eV\n", - "Energy per atom: Epot = 0.439eV Ekin = 0.442eV (T=3423K) Etot = 0.881eV\n", - "Energy per atom: Epot = 0.465eV Ekin = 0.416eV (T=3216K) Etot = 0.881eV\n", - "Energy per atom: Epot = 0.448eV Ekin = 0.432eV (T=3346K) Etot = 0.880eV\n", - "Energy per atom: Epot = 0.465eV Ekin = 0.417eV (T=3222K) Etot = 0.882eV\n", - "Energy per atom: Epot = 0.457eV Ekin = 0.423eV (T=3274K) Etot = 0.880eV\n", - "Energy per atom: Epot = 0.425eV Ekin = 0.456eV (T=3528K) Etot = 0.881eV\n", - "Energy per atom: Epot = 0.415eV Ekin = 0.465eV (T=3598K) Etot = 0.880eV\n", - "Energy per atom: Epot = 0.458eV Ekin = 0.427eV (T=3301K) Etot = 0.884eV\n", - "Energy per atom: Epot = 0.444eV Ekin = 0.442eV (T=3423K) Etot = 0.886eV\n", - "Energy per atom: Epot = 0.386eV Ekin = 0.499eV (T=3858K) Etot = 0.885eV\n", - "Energy per atom: Epot = 0.386eV Ekin = 0.499eV (T=3864K) Etot = 0.885eV\n", - "Energy per atom: Epot = 0.421eV Ekin = 0.466eV (T=3609K) Etot = 0.887eV\n", - "Energy per atom: Epot = 0.390eV Ekin = 0.498eV (T=3854K) Etot = 0.888eV\n", - "Energy per atom: Epot = 0.404eV Ekin = 0.488eV (T=3777K) Etot = 0.892eV\n", - "Energy per atom: Epot = 0.411eV Ekin = 0.480eV (T=3714K) Etot = 0.891eV\n", - "Energy per atom: Epot = 0.429eV Ekin = 0.460eV (T=3558K) Etot = 0.889eV\n", - "Energy per atom: Epot = 0.391eV Ekin = 0.497eV (T=3848K) Etot = 0.888eV\n", - "Energy per atom: Epot = 0.379eV Ekin = 0.507eV (T=3924K) Etot = 0.886eV\n", - "Energy per atom: Epot = 0.429eV Ekin = 0.460eV (T=3555K) Etot = 0.889eV\n", - "Energy per atom: Epot = 0.431eV Ekin = 0.458eV (T=3545K) Etot = 0.889eV\n", - "Energy per atom: Epot = 0.440eV Ekin = 0.447eV (T=3458K) Etot = 0.887eV\n", - "Energy per atom: Epot = 0.417eV Ekin = 0.467eV (T=3610K) Etot = 0.884eV\n", - "Energy per atom: Epot = 0.445eV Ekin = 0.442eV (T=3422K) Etot = 0.888eV\n", - "Energy per atom: Epot = 0.437eV Ekin = 0.447eV (T=3461K) Etot = 0.884eV\n", - "Energy per atom: Epot = 0.439eV Ekin = 0.447eV (T=3460K) Etot = 0.886eV\n", - "Energy per atom: Epot = 0.424eV Ekin = 0.464eV (T=3587K) Etot = 0.888eV\n", - "Energy per atom: Epot = 0.394eV Ekin = 0.493eV (T=3817K) Etot = 0.887eV\n", - "Energy per atom: Epot = 0.370eV Ekin = 0.515eV (T=3984K) Etot = 0.885eV\n", - "Energy per atom: Epot = 0.373eV Ekin = 0.511eV (T=3951K) Etot = 0.883eV\n", - "Energy per atom: Epot = 0.402eV Ekin = 0.485eV (T=3748K) Etot = 0.886eV\n", - "Energy per atom: Epot = 0.380eV Ekin = 0.506eV (T=3912K) Etot = 0.886eV\n", - "Energy per atom: Epot = 0.404eV Ekin = 0.483eV (T=3736K) Etot = 0.887eV\n", - "Energy per atom: Epot = 0.443eV Ekin = 0.444eV (T=3436K) Etot = 0.887eV\n", - "Energy per atom: Epot = 0.419eV Ekin = 0.469eV (T=3626K) Etot = 0.888eV\n", - "Energy per atom: Epot = 0.440eV Ekin = 0.451eV (T=3489K) Etot = 0.891eV\n", - "Energy per atom: Epot = 0.434eV Ekin = 0.453eV (T=3505K) Etot = 0.887eV\n", - "Energy per atom: Epot = 0.423eV Ekin = 0.468eV (T=3619K) Etot = 0.891eV\n", - "Energy per atom: Epot = 0.369eV Ekin = 0.523eV (T=4047K) Etot = 0.892eV\n", - "Energy per atom: Epot = 0.413eV Ekin = 0.478eV (T=3701K) Etot = 0.891eV\n", - "Energy per atom: Epot = 0.359eV Ekin = 0.531eV (T=4108K) Etot = 0.890eV\n", - "Energy per atom: Epot = 0.401eV Ekin = 0.491eV (T=3799K) Etot = 0.892eV\n", - "Energy per atom: Epot = 0.414eV Ekin = 0.477eV (T=3688K) Etot = 0.890eV\n", - "Energy per atom: Epot = 0.376eV Ekin = 0.513eV (T=3969K) Etot = 0.889eV\n", - "Energy per atom: Epot = 0.368eV Ekin = 0.522eV (T=4035K) Etot = 0.890eV\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Energy per atom: Epot = 0.370eV Ekin = 0.521eV (T=4029K) Etot = 0.891eV\n", - "Energy per atom: Epot = 0.379eV Ekin = 0.512eV (T=3959K) Etot = 0.891eV\n", - "Energy per atom: Epot = 0.393eV Ekin = 0.502eV (T=3881K) Etot = 0.894eV\n", - "Energy per atom: Epot = 0.373eV Ekin = 0.515eV (T=3984K) Etot = 0.888eV\n", - "Energy per atom: Epot = 0.367eV Ekin = 0.530eV (T=4100K) Etot = 0.897eV\n", - "Energy per atom: Epot = 0.380eV Ekin = 0.520eV (T=4026K) Etot = 0.900eV\n", - "Energy per atom: Epot = 0.384eV Ekin = 0.523eV (T=4048K) Etot = 0.908eV\n", - "Energy per atom: Epot = 0.417eV Ekin = 0.485eV (T=3751K) Etot = 0.902eV\n", - "Energy per atom: Epot = 0.402eV Ekin = 0.518eV (T=4005K) Etot = 0.919eV\n", - "Energy per atom: Epot = 0.417eV Ekin = 0.558eV (T=4313K) Etot = 0.974eV\n" - ] - } - ], - "source": [ - "# Running MD with ASE's EMT\n", - "\n", - "from ase.calculators.emt import EMT\n", - "calc = EMT()\n", - "\n", - "T = 150 # Kelvin\n", - "\n", - "# Set up a grid of water\n", - "water = make_water(1.0, [3, 3, 3])\n", - "water.set_calculator(calc)\n", - "\n", - "# We want to run MD using the Langevin algorithm\n", - "# with a time step of 1 fs, the temperature T and the friction\n", - "# coefficient to 0.002 atomic units.\n", - "dyn = Langevin(water, 1 * units.fs, T * units.kB, 0.0002)\n", - "\n", - "def printenergy(a=water): # store a reference to atoms in the definition.\n", - " \"\"\"Function to print the potential, kinetic and total energy.\"\"\"\n", - " epot = a.get_potential_energy() / len(a)\n", - " ekin = a.get_kinetic_energy() / len(a)\n", - " print('Energy per atom: Epot = %.3feV Ekin = %.3feV (T=%3.0fK) '\n", - " 'Etot = %.3feV' % (epot, ekin, ekin / (1.5 * units.kB), epot + ekin))\n", - "\n", - "dyn.attach(printenergy, interval=5)\n", - "\n", - "# We also want to save the positions of all atoms after every 5th time step.\n", - "traj = Trajectory('dyn_emt.traj', 'w', water)\n", - "dyn.attach(traj.write, interval=5)\n", - "\n", - "# Now run the dynamics\n", - "printenergy(water)\n", - "dyn.run(600) # CHANGE THIS IF YOU WANT LONGER/SHORTER RUN" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:10:51.483185Z", - "start_time": "2018-10-07T17:10:50.968420Z" - } - }, - "outputs": [], - "source": [ - "# wrap and save traj in .xyz --- the .traj is a non human readable database file, xyz is much better\n", - "out_traj = ase.io.read('dyn_emt.traj', ':')\n", - "for at in out_traj:\n", - " at.wrap()\n", - " if 'momenta' in at.arrays: del at.arrays['momenta']\n", - "ase.io.write('dyn_emt.xyz', out_traj, 'xyz')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## get e0 for H and O - energies of the isolated atoms\n", - "\n", - "This is the energy of the isolated atom, will be in the teach_sparse string in the following format: `e0={H:energy:O:energy}`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:10:51.495057Z", - "start_time": "2018-10-07T17:10:51.486315Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "e0_H: 3.21\n", - "e0_O: 4.6\n" - ] - } - ], - "source": [ - "isolated_H = Atoms('H', calculator=EMT())\n", - "isolated_O = Atoms('O', calculator=EMT())\n", - "\n", - "print('e0_H:',isolated_H.get_potential_energy())\n", - "print('e0_O:',isolated_O.get_potential_energy())\n", - "\n", - "# this made the e0 string be the following: e0={H:3.21:O:4.6}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T13:10:08.501028Z", - "start_time": "2018-10-07T13:10:08.496414Z" - } - }, - "source": [ - "# separate the dataset into a training and a validation set\n", - "\n", - "As we have 120 frames from the 600fs MD, I will do it 60,60 with taking even and odd frames for the two" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:10:51.802364Z", - "start_time": "2018-10-07T17:10:51.497953Z" - } - }, - "outputs": [], - "source": [ - "ase.io.write('train.xyz', out_traj[0::2]) \n", - "ase.io.write('validate.xyz', out_traj[1::2])" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:10:51.813416Z", - "start_time": "2018-10-07T17:10:51.805556Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "dict_keys(['numbers', 'positions'])" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "out_traj[0].arrays.keys()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T12:48:02.357449Z", - "start_time": "2018-10-07T12:48:02.347940Z" - } - }, - "source": [ - "## train our GAP model from the command line\n", - "\n", - "Will use a fit of 2b only, using the desciptor distance_2b.\n", - "\n", - "Let's understand how this works. The bash command takes named arguments separated by spaces.\n", - "\n", - "- `teach_sparse` the command which actually does the fit \n", - "- `e0={H:3.21:O:4.6}` the energies of the isolated atoms\n", - "- `energy_parameter_name=energy force_parameter_name=forces` names of the parameters\n", - "- `do_copy_at_file=F sparse_separate_file=T` just needed, don't want to copy the training data and using separate files for the xml makes it faster\n", - "- `gp_file=GAP.xml` filename of the potential parameters, I have always used this name, because I had separate directories for the different trainings potentials\n", - "- `at_file=train.xyz` training file\n", - "- `default_sigma={0.008 0.04 0 0}` sigma values to be used for energies, forces, stresses, hessians in order; this represents the accuracy of the data and the relative weight of them in the fit (more accurate --> more significant in the fit)\n", - "- `gap={...}` the potential to be fit, separated by ':'\n", - "\n", - "**distance_2b**\n", - "- `cutoff=4.0` radial, practically the highest distance the descriptor takes into account \n", - "- `covariance_type=ard_se` use gausses in the fit\n", - "- `delta=0.5` what relative portion of the things shall be determined by this potential\n", - "- `theta_uniform=1.0` width of the gaussians\n", - "- `sparse_method=uniform` use uniform bins to choose the sparse points\n", - "- `add_species=T ` take the species into account, so it will generate more GAPs automatically (see the output)\n", - "- `n_sparse=10` number of sparse points\n", - "\n", - "\n", - "## notice, that the script is running in parallel, using all 8 cores of the current machine" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:10:55.183809Z", - "start_time": "2018-10-07T17:10:51.816438Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "libAtoms::Hello World: 07/10/2018 18:10:52\n", - "libAtoms::Hello World: git version https://github.com/libAtoms/QUIP.git,531330f-dirty\n", - "libAtoms::Hello World: QUIP_ARCH linux_x86_64_gfortran_openmp\n", - "libAtoms::Hello World: compiled on Jul 2 2018 at 21:44:13\n", - "libAtoms::Hello World: OpenMP parallelisation with 8 threads\n", - "WARNING: libAtoms::Hello World: environment variable OMP_STACKSIZE not set explicitly. The default value - system and compiler dependent - may be too small for some applications.\n", - "libAtoms::Hello World: Random Seed = 65452599\n", - "libAtoms::Hello World: global verbosity = 0\n", - "\n", - "Calls to system_timer will do nothing by default\n", - "\n", - "\n", - "================================ Input parameters ==============================\n", - "\n", - "at_file = train.xyz\n", - "gap = \"distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform add_species=T n_sparse=10\"\n", - "e0 = H:3.21:O:4.6\n", - "e0_offset = 0.0\n", - "do_e0_avg = T\n", - "default_sigma = \"0.008 0.04 0 0\"\n", - "sparse_jitter = 1.0e-10\n", - "hessian_delta = 1.0e-2\n", - "core_param_file = quip_params.xml\n", - "core_ip_args =\n", - "energy_parameter_name = energy\n", - "force_parameter_name = forces\n", - "virial_parameter_name = virial\n", - "hessian_parameter_name = hessian\n", - "config_type_parameter_name = config_type\n", - "sigma_parameter_name = sigma\n", - "config_type_sigma =\n", - "sigma_per_atom = T\n", - "do_copy_at_file = F\n", - "sparse_separate_file = T\n", - "sparse_use_actual_gpcov = F\n", - "gp_file = GAP.xml\n", - "verbosity = NORMAL\n", - "rnd_seed = -1\n", - "do_ip_timing = F\n", - "template_file = template.xyz\n", - "\n", - "======================================== ======================================\n", - "\n", - "\n", - "============== Gaussian Approximation Potentials - Database fitting ============\n", - "\n", - "\n", - "Initial parsing of command line arguments finished.\n", - "Found 1 GAPs.\n", - "Descriptors have been parsed\n", - "XYZ file read\n", - "Old GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform add_species=T n_sparse=10}\n", - "New GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform n_sparse=10 Z1=8 Z2=8}\n", - "New GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform n_sparse=10 Z1=8 Z2=1}\n", - "New GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform n_sparse=10 Z1=1 Z2=1}\n", - "Multispecies support added where requested\n", - "Number of target energies (property name: energy) found: 60\n", - "Number of target forces (property name: forces) found: 14580\n", - "Number of target virials (property name: virial) found: 0\n", - "Number of target Hessian eigenvalues (property name: hessian) found: 0\n", - "Cartesian coordinates transformed to descriptors\n", - "Started sparse covariance matrix calculation of coordinate 1\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 1\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate1_sparse done in .91605700000000012 cpu secs, .11785597354173660 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate1 done in .91605700000000012 cpu secs, .11797238700091839 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 2\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 2\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate2_sparse done in 3.7722349999999998 cpu secs, .47368377633392811 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate2 done in 3.7722349999999998 cpu secs, .47379869595170021 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 3\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 3\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate3_sparse done in 3.5482220000000000 cpu secs, .44678380154073238 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate3 done in 3.5522219999999995 cpu secs, .44689681194722652 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_LinearAlgebra done in .56005000000000749E-001 cpu secs, .13479800894856453E-001 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_FunctionValues done in .00000000000000000E+000 cpu secs, .14016032218933105E-003 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse done in 8.3005189999999995 cpu secs, 1.0586479119956493 wall clock secs.\n", - "TIMER: GP sparsify done in 9.8286140000000000 cpu secs, 1.4589964970946312 wall clock secs.\n", - "\n", - "libAtoms::Finalise: 07/10/2018 18:10:55\n", - "libAtoms::Finalise: Bye-Bye!\n" - ] - } - ], - "source": [ - "! teach_sparse e0={H:3.21:O:4.6} energy_parameter_name=energy force_parameter_name=forces do_copy_at_file=F sparse_separate_file=T gp_file=GAP.xml at_file=train.xyz default_sigma={0.008 0.04 0 0} gap={distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform add_species=T n_sparse=10}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T12:56:59.802090Z", - "start_time": "2018-10-07T12:56:58.506422Z" - } - }, - "source": [ - "## use the potential with QUIP on trani.xyz and validate.xyz" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:11:02.087146Z", - "start_time": "2018-10-07T17:10:55.187005Z" - } - }, - "outputs": [], - "source": [ - "# calculate train.xyz\n", - "\n", - "! quip E=T F=T atoms_filename=train.xyz param_filename=GAP.xml | grep AT | sed 's/AT//' >> quip_train.xyz\n", - "! quip E=T F=T atoms_filename=validate.xyz param_filename=GAP.xml | grep AT | sed 's/AT//' >> quip_validate.xyz" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T12:58:09.146524Z", - "start_time": "2018-10-07T12:58:09.136959Z" - } - }, - "source": [ - "# make simple plots of the energies and forces on the EMT and GAP datas" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-08T08:48:46.462404Z", - "start_time": "2018-10-08T08:48:46.441786Z" - } - }, - "outputs": [], - "source": [ - "def energy_plot(in_file, out_file, ax, title='Plot of energy'):\n", - " \"\"\" Plots the distribution of energy per atom on the output vs the input\"\"\"\n", - " # read files\n", - " in_atoms = ase.io.read(in_file, ':')\n", - " out_atoms = ase.io.read(out_file, ':')\n", - " # list energies\n", - " ener_in = [at.get_potential_energy() / len(at.get_chemical_symbols()) for at in in_atoms]\n", - " ener_out = [at.get_potential_energy() / len(at.get_chemical_symbols()) for at in out_atoms]\n", - " # scatter plot of the data\n", - " ax.scatter(ener_in, ener_out)\n", - " # get the appropriate limits for the plot\n", - " for_limits = np.array(ener_in +ener_out) \n", - " elim = (for_limits.min() - 0.05, for_limits.max() + 0.05)\n", - " ax.set_xlim(elim)\n", - " ax.set_ylim(elim)\n", - " # add line of slope 1 for refrence\n", - " ax.plot(elim, elim, c='k')\n", - " # set labels\n", - " ax.set_ylabel('energy by GAP / eV')\n", - " ax.set_xlabel('energy by EMT / eV')\n", - " #set title\n", - " ax.set_title(title)\n", - " # add text about RMSE\n", - " _rms = rms_dict(ener_in, ener_out)\n", - " rmse_text = 'RMSE:\\n' + str(np.round(_rms['rmse'], 3)) + ' +- ' + str(np.round(_rms['std'], 3)) + 'eV/atom'\n", - " ax.text(0.9, 0.1, rmse_text, transform=ax.transAxes, fontsize='large', horizontalalignment='right', \n", - " verticalalignment='bottom')\n", - " \n", - "def force_plot(in_file, out_file, ax, symbol='HO', title='Plot of force'):\n", - " \"\"\" Plots the distribution of firce components per atom on the output vs the input \n", - " only plots for the given atom type(s)\"\"\"\n", - " \n", - " in_atoms = ase.io.read(in_file, ':')\n", - " out_atoms = ase.io.read(out_file, ':')\n", - " \n", - " # extract data for only one species\n", - " in_force, out_force = [], []\n", - " for at_in, at_out in zip(in_atoms, out_atoms):\n", - " # get the symbols\n", - " sym_all = at_in.get_chemical_symbols()\n", - " # add force for each atom\n", - " for j, sym in enumerate(sym_all):\n", - " if sym in symbol:\n", - " in_force.append(at_in.get_forces()[j])\n", - " #out_force.append(at_out.get_forces()[j]) \\ \n", - " out_force.append(at_out.arrays['force'][j]) # because QUIP and ASE use different names\n", - " # convert to np arrays, much easier to work with\n", - " #in_force = np.array(in_force)\n", - " #out_force = np.array(out_force)\n", - " # scatter plot of the data\n", - " ax.scatter(in_force, out_force)\n", - " # get the appropriate limits for the plot\n", - " for_limits = np.array(in_force + out_force) \n", - " flim = (for_limits.min() - 1, for_limits.max() + 1)\n", - " ax.set_xlim(flim)\n", - " ax.set_ylim(flim)\n", - " # add line of \n", - " ax.plot(flim, flim, c='k')\n", - " # set labels\n", - " ax.set_ylabel('force by GAP / (eV/Å)')\n", - " ax.set_xlabel('force by EMT / (eV/Å)')\n", - " #set title\n", - " ax.set_title(title)\n", - " # add text about RMSE\n", - " _rms = rms_dict(in_force, out_force)\n", - " rmse_text = 'RMSE:\\n' + str(np.round(_rms['rmse'], 3)) + ' +- ' + str(np.round(_rms['std'], 3)) + 'eV/Å'\n", - " ax.text(0.9, 0.1, rmse_text, transform=ax.transAxes, fontsize='large', horizontalalignment='right', \n", - " verticalalignment='bottom')" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-08T08:53:58.018401Z", - "start_time": "2018-10-08T08:53:50.892329Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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388555zDhg0buPrqq8MeQ0mVxtZUEZFYV7aQhWLK5huGWdJcWtT3/fDDD7Rp04a3336b7t2789JLL1G+fPmifIQiC/foH6+pR1BESr3j7SMYDocOHeL++++nS5cu1KtXj/T09KgqAqF0tqaKiMS6gorAgl4vaS4tyvu2bNlCYmIia9euZfz48bzyyitBLwJLI/UIikip59ViMLl27txJq1ateOedd3jkkUd4+umnOeGE6Pz1XNpaU0VEYl2VQnrsquTrsStpLj3e+9544w26du3KGWecwbp166hVq1YwPpagQlBEBPCugNmwYQMtW7bkl19+YcaMGbRu3TrsMYiIiBSmOMP+S5pLC3rf4cOH6du3LyNHjqR+/frMnDmTs846q/gfQAqlQlBEgiacm4lH+8blzjleeeUVHnroIapVq8by5cu57LLLvA5LRETkKF6Mmtm9ezetW7dm7dq1PPTQQwwbNiwqFk2LNioERSQowrmZeLRvXH7gwAHuv/9+JkyYQJMmTZgyZQrx8fFehyUiIlKgcI6aeffdd0lOTubHH3/kjTfeoH379mG5b2mkxWJEJCiOtQ9QNN8r2L766ivq1avHhAkTGDBgAAsXLlQRKCIiAowbN4569epRrlw53nnnHRWBIaYeQREJinDuxRdp+/4V1apVq2jdujUHDx4kNTWV22+/3euQREREPHfw4EEefPBBUlJSuPnmm5k6dSqnn36612HFPPUIikjAUrdkUibffkK5QrEXX7RtXO6c44UXXuCmm27ijDPOIC0tTUWgiIgIkJmZSYMGDUhJSaF///4sXrxYRWCYqEdQRAKSO1+voH2GQrUXXzRtXL5v3z7uuecepk2bRnJyMhMnTqRixYr/PR7ti96IiEhs8CIfrV+/npYtW/Lrr78ye/ZsWrRoEdL7ydFUCIpIQAqarwdQ1ixkm4l7ve9fUX3++ec0vKUpX+z4N/HXd+TbxI6s2PEzzWv4CkEvFr1R4SkiIvmFOx855xg1ahS9e/emevXqrFixgksvvTTo95FjUyEoEgO8/OO+sHl5R5wLaQyRvnH5smXLaNn6DvYdyuGslgOJuyCBnT8dOCqxHmvRm1B8tmhfbVVEREIjnPlo//79dO/enUmTJtG0aVMmT56sRdM8ojmCIlEu94/7zKz9OP73x33qlsyw3D/a5uuFmnOOZ555hltvvZUj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N506dSIoKCjV9vj4eL799lt69uxp2rZ161aaNWuWwRYK4XxOnDiBr68v+/btY/78+Xz99deycraZMpI7N5wIp9bkPZQaFUytyXtkHmEa7JUj79+/T+PGjWnYsCH//PMPly9fxt/f36wYe/XqZXbn5IM+/PBDfvvtNy5evEhISAhTpkxh+/btae67Y8cOJk+ezO7du/nzzz85f/4848aNM73/3HPPMWXKFFq2bJmhGHr27PlQjgRYsmRJqhy5f/9+KlasiLu7e4aOL1LQWjvNo3LlyjorioiI0DVr1tSADggI0LGxsUaH5LDW/3BZ1wzcrUuO3KJrBu7W63+4bHRITiUqKkq7urrqc+fOmbb5+/vrkSNHPvJzO3fu1CVKlEi17dy5czpnzpz61q1bpm21a9fWs2fPNr2OjIzUpUuX1ocPH9aA2b/79erV0yEhIWbtm1JG21ejRg09Z84c0+v58+fratWqPbRfrVq19MKFC1Nti4+P14GBgfqZZ57R+fPn1x06dNDXr1/XWmt98OBB7e7uru/cuWPaPzg4WBcqVCjVz8DPz0+vXbtWa61Nx3J3d9fPP/+8XrdundZa67Nnz+pcuXLpHDly6Lx582pPT0+tdeLPtnv37rpgwYL66aef1h9//LGOj4/XWmu9cOFCXbNmTT1s2DDt6empS5UqpQ8ePKgXLlyoixUrpgsVKqQXLVpk9s/VKECodoDck1UeWTVHLlmyROfOnVv7+Pjoo0ePGh1OlmRO7lz/w2Vd7v1tusTILaZHufe3SZ5NwZ45cs6cObp27dqZirNnz54P5SRzFS1aVO/YscP0+v3339edOnVKc98uXbro9957z/R6165d+qmnnnpov27duulx48Y9tP2bb77R5cqV015eXrpJkyb6zz//1FprfenSJe3i4mJ6rXVirnN1ddVXr141bXv77bf1Z599prXWesGCBbpcuXLa3d1dlypVSn/99dda68TvLHfu3FoppfPmzavz5s2rw8PDdUxMjB46dKguUqSILlKkiB46dKiOiYnRWmsdEhKifXx89CeffKILFSqkCxcurNevX6+Dg4N16dKltbe3t544caK5P1JDmJsf5YqgwQ4dOkTlypU5efIkK1asYMqUKTzxhNzVIz1tKvlwcFRDLkxuycFRDWX5ayv79ddfcXFxoUyZMqZtFSpUSLe381HOnDnD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+CfO9ygxxFjrayZeRGKZMaYRkA2cYq39IdLjERERiQaKj1JbaEZQREoYY651lbI0xLkeYQvwY2RHJSIiElmKj1IbKREUEU99cS7+3wmcAgy2KhsQERFRfJRaR6WhIiIiIiIiMUYzgiIiIiIiIjHG2/4xNVbLli3tCSecEOlhiIhImBQWFvLDDz9w6NAhgL3W2laRHlNNoRgpIlK7HTx4kO+//57i4uKg4mOtSgRPOOEENm7cGOlhiIhIGGzevJmUlBTy8vKYMmUKd9555/ZIj6kmUYwUEamdiouLefzxx3niiSc499xz2bRpU1DxUaWhIiIS9WbNmkX37t3Jz89n9erV3HHHHZEekoiISMRlZ2fTt29fnnjiCYYNG8batWuDfq0SQRERiVqFhYXcf//9DBkyhK5du5Kens4FF1wQ6WGJiIhE3NatWzn//PP56KOPePXVV3njjTdISEgI+vW1qjRURERqjz179jBo0CBWrVrFvffey3PPPYfD4Yj0sERERCLu3XffZdiwYTRu3JiVK1dy0UUXhXwOzQiKiEjU2bBhA127dmX9+vVMnz6dl19+WUmgiIjEvKKiIkaPHs3111/PWWedRXp6eoWSQFAiKCIiUWbatGn06NGDuLg41q1bx9ChQyM9JBERkYjbt28fV155Jc888wx33303K1eupF27dhU+nxJBERGJCvn5+dxzzz3ceuutXHTRRWzcuJFzzz030sMSERGJuIyMDLp27crq1auZOnUqkydPpl69epU6pxJBERGJuF27dtGrVy8mT57MqFGj+Oijj2jZsmWkhyUiIhJxM2bM4MILL6SwsJBPPvmE2267rUrOq2YxIiISUZ9++impqakcPHiQOXPmMHDgwEgPSUREJOIKCgoYNWoUL730EhdffDFz586ldevWVXZ+zQiKiEhEWGuZPHkyPXv2pEGDBnz++edKAsswxrxhjNljjPna47HHjDGZxpgvXf9cFckxiohI1fvll1+47LLLeOmllxgxYgTLli2r0iQQlAiKiEgE5OXlcdttt3HPPfdw+eWXs2HDBs4888xIDysavQn08fL4C9bac1z/fFjNYxIRkTD64osv6NKlCxs2bGDGjBm88MILYemcrURQRESq1U8//USPHj2YNm0ajzzyCO+//z7NmjWL9LCikrV2DbA/0uMQEZHq8c9//pMePXrgcDj4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GUy5a3WWxGqNoIhIjFq7di2pqakcOXKEd999lwEDBkR6SCIiIiX6dU6KSKOVgoIC7r//fl555RV69erFnDlzaNWqVcDXjerdsdQaQSg9i9kuMYFMLwmdZwIZzDFVRTOCIiIxxlpbEtyaNGnC559/riRQREQE2L17N5dccgmvvPIKI0eO5OOPPw4qCYTAs5jBlLtWZ0msZgRFRGJIbm4ud999N9OnT+eaa67h7bffJjExMdLDEhERibj169czYMAADhw4wKxZs7jhhhtCPoe/WUz34/46ggZzTFVRIigiEiO2b99O//792bRpE4899hiPPPIIcXEqDBEREZkyZQrDhw+nffv2rF+/nrPOOiss1wmm3LW6SmKVCIqIxIAVK1YwcOBACgoKeP/997nmmmsiPSQREZGIO3bsGMOHD2fq1Kn07t2bWbNm0bx580gPq1roVrCISC1mreXZZ5/l8ssv57jjjmPDhg1KAkVERICff/6Z3/3ud0ydOpUxY8awePHimEkCQTOCIiK11tGjR7ntttuYM2cOqampvPHGGzRu3DjSwxIREYm4NWvWcP3115OTk8OCBQtISUmJ9JCqnWYERURqof/97390796defPmMWHCBObOnaskUEREYp61lpdffplLL72UZs2a8cUXX8RkEgiaERQRqXWWLFnCjTfeSFxcHEuWLOGKK66I9JBEREQiLicnh7vuuosZM2Zw3XXXMX36dJo2bRrpYUWMZgRFRGqJ4uJinnzySa6++mqOP/54Nm7cqCRQREQE+PHHH0lOTmbmzJk88cQTLFy4MKaTQNCMoIhIrXDo0CFuueUW0tLSGDJkCFOmTKFBgwaRHpaIiEjELVu2jMGDB1NUVMQHH9IStZ4AACAASURBVHzAVVddFekhRQUlgiIiNdy3335LSkoK3333HS+++CL33XcfxphID0tERCSirLVMmjSJhx56iNNPP52FCxdy8sknV+hcaRmZ1bLJe3VSIigiUoOlpaVx8803U79+fZYvX07Pnj0jPSQREZGIO3LkCLfeeivz5s1j4MCB/POf/6RRo0YVOldaRiYPLdhCbkERAJnZuTy0YAtAjU4GtUZQRKQGKioq4uGHHyYlJYVTTz2V9PR0JYEiIiLAd999xwUXXMD8+fOZOHEi77zzToWTQIBJS7eVJIFuuQVFTFq6rbJDjSjNCIqI1DAH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B1iyZAkNGjTwOiwREZEK0zg5+Gyfid+uoVWrVuTk5PDuu+9y0003VXBkIiISCXFfCK5du5bmzZuzfv16Zs6cydixY6laVT1mRUQkvgzu2JSkxIRDX7u8XH5Z+QJfzhrN+eefT1paGi1atPAwQhERCae4LQSdczz//PO0a9eOpKQk1q1bR58+fbwOS0RExBPdzm3C6B7n0CQ5ifx9mWTOGc7uD15n0KBBLF++nOOOO87rEEVEJIzisulr//793HXXXUyaNInOnTszffp06tWr53VYIiIinup2bhMa56TTs+dQcnbuZNq0afTv39/rsEREJALirkXwhx9+oG3btkyaNImHHnqIhQsXqggUEREBXnzxRS688EKqVq3K2rVrVQSKiMSwuGoRfPvtt7nqqqvYv38/8+bNo1u3bl6HJCIi4rkDBw4waNAgxo8fz6WXXsqMGTM4+uijvQ5LREQiKC5aBJ1zPPXUU3To0IH69evzwQcfqAgUEREBtm3bxsUXX8z48eMZMmQIb775popAEZE4EPMtgllZWdxyyy288sordOvWjSlTplCnTh2vwxIREfHc6tWr6d27N7/++iuzZ8+mV69eXockIiIVJKZbBL/99ltatWrFjBkzGDVqFHPmzFERKCIicc85x7hx47j44oupXbs277//vopAEZE4E7MtgkuXLqVv374451i0aBGdO3f2OiQRERHPZWdnc+eddzJ58mQuv/xypk2bRnJystdhiYhIBYu5FkHnHGPGjKFTp04cf/zxpKamqggUEREBvv/+ey688EImT57M8OHDef3111UEiojEqZhqEczPz6d3797MmTOHPn368OKLL3LUUUd5HZaIiIjnfv31V5o3b87BgwdZsGABXbt29TokERHxkDnnvI4hbJKSktzBgwd57LHHuO+++zAzr0MSEZEIMbM051yK13FUFmbmfv/73zN//nzOOOMMr8MREZEIKWl+jKlC0MwygO8q8JTHADsq8HwVRddVucTqdUHsXpuuKzxOcs41qMDzVWoVlCNj9f/tArF+fRD71xjr1wexf42xfn1Q/mssUX6MqUKwoplZaizejdZ1VS6xel0Qu9em65JYFev/D8T69UHsX2OsXx/E/jXG+vVBxV1jzE0WIyIiIiIiIsVTISgiIiIiIhJnVAiWzwSvA4gQXVflEqvXBbF7bbouiVWx/v9A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A/AHYJmZASQA20OcZ5ZzLh/40sy+Ac4EZgP/Z2aDgRuBySH2DdX1BefcJ/6uK1cD/y3ZJf/GP+7h+BDdWxoBBXcqWwJnAWv811oNeM+/biaw1szux5fwAu+6duK3LjgnA8nOuQ3+r5PN7JSALkg/4/u+iIiI95QflR8lhqkQFPHNzlVUwXS6+4psV3SaXQM2OecuKMF5iu7rnHNZZrYMuBJfl5CUEhwnmAXA40A74OhS7nshsDrEumyghv+9Acucc1cX3cg594N/nMVF+P5oCPx+tADu8L+/Bmji3xagLtCP3+4S1/CfU0REvKf8qPwoMUxdQ0VgFf4uGGbWDtjhnPslyHZLgdvNrKp/2/rAZqCBmV3gX5ZovgHkwfQ2syr+biun+vcFX/eXfwMfOud2lfEaJgEjnXMby7BvJ3zjKIL5DPid//06oLWZ/Q7AzGr6u+sUmAE8CXztnNvq3+Zs4HPnXJ5/m2uAFs65k51zJ+PrWhTY/eUM4NMyXIOIiISf8qPyo8QwFYIiMAJIMbNPgDHAgBDbTQS+Bz4xsw1AP/8sYr2AR/3LPgZahdh/M77poN8EbnfO7QdwzqUBv1D8bGBFx0AUmmrbObfVOfd0Ca41mHYETFNdxCL/evyD2a8HZvi/V+so3E1lNnA2/kHwfocG2ZvZeUB+wNgI/N1tcvzrwDfmYnkZr0NERMJrBMqPyo8Ss/RAeRGPmVlj4G3gTP8YiYo89/HAC865ziHWJwErgdYBdy1Lc/xlwHXOuVDjQgK3PRe4zzl3bWnPIyIisUf58dC2yo8SESoERTxkZtfh6/9/n3NuttfxBGNmHYHPnHPfR/g8lwJfOue2RPI8IiIS/ZQfC51H+VEiQoWgiIiIiIhInNEYQRERERERkTijQlBERERERCTOqBAUERERERGJMyoERURERERE4owKQRERERERkTijQlBERERERCTOqBAUERERERGJMyoERURERERE4owKQRERERERkTijQlBERERERCTOqBAUERERERGJMyoERURERERE4owKQRERERERkTijQlBERERERCTOqBAUERERERGJMyoERURERERE4owKQRERERERkTijQlBERERERCTOqBAUERERERGJMyoERURERERE4owKQRERERERkTijQlBERERERCTOqBAUERERERGJMyoERURERERE4owKQRER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heDJJ59Mamqq12GIiEiY5ebmMmzYMB5//HFatWrFa6+9RuPGjb/zOq7KRDlSRCQ2ffbZZ3Tr1o29e/fy9NNPM2jQoBLlR3UNFRGRqLZjxw46derE448/zp133snKlStp1KiR12GJiIh4bu7cubRo0YLMzEzeeust/vrXv5Z4XxWCIiIStT766CNSUlJYvXo1kyZN4j//+Q/VqlXzOiwRERFP5eXl8fe//52ePXty1llnkZaWxkUXXVSqY6gQFBGRqDR16lRat25Nfn4+q1ev5oYbbvA6JBEREc/t2rWLLl268Mgjj3DzzTezatUqjj/++FIfR4WgiIhElZycHO6++24GDBhAy5YtSU1NJSUlxeuwREREPLdhwwbOP/98VqxYwfjx43nhhReoXr16mY6lQlBERKLGjz/+yCWXXMKzzz7Lfffdx7Jlyzj22GO9DktERMRzM2bM4IILLmD//v2888473HrrreU6ngpBERGJCuvWraN58+akpqYyffp0nnjiCapWjanJrUVEREotNzeX+++/n379+tG8eXPS0tK44IILyn1cFYIiIuK5CRMm0LZtW6pXr857771Hv379vA5JRETEcxkZGVx22WX861//YuDAgbz11ls0bNgwLMfWrVYREfHMgQMHGDhwIBMnTqRjx4688sor1K9f3+uwREREPJeamkqPHj3IyMhg8uTJDBgwIKzHV4ugiIh4YuvWrVx00UVMnDiRBx98kEWLFqkIFBERASZPnkybNm0wM1avXh32IhDUIigiIh5YtWoVvXv3Jisrizlz5tCjRw+vQxIREfHcwYMHuffeexk3bhzt27dn5syZNGjQICLnUougiIhUGOcc//73v7nkkktITk7m/fffVxEoIiICbN++nfbt2zNu3DgeeOABlixZErEiENQiKCIiFSQrK4vbb7+dadOmccUVVzB16lTq1q3rdVgiIiKeW7t2Lb169WLPnj3MnDmTPn36RPycahEUEZGI27JlC23atOHll19m5MiRzJs3T0WgiIjEPecczz//PO3atSMpKYl169ZVSBEIahEUEZEIW758OX379iU3N5eFCxfSpUsXr0MSERHx3P79+7nrrruYNGkSnTt3Zvr06dSrV6/Czq8WQRERiQjnHI899hgdO3akYcOGfPjhhyoCRUREgB9++IG2bdsyadIkHnroIRYuXFihRSCoRVBERCJg79693HjjjcyePZvevXszadIkatWq5XVYIiIinnv77be56qqr2L9/P/PmzaNbt26exKEWQRERCauvvvqKli1bMmfOHB577DFeffVVFYEiIhL3nHM89dRTdOjQgfr16/PBBx94VgSCWgRFRCSMFi1axDXXXENCQgKLFy/m0ksv9TokERERz2VlZXHLLbfwyiuv0K1bN6ZMmUKdOnU8jUktgiIiUm75+fmMHDmSrl27csopp5CWlqYiUEREBPj2229p1aoVM2bMYNSoUcyZM8fzIhDUIigiIuW0Z88errvuOhYsWED//v0ZP348NWvW9DosERERzy1dupS+ffvinGPRokV07tzZ65AOUYugiIiU2WeffUaLFi1YtGgR//73v5k6daqKwDAys0lm9rOZfRqwbISZpZvZx/7XX7yMUUREDuecY8yYMXTq1Injjz+e1NTUqCoCQYWgiIiU0dy5c2nRogWZmZmsWLGCu+++GzPzOqxYMxnoFGT5k865Zv7Xfys4JhERKcavv/5K7969GTZsGFdddRXvvfcep512mtdhHUaFoIiIlEpeXh4PPvggPXv25KyzziItLY22bdt6HVZMcs6tAnZ5HYeIiJTMF198QcuWLZk3bx6PP/44M2bM4KijjvI6rKBUCIqISInt2rWLLl26MHr0aG6++WZWrVrF8ccf73VY8WigmX3i7zoa8gnEZnarmaWaWWpGRkZFxiciEncWLlzI+eefz08//cTSpUu5//77o7qnjApBEREpkQ0bNpCSksKKFSsYP348L7zwAtWrV/c6rHj0HHAa0AzYDjwRakPn3ATnXIpzLqVBgwYVFZ+ISFzJz89nxIgRXHHFFfzud78jLS2NSy65xOuwjkizhoqIyBHNmDGDm266iXr16rFq1SpatmzpdUhxyzn3U8F7M3sBeMPDcERE4lpmZibXXnstb7zxBgMGDOC5554jKSkprOeYvz6dsUs2sy0zm8bJSQzu2JRu5zYp93HVIigiIiHl5uZy//33069fP5o3b05aWpqKQI+ZWaOAL7sDn4baVkREImfTpk20aNGCxYsX8+yzz/LSSy9FpAgcNncj6ZnZOCA9M5thczcyf316uY+tFkEREQkqIyODPn36sHLlSgYOHMgTTzxBtWrVvA4rrpjZDKAdcIyZbQWGA+3MrBnggC3AbZ4FKCISp1577TWuv/56atWqxYoVK7jwwgsjcp6xSzaTnZNXaFl2Th5jl2wud6ugCkERETlMamoqPXr0ICMjgylTpnDdddd5HVJccs5dHWTxixUeiIiIADAn9XsGPTCE9HdmUuuE3/PwC1O58MKUiJ1vW2Z2qZaXhrqGiohIIS+99BJt2rTBzFizZo2KQBEREWDqyk+57qpuviKwWSfqX/UIj6/eEZZumqE0Tg7e1TTU8tJQISgiIgAcPHiQu+66ixtvvJE2bdqQlpbGeeed53VYIiIinvv444+5teelZH2/kfqd7ubojgOxqomHumlGyuCOTUlKTCi0LCkxgcEdm5b72CoERUSE7du30759e8aNG8cDDzzA4sWLOeaYY7wOS0RExHPTp0+nVatW5Obk0LDfo9T+U8dC68PRTTOUbuc2YXSPc2iSnIQBTZKTGN3jnLDMGqoxgiIicW7t2rX06tWLPXv2MHPmTPr06eN1SCIiIp7Lyclh8ODBPP3007Rt25asNneTkXd4l8xwdNMsTrdzm4Sl8CtKLYIiInHKOcdzzz1Hu3btqFmzJuvWrVMRKCIiAvz0009ceumlPP300wwaNIjly5fz914XRKybphfUIigiEof279/PXXfdxaRJk+jcuTPTp0+nXr16XoclIiLiuQ8++IAePXqwc+dOpk2bRv/+/QEOtcpF4uHuXlAhKCISZ3744Qd69uzJhx9+yEMPPcSIESNISEg48o4iIiIx7sUXX+TOO++kcePGrF27lnPPPbfQ+kh10/SCCkERkTjy9ttvc9VVV7F//37mzZtHt27dvA5JRETEcwcOHGDQoEGMHz+eDh06MHPmTI4++mivw4oojREUEYkDzjmefPJJOnTowNFHH80HH3ygIlBERATYtm0b7dq1Y/z48QwZMoTFixfHfBEIUVIImtkkM/vZzD4NWFbfzJaZ2Zf+fzV4RUSkDLKysujfvz/33XcfXbt25f333+fMM8/0OiwRERHPrV69mvPOO4+NGzcya9YsxowZEzfDJaKiEAQmA52KLBsKvOWcOx14y/+1iIiUwjfffEOrVq2YMWMGo0aNYs6cOdSpU8frsERERDzlnOM///kPF198MbVr12bdunX07t3b67AqVFSMEXTOrTKzk4ssvhJo538/BXgbGFJhQYmIVHJLly6lb9++OOdYtGgRnTt39jokERERz2VnZ3PHHXcwZcoUunTpwssvv0xycrLXYVW4aGkRDOY459x2AP+/xwbbyMxuNbNUM0vNyMio0ABFRKKRc47Ro0fTqVMnjj/+eFJTU1UEioiIAN999x0XXnghU6ZMYfjw4SxYsCAui0CIkhbB8nDOTQAmAKSkpDiPwxER8dSvv/7KDTfcwJw5c+jbty8TJ07kqKOO8josERGRCjF/fXrI5/ytWLGCPn36cPDgQV5//XWuuOIKj6P1VjQXgj+ZWSPn3HYzawT87HVAIiLR7IsvvqB79+58/vnnPP7449x3332YmddhiYiIVIj569MZNncj2Tl5AKRnZjNs7kacc3yz8lX+9re/0bRpU+bNm0fTpk09jtZ70VwILgAGAGP8/77ubTgiItFr4cKF9O/fn2rVqrFs2TLat2/vdUgiIiIVauySzYeKwAL79u2jf/9ryPpsFS3bd2bp/FepXbu2RxFGl6gYI2hmM4D3gKZmttXMbsJXAF5qZl8Cl/q/FhGRAPn5+QwfPpwrrriC008/ndTUVBWBIiISl7ZlZhf6Omf3dn58+QGyPnuX5IsGsPuCu3nrq188ii76REWLoHPu6hCrLqnQQEREKpHMzEyuvfZa3njjDQYMGMBzzz1HUlKS12GJiIh4onFyEun+YjD7mzR2LHgMzDi29wiSTm3O/tx8xi7ZfGjMYLyLihZBEREpnU2bNtGiRQsWL17Ms88+y0svvaQiUERE4trgjk2pUdXYs/ZVfp49goS6x9JwwFMkndr80DZFWw3jWVS0CIqISMm99tprXH/99dSqVYuVK1fSpk0br0MSERHxXPvTapO89t9sfncJNc+6iKM73U2VxBqFtmmcrJumBdQiKCJSSeTl5TF06FB69+7NOeecQ1pamopAERER4PPPP+fPf/4zqauW8+STT/LytJc5qmbhxyclJSYwuKNmCy2gFkERkUpg586dXH311SxbtozbbruNp59+murVq3sdloiISNgV9yzAYF5//XWuvfZaatSowfLly2nXrh0AZlaq48QbFYIiIlFu/fr19OjRg23btjFx4kRuuukmr0MSERGJiFDPAgQOK+Ly8vIYMWIEo0aNIiUlhblz53LCCSccWt/t3CYq/IqhrqEiIlHs5ZdfplWrVuTk5PDuu++qCBQRkZgW7FmA2Tl5jF2yudCy3bt307VrV0aNGsUNN9zAu+++W6gIlCNTISgiEoVycnK45557uPbaa2nRogVpaWm0aNHC67BEREQiKtSsnoHLN27cyPnnn8/y5csZN24cL774IjVq1Ai6n4SmQlBEJMr89NNPdOjQgaeffppBgwaxfPlyjjvuOK/DEhERibhQs3oWLJ81axYtW7YkKyuLt99+mzvuuAMzq8gQY4YKQRGRKPLBBx/QvHlzPvzwQ6ZNm8ZTTz1FYmKi12GJiIhUiMEdm5KUmFBomQFbd+2lSds+9OnTh2bNmpGWlkarVq28CTJGaLIYEZEo8eKLL3LnnXfSuHFj1q5dS7NmzbwOSUREpEIVTO4ydslm0jOzMSA3aw87FjzG/u82kNz8cu558t80atTI20BjgFomMP2hAAAgAElEQVQERUQ8duDAAW6//XZuvvlmLrroIlJTU1UEiohI3Op2bhPWDG1Pk+Qk9v/4Fdun3Mv+rf/j6L/cQ90Ot/PUim+9DjEmqEVQRMRD27Zto2fPnqxbt44hQ4bw8MMPk5CQcOQdRUREYtwXq99g55L/UCWpLg2veYzqjU4HQk8oI6WjQlBExCOrV6+mV69e7N27l9mzZ9OrVy+vQxIREfHU/PXpPPrfTWya+wy/fvQG1U88hwZXDiWhZt1D24SaUEZKR4WgiEgFc84xbtw47rnnHk455RTeeustzj77bK/DEhER8dT89ekMnvoOP7z2CAe2bqL2+d2o1+4GrMpvPWWSEhMY3LGph1HGDhWCIiIVKDs7mzvuuIMpU6Zw+eWXM23aNJKTk70OS0RExHP/eGEe304bTv7+fRzTdTBHnXURAAlm5DtH4+QkBndsemhCGSkfFYIiIhXku+++o0ePHnz00UcMHz6cf/zjH1Sp8tucXfPXpzN2yWa2ZWYr2QkAZjYJuBz42Tn3B/+y+sCrwMnAFuAq59xur2IUEQmHCRMmsHH8vVStfTQNrx1LtWNPPbQu3zm+HdPFw+hik2YNFRGpACtWrCAlJYWvvvqKBQsWMGLEiMOKwGFzN5KemY0D0jOzGTZ3I/PXp3sXtESDyUCnIsuGAm85504H3vJ/LSJSKR04cIBbbrmF2267jeTTzqXhgKcKFYGgMYGRokJQRCSCnHM88cQTXHrppTRo0IAPP/yQrl27Hrbd2CWbyc7JK7QsOyePsUs2V1SoEoWcc6uAXUUWXwlM8b+fAnSr0KBERMJk69atXHTRRUycOJEHH3yQia+8Rq06hYdLaExg5KhrqIhIhOzbt4+bb76ZmTNn0rNnT1566SVq164ddNtQU2FrimwJ4jjn3HYA59x2MzvW64BEJDpF85CDVatW0bt3b7KyspgzZw49evQAICEhIWpjjjUqBEVEIuDrr7+me/fufPrpp4wePZohQ4ZgZiG3b5ycRHqQok/dYaQ8zOxW4FaAE0880eNoRKQiFQw5KOhtUjDkAPC0sHLO8cwzz3D//fdz6qmnsnLlSs4666xD67ud20SFXwVR11ARkTB78803SUlJYevWrSxevJihQ4cWWwQCDO7YlKTEwg+SV3cYCeEnM2sE4P/351AbOucmOOdSnHMpDRo0qLAARcR70TjkICsriwEDBjBo0CD+8pe/8MEHHxQqAqViqRAUEQmT/Px8Hn74Ybp06cJJJ51Eamoql112WYn27XZuE0b3OIcmyUkY0CQ5idE9ztFdUQlmATDA/34A8LqHsYhIlIq2IQdbtmyhTZs2vPzyy4wcOZJ58+ZRt27dI+8oEaOuoSIiYfDLL78wYMAA5s+fT79+/XjhhReoWbNmifeP5nEc4h0zmwG0A44xs63AcGAMMMvMbgK+B3p7F6GIRKtIDzkoTd5avnw5ffv2JTc3l4ULF9Klix4FEQ1UCIqIlNPnn39O9+7d+fLLL3nyyScZNGjQEbuCBirvOA4VkbHLOXd1iFWXVGggIlLpDO7YtFBugfANOShp3pr30Vbu/ftIvlvyIknHnsjjL06lS5cLy31+CQ91DRURKYf58+fTokULdu7cyfLly7nnnntKVQRC+cZx6PmDIiISTCSHHJQkb81Y8wUD+vfju8UvUPOMVhzTbyzPpO47lJ/mr0+n9ZgVnDJ0Ea3HrFDe8oBaBEVEyiAvL48RI0YwatQoUlJSmDt3LieccEKZjlWecRzFJWO1CoqIxLdIzcB5pLz11VdfcVPPjmT//D3J7W6gTosemFmhYjEaZzSNN2oRFBEppd27d9O1a1dGjRrFjTfeyLvvvlvmIhBCj9coyTiOaJsMQEREKo+ytsoVl7cWLVpESkoKB/bs5NirRlL3zz0L9ZTZlpkdlTOaxiMVgiIipbBx40bOP/98li9fznPPPcfEiROpUaNGuY5ZnkdHlKeIFBGR2FPS4q48QwuC5a0aVY0Tv/svXbt25dRTT6XZX58n6eRmh+3bODlJNzGjhApBEZESevXVV2nZsiVZWVm8/fbb3H777aUeDxhMecZx6PmDIiJSoDTFXXla5QLzFoAdyOL7V0cy8/knuOgvPVizZg3/1/eikPlJNzGjg8YIiogcQW5uLsOGDePxxx+nVatWvPbaazRq1Cis5yjrOI6CfTRrqIhI7CjrbNChirv7Z20ACo+/K2+rXMGx7puwiB9mjSQ380fqdbiN7c2uZMnnu46YnyI1o6mUnApBEZFi7Nixg759+/LWW29x55138uSTT1KtWjWvwyokUpMBiIhIxSvPI4VCFXF5zh12jHA8Z3DoExPZMvtRrGp1juv7MDVO+AP7c/MPTVgWKj/pJmZ0UNdQEZEQPvroI5o3b87q1at56aWX+M9//hN1RaCIiMSW8nTZLK6IK3qMwR2bklil8PCGxCpWola5vLw8HnzwQTZPH0Hi0SfSaMBT1DjhD4fWF9eqWDCG8d5XPwbgyT7NWDO0vYpAD6gQFBEJYurUqbRu3RrnHKtXr+b666/3OiQREYkD5emyGWzceLHHKDrMvQTD3nft2kWXLl0YPXo0x53fhYb9xlC1zjGFtglVkOrZt9FFhaCIxLzSTI+dk5PD3XffzYABA2jZsiVpaWmkpKRUYLQiIhLPyjORSsEkLgkhJjIrOMb89encP2sDOXmu0PqcPFdsy+OGDRtISUlh5cqVTJgwgefHj6dmUuGZs4sb66fHRkQXjREUkZhWmrEWP/74I71792b16tXcd999PProo1Stqo9JERGpOIM7Ni3XRCpHmoylIC/mORd0/1AtjzNmzOCmm26iXr16vPPOO7Rs2fLQupKO9Qt17PTMbJr9cymZ2TkA1KuZyPCuZ6u7aITpLxwRiWnF3X0MTDDr1q2jZ8+e7N69m1deeYWrr766okMVEREJy0QqxR2j9ZgVh+XFQHWTEgt9nZuby5AhQ/jXv/7FhRdeyKxZs2jYsGGhc5U0tlAT1ACHikCA3Vk5DH7t8JlOJbxUCIpITCvJWIsJEyYwcOBAjj/+eN577z3+9Kc/VVR4IiIihwnHbNChjnGksYaBvUozMjLo06cPK1eu5O677+aJJ54gMTEx9M5HEKy104BgbZMF3VRVCEaOCkERiWnFTY994MABBg4cyMSJE+nYsSOvvPIK9evX9yBKERGRsj8/sDSKa5UDyMzytcylpqbSo0cPMjIymDJlCtddd125zx2spbK4WEr6TEMpG00WIyIxLdgMakmJCdzQrDZt27Zl4sSJPPjggyxatEhFoIiIeKaiZtQ80syijZOTeOmll2jTpg1mxpo1aw4VgaWZfC2Ubuc2Yc3Q9nw7pgtrhranSTGT4JTmmYZSeioERSSmFcyg1iQ5CQOaJCfR/6R9/H3A5fzvf/9jzpw5PPzwwyQkhE6KIiIikVZRM2oW5MV6NQ/v4lmjSj51PprKjTfeSJs2bUhLS+O8884DIleoBnueIUBiQsmeaShlp66hIlJplLXLTME4CecczzzzDPffcT+nnnoqK1eu5KyzzqqAyEVERIpXnucHllZBXgzMq8dUyWLvm2NZ/PGHDB48mEceeaTQzNlHmnytPDkaYMSCTZo1tIKpEBSRSqE0j4EIJisri9tuu42XX36ZK664gqlTp1K3bt2IxlxWFTFGREREoktxY9ojlRcKjjH42VmsnzWK/AP7OKn3MFpdfddhj08qrlAtb44Ox+Q4UnrqGioilUJ5usxs2bKF1q1bM336dEaOHMm8efOiugisiDEiIiISXUKNab/4zAYRywvzPtrKrcMe4avJf8OqVqfhtU/Aqa0Z/NqGw45f3IPu9aD4ykktgiJSKZS1y8yyZcvo27cveXl5LFy4kC5dukR1i1tJn3sYC6L55yAiUtFCPfvvSEXW2CWbSc/MJsGMPOdoUuTzNNRn7f79+7n9tlvISF1M0qkpHN31ARJq1AIKP7qhYP/0zOzDHvVgUOysn+n+1kJ9tkcnFYIiUikU12UmGOccY8eOZdiwYfz+979n3rx5nH766eXuvgKRLWAqcoyIl8LxcxARiTXBukje++rHQbct+Nws+BzNc67Q8gLBPmt3/LiNRwffws+bNlC3VV/qtumHWeGOgsG6fDp+e+5fqOf/FaXP9uilrqEiUimE6jITbEaxvXv30qdPH4YMGULPnj1Zt24dp59+OlD+Wdki3XWzuK435RWOab/DRd2IRERKJtTnf4LZYZ+jBbJz8rh31sfc8+rHh22z++v13NHrUr758gsadP87yRf2P6wILDjviAWbDtvf+c9dkiKwIBZ9tkenqC8EzWyLmW00s4/NLNXreEQkuEgWGQUtcNk5eSSYb4rpJslJjO5xzmF3GL/88ktatmzJnDlzeOyxx3j11VepVavWofXlbXGLdAFTmoK3NKJt7GG8tHyKiJRXqLxQ0AIYStHVzjl++XA+P818CGrUpuF1/6LmGReE3P/ko5MOzeJZ1JHOXZQ+26NT1BeCfhc755o551K8DkREDhfJIiPw2OBLPgWFUdEicNGiRZx//vls376dJUuWMHjwYMwKP5uovC1ukS5ggj33MFjBW1rR1gIXyZZPEZFYEiovFPcg9qLyc/az443H2b1iIkmn/5lG1/2LxKNPKHafdd/sDrkuwQ5/7l9x9NkenTRGUETKJHCcXBX/APVA4ZrgpCSTp+Tn5zNq1ChGjBhBs2bNmDt3LieffHLQ4118ZgOmr/u+UJeW0rS4lXasYllEYhrtaGuBG9yxaaFxJxCelk8RkVgUKi8Mnr2BnPziW+dyMn8kY+4ocjK+I7ntddRp2StoV9Ciimv1u/rPJzAnLT1k19RA+myPXpWhEHTAUjNzwHjn3ITAlWZ2K3ArwIknnuhBeCLxp+jg8VDJIhxFxpEKmD179nDdddexYMECrr32WsaPH09SUvCibP76dOakpR8241nP5iUvvIorYKJ5FsyKKGBLI9TseNHy/RIRiVaBs3geSfY3aexYOBac49hew0k6reSd6xKC3OQFOKpaAis/zzg0XKO4grHoDKYSXSpDIdjaObfNzI4FlpnZ5865VQUr/YXhBICUlJTSdVgWkTIJ1koXTHLNxHKfK1QBk1wzkXPvncSnU/9BXuaP3Py3kUwY89BhXUEDBYvbASs/zyhxPKEKGAg+M1vgPl6KxhY4PUBYRKR489enM2LBpkNj9Y6qlsDB3PwjtgI65/hl3WwyV00jscFJNOj+dxLrNSrxeRMTjD7nH97ql5hgHMzNLzRcIzHBwFEopqTEhLAMa5DIKnEhaL425D8BjYFsYJNz7qdIBVbAObfN/+/PZjYPaAGsKn4vEYmkkrb0lXIsOXD4oxkuPrNB0ESUvv4ddvz3SaxqdY7t+zBv2R94/eNtxSadcHWPDFbAtB6zIqqf/6cWuMjyKkeKSOyavz79sK6f+w4e+SZs/oEsdv73KbK+WEvN37fl6E5/pUq1GqU6d2IVI+Wk+qScVL9Q3th3IPewCWRy8hz1aibiHIfW1UisLNOQxLcjFoJmdhowBOgAfAlkADWAM8wsCxgPTHHO5Yc7ODM7CqjinPvV//4yYGS4zyMipROqla6ozOwcWo9ZUaioW/l5RrFfBxZ96ZnZzElLp2fzJoe2a1SnGp+/8QK735tNtUZNadBtGFXrHEM+MGzuJ8UWNpHsHhltY/CCUQtc+HmZI0UkekRiaMDYJZuP2PJXVM7OrWTMe5icXenUu/gmap/frdieMgWqAIEfUlk5+Qx+bQNje/2JNUPbH1p+ytBFQfffnZVTaGbT3Vk5UdUrRoIrSbk+CngZOM0519E5198518s590fgSqAucG2E4jsOWG1mG4APgEXOucUROpdIXCrLYx+CTWUdjEGhmURfXvf9Eb8O1qq28vMM1gxtT9rfLqDOqifY/d5sav2pIw37jaFqnWMCti3+b+1IPZoBNAtmHPMyR4pIFIjUzNmlvZGY9eX7bJ96H3lZezi2z/+jTovuJSoCoXARWCAnzx02s3SonGYQVTNTS8kcsUXQOXd1Mat3OOeeCmM8Rc/9Db6uNiJSTsHuVkLZxrUV7WZYNymRfQdzyckrfOcyXIN2t2Vms2HDBrp37056ejr1Ow6kdrNOJd4/8NrrJiVSI7EKmVk5Ye0eGY1j8CTyvMyRIhIdSjK7dVElaUEsae8b5/LZs3oGe9bOoFrD39Gg+4NUrXNs2S8oQNFiNFiuS6xiIVsuo6lXjByuTJPFmNkFQH+gO77xECISxYrO8llQ8FWvWqXM49oCuxkGG8xeknEMJZW4ZS0XXPAk9erV45133qHf6zsJlnOqBLnxWfTaM7N93Vee7NPssGssT9cejcGTAsqRIvGltEMDguXke179mBELNjHiirMP5Y3BHZse8fEQ+fv3suONJ8j++kOO+kMH6l92BwmJ1cN2I7ZuUuFJ34LluqyDuezOCv7gefWKiW7FFoJmluycy/S/PxNfYuuDL7HdBfwj4hGKxLlwjDsIdbcy1Myfxd3BK8lkLllhKgJdfh6/rprM7vfncda5f6Zm5we4ev5OaiRWCdoN9IJT6x+2rKR3akMVy1Dy8Q0agxdflCNFZP769KDP0gVfERQsh4eaeTszu/C4uoJ88uDcT8gKkvMOZnxHxrxR5O75mfqX3kGtc/+CmVE1wQ7roVNW+w7mMn99eqHcVjTXhRo3CKhXTJQLWQia2bPAcWZWH6gD5AIzgLbAWufc5AqJUCSOhaM4gdJ3zQh1By9YPEUfzg7h6RKaty+TXQsfI+u7T+jS9wa+OqUHGXm+Jr9QYwHXfr2Lh+ZvZFS3cw4tK+md2rJ07YHITBAg0U85UkQKcmKwIjCxipGZdZB7Xv340LKCHF7c45eK5p2Comv++nT+uXDToZa3fZ+vZud/n6JKtSSOu/oRahx/9qFjlLUINDt8tu+CcYJlmYitXs1E5cMoV1yLYEvgJmA9vsc1PAIsd87l+x/uLiIRVtbipKjiPqT35+Qfdo59Bw6/Axgqnkh8GBzY/gUZ80aTn72Hv458itTEc9hfknESwPR135NyUv1DsZd0ptCyzPoZrkJdKiXPc6SZbQF+BfKAXOdcyZ8ULSLlFqplz/z/CTZEoiQPYU/PzOaUoYtI9j+SYU92zqGx+C4/j8xVU/nl/TlUb3wmx3QbRtXaR5f7WpokJ5V59utQY+SHdz27mL0kGhQ3a+jt+JLcxcBY4Abga/9d0OoVEJtI3AvXIwlCzZY5vOvZjO5xDvWKPPg9MzuHwbM3HDbjWXkGfSdWMUoyd9neT5bx4/QhYHDcNY+RmnhOqc7roNAsZcXNFBo4Y2qVEDOrFTe+obhCXWJetOTIi51zzVQEilS8ULnJUXyr3KGHsBfD4XsEQ2Z2Dg5fXt6/dw8/zxrOL+/PoVazzhx39eiwFIFJiQlcfGaDMuVB8N34HN3jHJokJ2H4iko9TL5yCNki6JxLBVIDFi0ys1pAD3zPR/oWWOic+2uEYxSJK4FdDYsbd1AaR5rIZMSCTYftk5PvGLFgU6EP8uSaiSEHhB9JYkLoWcUAXF4Ou956gb3r/0uNk/7EMVf8jYSadQ/FW5KZ0woEJudQ1w6FZ0wN9n0+0qyfleHZgRIZypEilU+4u/KXNjcVOKpaAgdzS/do0YM/fc3P8x4hb+9O6nf6K7X/dFmpzxuMGfRs3oQ5aellyoMFNEa+cirJA+UbOee2Azjn9gJTgalm1gjoG+H4ROJK0a6G5flQLqq4D+mC2T6LWz5/fTp7ylgEAkEHuhfI3buLHfNHcyD9M+q06EHyRQOwKr5WvOSaicGnqy5mMHzRQjnYtbcesyJol54EM/KdOzQRztglm7n31Y+D/tEQyQfUS+XgcY50wFJ/V9TxzrkJQeK7FbgV4MQTT4xwOCLRKxJd+UN1iayRWKXYm6alnVV776aV7Fr8DFWS6tCw36NUb1zyvwGO1A21ahVj0SfbQ+ZDtezFtpI8PmKSmdUD3gYWA6udc7n+xPdkJIMTiTehxhsEFicVPRlJwVjBEQs2BX3gbHnt3/oZO14fTf6BfRxzxd846vdtC613LnSrXup3uw6brCaw22fR2U1Xfp5xxBbGfOf4dkyXEv3RoGcHCt7myNbOuW1mdiywzMw+d86tCtzAXxxOAEhJSdH4folb4RpzX5RvFmvfcZOTEhlxhW9c3JEmhSkJl5fL7pUv8mvaQqqf8AcaXDmUhKOSS3WMq/98wmEzewfKyXMhi9Z851QExriSPFC+s5nVANrheybS42b2Pb6Et9g5931kQxSJH6G6FBYUJ5FSr5gun4Nnb+CfCzeFbDUsK+ccez9+k13LJ1C1TgMaXjWSag1OPmy7zOycQ8Vo0YTU7dwmpJxU/4jdPtMzs3l53W8fVemZ2RjBJ7opaM0ryR8NenageJkjnXPb/P/+bGbzgBb4Jq4RkSLK2pV//vp0/j5vY6FWvKTEKvRsfvxhBdaBgO6ewZ7TWxp5+3aT8fqjHPjhU2qnXEm9djdgCaV//PeiT7Yzusc5jF2yudTdWNW7JfaV6P8o59x+/EkNwMxOAToDz5pZQ+dci8iFKBI/KqKrYbCWsmJ6jZCTH/puYVm53IPsXDqOfRuXk3RqCkd3fYCEGrVCbj/4tQ1A8O47RQvE+evTuX/WhmK7woCvCCxaDAa25pX0jwaNixAvcqSZHQVUcc796n9/GTAy3OcRiRXF5ddQYwfnr0/n/tkbyCsyvj07J7/QzcXflufxz4Wbgs7GXRoHtm0mY94j5O/fy9GX30+tsy8u87EK8vfgjk1D5sbkpEQO5Oard0scKskYwWeBGc65NQXLnHPfAuOAcWZWLYLxicSVcHQ1nL8+nRELfmvBq1czkeFdzz6U1IprKasIub9kkDHvEQ7++CV1W/Wlbpt+mBU3gfFvzzGC4lvfinumUzCO36bMLno8jf+TkvAwRx4HzDPfLH9VgVecc4sjdC6RsCnPhC0l2TfUNsHyK8CufQcY/NqGQ2POA4cBjF2y+bAi8EjKe+P01w1L2LXsORJqHU3D/mOpdtyp5Toe+CaEO5CbH/J5h2aFH2vRRL1b4kZJWgS/xNfVpRHwKr6Ed+jpmM65g5EKTsRL4ZxdrKTHKk9Xw6IPmy2wOyvnUItaqDGIFWX/95+Q8fqjuNyDNOj+d2qecUGJ9y36IN5gY/ZKe31NkpNYM7R90HUlKcr1MHnBoxzpnPsG+FMkji0SKaWdsCXwMza5ZiJ79+cemn266L5Fb4IW3QaCd9fMDjKRWcEwgIqcAdrl5rBr+Xj2/n/27jy8ySpt/Pj3NE0hLUtbKBSqCG7oIEK1CoorLriBBWEQFNyd37z6OigyU5dXYRRbRdEZnRkHR4fRQUUFyiaLI4gjilhsUUFwBykKLbQIXdP2/P4IKWmaJ3mSZm3uz3VxadMkz2kDuXOfc5/7bF5Jx77ZdB81DYutS1Ce22hrh/O8Q+fnhkatm+OcxLL4oLTJmXOl1DE4OqBdC3QEXgNe11p/Fbrh+ScnJ0cXFRX5vqMQPrgHK3AkAYF0zwrmc/lzDXfOla9IdIvQWnOwaDEVa18iMa03PcY8gLXb0X49h9GePtdkrl/ectM/n5nXwFuiF47XVXinlNoULefnSYwUwrdhBWs8Vlp4mpQzE9ecjzVa7XNKS7ZyqK7B69l+7hT+H5mUoMDPBUQAGg6WU1aYT/3u7XQZOpbUcyc1d86OBG+TpCI2mI2Ppnedaq13AI8DjyulsoGXgIeByP1NFcJPZldwgtldzN/nCmSVycxKWGlljc820qHQZK9l38pnqd66DtuJZ9H9irtJ6JDs9/MYjdp1xtaonNOiFBOGHN2ia6iZ36u3/X+h6kAnYpPESCF886dhi9kKj92VNT7vG0i5ZoJS1PlZQRNIEli7awtlhfno+lq6X51Hyknn+P8kQSZn4cYP04mgUsoKXIZjtvMiYB0wI0TjEiLo/ClJCeZB4f48l6cxTplfwt1vlKA1hnX7ZscV7iTQXvkzZQsfxV62g9TzJtNl6Fif+wH95bpnz6icMxSrdHKYvHAlMVII34wm6xKUau4O7WS2w2Xvw9UuwdaoNdX20MVMrTUHP11GxZp/kNi1JxnjZ5KUcUzIrueJUcdw2QsfP8w0i7kEmABcCWwEXgdu11pXhXhsQgSVPys4wWwUYua5nKuARoHPmb85k9eiHftZu62seZUvGg8Hq/luE+VLZ4HW9Bj7MLbj2lbBl5Jk8XgIb99uR36P/uyxbOv+PmkmI0BipBD+MCrhbNS61cSsmQoW5342b/HTZrVEdG+8J032Ovav/gtVX6zBdtwZdL9qKgleOmeHQqrN0UhOzsKNb2am5u8HPgJO1lqP1FrPkwAnYpE/KzjTRvTHZm1Z0RXom6Ov53KuApqd/ayxNzJvw87m+4d7lc8XrTUHPnqDvW9Ox9K5O5k3PNPmJBDAavH8dvXht/spLC5t/jo3O4v1ecP5vuBK1ucNN0wCnb9zzZEE2/V5fAnm3xER0yRGCmFSbnYW+WMGYnF0u23BOTHr5C22KRyJTEdrAnfPL6G6vgFrQuvnTEu2kj9mIKk2a1DGHwwNB/ayZ97vqfpiDV2HTSDjmv8LexJos1qYPmpA8+uRdXgC06JU8+vgTzwUscvMgfLNh5copc4BTtBa/1MplQF0OtwmW4io588KjqeVpQtPymDWqu3cPb/ErxUkX8+VEMC+vehK/Y5oqqtm39vPUP3VhySffB7dLruLhKSObX7eYcel8+G3+z1+TwNT3zA+Z9CTGUu3tHl/nxwmL0BipBD+ys3O4u75JR6/5zoxm2UQsz01h6motmO1KFJtVg7U2D2+H08xuGY41ezYTPnix9GNDWRc838kHz8k7GNw32Li/K/7ERrezu8V7Yc/ewQfBnKA/sA/ASvwb2BYaIYmRHD5e0afa90AGFkAACAASURBVKMQM/sLvZUauiYNpZU1zNuwszmZi7YVvUDZ9+2ibNFM7PtLSbvwFjqfkYvyMOvrr2RrAvNuO8uw2xx4LisyUlhcatg4wN99JnKYvHCSGCmEeWYmZr3FbE9bPeyNmpQOiZQ8fGlzPHZO3LpuIYgErTW/bFxE5bq5WNOPImPMA1jTwx87jI6GmLF0S6uOqvZGzYylWyTGtXP+dG0YDYwCqgC01ruBzqEYlBCh4FoCoXDMipltIuJtfyF4LjW8e34JDxZ+3ur7EL0reoGq/vpjfnr5HhqrD9Bj/CN0OXN0UJJAq0Xx2JhTAceHAm/P6F5W5KqwuJRhBWvol7e8efXQE9nfJ9pAYqQQJpnZMuGMu84yUteY7W2rh6d4vN6goiQcmuprKV/yBJXvvUTyCUPJnPRkm5NABR7La30xipNGk6OBdFsVscX0iiBQr7XWSikNoJRKCdGYhAiZQFdwjIJOaWWN4UqVBuZt2EnOMenct/Azj4fW+kspRwAIpEV1KGjdxIEPXuPAh6+RlHk8GaPvJ7FLD1OPdZaneNvkn5KU2GJVtWjH/harqe5cn8e1AY/rGYTeVmBlf59oA4mRQpjkrbTevQLH0yHn3lYUzR47EQ72ip8clTLlO0k9/wa6DBnb5klSmzWBLx+5nH55yw3v88z4wdw9v8RjrJTO1sKVP4ngG0qpvwOpSqnbgJuBF0IzLCGii1HQAe8trjUELQkER/fQKMkBaao9RPmyp6j59hNSTrmY9Et/S4K1g+nHl1bWNCfmRgfBH6hpORv5aO5AAP69YafH53TOkLp/kDDzO0u1WaUERrSFxEgh/GA0MeurAsfTBB84JknNNl0Lh5pvixyds5Wix7jp2PqdFpTn7Xh4JdXoc0lWqo3c7CzDSVZPlS+pNiuVNa1X/6KpyY4IDdOloVrrJ4G3gAU49kA8pLV+NlQDEyIUXEsEs/+4msEzVtMvbznDCtZ47ZDlqYzFrGAlgdGkvmwHP718NzXff0r6Jb+l2xW/8ysJBEfQdv7OjUoyNbR6bdZuKzN8Tudqn78zws4OakIESmKkEMHhrQJn2lubDbdYRMskqdZNVH74OnvfmoGlaw9H5+wgJYEAlYfLNX2V1/rT2Xr6qAGtuq5aE5TExThg5hxBpbXj05XW+h3gHW/3ESJaua8Suda+eztc3vW2qW9sbjfNXQJVte0D9r39DAlJNnpOeIyORwUWKDQ0d+k0OlsKWr82vspavDWVcWVRiiatpdunaBOJkUIEl7cKHPeGJtGmqa6a8uWzqfl6Aym/uoD0y+4kwdr2ztmuEpSisLjUZ+dqfzpbSxfs+KV8xSal1Hs4ZjgXa613utyeBJwD3ACs1VrPDd0wzcnJydFFRUWRHoaIUmYTBPfWyq6MShjjgW5qpPL9l/nl4wV06H0S3XPvI7FztzY/r4LmIzXWbiszfI2yUm2szxtu6nV0LxlyZ7NaTDcK8ldbD6oX5imlNmmt235IZdvG8B4SI4UIGvdJ21hh3/cjexfOpKFit6Nzds6ooDRN8ySUMUy0D2bjo5k9gpfh2OvwmlKqH1AJ2HCUla4GntZaR/5wFiF8MLtB2tvqoLeZSnB0uYz2GctANNb8QvniJ6jdUUKnwZeTftHtqMTg7B1wdnVbsKmU/DEDfW5wv/CkDK8NY5zP6Wn/iMZ7ot9WZo4ZEe2OxEghgsj5XhkN5/6ZVf3VR5Qvn41KTKLntY/Ssc+pIb1ejb2RKfNLmquUzMY1magU7nyuCLa4s1JWoDtQo7WuDNmoAiSzncIbsyuCTs4VKFeeZirdE4xYCl5m1O/5lr2LHqPx0D7SL/kfOg+6NGTXyjq8V9DsIcJmni+cAc/o75inv0ui7aJhRdCVxEghgqevl66Y0UI3NVL5wav88tF8knqdQEbu/SR2yYjIWHytEpr5/CJJYfsRzBXBZlprO/BTwKMSIkTMzHL5m0R4WkE0U0ffnhLBQ1vWsn/lsyTYupA58XE69A7t8QqllTWkJbdeaXR2g/Nnj2Ykki9vZ1uJ9k9ipBDB4a15W7RorD1E+dJZ1H63iZSBl9Dt0t+iEpMiNh5nZ1WjZM5TEzVnNJXqlfjlVyIoRDQyW47n/P8ZS7eYOiTVqJOlp5bXrmfWtQe6sYGKtS9ycNNSOhx9ChlX52FJSQ3LtT29NmbOAHRlTVBU1zfQL295WMtfvJ1tJYQQwrfC4lKmvbk50sPwqr7sB8oWzqThlzLSL/0fOg2+PGT7Af3hbdLR14Skr0RStE+SCIqY5+3MIfc3NOfZOr4SQfcWy+4rjs7GJrsra0hNtnKg2k57OSSisaqCssWPU/fjF3TOuZq0C25CWaL7rSIt2UpyUiK7K2voarNysK6h+TV2thyH0M90elp1NmrXLYQQorVZq7Zjb4revfZVX77PvhV/IqFDCpkT8+mQdXKkh9TM26Sjrx4HINUr8cjM8RGrgJXACq31ttAPSQj/+FuO5+uNLtVmZfqoAc1Jg6cVR9cDzc2sLsaKut3bKVv0GE21h+h21VQ6Dbgw0kPyyWa18PDII69X9h9X0+j2IcLeqJmxdEvIE0FpwR1/JEYK4R9PWzngyPtmtKaAuqmRyvfm8ssni+iQ9Su65+aR2Ck9KM9tsyaYOnM4QTmOPvKUKNusFi48KYNhBWs8xh8z22OkeiX+mJnmvwFHV7TpSqkTgY9xBL13tdaHQjk4IczwtxzP16xYSofEFh/c/T2cPFYd3LyK/e/8DUunbmReP4uknsdGekiGvJ0BaJSYhyth91Q6LNo1iZGiXQhHR0lPE6vT3toMmqheBWysPkD5ksep3fEZnU+7krTht6IswemcDdDx8MHvvpLBJg2WBEi2JlDtct9Um5WrBvViwaZSw20yrhOVpZU1rTprS/VKfPK3a2gCMAS4HLgIqAFWa62fCM3w/CMd0eKTp05Y3rpnmTmjKOtwsmhRqt0fIK8b7Oz/z985tHklHftm033UNCy2LpEeliFfndG8dZr7oeDKUA1LHBbO9uRR2DVUYqSISf7GUffHGm2dcH8P8Ld7dzSo+/kbyhbNpLGqkm4j7qDTwIsjPSSPSVxHa4LHCU+jDujTl2yhssZx/7Rka4vKGhH7QtU1tAn46PCfh5RS3YERgQ1RiODwtxzPebu3DpTOQNXek8CGg+WUFeZTv3s7XYaOJfXcSagES9jH4esAeCeLUlxzuvGKm7dOc6m24M3eCs/i/RxFiZEiVvmz196Vr60TpZU13D2/hCnzS5onWGPJoc/fZd+q57Akp5J53RN06HVCpIcEtI6XNfZGw8lt9+0wnpL+WhNlqaJ9alMHCK11OTAvSGMRImCBlON17pjYPBsWj2p3baGsMB9dX0v3q/NIOemciI3l7OPS+WFfjc8PCY1as2BTKTnHpHt8vWet2m742OmjBrR5nMK7QD9MtlcSI0WsCHRPvZmtE65HFMQK3WinYs0/OPjpcjr0OZWMq/+AJblrpIcVEPdtMvI+LVxFdytAIYKssLjU8PiItGRru2r8YkRrzcFPl1Gx5h8kdu1JxviZJGUcE9Exfbqzki8fuZzsP672+Ro4Axa0XgX29qFFAlzoyTmKQsSmQI++aY//thsPVVC2OJ+6XVvpcsZoUi+4MSKVMv5KtVmpa2jy2bVa3qeFq4RID0CIcHGWQxglGslJ7X9epMlex763n6biP3/H1u80ek2eHfEkEBwb5B8s/JxDtQ2m7u8sOSw93GHO+XVXg/LPLOmEFhbeGjQJIaLXtBH9sVlbJjtmmoe0t3/bdaVf8tO/fkf9z9/SfeQ00obfEhNJoM1qYfqoAeSPGUhWqg2FI+552uMp79PClZnjI4YAc4DjgM+BW7TWW0M9MBE/wtVcwlcJS2llDak2a7stF204sJeyRTOp3/MtXYdNoOuwCTh6W0SH1z7+0a89mZ5KWzpaE7BZLXKOX4TE4zmKEiNFexDo0TfTRvRnyvyScAwx5A6WrGT/O8+T2KU7mZNmkNSjX6SHZEqW22tl5jWLt/dpYczMEshfgHuB94FRwNPI5ncRJOFsLuGr7EEBVw3q1WKje3tRs2Mz5YsfRzc2kHHN/5F8/JBID6mVYDTmqay28/T4wXKOX4TE6TmKEiNFuxDIXvvc7CzD7RaxQjfUs/+d5zn02Wo69jud7iPvxWLrHOlh+WS1KGaNHRTQawZx9z4tDJhJBBO01u8c/v83lVL3hXJAIr6Ec9Oyr/MDNbB2W1lQrxlpWmt+2biIynVzsaYfRcaYB7CmR+ebfTCO6uidapNz/CIsDn//EiNFXHt45ACfRzJFq4ZfyikrfIz6n76iy1njST1nYkyUgrb1uIc4fJ8WBswkgqlKqTFGX2utFwZ/WCJehHPTsqdyCHex1NXMl6b6Wvat+BPV2/5L8oln0+2KKSR0SI70sDyyJCgmnHl0i8NwAawJChTYG30niFLaIiJEYqSIS+7N18weAxQtand+Ttnix9ENdWSMvp/kE8+O9JC8Sku2UvzQpZEehmhnzCSC64CRBl9rQIKcCFigncoCkZudRdGO/X7vRYtF9oqfKFs0E3v5TlLPv4EuQ8ailIr0sAw1NWkezR1IzjHprcpVwLFy7C1Jd98jIUQYSYwUpoRrP3w4FBaXMu2tzS0m6WIlqmqtObhpqaNzdlpveozOx9r96EgPyytLguLhkXIEkgg+n4mg1vomo+8ppXoGdzgi3oRz03JhcSkLNpW2+ySw5tsiypfOAqXoMW46tn6nRXpIPjlfEaNyldzsLI+H4NqsFo9d0YQIF4mRwoxw7ocPh1mrtpuq1Ig2TfZa9q/6C1Vb1mI7YSjdr7wnaitlXEVPWzfR3vjdL18p1RW4BpgInAzE3juYiBq+Ni0HcwbVzMG3sUzrJg589AYH/jsPa4++ZIx+AGtqZqSHZVphcanP17ajNaH5NUy1WZk+yvMeifY08y5ii8RI4Ul7OsS7sLg0JrdRNBzYw96FM7Hv/Z6u515P17N+HVWds72xN+mY/Lsiop+pRFApZcPRDW0icBrQGcjF0SVNiDYxWgUK9gxqez4stamumvLls6n5egMpv7qA9MvuJMHaMdLD8ov7a+uazKUmWzlU24C96cgMdF1Dk8fnaW8z7yL6SYwUvrSXQ7yd76+xpub7YsqXPIHWTfQY+xC2486I9JD8Fmt/V0RsMHOO4DzgPGA18BywBvhGa/1eaIcm4l2wZ1BTk60x3eLaiH3fj+xdOJOGit2kDb+Nzjmjono/oBHX19Y9mfP0ujnvDy1XlKvqGtrNzLuIfpGMkUqpy4A/ARbgH1rrglBfUwQmnPvhQynWKmscnbMXULnuZazdjnZ0zk7rHelhBSTW/q6I2GBmTfwUoAL4EtimtW4kjHuClVKXKaW2K6W+UUrlheu6IvKCPYPaHrcGVn/1ET+9fA9NtQfpee2jdDnj6phMAp2cr63ZDxvO1b7Syhr04a8razwn+zKbKkIkIjFSKWXBcYbh5cCvgAlKqV+F+roiMNNG9MdmbXksQSx2Oo6l99Gm+hrKFz9O5XtzSe4/jMxJT8ZsEghQXd9AYXFppIch2hkzzWIGKaVOwlHy8h+l1F6gs1IqU2v9cygH5xLoLgF2AZ8opZZorbeG8roiOrRlBtXTHrEDBglCLNJNjVR+8Cq/fDSfpF4nkJF7P4ldMiI9rDZzvrZmP2xYlDI9Oy2zqSIUIhgjz8Sx8vgdgFLqdeBqQOJjFGovh3jHSmWNfX+po3P2vl2kXnAzXc4cHVOTpFaLIjFBUWM/sgWiotou2xxE0JnaI6i13gY8BDyklMrBEfA2KqV2aa1DefCKBLo4FmhHUaM9YrESwHxprD1E+dJZ1H63iU6nXkr6Jf8PlZgU6WG1metrazQJ4H5/s0lgLM68i9gRoRiZBfzo8vUuYIj7nZRStwO3A/Tp0ydEQxFmxNIh3kYNt2Khsqb6m42UL3sKlWChx6//iK3v4EgPyW/2Rk2Th23wss1BBJvfXUO11kVAkVJqKo59EaFkKtCJ9snMDKqnYGW0t7DW3ojVolq0vHb/OtrVl/1A2cKZNPxSRvqIO+g06LKYmuV0Z7MmUGtvavXaepoEsFoUKUmJHKixt3itPSWMaclWkpMSY3rmXcSmMMZIT//wW72Zaa3nAHMAcnJyYufNTkSMp8nUKfNLmDK/JMIj807rJg58OJ8DH8wjqedxZIy+n8SusXuCi9FRV7FUniuin5lmMQOA47TWSw5//TTQ9fC3nwvh2MBEoJPZzvbN2wyq0cqf0SqRhhZJn/P4gfsWftai/CJaVX35PvtW/ImEDilkTsynQ9bJkR5Sm9U3aL4vuLLFbc7kvsbeiEUpGrX2emC8p1Xjh0d6PlZCiGCLYIzcBbiegn0UsDuE1xNxItYawgA01VVRvmw2Nd98TMopw0m/9A4SrB0iPaw2ccY/dxoYVrBGJjhFUJhpFlMAlLt8PQJYDqzFUQoTSj4DndZ6jtY6R2udk5ER+3ukhHlGK38WkytkzuMHOrpt4I82uqmRijUvUr7kCZJ6HEfmDc+0iyQQWs94OpN75ypfo9bNZZ1GB83njxlIVqoNBWSl2uSAeRFukYqRnwAnKKX6KaWSgGuBJSG8nohRhcWlDCtYQ7+85QwrWOOz4UisrTjVl+/kp5fvoea7ItIu/g3drrg75pNAm9XChCFHt2ow5OSc+JbmMaKtzJSG9tJaf+jy9S9a6wUASqnfhGZYzZoDHVCKI9BNDPE1RRRzrhZ52z/mTB58zWg6a+0ro3jfYGP1AcqXPE7tjs/ofNqVpA2/FWWxRnpYQeOetAdyZEgs7bsR7VJEYqTWukEpdSewCsfxES9prbeE6noiNgVyrqqZPdrRomr7eva9/QwqsQM9r51Jx6NPifSQ2syiVPOEZs4x6YafeWS/oAgGMyuCnV2/0FoPdfmyR3CH05LWugFwBrovgTck0MWnwuJSsv+4minzS3wGKOeqkBmllTWeC5CjQN3P3/DTv6ZQu+tLul0xhfRLftuukkCACUOObvF1ezl0WcSVSMbIt7XWJ2qtj9NazwzltURs8ja5ZuTCk6K/uko3NVKx7l+UF+Zj7daHXjc+0y6SQIAmrZuTu9zsLNbnDTf8mCKxUbSVmRXB3UqpIVrrj11vVEoNJQz7EbTWbwNvh/o6IvKMupS5z2h6o6D5cWY3tkdjF7RDn7/LvlXPYUlOJfO6J+jQ64RIDykkHs1tmbC3l0OXRVyJaIwUwht/JtfMVNxEg8aag47O2d9/SqdBl5F+8W9Qie1nktRTvJPYKELFTCL4B2C+Umou8Onh204HbgDGh2hcIs54K1/xZ+O65ki5S1YMlbc46UY7FWv+wcFPl9Ohz6lkXP0HLMldfT8wBqUktd77EOiRIUJEkMRIEbXMJhD+TLhGUv3e7xydsw/tI33EnXQefFmkhxSwVJuVuoYmU/FOYqMIFTMHym9USg3BUaJ54+GbtwBDtdZ7Qjg2EUe8la/4W/qQ/cfVaA2VMXaAfOOhCsoW51O3aytdzhhN6gU3ohKiu5FNW9Q3NFFYXNpif0N7OXRZxA+JkSKaeUogAKrrG1q8/8ZCp9Cqre+xb8WzJHTsRObEx+nQO3aTIJvVwvRRAwBz8U5iowgVpaOxLi5AOTk5uqioKNLDEAHol7e89QFYOEo9Y2njeqDqSr+krDCfptoqul1+Fym/Oj/SQwqLrFQb6/OGm7qvUemwiF9KqU1a65xIjyNWSIyMT4XFpUxfsqXV5KjNamluSmIUg6OBbmqkYu1LHCxaTIejBpCRm4clJS3SwwqYzZrANacfxdptZRLPRMiYjY9mmsUIEXJGde7ON0ijFsrtwcGSlfz86n2oxCQyJz0ZN0kgmN/o7nqshEZaZwshhFm52VmkdGhdAFZjb2TK/BKGFawhNTk699g1VlWyZ/6DHCxaTOfTR9Lz2plRnwT66j/X0WphwaZSiWciKkgiKKKCp2TPtf69o/XIX9VkawLWhCht9ekH3VDPvhV/Zv+q5+h4zCAyJz9NUo9+kR5WWJnd6B5I5zshhBAO3ibdSitrOFTbgNUSXXG17qev+OlfU6jfvZ1uV97jaApjMdPaIjKyUm38UHClz/tVVNslnomoYfpflFLqFK31F6EcjIhfRvXvQKv9DdX2poiMMZgafimnrPAx6n/6ii5njSf1nIntej+gkb7dzCWCcqyEiHYSI0U087XFwt6kSbVZOVjbQGMUbBk69Nlq9q3+G5aUNHpe9wQdMo+P9JB8Kq2soV/echKUCuh3KPFMRII/UyvPK6WSgLnAq1rrytAMScQrTweDDytYE/Ub2P1Vu/NzyhY/jm6oI2P0/SSfeHakhxQxG76rMHU/ow8xqclWhhWskX0WIhpIjBRRa9qI/tw9v8TrPsADNXaeHj+YaW9uxt4UmWRQN9rZ/+4LHCp+m47HDKb7qGkx1Tlbg9ck0Ga10CExwWMzOzkKQkSC6dJQrfU5wHXA0UCRUupVpdQlIRuZELSvGTKtNb8ULWHP6w+Q0LETvSbNjuskELwHTFeeSoetFsWh2gbZZyGigsRIEc1ys7N8NoPRwJT5JRFbEWw4uI89r93PoeK36TLkGnr8ekZMJYFGLMpRcpuVaiN/zECmjxrgdSuMEOHkV7G11vprpdSDQBHwZyBbKaWA+7XWC0MxQBHf2kvH0CZ7LftX/YWqLWuxnTCU7lfeQ0KH5EgPK+KcAdIXT6XDVXUNrWZVnfssZFVQRILESBFu/nRTNnu2biQWA2t3baW8MJ+m+hq6j/oDKSefG/5BhIC3ztjSBVtEA3/2CJ4K3ARcCbwDjNRaf6qU6g18BEiQE0FndAZSLGk4sIe9C2di3/s9Xc+9nq5n/RqlpE8TwIQhR5u+r3vpcL+85R7v155WkUXskBgpwqmwuJQZS7dQUX1kMsxZFQF4TCqiMZ5qrTlU/Db7332BxC4ZZI5/hKSMvpEelldWi2LW2EE+S23BOB552gojRCT482n0OeBTYJDW+g6t9acAWuvdwIOhGJwQudlZ5I8ZSNbh2vlAe5opHDNzw45LD9rYzKj5vpif5k6h4cAeeox9iNSzr43bJDCBIyuAFqW4fmgfHs0dGPDzeTtyRIgIkBgpwsJ5nI5rEujk2n2ysLiUYQVr6Je3nGEFawBaxNNIc3TO/hP73/kbtr6D6XXD01GfBKYlW5k1dhC52VmmYo3RfdxfG9nSICLF9Iqg1vq8wxvhT1JKaWC71rr+8PdeCdUARXwxKnNxzpy5fj812cqBGrvPMhb30owHCz/ntY9/DOk+CK01v2xcQOW6l7F2O5qMMQ9gTesdsuvFgibgh/wrmr92BsJAS2M8zW7LPgsRKRIjRbh4Ok7H1e7KmuZk0Xk/52ph/piBrM8bHvED5Bt+2UvZonzqf/6armdPoOs5E6J2kvSZ8YMDWmE1ikdGrw14XskVIpT8KQ29Avg78C2OBZZ+SqnfaK1XhGpwIr4UFpcy7a3N2Bsd4am0soZpb20Gjrw5uiaFwwrWeJwRdWX0RqxDGAKb6mvY9/afqN7+AcknnUu3y+8iISk6ZmCjRTACodGRIxJIRSRIjBTh4qv8vXeqzevZq87VrEjtv6/d8RlliwvQjXYyxjxI8glDIzIOs4xik3sMSk22orWj+6q3eOTrtREinPxpFjMbuFBr/Q2AUuo4YDkgQU74ZGZD+4ylW5qTQCd7o2bG0i0e3xy9BUMFzW/Kd88vYdaq7Uwb0Z+iHfv594adQfmZPLHvL6Vs0Uzs+3aResHNdDlzNMpkQ5T2Li3Z2vz/wQqEss9CRBGJkSIsvCVxzsnPu+eXePy+M25GYr+g1pqDRYupWPsSiWm96THmQazdjgrb9QPlLTYFEoPkXFwRTfxJBPc6A9xh3wF7gzyemOZP9654Ynb1x2h1z+h2o2CYdfh3737NKQaBMViqv9lI+bKnUAkWevz6j9j6Dg7p9WJJgoKHRw5o/loCoWiHJEaKsDBK4lJtVqaPGkBudhazVm33efZqR2sCCSo8XUKb7LXsW/Es1V+uI/nEs+l2xZSY6pwdzNhk9NlF9reLSPBZkK2UGqOUGgNsUUq9rZS6USl1A7AU+CTkI4wRzmRHzjRrzdvqT1t4OlvOZrVw4UkZTH1jc9hmOrVuonL9a5Qt+CPW1Ex63fC0JIFuunS0tkj6pdGLaC8kRopwc22i5myE9sz4wZQ8fGnz+6yn+JigHBOrzs8pNfamsCSB9sqf+fmVe6n+8n1Sz5tM99z7YioJBEhQKmiNXYw+u8j+dhEJZlYER7r8/x7g/MP/XwakBX1EMUpqvo15W/1xXUVVCjz1b7FZPc9XeNojduFJGSzYVBq2A3Gb6qooXzabmm8+JuWU4aRfegcJ1g5huXYsOeB23p+nGW0FXHhSRphHJkSbSYwUYeerJNE9Pna1WVuduxoONd9tonzpLAB6jJuO7djTwz6GYHB+pghGYxfZ3y6iic9EUGt9UzgGEuuiudQtUiWrzusapWRdbdaWyYDBHRuaNIXFpabq84cVrAnbSmB9+U7KFs2kofJn0i7+DZ1Pu0r2AxpwX+nLzc6iaMd+5m3Y2fyya2DBplJyjkmXgChihsRIEa3cm6uFMxHUWvPLhjepfP8VrBnHkDHmQaypmWG7figFY5Jf9reLaBGdvXpjULSWukWqZNX1up7YrBaUwlTSZm/UPstInUcRhKsLWtX29fz8ylSaaqvoee1Mupw+sl0ngQq4fmifVuUsZnkqeVm7raxV7h+MkmEhhBAtmZ2UTrVZSTaowjGrqa6a8sJ8Kt9/meSTzyPz+ifbTRLoFA2T/EIEgySCQRKtNd+h2p8XyHWdslJt5I8ZSKWPox9ceXvT9ZV0BpNuaqRi3b8oL8zH2q0PvW58ho5HnxLy60aDR3MHkj/G/wPg05KtfnV9lQArhGhPouHwcDOTWuIcigAAIABJREFU0jarhasG9UIT+KSmfd8ufn5lKtVfbyBt+K10H3kvCUkdA36+aBXpSX4hgkUSwSDxtHk7f8zAiC/9R+rDtq/nv3t+CQl+rKB5e9P1dbhusDTWHGTvWzP4ZcObdBp0GZkTC0js3D3k140Gzt9/bnYWWX4EQKtFtegW6uk5zd4uhBCxJpRVOf4kmJ4mqwGSrQktPrOs3VYWcDyt/vpjfnr5HhqrD9Bz/KN0OSM3Kitl0pKtflW3uP8E0TDJL0Sw+HOgfE/gMaC31vpypdSvgLO01i+GbHQxJhprviPVptjougqabzfb0MXoTde5BzEcK4H1e7+jbOFMGg7tI33EnXQefFnIrxkt3H//Rq3Lk60J1NqbaHK90ctL7Ol5JMCKWCUxUngSqkZyZo9lcjJqUOJ6W6DxVOsmDnzwGgc+fI2kzOPJGH0/iV16BPqjhZxzctLsz6txJMrS2EW0R/6cIzgX+CfwwOGvvwLmAxLkolikPmwbdYX0p5enAsM3XfcgGEpVW99j34pnSejYicyJj9Ohd3wlKu4r20YfKDwFVXuT9noQr6fnkQArYtRcJEYKN6GqygkkwXSfrPaUTPqrqfYQ5cueoubbT0g55WLSL/1tVHfO7pCY0Pw7yM3OMvVZIivVxvq84eEaohBh5U8i2F1r/YZS6j4ArXWDUio87RlFwCL1YdvTdf0JMr7eeMNRDqqbGqlY+xIHixbT4agBZOTmYUlpf93gs1JtVNU1eOwol2rzvL/P0+r33fNLPD6/tw880biKLkSAJEaKVkJVlROMBLOtcbS+7AdH5+wDe0m/9H/oNPjyqCwFdeVeEuqMP1Pf2OyxSknhudmZEO2FP4lglVKqG4cXdZRSQ4EDIRmVCKpIfdj2dLSDUbmo69uvr1LQ3Yf3WoRSY1UlZUsep27n53Q+fSRpF96CsvjzzyU2pNqsrM8bTmFxKdPe3Izd7XThqvoGw6M73EWqDFmIKCExUrQSqqocf99vPR0j1ZZVyaptH7Dv7WdISLLRc0I+HY/6VcDPZVZKkgWrJaFNx2C4n2nr1LljYqvnVcB1Q/vIZKVo1/xpFnMPsAQ4Tim1HngZ+N+QjEq0S0adVa8b2qe5AYlFqebyFteN7+4b7kOp7qev+OlfU6jfvZ1uV95D+sW/aZdJoDVBMX2UY69EbnYWnTq2/hnNHN3hFK2dc4UIE4mRopVQNZLz5/3WqGGNLYBjInRTIxXv/ZPyxQUkZfQl84ZnwpIEOk0fNQCrJfBVR/dE2fm7cU8C05KtPD1+MI/m+t8pW4hYYvrTrdb6U6XU+UB/HBMl27XW4TudVERUMA6l91am6mmvwpT5JTyw6HOq6xtJUMp0c5m2OPTZavat/huWlDR6XvcEHTKPD/k12+qZ8YO5f+FnVNubfN/ZhXviZ3Sch9lZY9nzJ+KZxEhhJBRVOf683xrtJ/RXY80vlC9+gtodJXQafDnpF9+OslgD+wECUFXvGPOssYOYsXQLFYdjlrOqKCvVxoUnZbB2WxmllTWmqo2MymOTkxIldom44E/X0DuAeVrrLYe/TlNKTdBa/zVkoxNRwd/uZN4YBUSjN2PnG3+ok0DdaGf/uy9wqPhtOh4zmO6jpmFJ7hrSawaD84w+o/154Fj569QxkYpqe4vAWFFtb/E6BqO0U/b8iXglMVIEm68JWLPvt8E4Lqp+z7fsXTiTxqr9dLv8LjqdemmbnzMQs1ZtZ33ecFM/t5kJbDnPVsQ7f+oCbtNaVzq/0FpXALcFf0gi2oTjUPpIvuk2HNzHntfu51Dx23QZcg09fj0jJpJAxZE22EbJmkUpZo0bRPFDl5KVamtVVuv6OkpppxBtIjFSBI2Z8wfNniPY1n3ah75Yw8//nga6iczrnvCYBFoON4nx55zZQJRW1pg+OzE3O4v1ecP5vuBKw+RRzrMV8c6fRDBBubSDUkpZgKTgD0lEm3DMmEXqTbd211Z+/tcU6vd+T/dRfyDtgptQCeYPmo0kjaPTWd+85VTXN2BNaLlvQuFYSXXut/T1OoZqL4sQcUJipAgaXxOw/hxUP21E/1aHopuhGxvY/5+/s2/5bJJ6nUivG56hQ68TW90v2ZpAk9ZkHV51S0v2XC6almxtc6LoPIvY189slkyAinjnTyK4GnhDKXWRUmo48BqwMjTDEtEk1DNmhcWlVNc3BOW5zNJac/DT5ex57X6UtSOZk54k5eRzwzoGd85AbRRE3e8HR0pmK6rtoBxdQJ33ca7+OYNlqsHzur6OZmZQhRAeSYwUQeNr4s6fSp3c7Cy/m6w1VlWwZ/6DHNy0lM45V9Nz/KNYUlI93rfa3tScmE17azNXntqrVUMXq0Xx8MgBXHhSRkBJqZO3qpZAyASoiHf+tEL8PXA78FscnzNXA/8IxaBEdJk2on+rowWsCcrnEQ9mmoWE82B4J91Qz77Vf6Xq8/9gOzaH7iPvJaFjp7Bd38jT4weTm53FsII1zZvgXSkgNdnq8Xvg6PCZ0iGRlA6Jrfb61dgb6ZCYgM1qCXobcyEEIDFSBJGvPdtGiWJpZU2rI3/8XTGr272dskWP0VR7iO4j7yXlVxeYfqy9UbP8s5+YNXZQq88CAPM3/hj0zt9trU6Sve0inplKBA+XuPxLa3098HxohySikvsUnocpvUCayng70DbVZkUpRzfLYHUNbfhlL2WL8qn/+Wu6nj2BrudMQCn/W2gHW1aqrfl3ZBTUNMadPZ28BcQDNXaeHj9YunoKEWThjpFKqek49h+WHb7pfq3126G+rggfX+cPGiWKQIu464zLZh0sWcn+/zxPYqduZF4/i6Sex/o99opqu8fkavCM1a3Oqg0G2c8nROBMJYJa60alVIZSKklrXR/qQYnoMmvVduyNLd+8nefLub7RG5WqPLDoc6a+sZlGrbEoxYQhRzefzeMtcUnpkNicqPTLW97mn6N2x2eULS5AN9rJGPMgyScMbfNzBovrqpxRgHfurTAK/s7HGt2n9+FkUxI/IYIrQjHyaa31k2G6lggzX8dDeEoUnZzlkrnZWV4nW13pBjv7//N3Dm1eSce+2XQf9Xssts4Bj999VRJo00HwRqSqRYi28ac09AdgvVJqCVDlvFFrPTvYgxLRxWyzGKP7OY+AAMeetn9v2AnAo7kDvZY6Os8SnLF0C11t1oCDiNaag0WLqVj7Eolpvekx5kGs3Y4K6LnMcD+7yBfnERBOvmaCjYK/s1y3aMd+5m3Y6fP8JCFEUP2AxEgRRN4m7nKzsyjasb85nrpzxmMzZZMNB8spK8ynfvd2ugwdS+q5k9rcNM11FdKZzAZDSpKF1OQkqWoRIkj8SQR3H/6TAAQ+TSRijtnz5byVqrh79eOdrN1WZpgEuqqotmO1KKwJyu+ykiZ7LftWPEv1l+tIPvFsul0xhYQOyX49h780jlbaZkpZbVZL8xEQTmYOCp6+ZEvrxFhB0Y79LNhU2iIJVMA1p8tKoBAhFu4YeadSajJQBEw9fFyFiAPOvfhmqkN8xeXaH79wVMrU19I99z5S+g8Lyhhr7I3MWLqFWntTQD0ALAmKRrd4b7UoZo6WRi5CBJPSfu67Ukp1BrTW+lBohhS4nJwcXVRUFOlhtDueGrrYrJZWnbUKi0uZ9tbmVmWkwZKWbOWXmgbTewXtlT9TtvBR7GU7SD1vEl2GjsOlu3vIOFtou//OFHD2cen8sK+mzbOZwwrWeAzuRgloVqqN9XnD/b6OENFMKbVJa50T6XG4ClaMVEr9B8j08K0HgA1AOY55p0eAXlrrmw2e53YcTWzo06fP6Tt27GjLsESEmW2w9szh5mOFxaVMmV/S6vuOztnLqFjzDxK79iRjzIMkde8TqmGb4qymyXJpLiN72oUIjNn4aHpFUCl1CvAKkH7463JgstZ6S8CjFDHBzAqV834eV6qCpKLabrrtdM13myhfOguAHuOmYzv29JCMyZ3Volr8bkIVxIzKbIyS5GCe+SiEaC3YMVJrfbHJ674ALPPyPHOAOeCYLA1kLCJ6mNnzl2o7st0gNzuLBxZ93mKLRpO9jv2r/0LVF2uwHX8m3a+aSkKHlJCO2wxnEug6aSmJnxCh5U9p6BzgHq31WgCl1AXAC8DZIRiXiDJmm4wcCFES2MzHBjytNb9seJPK91/BmnEMGWMexJrqaVI9OFJd9i6mJVt5eOSAFgE4VEHMqNzHaEVQuqoJEXJhi5FKqV5a658Ofzka+CLY1xDRydekns1qYfqoAS2OcnKNCA0H9lK2aCb1e76l67CJdB12bUg6Z9usFjokJvg9MSyTlkKElz+JYIozwAFord9TSkV+CklEFX/2CQbCW1VoU101+95+huqvPiT55PPpdtn/kpDUMWRjASh5+NKQPr8Ro4Yy15yexYJNpXJWoBDhF84Y+YRSajCOabEfgN+E6DoiynhrsOZaUumpfLTmhxLKlzyBbmwg45qHSD7+TJ/XUzjielVdg+mkzts4rAkKFIZbSGTSUojw8icR/E4p9X84Sl8Arge+D/6QRCzzlKBYLQo0ITk/yMm+bxdli2Zi319K2vBb6Zxzdcj3A2YZBCzXmdhQ7WvwVnqac0y6X9cPx3iFiANhi5Fa60mheF4R3QqLSzlU29DqdqtFMWvsIIp27G8+qsmV1ppfNi6kct2/sKYfRcaYB7Cm+36Pdy3TNLs3UQHr84Y3x5Uae2NzpYprgjhj6ZZWCa1MWgoRfv4kgjcDM4CFh79+H7gp6CMSbRbJD/aeEhQzM4lpXmY5fan++mPKlz2FsiTSc/yjdDzm1ICex53NmkB6SgdKK2taVaQaBSz3YFlaWdPicN9gMio99ackNZzjFaKdkxgZhwKNt4E8btaq7R4nVO2NmvsXfka1vanV95rqa9m34k9Ub/svyf2HOTpnJ/ledXOPce6xPcHLNgT3uNKodfPzuW6dkElIISLPZ9dQpdQrWutJSqnfaa3/FKZxBUS6hprv8BlO/fKW+zxXz9+z9wC0buLAB69x4MPXSMo8nozR95PYpUeAo2zJmqCYNW5Q8+/MbMAy6uYZrV07Y228QriKhq6hEiPj14OFn3s8s9VXvA30cWZiqSt7xW7KFs7Evu9HUs+bTJch15iulHF2HTXi7bOG0dEWEleECJ9gdg09XSl1DHCzUuplaNm4UWu9P8AxihDw1FGsxt7IrFXbI5YImtk36G8S2FR7iPJlT1Hz7SeknHIx6Zf+lgRrB7/Hdv3QPqzdVkZpZU2r8hXX35fZVTajje7RugE+1sYrRBSSGBmHCotLWyVz4DveBvo48G8Pfs23n1C+9ElQCfQYNwNbv2xTjwNHwuYr3nnbnnC3h+MqQOKKENHITCL4PLASOBbYRMsgpw/fLqJENH6w97RvsC3qy36gbNFMGg7sJf3S/6HT4MsD2g+YlWrj0dyBQRmTk1GgjtYN8LE2XiGikMTIODRr1XbDCUxv8TbQxxUWl1Jd33p/oDutmzjw4XwOfPAq1h79yBh9P3379uPCkzJ47eMffZ7Dq8D0Pj2jCVKJK0LEDp89g7XWf9Zanwy8pLU+Vmvdz+WPBLgoY/RG25Y34MLiUoYVrKFf3nKGFayhsLjUr8fnZmeRP2ag6TMAwRGMPDVjqdr2AT+/ci+6vpaeE/LpnH1FwE1hquoa/P5ZfJk2oj82q6XFbdG8AT7WxitEtJEYGZ+8JW3u8dY1hnpb0TOK084yTF/76Jvqqilb9BgHPphHyq/OJ/P6J+ia0Zv1ecN5NHcgTSaSwOuG9mlz9ZDEFSFih+lmMVrr34ZyICI4jI4V8OcN2HU/XGqylUO1Dc0b1ANtJuK877Q3N/vsHpqWbKX4IcexDM49bLqpkcr3X+aXjxfQofdJdM+9j8TO3Uxf35PKGnvQG6OE+iD5YIu18QoRrSRGtl+e9ogbrXq5r6j5023TKE4bHSLvurfeXv4jexfNpKFiN2kX3Ubn00ehlKLGpYGMt9JS93Nw20LiihCxw5+uoWGllJoO3AaUHb7pfq3125EbUXgF2k2rrW/A7kHL0wxkjb2RGUu3+NxIPn3JllaHrc8aN6h5bF1tVg7WNdDolhgeqnWs1OVmZzFtRH/+d+77lC9+gtodJXQafDnpF9+OslhN/Ty+hGL/ZCgPkg+FWBuvEEKEi1FnZU9ntnpaUTNK4lz5WokzWn3UOCpnvt74LuXLn0YlJtHz2pl07HNky4PrKqO3bRq1HjqOtoXEFSFig8/S0Ah7Wms9+PCfuEoC71v4uWMljCOBx2wZY252FuvzhvP0+MEA3D2/xHRJp5mgBY4E0ej5CotLmfbm5hZHRlRU25n21mbAccbQ9wVXUvLwpXTu0Houwt6kmbVqOwDHsJc9L99N7a4v6Hb5XXQbcYffSaBFKa9lqbKBXQghhCdGDdjWbisjf8xAslJtzVsZnh4/uNW+c2/xxdvjXBlu+eiSxAUH36Vs0WOk9+5Hrxv+1CIJ9HQERP6YgVg8bKdwTooKIeJL1K4IxrNgdP4M9Hw4f5Iio/F4O+vI/TEHDM4X3F1ZwyuvvMLtt99Ol65pdBrzJGQc73U8ng6ud23JbXRUgmxgF0II4Ym3BmxmVr2MyjH9OUrB00peUkM1tcuf5LEP3+Pi3GvZedIE6nTLfXkJHmZApaunEMJVtK8I3qmU+kwp9ZJSKs3THZRStyulipRSRWVlZZ7uEnOC0fnTWzLpjT9JUSDjdP+ep+vpxgbq/vsikydPZsiQIWz9rIRn7hzbYub1mfGDeWb84Ba3zRo7iFnjBpGWfGTFsEPikb/isoFdCCGEP/xtwObeXK1vN1urihR/445zJc8Z71JrdvPL/Gl88cl6nn/+earOvLVVEghQVd/osZrIaOwaAmoIJ4SIXRFdEVRK/QfI9PCtB4C/AY/geG96BHgKuNn9jlrrOcAccByWG7LBhlEwWi8Hmkz6c9SDtwBptCHd/THu12usqmDfksep2fkFU6ZM4YknnsBqtZLbw/NKpvtthcWlLfY6eGoIIxvYhRBCmOFPAzZPlTjusVAB15zu//455+rj66+/zi23TKFr166sW7eOs846i4K85YaP81RN5C3OB9oQTggRmyKaCGqtLzZzP6XUC8CyEA8nagSj86dRMtbV5n1/nadk6cKTMlptivc2nmkj+nvsDmq1qFaPcb3e91tL2Lc4H+oPMW/ePCZOnOj7B3Xjq6xWNrALIYRw5a05mz8TiGb22Gtg7bbW1UtGDdac12loaCAvL4+nnnqKYcOG8eabb9KrVy/A90Hz7hPArj+Tp8eFoomaECI6Re0eQaVUL631T4e/HA18EcnxhFMwVq6MkrGq+iMdOb1d3/37OcekexyPtwDqLai5X6980wrumH8fWVlZLFz4DoMHDzb9s7oKRlmtEEKI+GBmP73ZCUSzccb9fs4Ga67x2rXB2rCjkrj22mtZs2YNd9xxB7NnzyYpKan5vheelMG/N+w0vJ6n6h3nz9Qvb7nHA+4lZgoRH6I2EQSeUEoNxjGB9gPwm8gOJ7zaunKVm53FjKVbWh3/4KlhS6Dj8RVAzVyjrq6Ou+66izlz5nDppZfy2muvkZ6e7tfYXAWjrFYIIUR8CEZzNidfK3Ou93Mfg1GDtYdfWkrlknz27NnD3LlzueGGG1rdb9nmn1rd5uSrmkhiphDxLWqbxWitJ2mtB2qtT9Vaj3JZHRQmVXo4AxCCN9MXSEMa1430OffN59QzzmbOnDnk5eXx9ttvtykJBGkII4QQwrxgVpF4ij/uPMUjo2sd+vw/fPa3uwBYv369xySwsLi0xVFN7pxds/0Zs8RMIeJHNK8IijYK9UyfvwHUdQWx9scvKF5cgK6v5fez/k7+vbe3aSyuJapdbVY6WhOorLZLQxghhBCGghknc7OzKNqxn3kbdrYot1QcOfzdUzzqarO2SOZ0o52KNf/g4KfL6XpcNkUfrSIjI6PV9Zwx1deYXO9vtJVDmqgJEZ8kEWzH2tJ0xlvAcPI3gM5atZ3q+gYOfrqMijX/ILFrTzKufYwPGryfD2hmrK4/Z2WNHZvVwtPjB0swE0IIYSgYzdlcrd1W1mrPnTMJNDo30PV898ZDFZQV5lNXupUuZ4zmxb883SIJdI3NCUrRqI2bpbsepRSMrRxCiPZHEsEwM5NgBUugM31mD6P3N4DuKqtk3+q/UPXFGmzHn0n3q6aS0CGlzaWqwdzjIYQQIn4Ee0XMaI+gtzjn3MZRV/olZYX5NNVV0X3kNFJ+dT7XnHFM8/3cY7O3JNBqUTw8ckDz1xInhRCeSCIYRmYTrGDyd6avsLiUqW9sbhVgPAUMfwLojh072Pd6HlW7v6brsIl0HXYtSjm2qLquIAaSKEunUCGEEIEK1opYYXFpcxmoO2+lpr26dmT7ukXsf+fvJHbpTua4GST16EeWh6YyZs74tSjFrLGDWvxMEieFEJ5IIhhG0T4j50xUjWYZPQUMMwH03XffZfz48ei6eo4aPx1L35zm77muIJpJlD0litL1TAghRKTNWrXdYxKowLBSpra2lpSN/2D/qtfp2O90uo+8F4uts19NZVzZrBaPDWIkTgohPInarqHtUbTPyPmabfQ3YGitefLJJ7n00kvp2bMnJZ9u4tk/3EpWqg2FY8+Ea8Dy1YXUmSiWVtagOZIoXnhShnQ9E0IIEVFGsVzjuernxx9/5LzzzuM/ha8z9pa7yL41n0Rb51ax0ckoBluU8hhTnQqLS6mqa2j1OImTQghZEQyjaJ+R85aQuq/c+SrfrKqq4pZbbmH+/PmMHTuWf/7zn3Tq1IkTMC6D9ZUoGyWKa7eVkT9moHQ9E0IIETFGMd69xLOwuJQH//YGX86bgWq0k/fUP8i/5xafz2+0L9/bERHulTZOaclWHh45QOKkEHFOEsEwCnZ3smAzCmIWpZoDjZnyzW+++YbRo0ezdetWCgoK+P3vf49ybYvm5/WdibK3RFG6ngkhhIgkMzF+0ae7+H/3Pcred14gMa03PcY8QGFFFkOKS33GsEAa20xfssVjpU9yUqLETCGEJILhFO3n9ZiZbfS1z3HFihVMnDiRhIQEVq5cySWXXNLm6zuDaLSvqAohhIhfvmJ8dXU1t99yI+Ul72I7YSjdr7yHhA7JfvUK8GfS09th89GyJUUIEVlxnQiG8ygHp2heuTKTqBoFj9KKKh599FEeeughBg0axMKFC+nXr19Qrx/tK6pCCCHim1GM//777xkzZgzlJZvpeu71dD3r182dsyE0iZlzf70nMoEqhIA4TgQjcZRDLPCVqHpalWuqq6Zq9TP839YPue6665gzZw7JyclBv360r6gKIYQQ7lavXs2ECRNoamri5BtnUt3z1Fb3CUVi5i25lAlUIQTEcddQXx0qhWfTRvRv0aHTvu9Hfn75HvZv20D6Rbfx3YCbWL29ImTXz83OYn3ecL4vuJL1ecMlCRRCCBGVtNY8/vjjXH755WRlZfHJJ5/w2F2Tw9bl2ii5TEu2SuwUQgBxvCIY7Uc5RCvXVbmvN65h3/KnUYlWeo6fScc+A9l9oFZWVoUQQrQr/m4lOXjwIDfffDNvvfUW48eP58UXXyQlJYXjD38/HJUtRtspHh45IOjXEkLEprhNBKXxSOBGnppJ0YK/8eGimXQ66iS6jswjsUv35u/7s/FdCCGEiGb+biX5+uuvyc3NZdu2bcyaNYupU6e26Jwdrl4Bsp1CCOFL3CaC0ngkMBUVFUycOJGVK1dyyy238E7qlajEpFb3k5VVIYQQ7YGvbtmuli1bxnXXXYfVamX16tVcdNFFfl8vmI3sorlBnRAi8uI2EZSZsiMKi0uZvmRLc5tpo4NmP/vsM0aPHs2PP/7I888/z+233845j6+VlVUhhBDtlpmtJE1NTTzyyCNMnz6d0047jYULF3LMMcf4fa0HCz9n3oad6MNfSyM7IUQoxW0iCDJTBo4kcNqbm7E36ebbKqrtTHtrM3Ak8Lz++uvccsstdO3alXXr1nHWWWcBsrIqhBCiffO1leTAgQNMmjSJpUuXMnnyZJ5//nlsNv8nQwuLS1skgU6y3UIIESpx2zVUOMxatb1FEuhkb9TMWrWdhoYG7r33XiZMmEB2djabNm1qTgLBkSjmjxlIVqoNBWSl2locQC+EEELEisLiUoYVrKFf3nKGFaxxTJa6dcuGIxOeW7du5YwzzmDFihU8++yzzJ07N6AkEBzxuHU0dpDtFkKIUIjrFUHhPbj8uPtnRowYwZo1a7jjjjuYPXs2SUmt9wPKyqoQQohY514hU1pZw7Q3NzNr3CDyxwxstZWk4duPOPPGG+nUqRNr1qzh3HPPbdP1vcVj2W4hhAgFWRGMc0bBpe7nb9jzyt2sX7+euXPn8txzz3lMAoUQQoSGUmqcUmqLUqpJKZXj9r37lFLfKKW2K6VGRGqM7cn0JVtaVcjYmzTTl2xpcYbt+9PO5+M3nmPcuHEMHDiQTZs2tTkJBON4rJAD4IUQoSGJYJybNqI/1gTV4rZDn7/Lnn9Po2tHK+vXr+eGG26I0OiEECKufQGMAd53vVEp9SvgWmAAcBnwV6WUpfXDhT+cDdO83b5v3z6uuOIKCgoK+M1vfsN7771HVlZwKmI8laAq4LqhfaTqRggRElIaGuecwWX6ki1UHKqmYs0/OPjpcgaeMYx3ly8iIyMjwiMUQoj4pLX+EmhxBt1hVwOva63rgO+VUt8AZwIfhXeE8aWkpITRo0eze/duXnjhBW699dagPr90MxdChJskgoLc7CyG9rIwbtw4dn76AVOnTqWgoIDERPnrIYQQUSgL2ODy9a7Dt7WilLoduB2gT58+oR9ZDEtLtlJR3XpVMC3Zyrx587jttttIT0/n/fffZ8iQISEZg+y5F0KEk5SGCj766CNOO+00Pv30U1577TWefPJJSQKFECIMlFL/UUp94eHP1d4e5uE2jw1M15vSAAAgAElEQVQntdZztNY5WuscqfDw7uGRA7BaWv5qE2nk6O1vcv3113PGGWewadOmkCWBQggRbvJpP45prZkzZw7/+7//y9FHH83KlSs59dRTIz0sIYSIG1rriwN42C7gaJevjwJ2B2dE8cu9NLN7Yi01K59k6aYN/O53v+OciVMY888t7K4skrJNIUS7IIlgnKqtreXOO+/kxRdf5LLLLmPevHmkp6dHelhCCCF8WwK8qpSaDfQGTgA2RnZI7YOzNPOTTz5hzJgxlJeX88orr9BpwIXct/BzauyNgONoifsWft78GCGEiEVSGhqHfvzxR84//3xefPFFHnjgAZYtWyZJoBBCRBml1Gil1C7gLGC5UmoVgNZ6C/AGsBVYCdyhtW6M3Ejbl5deeolzzz0Xi8XChx9+yPXXX8+sVdubk0CnGnsjs1Ztj9AohRCi7WRFMM6sW7eOcePGUVtby8KFCxk9enSkhySEEMIDrfUiYJHB92YCM8M7ovatvr6e3/3udzz//PNcfPHFvP7663Tr1g0wPuzd2yHwQggR7WRFME5orfnTn/7ERRddRHp6Oh9//LEkgUIIIQSwe/duLrjgAp5//nn+8Ic/sHLlyuYkEIwPeze6XQghYoEkgnGgurqaSZMmMWXKFK666io2btzIySefHOlhCSGEEBG3fv16Tj/9dD777DPeeOMNCgoKsFhaHuzu6bB3m9XCtBH9wzlUIYQIKkkE27nvv/+eYcOG8eqrr/LII4+wcOFCunTpEulhCSEM9O3bF5vNRqdOncjMzOTGG2/k0KFDANx4440opViyZEmLx0yZMgWlFHPnzgUcJW5Tp07lqKOOolOnTvTr14+7777b4zWcf+68886w/YxCGCksLmVYwRr65S1nWMEaCotLQ3YtrTV//etfueCCC+jUqRMbNmxg3LhxHu+bm51F/piBZKXaUEBWqo38MQOlUYwQYSYxMrgkEWzH3nnnHXJycvjhhx9YtmwZDz74IAkJ8pILEe2WLl3KoUOHKCkpobi4mPz8/ObvnXjiifzrX/9q/rqhoYE333yT4447rvm2/Px8ioqK2LhxIwcPHmTt2rVkZ2d7vIbzz3PPPRf6H0wILwqLS7lv4eeUVtagOdKZMxTJYG1tLTfffDN33HEHI0aM4JNPPuGUU07x+pjc7CzW5w3n+4IrWZ83XJJAISJEYmTwSFbQDmmtefzxx7nsssvo3bs3n3zyCVdccUWkhyWE8FNmZiYjRoygpKSk+baRI0eyfv16KioqAJrP/8zMzGy+zyeffMLo0aPp3bs3Sin69u3L5MmTwz5+IfwRrs6cO3fu5JxzzmHu3Lk89NBDLFmyhNTU1KBeQwgRehIj204SwXbm0KFD/PrXvyYvL4+xY8fy0Ucfcfzxx0d6WEKIAOzatYsVK1a0+DfcsWNHRo0axeuvvw7Ayy+/3CqADR06lNmzZ/PXv/6Vzz//HK216Wvu3LmT1NRUdu7cGZwfQsSVtpR2hqMz59q1azn99NP5+uuvWbx4MTNmzJBKGSFilMTItpN3v3bk66+/ZsiQISxcuJBZs2bx+uuv06lTp0gPSwjhp9zcXDp37szRRx9Njx49mDFjRovvT548mZdffpkDBw6wbt06cnNzW3z/vvvu4w9/+APz5s0jJyeHrKysFqUyzmukpqY2/3nhhRcA6NOnD5WVlfTp0ye0P6Rod9pa2hnKzpxaa2bPns0ll1xCRkYGGzduZNSoUW1+XiFE+EmMDB5JBNuJZcuWccYZZ7Bnzx5Wr17Nvffei1Iq0sMSQgSgsLCQgwcP8t5777Ft2zbKy8tbfP+cc86hrKyMRx99lKuuugqbreUHZYvFwh133MH69euprKzkgQce4Oabb+bLL79scY3KysrmP7fddltYfjbRfrW1tDNUnTmrqqqYOHEiU6dO5eqrr+bjjz+mf3/p9ilErJIYGTySCMa4pqYmZsyYwciRIzn22GPZtGkTF110UaSHJYQIgvPPP58bb7yRe++9t9X3rr/+ep566imf+xpsNht33HEHaWlpbN26NVRDFaLNpZ2h6Mz57bffcvbZZzN//nwee+wx3nrrLTp37hzw8wkhoofEyLZLjPQAROAOHDjApEmTWLp0KZMnT+b5559vNeshhIhtU6ZMoW/fvi02wwPcddddnHvuuZx33nmtHvPMM88wePBghgwZgtVqZd68eRw8eLBVVzQhgql3qo1SD0mfP6WdudlZQevGuXLlSiZMmIBSihUrVjBixIigPK8QInpIjGwbWRGMUVu3buXMM89kxYoVPPvss8ydO1eSQCHaoYyMDCZPnswjjzzS4vb09HQuuugijyXgNpuNqVOnkpmZSffu3fnLX/7CggULOPbYY5vvM3LkyBZnJI0ePRpwbITv1KlTu9kIL8InWg5d11rz2GOPccUVV9CnTx+KiookCRSinZIY2TbKn0450S4nJ0cXFRVFehght2DBAm688UZSUlJ48803OffccyM9JCGECDul1CatdU6kxxErwhEjC4tLmbVqO7sra+idamPaiP5hPW/v4MGD3HDDDSxatIgJEybwwgsvkJKSErbrCyFENDAbH6U0NIY0Njby4IMPUlBQwJAhQ1iwYAFZWXKgrRBCiOgQzNJOf23fvp3c3Fy+/vprZs+ezZQpU6RpmhBCeCGJYIzYv38/EyZMYPXq1dx+++38+c9/pkOHDpEelhBCCBFxixcvZtKkSXTs2JF33nmHCy+8MNJDEkKIqCd7BGPA5s2bycnJ4b333uOFF17g73//uySBQoigu+mmm8jMzGTOnDmRHoqIcW05WN4fTU1NPPTQQ+Tm5tK/f382bdokSaAQIujq6uq48sorOeqoo1i2bFmkhxM0kghGuVdffZWzzjqL+vp63n//fW699dZID0kI0Q6VlJRQXl7OV199xXPPPRfp4YgY1taD5c2qrKxk5MiRPPLII9x0003897//5eijjw7qNYQQAmDFihX069ePjRs3kp+fH+nh/P/27jw8ijJb/Pj3iCwhJMGwGCFogoosiigBFEQisoPsm7KEZRDienFmuOPICKjDjMuPAZ3JKGjEhO1GQECIeoGRdQCToCAMwrBeNpHIJhCynt8faXoS6EBCCJV0n8/z9ENX1VtV5610cjhVb1VfN1YIllKZmZmMHTuWQYMGERERQUpKCi1atHA6LGOMl6pVqxZ+fn6kp6dTv359p8MxZVhxv1i+MLZt20ZERATLly8nJiaGjz76iEqVKl237RtjTF4XnyiakZFBgwYNHI7m+rFCsBT66aefaN++PVOnTuWFF15g5cqV3HrrrU6HZcxV/fWvfyUiIoKKFSsybNiwK7b95JNPaNq0KYGBgYSGhjJu3DiysrKA3CEYI0eO5I477iAgIIAHHniAL774wr3u7Nmz8z3WuXLlyogIKSkpJdk95s2bxz333ENQUBA1a9YkKiqKM2fOeGy7a9cuevToQY0aNQgODqZjx47s3Jn/P8J79+6lW7duBAQEUL16dcaNGwdcvf9X0rFjR1599dXL5i9evJiQkBD3MQaoV68eu3btAqBmzZqICDVr1rShdaZYivvF8leTkJBAixYtOHfuHKtWrSI6OtoeCmPKhKLkyLzatm2LiOT7+/3YY49Ro0YNAgMDuf/++1m8eLF72dGjR+nevTu1atVCRNi/f/917EXBpk6dSt26dQkMDKRWrVqMHTs2X8x57d+/HxHJl8vzfgXEiRMnGDBgANWrV6d69eoMGjTosnw7bdo0wsPD8ff3p0GDBu58diX169cnNjb2svnTpk0jIuI/D9nMyMigevXqnD17FoDGjRuTlJREeHg4ffr0KdTxKAscLQRFpJ+IbBeRHBGJuGTZyyKyW0R2iojPfAFQUlISTZs2ZdOmTcTHxzNt2jTKly/vdFjGFEqtWrUYP348I0aMuGrb8+fPM3XqVFJTU9m0aRMrV67knXfeASArK4s6deqwevVqTp8+zeuvv07//v3dyWzQoEGcPXvW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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax_list = plt.subplots(nrows=3, ncols=2, gridspec_kw={'hspace': 0.3})\n", - "fig.set_size_inches(15, 20)\n", - "ax_list = ax_list.flat[:]\n", - "\n", - "energy_plot('train.xyz', 'quip_train.xyz', ax_list[0], 'Energy on training data')\n", - "energy_plot('validate.xyz', 'quip_validate.xyz', ax_list[1], 'Energy on validation data')\n", - "force_plot('train.xyz', 'quip_train.xyz', ax_list[2], 'H', 'Force on training data - H')\n", - "force_plot('train.xyz', 'quip_train.xyz', ax_list[3], 'O', 'Force on training data - O')\n", - "force_plot('validate.xyz', 'quip_validate.xyz', ax_list[4], 'H', 'Force on validation data - H')\n", - "force_plot('validate.xyz', 'quip_validate.xyz', ax_list[5], 'O', 'Force on validation data - O')\n", - "\n", - "# if you wanted to have the same limits on the firce plots\n", - "#for ax in ax_list[2:]:\n", - "# flim = (-20, 20)\n", - "# ax.set_xlim(flim)\n", - "# ax.set_ylim(flim)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T12:48:02.357449Z", - "start_time": "2018-10-07T12:48:02.347940Z" - } - }, - "source": [ - "## train our GAP_3b model from the command line\n", - "\n", - "Let's add three ody terms to the fit, which will hopefully improve it. We will be using the desciprtors distance_2b and angle_3b.\n", - "\n", - "**angle_3b**\n", - "- `theta_fac=0.5` this takes the input data and determines the width from that; useful here, because the dimensions of the descriptor are different\n", - "- `n_sparse=50` higher dimensional space, more sparse points\n", - "\n", - "## both training and quip takes significantly more time than the last one!!!" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:12:12.409600Z", - "start_time": "2018-10-07T17:11:09.080521Z" - }, - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "libAtoms::Hello World: 07/10/2018 18:11:09\n", - "libAtoms::Hello World: git version https://github.com/libAtoms/QUIP.git,531330f-dirty\n", - "libAtoms::Hello World: QUIP_ARCH linux_x86_64_gfortran_openmp\n", - "libAtoms::Hello World: compiled on Jul 2 2018 at 21:44:13\n", - "libAtoms::Hello World: OpenMP parallelisation with 8 threads\n", - "WARNING: libAtoms::Hello World: environment variable OMP_STACKSIZE not set explicitly. The default value - system and compiler dependent - may be too small for some applications.\n", - "libAtoms::Hello World: Random Seed = 65469770\n", - "libAtoms::Hello World: global verbosity = 0\n", - "\n", - "Calls to system_timer will do nothing by default\n", - "\n", - "\n", - "================================ Input parameters ==============================\n", - "\n", - "at_file = train.xyz\n", - "gap = \"distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform add_species=T n_sparse=10 : angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 add_species=T n_sparse=30 sparse_method=uniform\"\n", - "e0 = H:3.21:O:4.6\n", - "e0_offset = 0.0\n", - "do_e0_avg = T\n", - "default_sigma = \"0.008 0.04 0 0\"\n", - "sparse_jitter = 1.0e-10\n", - "hessian_delta = 1.0e-2\n", - "core_param_file = quip_params.xml\n", - "core_ip_args =\n", - "energy_parameter_name = energy\n", - "force_parameter_name = forces\n", - "virial_parameter_name = virial\n", - "hessian_parameter_name = hessian\n", - "config_type_parameter_name = config_type\n", - "sigma_parameter_name = sigma\n", - "config_type_sigma =\n", - "sigma_per_atom = T\n", - "do_copy_at_file = F\n", - "sparse_separate_file = T\n", - "sparse_use_actual_gpcov = F\n", - "gp_file = GAP_3b.xml\n", - "verbosity = NORMAL\n", - "rnd_seed = -1\n", - "do_ip_timing = F\n", - "template_file = template.xyz\n", - "\n", - "======================================== ======================================\n", - "\n", - "\n", - "============== Gaussian Approximation Potentials - Database fitting ============\n", - "\n", - "\n", - "Initial parsing of command line arguments finished.\n", - "Found 2 GAPs.\n", - "Descriptors have been parsed\n", - "XYZ file read\n", - "Old GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform add_species=T n_sparse=10}\n", - "New GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform n_sparse=10 Z1=8 Z2=8}\n", - "New GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform n_sparse=10 Z1=8 Z2=1}\n", - "New GAP: {distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform n_sparse=10 Z1=1 Z2=1}\n", - "Old GAP: { angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 add_species=T n_sparse=30 sparse_method=uniform}\n", - "New GAP: { angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 n_sparse=30 sparse_method=uniform Z=8 Z1=8 Z2=8}\n", - "New GAP: { angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 n_sparse=30 sparse_method=uniform Z=8 Z1=8 Z2=1}\n", - "New GAP: { angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 n_sparse=30 sparse_method=uniform Z=8 Z1=1 Z2=1}\n", - "New GAP: { angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 n_sparse=30 sparse_method=uniform Z=1 Z1=8 Z2=8}\n", - "New GAP: { angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 n_sparse=30 sparse_method=uniform Z=1 Z1=8 Z2=1}\n", - "New GAP: { angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 n_sparse=30 sparse_method=uniform Z=1 Z1=1 Z2=1}\n", - "Multispecies support added where requested\n", - "Number of target energies (property name: energy) found: 60\n", - "Number of target forces (property name: forces) found: 14580\n", - "Number of target virials (property name: virial) found: 0\n", - "Number of target Hessian eigenvalues (property name: hessian) found: 0\n", - "Cartesian coordinates transformed to descriptors\n", - "Started sparse covariance matrix calculation of coordinate 1\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 1\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate1_sparse done in .89205500000000093 cpu secs, .11343244090676308 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate1 done in .89205500000000093 cpu secs, .11355240270495415 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 2\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 2\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate2_sparse done in 3.6442280000000018 cpu secs, .45859701186418533 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate2 done in 3.6442280000000018 cpu secs, .45871092379093170 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 3\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 3\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate3_sparse done in 3.4482159999999986 cpu secs, .43381043337285519 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate3 done in 3.4482159999999986 cpu secs, .43392318300902843 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 4\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 4\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate4_sparse done in 9.9606220000000008 cpu secs, 1.2572825513780117 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate4 done in 9.9606220000000008 cpu secs, 1.2574077267199755 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 5\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 5\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate5_sparse done in 50.367147000000003 cpu secs, 6.3618728630244732 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate5 done in 50.367147000000003 cpu secs, 6.3619935847818851 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 6\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 6\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate6_sparse done in 55.067442000000000 cpu secs, 6.9305843152105808 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate6 done in 55.067442000000000 cpu secs, 6.9306991323828697 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 7\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 7\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate7_sparse done in 25.993623999999983 cpu secs, 3.2758836857974529 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate7 done in 25.993623999999983 cpu secs, 3.2760034948587418 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 8\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 8\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate8_sparse done in 112.66704200000001 cpu secs, 14.186459138989449 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate8 done in 112.67104200000003 cpu secs, 14.186579804867506 wall clock secs.\n", - "Started sparse covariance matrix calculation of coordinate 9\n", - "\n", - "Finished sparse covariance matrix calculation of coordinate 9\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate9_sparse done in 103.17444799999998 cpu secs, 12.990166073665023 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_Coordinate9 done in 103.17444799999998 cpu secs, 12.990285659208894 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_LinearAlgebra done in .44402700000000550 cpu secs, .12523609958589077 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse_FunctionValues done in .00000000000000000E+000 cpu secs, .13986043632030487E-003 wall clock secs.\n", - "TIMER: gpFull_covarianceMatrix_sparse done in 365.71485500000000 cpu secs, 46.178485954180360 wall clock secs.\n", - "TIMER: GP sparsify done in 368.54703300000000 cpu secs, 47.065343659371138 wall clock secs.\n", - "\n", - "libAtoms::Finalise: 07/10/2018 18:12:12\n", - "libAtoms::Finalise: Bye-Bye!\n" - ] - } - ], - "source": [ - "! teach_sparse e0={H:3.21:O:4.6} energy_parameter_name=energy force_parameter_name=forces do_copy_at_file=F sparse_separate_file=T gp_file=GAP_3b.xml at_file=train.xyz default_sigma={0.008 0.04 0 0} gap={distance_2b cutoff=4.0 covariance_type=ard_se delta=0.5 theta_uniform=1.0 sparse_method=uniform add_species=T n_sparse=10 : angle_3b cutoff=3.5 covariance_type=ard_se delta=0.5 theta_fac=0.5 add_species=T n_sparse=30 sparse_method=uniform}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T12:56:59.802090Z", - "start_time": "2018-10-07T12:56:58.506422Z" - } - }, - "source": [ - "## use the potential with QUIP on trani.xyz and validate.xyz" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-07T17:12:47.633785Z", - "start_time": "2018-10-07T17:12:12.412800Z" - } - }, - "outputs": [], - "source": [ - "# calculate train.xyz\n", - "\n", - "! quip E=T F=T atoms_filename=train.xyz param_filename=GAP_3b.xml | grep AT | sed 's/AT//' >> quip_3b_train.xyz\n", - "! quip E=T F=T atoms_filename=validate.xyz param_filename=GAP_3b.xml | grep AT | sed 's/AT//' >> quip_3b_validate.xyz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# look at the outputs - clear improvement" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "ExecuteTime": { - "end_time": "2018-10-08T08:54:43.555206Z", - "start_time": "2018-10-08T08:54:36.324200Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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5PDhw0ybNo127drl6/v5rBAUEZHiZefOnbRu3Zpvv/2WV155hZdffpkSJXw1Q6FwU2OpiEjxMnbsWHr16sWll17K+vXrufnmm/P9PVUIiohIvlu+fDlhYWEkJCQwb9487r//fl+HJCIi4nN//fUXvXr1Yvz48TRv3pxp06ZRqVKlAnlvNcWKiEi+cc7x7rvvctddd1GlShWioqJUBIqIiAC///47t99+O+PHj+eFF15gwYIFBVYEgnoERUQknxw/fpyuXbsyc+ZMHnzwQSZNmkSFChV8HZaIiIjPrVq1irZt23LixAnmzJlD69atCzwG9QiKiEie++mnn6hXr96ZbSFmz56tIlBERIo95xwjR46kSZMmBAYGsnHjRp8UgaAeQRERyWOLFi2iffv2mBmLFi2iWbNmvg5JRETE506cOMHjjz/OlClTaNmyJZMnT6ZixYo+i0c9giIikidOnz7NG2+8wb333svll19OTEyMikARERHg119/pUGDBkydOpXXXnuNiIgInxaBoB5BERHJA3/++SePPPIIkZGRdOjQgbFjx1KuXDlfhyUiIuJzS5cuJTw8nKSkJL788kvuueceX4cEqEdQRERy6ccff6ROnTrMnz+f4cOH89lnn6kIFBGRYs85xzvvvMPdd99N1apViY6OLjRFIKhHUEREciEyMpJOnTpRtmxZvv76axo3buzrkERERHzu2LFjdOnShdmzZxMWFsaECRMoX768r8M6i3oERUQk25KSknjppZcIDQ3luuuuIyYmRkWgiIgI8N///pe6desyZ84c3nnnHWbMmFHoikBQj6CIiGTT4cOH6dChA4sWLaJLly589NFHlC1b1tdhiYiI+NyCBQvo0KEDpUqVYsmSJTRt2tTXIaVLPYIiIpJlW7dupVatWnz99deMHj2a8ePHqwgUEZFi7/Tp07z22mvcf//9XHXVVURHRxfqIhDUIygiIlk0c+ZMunTpQsWKFVm5ciX16tXzdUgiIiI+d+TIETp16sS8efPo2LEjH3/8MQEBAb4OK1PqERQRkQwlJiby3HPPER4eTs2aNYmJiVERKCIiAmzbto3atWuzcOFCRo4cyaeffuoXRSCoR1BERDJw4MABHnroIZYvX07Pnj0ZNmwYZcqUASBycyxDl2xnd9xJqgYG0K95dVrVDPJxxCIiIgVj7ty5PPLII5QrV45l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