diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..375d10a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,236 @@ +name: CI + +on: [push, pull_request] + +env: + BUILD_DIR: _build + PIP_PACKAGES: >- + meson==0.58.0 + cmake + ninja + gcovr + MACOS_BASEKIT_URL: >- + https://registrationcenter-download.intel.com/akdlm/irc_nas/17969/m_BaseKit_p_2021.3.0.3043.dmg + MACOS_HPCKIT_URL: >- + https://registrationcenter-download.intel.com/akdlm/irc_nas/17890/m_HPCKit_p_2021.3.0.3226_offline.dmg + LINUX_INTEL_COMPONENTS: >- + intel-oneapi-compiler-fortran + intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic + intel-oneapi-mkl + intel-oneapi-mkl-devel + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + build: [meson] + build-type: [debug] + compiler: [intel] + version: [2021] + + include: + - os: ubuntu-latest + build: meson + build-type: debug + compiler: gnu + version: 11 + + - os: ubuntu-latest + build: meson + build-type: debug + compiler: gnu + version: 9 + + env: + FC: ${{ matrix.compiler == 'intel' && 'ifort' || 'gfortran' }} + CC: ${{ matrix.compiler == 'intel' && 'icc' || 'gcc' }} + GCC_V: ${{ matrix.version }} + OMP_NUM_THREADS: 1,2,1 + PYTHON_V: 3.8 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - uses: actions/setup-python@v1 + with: + python-version: ${{ env.PYTHON_V }} + + - name: Install GCC (OSX) + if: ${{ contains(matrix.os, 'macos') && matrix.compiler == 'gnu' }} + run: | + brew install gcc@${{ env.GCC_V }} + ln -s /usr/local/bin/gfortran-${GCC_V} /usr/local/bin/gfortran + which gfortran-${GCC_V} + which gfortran + + - name: Install GCC (Linux) + if: ${{ contains(matrix.os, 'ubuntu') && matrix.compiler == 'gnu' }} + run: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install -y gcc-${{ env.GCC_V}} gfortran-${{ env.GCC_V }} + sudo update-alternatives \ + --install /usr/bin/gcc gcc /usr/bin/gcc-${{ env.GCC_V }} 100 \ + --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${{ env.GCC_V }} \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-${{ env.GCC_V }} + + - name: Install GCC (Windows) + if: ${{ contains(matrix.os, 'windows') && matrix.compiler == 'gnu' }} + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: false + install: >- + git + mingw-w64-x86_64-gcc-fortran + mingw-w64-x86_64-openblas + mingw-w64-x86_64-lapack + mingw-w64-x86_64-cmake + mingw-w64-x86_64-meson + mingw-w64-x86_64-ninja + + - name: Prepare for cache restore + if: ${{ matrix.compiler == 'intel' }} + run: | + sudo mkdir -p /opt/intel + sudo chown $USER /opt/intel + + - name: Cache Intel install + if: ${{ matrix.compiler == 'intel' }} + id: cache-install + uses: actions/cache@v2 + with: + path: /opt/intel/oneapi + key: install-${{ matrix.compiler }}-${{ matrix.version }}-${{ matrix.os }} + + - name: Install Intel (Linux) + if: ${{ contains(matrix.os, 'ubuntu') && contains(matrix.compiler, 'intel') && steps.cache-install.outputs.cache-hit != 'true' }} + run: | + wget https://apt.repos.intel.com/intel-gpg-keys/${{ env.KEY }} + sudo apt-key add ${{ env.KEY }} + rm ${{ env.KEY }} + echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list + sudo apt-get update + sudo apt-get install ${{ env.PKG }} + env: + KEY: GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB + PKG: ${{ env.LINUX_INTEL_COMPONENTS }} + + - name: Install Intel BaseKit (OSX) + if: ${{ contains(matrix.os, 'macos') && contains(matrix.compiler, 'intel') && steps.cache-install.outputs.cache-hit != 'true' }} + run: | + curl --output ${{ env.OUT }} --url "$URL" --retry 5 --retry-delay 5 + hdiutil attach ${{ env.OUT }} + if [ -z "$COMPONENTS" ]; then + sudo /Volumes/"$(basename "$URL" .dmg)"/bootstrapper.app/Contents/MacOS/bootstrapper -s --action install --eula=accept --continue-with-optional-error=yes --log-dir=. + installer_exit_code=$? + else + sudo /Volumes/"$(basename "$URL" .dmg)"/bootstrapper.app/Contents/MacOS/bootstrapper -s --action install --components="$COMPONENTS" --eula=accept --continue-with-optional-error=yes --log-dir=. + installer_exit_code=$? + fi + hdiutil detach /Volumes/"$(basename "$URL" .dmg)" -quiet + exit $installer_exit_code + env: + OUT: webimage-base.dmg + URL: ${{ env.MACOS_BASEKIT_URL }} + COMPONENTS: intel.oneapi.mac.mkl.devel + + - name: Install Intel HPCKit (OSX) + if: ${{ contains(matrix.os, 'macos') && contains(matrix.compiler, 'intel') && steps.cache-install.outputs.cache-hit != 'true' }} + run: | + curl --output ${{ env.OUT }} --url "$URL" --retry 5 --retry-delay 5 + hdiutil attach ${{ env.OUT }} + if [ -z "$COMPONENTS" ]; then + sudo /Volumes/"$(basename "$URL" .dmg)"/bootstrapper.app/Contents/MacOS/bootstrapper -s --action install --eula=accept --continue-with-optional-error=yes --log-dir=. + installer_exit_code=$? + else + sudo /Volumes/"$(basename "$URL" .dmg)"/bootstrapper.app/Contents/MacOS/bootstrapper -s --action install --components="$COMPONENTS" --eula=accept --continue-with-optional-error=yes --log-dir=. + installer_exit_code=$? + fi + hdiutil detach /Volumes/"$(basename "$URL" .dmg)" -quiet + exit $installer_exit_code + env: + OUT: webimage-hpc.dmg + URL: ${{ env.MACOS_HPCKIT_URL }} + COMPONENTS: all + + - name: Setup Intel oneAPI environment + if: ${{ matrix.compiler == 'intel' }} + run: | + source /opt/intel/oneapi/setvars.sh + printenv >> $GITHUB_ENV + + - name: Install build and test dependencies + if: ${{ ! contains(matrix.os, 'windows') }} + run: pip3 install ${{ env.PIP_PACKAGES }} ${{ env.PIP_EXTRAS }} + + - name: Configure build (meson) + if: ${{ matrix.build == 'meson' }} + run: >- + meson setup ${{ env.BUILD_DIR }} + --buildtype=debug + --prefix=$PWD/_dist + --libdir=lib + --warnlevel=0 + -Db_coverage=${{ env.COVERAGE }} + ${{ env.MESON_ARGS }} + env: + COVERAGE: ${{ matrix.build-type == 'coverage' }} + MESON_ARGS: ${{ matrix.compiler == 'intel' && '-Dfortran_link_args=-qopenmp' || '' }} + + - name: Configure build (CMake) + if: ${{ matrix.build == 'cmake' }} + run: >- + cmake -B${{ env.BUILD_DIR }} + -GNinja + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_INSTALL_PREFIX=$PWD/_dist + -DCMAKE_INSTALL_LIBDIR=lib + + - name: Build library + run: ninja -C ${{ env.BUILD_DIR }} + + - name: Run unit tests + if: ${{ matrix.build == 'meson' }} + run: | + meson test -C ${{ env.BUILD_DIR }} --print-errorlogs --no-rebuild --num-processes 1 -t 2 + + - name: Run unit tests + if: ${{ matrix.build == 'cmake' }} + run: | + ctest --output-on-failure --parallel 2 + working-directory: ${{ env.BUILD_DIR }} + + - name: Create coverage report + if: ${{ matrix.build == 'meson' && matrix.build-type == 'coverage' }} + run: + ninja -C ${{ env.BUILD_DIR }} coverage + + - name: Install project + run: | + ninja -C ${{ env.BUILD_DIR }} install + echo "PLOTMS_PREFIX=$PWD/_dist" >> $GITHUB_ENV + + - name: Create package + if: ${{ matrix.build == 'meson' }} + run: | + tar cvf ${{ env.OUTPUT }} _dist + xz -T0 ${{ env.OUTPUT }} + echo "PLOTMS_OUTPUT=${{ env.OUTPUT }}.xz" >> $GITHUB_ENV + env: + OUTPUT: plotms-${{ matrix.compiler }}-${{ matrix.version }}-${{ matrix.os }}.tar + + - name: Upload package + if: ${{ matrix.build == 'meson' && matrix.build-type != 'coverage' }} + uses: actions/upload-artifact@v2 + with: + name: ${{ env.PLOTMS_OUTPUT }} + path: ${{ env.PLOTMS_OUTPUT }} + + - name: Upload coverage report + if: ${{ matrix.build == 'meson' && matrix.build-type == 'coverage' }} + uses: codecov/codecov-action@v1 diff --git a/config/meson.build b/config/meson.build new file mode 100644 index 0000000..5d69d65 --- /dev/null +++ b/config/meson.build @@ -0,0 +1,47 @@ +# This file is part of PlotMS. + +os = host_machine.system() +fc = meson.get_compiler('fortran') +fc_id = fc.get_id() +static = get_option('default_library') == 'static' and get_option('fortran_link_args').contains('-static') + +#> choose compiler options +if fc_id == 'gcc' + add_project_arguments( + '-ffree-line-length-none', + '-fbacktrace', + language: 'fortran', + ) + if fc.version().version_compare('<8') + error('GCC version 8 or higher is required.') + endif +elif fc_id == 'intel' + add_project_arguments( + '-traceback', + language: 'fortran', + ) + add_project_arguments( + '-DLINUX', + language: 'c', + ) +elif fc_id == 'pgi' or fc_id == 'nvidia_hpc' + add_project_arguments( + '-Mbackslash', + '-Mallocatable=03', + '-traceback', + language: 'fortran', + ) + error('This project does not support compilation with NVHPC yet') +endif +######################################################################## + + +# Create the tool chain library as subproject +mctc_dep = dependency( + 'mctc-lib', + version: '>=0.2', + fallback: ['mctc-lib', 'mctc_dep'], + default_options: ['default_library=static'], + static: static, +) +exe_deps += mctc_dep diff --git a/entries.f90 b/entries.f90 deleted file mode 100644 index 1961e2b..0000000 --- a/entries.f90 +++ /dev/null @@ -1,165 +0,0 @@ -module count_entries - use xtb_mctc_accuracy, only: wp - implicit none - - contains - - subroutine check_entries(index_mass, isotope_masses, exact_intensity, & - list_masses, intensity, count_mass, chrg) - - integer :: loop, loop2, index_mass - integer :: count_mass - integer :: sum_index, i - integer :: check - - !real(wp) :: intensity(index_mass) - real(wp) :: xmass - real(wp) :: isotope_masses(index_mass) - real(wp) :: exact_intensity(index_mass) - real(wp) :: chrg - - real(wp) :: list_masses(10000) - real(wp) :: intensity(10000) - - !real(wp), allocatable :: list_masses(:) - !real(wp), allocatable :: save_list(:) - !real(wp), allocatable :: save_int(:) - !real(wp), allocatable :: intensity(:) - !real (wp) :: save_list(1000) - !real (wp) :: save_list(1000) - - logical :: there = .true. - - xmass = 0 - loop = 0 - loop2 = 0 - check = 0 - !count_mass = count_mass + index_mass - if(count_mass == 0)then - sum_index = index_mass - ! allocate ( list_masses(index_mass)) - ! allocate ( intensity(index_mass)) - else - sum_index = count_mass + index_mass - ! if(allocated(save_list)) write(*,*) 'JOP' - ! if(allocated(save_list))deallocate (save_list) - ! if(allocated(save_int))deallocate (save_int) - ! allocate ( save_list(count_mass)) - ! allocate ( save_int (count_mass)) - - ! !write(*,*) 'COUNT MASS', count_mass - ! do i = 1, count_mass - ! save_list(i) = list_masses(i) - ! save_int(i) = intensity(i) - ! enddo - - ! deallocate (list_masses) - ! deallocate (intensity) - ! allocate ( list_masses(sum_index)) - ! allocate ( intensity(sum_index)) - - ! !write(*,*) 'save', save_list - ! !write(*,*) 'list', list_masses - - ! do i = 1, count_mass - ! list_masses(i) = save_list(i) - ! intensity(i) = save_int(i) - ! enddo - endif - - - !if (.not. allocated(list_masses)) allocate ( list_masses(sum_index)) - - !write(*,*) 'BUCKET' - !write(*,*) 'index', index_mass - !write(*,*) 'sum', sum_index - !write(*,*) 'count', count_mass - -outer: do loop2 = 1, index_mass - loop = 0 - !write(*,*) isotope_masses(loop2) - - !write(*,*) 'LOOP2', loop2 - do - loop = loop + 1 - - if ( list_masses(loop) == isotope_masses(loop2) ) then - !write(*,*) 'LOOP', loop, 'TRUE' - there = .true. - !write(*,*) 'TRUE' - !write(*,*) 'intensity', loop,intensity(loop) - !intensity(loop) = intensity(loop) + 1 - intensity(loop) = intensity(loop) + 1 * exact_intensity(loop2) & - * abs(chrg) - if (loop2 < index_mass ) cycle outer - if (loop2 == index_mass ) exit - - elseif ( list_masses(loop) == 0.0_wp ) then - there = .false. - !write(*,*) 'NULL' - exit - - !>> false if not in list, store - elseif ( list_masses(loop) /= isotope_masses(loop2) ) then - !write(*,*) 'LOOP', loop, 'FALSE' - there = .false. - !write(*,*) 'FALSE' - !write(*,*) loop, loop2 - !if (loop == count_mass ) exit - if (loop == sum_index ) exit - endif - enddo - - - if ( .not. there ) then - count_mass = count_mass + 1 - list_masses(count_mass) = isotope_masses(loop2) - intensity(count_mass) = intensity(count_mass) + 1 * exact_intensity(loop2) & - * abs(chrg) - endif - - enddo outer - - !if(allocated(intensity) .and. allocated(save_int)) then - ! deallocate (save_int) - ! allocate (save_int(count_mass)) - - ! do i = 1, count_mass - ! save_int(i)=intensity(i) - ! enddo - - ! deallocate (intensity) - ! allocate ( intensity(count_mass)) - - ! do i = 1, count_mass - ! intensity(i) = save_int(i) - ! enddo - !endif - - !if(allocated(list_masses) .and. allocated(save_list)) then - ! deallocate (save_list) - ! allocate (save_list(count_mass)) - - ! do i = 1, count_mass - ! save_list(i)=list_masses(i) - ! enddo - - ! deallocate (list_masses) - ! allocate ( list_masses(count_mass)) - - ! do i = 1, count_mass - ! list_masses(i) = save_list(i) - ! enddo - ! write(*,*) - !endif - !deallocate (save_list) - !deallocate (save_int) - - ! do i = 1, count_mass - ! write(*,*) i,list_masses(i),intensity(i) - !write(*,*) i,intensity(i) - ! enddo - - end subroutine check_entries - -end module count_entries diff --git a/meson.build b/meson.build index 585ff03..b0feb66 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,9 @@ # This build file is part of the PlotMS program # for plotting QCxMS files -project('PlotMS', +project('plotms', 'fortran', - version: '6.0', + version: '6.2', license: 'LGPL-3.0-or-later', meson_version: '>=0.55', default_options: [ @@ -11,19 +11,42 @@ project('PlotMS', 'default_library=static', ], ) +install = not (meson.is_subproject() and get_option('default_library') == 'static') -srcs = ['main.f90', - 'isotopes.f90', - 'boxmuller.f90', - 'entries.f90', - 'readcommon.f90', - 'readl.f90', - 'xtb_mctc_accuracy.f90', - 'xtb_mctc_symbols.f90', - 'xtb_mctc_resize.f90' - ] + +prog = [] +srcs = [] +exe_deps = [] + +# Collect sources +subdir('config') +subdir('src') #plotms_exe = executable( -executable('plotms',srcs) +#executable('plotms',srcs) +# +# Create library target +plotms_lib = library( + meson.project_name(), + sources: srcs, + dependencies: exe_deps, +) + +# Export as dependency +plotms_inc = plotms_lib.private_dir_include() +plotms_dep = declare_dependency( + link_with: plotms_lib, + include_directories: plotms_inc, + dependencies: exe_deps, +) + + +# Create executable target +plotms_exe = executable( + meson.project_name(), + sources: prog, + dependencies: plotms_dep, + install: install, +) diff --git a/boxmuller.f90 b/src/boxmuller.f90 similarity index 100% rename from boxmuller.f90 rename to src/boxmuller.f90 diff --git a/src/entries.f90 b/src/entries.f90 new file mode 100644 index 0000000..7dd1927 --- /dev/null +++ b/src/entries.f90 @@ -0,0 +1,91 @@ +module count_entries + use xtb_mctc_accuracy, only: wp + implicit none + + contains + + subroutine check_entries(index_mass, isotope_masses, exact_intensity, & + list_masses, intensity, count_mass, chrg) + + integer :: loop, loop2, index_mass + integer :: count_mass + integer :: sum_index, i + integer :: check + + !real(wp) :: intensity(index_mass) + real(wp) :: xmass + real(wp) :: isotope_masses(index_mass) + real(wp) :: exact_intensity(index_mass) + real(wp) :: chrg + + real(wp) :: list_masses(10000) + real(wp) :: intensity(10000) + + real(wp) :: mass_diff + + !real(wp), allocatable :: list_masses(:) + !real(wp), allocatable :: save_list(:) + !real(wp), allocatable :: save_int(:) + !real(wp), allocatable :: intensity(:) + !real (wp) :: save_list(1000) + !real (wp) :: save_list(1000) + + logical :: there = .true. + + xmass = 0 + loop = 0 + loop2 = 0 + check = 0 + !count_mass = count_mass + index_mass + if(count_mass == 0)then + sum_index = index_mass + else + sum_index = count_mass + index_mass + endif + + + !> loop over the list from the isotope subroutine +outer: do loop2 = 1, index_mass + loop = 0 + + !> loop over all entries of new (check) list +inner: do + loop = loop + 1 + mass_diff = abs(list_masses(loop) - isotope_masses(loop2)) + + if ( list_masses(loop) == 0.0_wp ) then + there = .false. + !write(*,*) 'NULL' + exit inner + + !elseif ( list_masses(loop) == isotope_masses(loop2) ) then + elseif ( mass_diff < 1.0d0-10 .or. mass_diff == 0.0_wp) then + there = .true. + intensity(loop) = intensity(loop) + 1 * exact_intensity(loop2) & + * abs(chrg) + if (loop2 < index_mass ) cycle outer + if (loop2 == index_mass ) exit inner + + + !>> false if not in list, store + !elseif ( list_masses(loop) /= isotope_masses(loop2) ) then + elseif ( mass_diff > 1.0d0-10 ) then + there = .false. + if (loop == sum_index ) exit inner + endif + enddo inner + + + if ( .not. there ) then + count_mass = count_mass + 1 + list_masses(count_mass) = isotope_masses(loop2) + intensity(count_mass) = intensity(count_mass) + 1 * exact_intensity(loop2) & + * abs(chrg) + endif + + enddo outer + + + end subroutine check_entries + +end module count_entries diff --git a/isotopes.f90 b/src/isotopes.f90 similarity index 95% rename from isotopes.f90 rename to src/isotopes.f90 index 3143839..d20c768 100644 --- a/isotopes.f90 +++ b/src/isotopes.f90 @@ -16,7 +16,7 @@ module isotope_pattern contains subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & - no_isotopes, index_mass, exact_intensity, isotope_masses) + no_isotopes, index_mass, exact_intensity, isotope_masses, z_chrg) integer :: ntot,iat_save(*),maxatm integer :: niso(200) @@ -27,6 +27,7 @@ subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & ! integer :: tmp_intensity integer :: store_int(1000) integer :: mzmin + integer :: z_chrg real(wp) :: rnd(nrnd,maxatm) real(wp) :: r,sum_prob @@ -34,7 +35,6 @@ subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & real(wp) :: list_masses(nrnd) real(wp),allocatable :: isotope_masses(:) - real(wp) :: save_mass(nrnd) real(wp) :: current_mass real(wp), allocatable :: exact_intensity(:) @@ -126,6 +126,19 @@ subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & prob(17,2) = 24.24_wp massiso(17,1) = 34.968852_wp massiso(17,2) = 36.965902_wp + + ! 20 Ca (Calcium) + niso(20) = 5 + prob(20,1) = 96.94_wp + prob(20,2) = 0.65_wp + prob(20,3) = 0.14_wp + prob(20,4) = 2.09_wp + prob(20,5) = 0.18_wp + massiso(20,1) = 39.962591_wp + massiso(20,2) = 41.958618_wp + massiso(20,3) = 42.958766_wp + massiso(20,4) = 43.955482_wp + massiso(20,5) = 45.953688_wp ! 22 Ti (Titanium) niso(22) = 5 @@ -344,6 +357,13 @@ subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & prob(53,1) = 100.00_wp massiso(53,1) = 126.904473_wp + ! 57 La (Lanthanum) + niso(57) = 2 + prob(57,1) = 0.09_wp + prob(57,2) = 99.91_wp + massiso(57,1) = 137.907112_wp + massiso(57,2) = 138.909477_wp + ! 74 W (Tungsten) niso(74) = 5 prob(74,1) = 0.12_wp @@ -414,7 +434,6 @@ subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & ! postprocessing starts here prob = prob * 0.01_wp - save_mass = 0.0_wp list_masses = 0.0_wp index_mass = 0 loop = 0 @@ -463,7 +482,8 @@ subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & enddo xmass = xmass + x enddo - current_mass = xmass !/ real(abs(z_chrg),wp) + + current_mass = xmass / real(abs(z_chrg),wp) there = .true. if ( current_mass > mzmin ) then @@ -519,7 +539,7 @@ subroutine isotope(counter, mzmin, ntot, iat_save, maxatm, rnd, & xmass = xmass + x enddo - current_mass = xmass !/ float(abs(z_chrg)) + current_mass = xmass / float(abs(z_chrg)) there = .true. !> only values larger than user input diff --git a/main.f90 b/src/main.F90 similarity index 60% rename from main.f90 rename to src/main.F90 index c74deb9..1291e23 100644 --- a/main.f90 +++ b/src/main.F90 @@ -35,9 +35,12 @@ program plotms use count_entries, only: check_entries use isotope_pattern, only: isotope + use version, only: print_version use qcxms_boxmuller, only: vary_energies - use qcxms_readcommon + use readcommon + use mctc_env, only : error_type, get_argument use xtb_mctc_accuracy, only: wp + use xtb_mctc_convert use xtb_mctc_symbols, only: toSymbol implicit none @@ -58,6 +61,7 @@ program plotms integer :: sorted_index integer :: list_length integer :: exp_entries + integer :: iarg, narg real(wp) :: xx(100),tmax,r,rms,norm,cthr,cthr2 real(wp) :: chrg,chrg2,checksum,score @@ -78,21 +82,43 @@ program plotms !real(wp), allocatable :: intensity(:) real(wp) :: list_masses(10000) real(wp) :: intensity(10000) + real(wp) :: mmax - logical :: sel,verbose,exdat,small + logical :: sel,verbose,small + logical :: expdat, nistdat logical :: ex,ex1,ex2,ex3,ex4 logical :: noIso, Plasma - logical :: args = .false. ! fname= or result file, xname contains the mass.agr plot file - character(len=80) :: arg(10) character(len=80) :: xname character(len=:), allocatable :: fname,fname1,fname2,fname3,fname4 character(len=4096), external :: fullpath - - verbose = .false. + character(len=:), allocatable :: exp_dat + character(len=:), allocatable :: arg + + !!!!!!!!!!!!!!!!!!!!! + integer :: sm + integer, allocatable :: int_exp(:), int_thr(:) + integer :: store_check_list + integer :: new_length_thr, new_length_exp + real(wp) :: store_intensities + real(wp), allocatable :: added_thr_intensities(:), added_exp_intensities(:) + real(wp), allocatable :: added_thr_masses(:), added_exp_masses(:) + real(wp) :: store_thr_int(1000) + real(wp) :: store_exp_int(1000) + real(wp) :: store_thr_mass(1000) + real(wp) :: store_exp_mass(1000) + !real(wp) :: store_check_list + !integer, allocatable :: added_masses(:) + !integer, allocatable :: exp_mass (:) + !integer :: store_mass(1000) + !!!!!!!!!!!!!!!!!!!!! + + expdat = .false. + verbose = .false. small = .false. isec = 0 + iarg = 0 norm = 1.0 nagrfile = 410 mzmin = 10 @@ -115,72 +141,105 @@ program plotms !xname='~/.mass_raw.agr' !xname='~/.mass_jay.agr' fname='' - - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! start loop reading arguments - do i=1,9 - arg(i)=' ' - call get_command_argument(i,arg(i)) - if (arg(i) /= ' ') args = .true. - enddo - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + exp_dat="exp.dat" ! start loop processing aruments ! comand line parameters - ! -a count charges from -1000 (cthr) to cthr2 - ! -v print spectra "mass % intensity counts Int. exptl" to stdout; - ! with "Int. exptl" (experimental) taken from exp.dat but not all exp peaks are exported - ! if no theoretical counterpart exists - ! -f filename or -f - ! -t couting ions with charge x to y (give the value, e.g. "-t 1 2" for charge 1 to 2) - ! -w broadening the charges by an SD - ! -s Take only secondary and tertiary fragmentations (give the value, e.g. "-s 2" for secondary) - ! -m set minimum value for m/z, so 100% value will be calc. for higher values (x-axis) - ! -i set the minimum rel. intensity at which the signals are counted (y-axis) - ! -p DO NOT calculate isotope pattern - - do i = 1, 9 - if(index(arg(i),'-a') /= 0) cthr = -1000.0_wp - if(index(arg(i),'-v') /= 0) verbose = .true. - if(index(arg(i),'-f') /= 0) fname = arg(i+1) - if(index(arg(i),'-p') /= 0) noIso = .true. - - ! if = 0, unity intensities are regarded - if(index(arg(i),'-t') /= 0) then - call readl(arg(i+1),xx,nn) - cthr=xx(1) - endif - - ! set width distr. of boltz sampling - if(index(arg(i),'-w') /= 0) then - call readl(arg(i+1),xx,nn) - chrg_wdth=xx(1) - endif - - ! set number of cascading runs - if(index(arg(i),'-s') /= 0) then - call readl(arg(i+1),xx,nn) - isec=int(xx(1)) - endif - - ! set minimum mass - if(index(arg(i),'-m') /= 0)then - call readl(arg(i+1),xx,nn) - mzmin=int(xx(1)) - endif - - ! get initial charge of system - if(index(arg(i),'-c') /= 0)then - call readl(arg(i+1),xx,nn) - total_charge = real(xx(1),wp) - endif - - ! set the minimum intensity - if(index(arg(i),'-i') /= 0)then - call readl(arg(i+1),xx,nn) - min_intensity = real(xx(1),wp) - endif + !> -a count charges from -1000 (cthr) to cthr2 + !> -v print spectra "mass % intensity counts Int. exptl" to stdout; + ! with "Int. exptl" (experimental) taken from exp.dat but not all + ! exp peaks are exported if no theoretical counterpart exists + !> -f filename or -f + !> -t couting ions with charge x to y (give the value, e.g. "-t 1 2" + ! for charge 1 to 2) + !> -w broadening the charges by an SD + !> -s Take only secondary and tertiary fragmentations (give the value, + ! e.g. "-s 2" for secondary) + ! -m set minimum value for m/z, so 100% value will be calc. for higher values (CID) + ! -p calculate NO isotope pattern + ! -i set minimum intensity that is considered in rel. intensity percent + ! -e provide exp. input file (in .csv format) + + narg = command_argument_count() + + do while(iarg < narg) + iarg = iarg + 1 + call get_argument(iarg, arg) + + select case(arg) + !if(index(arg(i),'-a') /= 0) cthr = -1000.0_wp + !if(index(arg(i),'-v') /= 0) verbose = .true. + !if(index(arg(i),'-f') /= 0) fname = arg(i+1) + !if(index(arg(i),'-p') /= 0) noIso = .true. + !if(index(arg(i),'-e') /= 0) exp_dat = arg(i+1) + + + case("-a") + cthr = -1000.0_wp + case("-v","--verbose") + verbose = .true. + case("-p","--noisotope") + noIso = .true. + + case("-t","--unity") + iarg = iarg + 1 + call get_argument(iarg, arg) + call readl(arg,xx,nn) + cthr = real(xx(1),wp) + + !> -f filename or -f + case("-f","--file") + iarg = iarg + 1 + call get_argument(iarg, arg) + fname = arg + + ! -e provide exp. input file (in .csv format) + case("-e","--exp") + iarg = iarg + 1 + call get_argument(iarg, arg) + exp_dat = arg + + ! set width distr. of boltz sampling + case("-w","--width") + iarg = iarg + 1 + call get_argument(iarg, arg) + call readl(arg,xx,nn) + chrg_wdth = real(xx(1),wp) + + ! set number of cascading runs + case("-s","--cascades") + iarg = iarg + 1 + call get_argument(iarg, arg) + call readl(arg,xx,nn) + isec = int(xx(1)) + + ! set minimum mass + case("-m","--min") + iarg = iarg + 1 + call get_argument(iarg, arg) + call readl(arg,xx,nn) + mzmin=int(xx(1)) + + ! set minimum intensity that is considered for the output + ! default: >1%. For all (>0%): set = 0 + case("-i","--mzmin") + iarg = iarg + 1 + call get_argument(iarg, arg) + call readl(arg,xx,nn) + min_intensity = real(xx(1),wp) + + ! get initial charge of system <- not used anymore + case("-c","--charge") + iarg = iarg + 1 + call get_argument(iarg, arg) + call readl(arg,xx,nn) + total_charge = real(xx(1),wp) + + + case default + error stop ' -- Unrecognized Keyword -- ' + end select enddo @@ -238,29 +297,26 @@ program plotms spec = 1 ! EI endif + call print_version - write(*,*) - write(*,'(6x,''* * * * * * * * * * * * * * * * * *'')') - write(*,'(6x,''* P l o t M S *'')') - write(*,'(6x,''* * * * * * * * * * * * * * * * * *'')') - write(*,'(6x,''* QCxMS spectra plotting tool *'')') - write(*,'(6x,''* - v. 6.0.1 - *'')') - write(*,'(6x,''* 04. Dec 2021 *'')') - write(*,'(6x,''* * * * *'')') - write(*,'(6x,''* S. Grimme *'')') - write(*,'(6x,''* J. Koopman *'')') - write(*,'(6x,''* * * * * * * * * * * * * * * * * *'')') - write(*,'(6x,'' Contributor: C.Bauer, T.Kind '')') - write(*,*) - write(*,'(6x,'' -> Reading file: '',(a) )')trim(fname) + write(*,'(6x,''-> Reading .res file: '',2x,(a))')trim(fname) + + !> write out if compared + if ( exp_dat == 'exp.dat') then + inquire(file='exp.dat',exist=expdat) + elseif (exp_dat /= 'exp.dat') then + inquire(file=exp_dat,exist=expdat) + endif + if (expdat) write(*,'(6x,''-> Experimental file: '',2x, (a))') exp_dat + + !> give info about xmgrace file (if verbose) if (verbose) write(*,'(6x '' -> xmgrace file body '',(a) )')trim(xname) write(*,*) - write(*,*) ! ------------------------------------------------------------------------------------------------------! ! execute arguments - if (args) then + if (narg > 0 ) then write(*,'(50(''!''))') write(*,'('' The following settings are being used : '')') write(*,*) @@ -291,11 +347,11 @@ program plotms endif ! -t (choose if unity intensities or normal) - if(cthr >= 0)then - write(*,'('' - couting ions with charge from '',f4.1,'' to '',f4.1)') cthr,cthr2 + total_charge - else - write(*,'('' - counting all fragments with unity charge (frag. overview)'')') - endif + !if(cthr >= 0)then + ! write(*,'('' - couting ions with charge from '',f4.1,'' to '',f4.1)') cthr,cthr2 + total_charge + !else + ! write(*,'('' - counting all fragments with unity charge (frag. overview)'')') + !endif write(*,*) write(*,'(50(''!''))') endif @@ -322,12 +378,12 @@ program plotms !write(*,*) 'COUNTER', counter if (spec == 1) then !EI - read(io_spec,*,iostat=io) chrg2, irun, jsec, nf, atm_types, (iat(kk),nat(kk), kk = 1, atm_types) + read(io_spec,*,iostat=io) chrg2, z_chrg, irun, jsec, nf, atm_types, (iat(kk),nat(kk), kk = 1, atm_types) if(io<0) exit !EOF if(io>0) stop ' -- Fail in read -- ' !fail elseif(spec == 2) then !CID - read(io_spec,*,iostat=io) chrg2, irun, jcoll, jsec, nf, atm_types, (iat(kk), nat(kk), kk = 1, atm_types) + read(io_spec,*,iostat=io) chrg2, z_chrg, irun, jcoll, jsec, nf, atm_types, (iat(kk), nat(kk), kk = 1, atm_types) if(io<0) exit !EOF if(io>0) stop ' -- Fail in read -- ' !fail @@ -338,12 +394,27 @@ program plotms if (isec > 0 .and. isec /= jsec) cycle !check tertiary,etc fragmentation (isec) - - if (chrg2 > cthr) then !default: chrg2 > 0.001 - ntot = sum(nat(1:atm_types)) - if ( ntot > maxatm ) maxatm = ntot !get highest number of atoms in fragment + + if ( z_chrg > 0 ) then + if (chrg2 > cthr) then !default: chrg2 > 0.001 + ntot = sum(nat(1:atm_types)) + if ( ntot > maxatm ) maxatm = ntot !get highest number of atoms in fragment + if ( z_chrg > int(total_charge) ) total_charge = real( z_chrg, wp ) + endif + elseif ( z_chrg < 0 ) then + if (chrg2 < -1.0_wp*cthr) then !default: chrg2 > 0.001 + ntot = sum(nat(1:atm_types)) + if ( ntot > maxatm ) maxatm = ntot !get highest number of atoms in fragment + if ( z_chrg < int(total_charge) ) total_charge = real( z_chrg, wp ) + endif endif + + !if (chrg2 > cthr) then !default: chrg2 > 0.001 + ! ntot = sum(nat(1:atm_types)) + ! if ( ntot > maxatm ) maxatm = ntot !get highest number of atoms in fragment + !endif + i = i + 1 !count number of single fragments with charge > chrt if ( irun > maxrun ) maxrun = irun !save highest run number @@ -405,12 +476,12 @@ program plotms !> read the fragment entry from qcxms.res line by line if (spec == 1) then !EI - read(io_spec,*,iostat=io) chrg2, irun, jsec, nf, atm_types, (iat(kk),nat(kk),kk=1,atm_types) + read(io_spec,*,iostat=io) chrg2, z_chrg, irun, jsec, nf, atm_types, (iat(kk),nat(kk),kk=1,atm_types) if(io<0) exit !EOF if(io>0) stop !fail elseif(spec == 2) then !CID - read(io_spec,*,iostat=io) chrg2, irun, jcoll, jsec, nf, atm_types, (iat(kk),nat(kk),kk=1,atm_types) + read(io_spec,*,iostat=io) chrg2, z_chrg, irun, jcoll, jsec, nf, atm_types, (iat(kk),nat(kk),kk=1,atm_types) if(io<0) exit !EOF if(io>0) stop !fail @@ -427,11 +498,19 @@ program plotms if ( cthr < 0 ) chrg = total_charge !> count the moment where fragments were created (first/second MD or collision...) + !if ( (z_chrg > 0 .and. chrg > cthr) .or. (z_chrg < 0 .and. chrg < -1* cthr) ) then ! default: yes if ( abs(chrg) > cthr ) then ! default: yes sel = .true. ial = ial + 1 !count total amount of fragmentations jal(jsec) = jal(jsec) + 1 !count amount of first, sec., tert., ... fragmentations + !> if not fragmented (i.e. smaller/larger than total_charge) count it to kal(1) + !if ( abs(chrg) < abs(total_charge) ) then + ! kal(jcoll) = kal(jcoll) + 1 !count the collisions + !else + ! kal(1) = kal(jcoll) + 1 !no fragmentation + !endif + !> count the collision at which most framentation occured if ( spec == 2 ) then @@ -466,7 +545,7 @@ program plotms !> compute isotope patterns via monte carlo and save intensites ! of all possible combinations call isotope (counter, mzmin, ntot, iat_save, maxatm, rnd, & - noIso, index_mass, exact_intensity, isotope_masses) + noIso, index_mass, exact_intensity, isotope_masses, z_chrg) !>> distribute charge (if set as input) if(chrg_wdth > 1.0d-6) chrg = vary_energies(chrg,chrg_wdth) @@ -488,10 +567,13 @@ program plotms list_length=0 + !if (z_chrg == 1) extra_mass = z_chrg * amutoau !init for electron mass + !> find the highest intensity tmax = maxval(intensity) - intensity(:) = intensity(:)/tmax * 1000.0_wp + !> normalize to 100% + intensity(:) = intensity(:)/tmax * 100.0_wp allocate(reduced_masses(count_mass), reduced_intensities(count_mass)) @@ -567,18 +649,18 @@ program plotms imax=0 imin=0 - inquire(file='exp.dat',exist=exdat) + inquire(file='nist.dat',exist=nistdat) + - if(exdat) then - write(*,*) 'Reading exp.dat ...' - open( file = 'exp.dat', newunit = io_exp, status = 'old') + if(nistdat) then + write(*,*) 'Reading nist.dat ...' + open( file = 'nist.dat', newunit = io_exp, status = 'old') - !> read the experimental (NIST) file + !> read the experimental (NIST/JCAMP) file rd: do read(io_exp,'(a)',iostat=iocheck)line if (iocheck < 0) exit rd - !> read the maximum intensity of exp. if(index(line,'##MAXY=') /= 0)then line(7:7)=' ' @@ -615,22 +697,89 @@ program plotms kk = kk + 1 exp_mass(kk) = xx(2 * k - 1) exp_int (kk) = xx(2 * k) - if(exp_mass(kk) > imax) imax = exp_mass(kk) - if(exp_int (kk) < imin) imin = exp_int (kk) + !if(exp_mass(kk) > imax) imax = exp_mass(kk) + !if(exp_int (kk) < imin) imin = exp_int (kk) enddo enddo endif enddo rd - + + imax = maxval(exp_mass) + !imin = minval(exp_mass) + + mmax = maxval(exp_int) + + ! bring exp to the right order of magnitude + do i = 1, exp_entries + exp_int(i) = exp_int(i) / norm + enddo + endif + + !!!!!!!!!!!!!!!!!!!!! + !> if it is a csv file + + + + if (expdat) then + write(*,*) 'Reading ', exp_dat,' ...' + open( file = exp_dat, newunit = io_exp, status = 'old') + + exp_entries = 0 + + !>> count the entries + do + read(io_exp,'(a)',iostat=iocheck)!line + if (iocheck < 0) exit + + exp_entries = exp_entries + 1 + enddo + + rewind(io_exp) + + !>> allocate memory + allocate (exp_mass(exp_entries), & + & exp_int(exp_entries)) + + kk = 0 + iocheck=0 + + !>> count the intensities and masses + do kk = 1, exp_entries + read(io_exp,'(a)',iostat=iocheck)line + if (iocheck < 0) exit + + do k=1,80 + if(line(k:k) == ',') then + line(k:k)=' ' + endif + enddo + + call readl(line,xx,nn) + + exp_mass(kk) = xx(1) + exp_int (kk) = xx(2) + enddo close(io_exp) + + imax = maxval(exp_mass) + !imin = minval(exp_mass) + + mmax = maxval(exp_int) + + do kk = 1, exp_entries + exp_int (kk) = (exp_int(kk) / mmax) *100.0_wp + enddo + + endif + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !> get the minimum m/z value that is to be plotted imin = mzmin !> get the maximum m/z value that is to be plotted - imax = nint(maxval(sorted_masses)) + if (imax < nint(maxval(sorted_masses))) imax = nint(maxval(sorted_masses)) ! counts of structures in the 100% peak write(*,*) 'Theoretical counts in 100 % signal:', idint(tmax) @@ -751,22 +900,24 @@ program plotms !> Write out the results into the mass.agr file ! do i = 1, list_length ! count_mass - write(io_mass,*) sorted_masses(i), sorted_intensities(i)/10.0_wp + write(io_mass,*) sorted_masses(i), sorted_intensities(i) enddo write(io_mass,*)'&' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! TK establish again if exdat (experimental JCAMPDX) exists + ! TK establish again if expdat (experimental JCAMPDX) exists ! TK exp_mass(i) - contains the exp masses and exp_int(i) contains the abundance ! TK exp_entries contains number of experimental spectra - if(exdat)then + if(nistdat .or. expdat)then + write(io_mass,*)'@target G0.S2' write(io_mass,*)'@type bar' do i=1,exp_entries - write(io_mass,*) exp_mass(i),-exp_int(i)/norm + write(io_mass,*) exp_mass(i),-exp_int(i) enddo write(io_mass,*)'&' + endif endif !ex @@ -776,35 +927,169 @@ program plotms !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! compute deviation exp-theor. - if(exdat)then + if(expdat .or. nistdat)then rms = 0 nn = 0 kkk = 0 kkkk = 0 -ol: do i = 1, exp_entries -il: do j = 1, list_length - if ( exp_int(i)/norm > 5.0_wp )then !.and. sorted_intensities(j) > 1.0_wp )then - if ( exp_mass(i) == nint(sorted_masses(j))) then - r = sorted_intensities(j) - (exp_int(i) / norm) - !write(*,*) i, exp_mass(i), exp_int(i) - !write(*,*) j, sorted_masses(j), sorted_intensities(j) - !write(*,*) r + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !> this is for integer masses + !> calculate integer list of theor. masses + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + store_thr_int = 0 + + allocate(int_thr(list_length)) + + !> transfer to integer + do j = 1, list_length + int_thr(j) = nint(sorted_masses(j)) + enddo + + + new_length_thr = 0 + sm = 0 + + do i = 1, list_length + store_intensities = 0.0_wp + + store_check_list = int_thr(i) + + if(sm /= store_check_list) new_length_thr = new_length_thr + 1 + + do j = 1, list_length + if (store_check_list == int_thr(j))then + store_intensities = store_intensities + sorted_intensities(j) + endif + + enddo + + sm = store_check_list + + store_thr_int (new_length_thr) = store_intensities + store_thr_mass(new_length_thr) = store_check_list + + !write(*,*) store_check_list, new_length_thr, added_intensities(new_length_thr) + + enddo + + allocate(added_thr_intensities(new_length_thr), & + added_thr_masses(new_length_thr)) + + do i = 1, new_length_thr + added_thr_masses(i) = store_thr_mass(i) + added_thr_intensities(i) = (store_thr_int(i)/maxval(store_thr_int)*100.0_wp) + ! write(*,*) added_masses(i), added_intensities(i) + enddo + + + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !> for experimental masses in integer + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + store_exp_int = 0 + allocate(int_exp(exp_entries)) + + do j = 1, exp_entries + int_exp(j) = nint(exp_mass(j)) + enddo + + new_length_exp = 0 + sm = 0 + + do i = 1, exp_entries + store_intensities = 0.0_wp + + store_check_list = int_exp(i) + + if(sm /= store_check_list) new_length_exp = new_length_exp + 1 + + do j = 1, exp_entries + if (store_check_list == int_exp(j))then + store_intensities = store_intensities + exp_int(j) + endif + + enddo + + sm = store_check_list + + store_exp_int(new_length_exp) = store_intensities + store_exp_mass(new_length_exp) = store_check_list + + !write(*,*) store_check_list, new_length_exp, added_intensities(new_length_exp) + + enddo + + allocate(added_exp_intensities(new_length_exp), & + added_exp_masses(new_length_exp)) + + do i = 1, new_length_exp + added_exp_masses(i) = store_exp_mass(i) + added_exp_intensities(i) = (store_exp_int(i)/maxval(store_exp_int)*100.0_wp) + ! write(*,*) added_masses(i), added_intensities(i) + enddo + + +ol: do i = 1, new_length_exp +il: do j = 1, new_length_thr + if ( added_exp_intensities(i) > 5.0_wp )then + !if ( nint(exp_mass(i)) == added_masses(j) ) then + if ( added_exp_masses(i) == added_thr_masses(j) ) then + r = added_thr_intensities(j) - added_exp_intensities(i) kkk = kkk + 1 - if ( sorted_intensities(j) > 2.5_wp) kkkk = kkkk + 1 + if ( added_thr_intensities(j) > 2.5_wp) kkkk = kkkk + 1 rms = rms + abs(r) endif endif enddo il enddo ol +! new_length = 0 +! sm = 0 +! do i = 1, list_length +! +! store_check_list = sorted_masses(i) +! +! store_intensities = 0.0_wp +! +! do i = 1, list_length +! if (sorted_masses(i) - store_check_list <= 1.0d-2)then +! store_intensities = store_intensities + sorted_intensities(j) +! store_mass = store_intensities + sorted_intensities(j) +!! store_intensities = sorted_masses(i) - sorted_masses(i-1) +!! write(*,*) i, sorted_masses(i-1), sorted_masses(i), store_intensities +! endif +! enddo +! +! if(sm /= store_check_list) new_length = new_length + 1 +! sm = store_check_list +! +! stop +! +! +! +!ol: do i = 1, exp_entries +!il: do j = 1, list_length +! if ( exp_int(i) > 5.0_wp )then +! !if ( nint(exp_mass(i)) == added_masses(j) ) then +! if ( exp_mass(i) == sorted_masses(j) ) then +! r = added_intensities(j) - exp_int(i) +! kkk = kkk + 1 +! if ( added_intensities(j) > 2.5_wp) kkkk = kkkk + 1 +! rms = rms + abs(r) +! endif +! endif +! enddo il +! enddo ol + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! write(*,*)'MAD(exptl./theor.) = ', rms / kkk write(*,*)'# exptl. > 5 % = ', kkk write(*,*)'% correctly found = ', 100 * float(kkkk)/float(kkk) - endif close(io_spec) close(io_raw) @@ -813,32 +1098,32 @@ program plotms close(io_jcamp) ! compute mass spectral match score - if(exdat)then score = 0.0_wp - ! bring exp to the right order of magnitude - do i = 1, exp_entries - exp_int(i) = exp_int(i) / norm - enddo - write(*,*) - write(*,*) ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ' - write(*,*) ' - NIST mass comparison not supported in v6.0 - ' - write(*,*) ' - no automatic score comparison -' - stop ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ' + !write(*,*) + !write(*,*) ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ' + !write(*,*) ' - NIST mass comparison not supported in v6.0 - ' + !write(*,*) ' - no automatic score comparison -' + !write(*,*) ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ' + !stop ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ' - ! call match(sorted_masses, sorted_intensities, list_length, & - ! exp_entries, exp_mass, exp_int,score,tmax) + !call match_accurate(sorted_masses, sorted_intensities, list_length, & + ! exp_entries, exp_mass, exp_int,score,tmax) + !call match(added_masses, added_intensities, new_length, & + ! exp_entries, exp_mass, exp_int,score,tmax) + call match(added_thr_masses, added_thr_intensities, new_length_thr, & + new_length_exp, added_exp_masses, added_exp_intensities,score,tmax) write(*,*) write(*,*)"!!!!!!!!!!!!!!!!!!!!!!! " write(*,*)" Matching score: " - ! write(*,'(6F10.3)') score + write(*,'(6F10.3)') score write(*,*)"!!!!!!!!!!!!!!!!!!!!!!! " write(*,*) write(*,*)"Composite match score, see " write(*,*)"Stein, S. E.; Scott, D. R. J. Am. Soc. Mass Spectrom. 1994, 5, 859-866" - write(*,*)"For our implementation, see " - write(*,*)"Bauer, C. A.; Grimme,S. J. Phys. Chem. A 2014, 118, 11479-11484" + !write(*,*)"For our implementation, see " + !write(*,*)"Bauer, C. A.; Grimme,S. J. Phys. Chem. A 2014, 118, 11479-11484" endif @@ -848,7 +1133,7 @@ end program plotms !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -subroutine match(sorted_masses, sorted_intensities, list_length, & +subroutine match(added_masses, added_intensities, new_length, & exp_entries, exp_mass, exp_int,score,tmax) use xtb_mctc_accuracy, only: wp implicit none @@ -856,7 +1141,9 @@ subroutine match(sorted_masses, sorted_intensities, list_length, & integer :: i,j integer :: pp !peak pair number pp integer :: numcalc ! number of calculated peaks - integer :: list_length, exp_entries + integer :: new_length, exp_entries +! integer :: entry, entry2 + integer :: cnt !real(wp) :: tmass(10000) !real(wp) :: iexp(2,10000) @@ -864,118 +1151,144 @@ subroutine match(sorted_masses, sorted_intensities, list_length, & real(wp) :: exp_int(exp_entries) real(wp) :: score real(wp) :: tmax - real(wp) :: w(2,10000) !weighted vectors + real(wp),allocatable :: w_exp(:) !weighted vectors + real(wp),allocatable :: w_thr(:) !weighted vectors + !real(wp) :: w(2,10000) !weighted vectors + !real(wp), allocatable :: w_exp(:) !weighted vectors + !real(wp), allocatable :: w_thr(:) !weighted vectors real(wp) :: sum1,sum2,sum3,sum4,dot,fr - real(wp) :: norm(2) - real(wp) :: p(2,10000) ! peak pair matrix - real(wp) :: sorted_masses(list_length), sorted_intensities(list_length) + !real(wp) :: norm(2) + real(wp) :: norm + !real(wp) :: p(2,10000) ! peak pair matrix + real(wp), allocatable :: p(:,:) ! peak pair matrix + real(wp) :: added_masses(new_length) + real(wp) :: added_intensities(new_length) + !!!!!!!!!!!!!!! + !integer :: exp_mass(exp_entries) + !integer :: added_masses(new_length) + !!!!!!!!!!!!!!! + + +! numcalc = new_length +! entry = 0 + + !allocate( w_exp(exp_entries), & + ! w_thr(new_length)) + + !> weighted spectral vectors, m**3, int**0.6 scaling + !w = 0.0_wp + + pp = 0 + !> get weighting of experimental spectrum + allocate(w_exp(exp_entries)) + norm = 0.0_wp + do i = 1, exp_entries + w_exp(i) = exp_mass(i)**2 * exp_int(i)**0.6_wp + norm = norm + w_exp(i)**2 + enddo - numcalc=0 - - !do i = 1, 10000 - ! if ( tmass(i) /= 0.0_wp ) numcalc = numcalc + 1 - !enddo + norm = sqrt(norm) - numcalc = list_length - - !weighted spectral vectors, m**3, int**0.6 scaling - w = 0.0_wp - !do i = 1, 10000 - ! do j = 1, 10000 - ! if ( j == iexp(1, i) ) then - ! w(2,j) = j**2 * ( 1000.0_wp * tmass(j) / tmax )**0.6_wp ! changed this to 1000 <- check it! - ! w(1,j) = iexp(1,i)**2 * iexp(2,i)**0.6_wp - ! endif - ! enddo - !enddo - !do i = 1, 10000 -ol: do i = 1, exp_entries -il: do j = 1, list_length - if ( sorted_masses(j) == exp_mass(i) ) then - !w(2,j) = j**2 * ( 100.0_wp * tmass(j) / tmax )**0.6_wp ! changed this to 1000 <- check it! - w(2,j) = j**2 * ( sorted_intensities(j) )**0.6_wp ! changed this to 1000 <- check it! - w(1,j) = exp_mass(i)**2 * exp_int(i)**0.6_wp - endif - enddo il - enddo ol - - norm = 0.0_wp - do i = 1, 10000 - norm(1) = norm(1) + w(1,i)**2 - norm(2) = norm(2) + w(2,i)**2 + do i = 1, exp_entries + w_exp(i) = w_exp(i) / norm enddo - + + !> get weighting of calculated spectrum + allocate(w_thr(new_length)) + norm = 0.0_wp + do j = 1, new_length + w_thr(j) = (added_masses(j))**2 * ( added_intensities(j) )**0.6_wp + norm = norm + w_thr(j)**2 + enddo + norm = sqrt(norm) - - do i = 1, 10000 - w(1,i) = w(1,i) / norm(1) - w(2,i) = w(2,i) / norm(2) + + do j = 1, new_length + w_thr(j) = w_thr(j) / norm enddo + dot = 0.0_wp sum1 = 0.0_wp sum2 = 0.0_wp sum3 = 0.0_wp - do i = 1, 10000 - sum1 = sum1 + w(1,i) * w(2,i) - sum2 = sum2 + (w(1,i))**2 - sum3 = sum3 + (w(2,i))**2 - enddo + + do i = 1, exp_entries + do j = 1, new_length + if ( exp_mass(i) == added_masses(j)) then + pp = pp + 1 + sum1 = sum1 + w_exp(i) * w_thr(j) + sum2 = sum2 + (w_exp(i))**2 + sum3 = sum3 + (w_thr(j))**2 + endif + enddo + enddo + dot = sum1**2 / (sum2 * sum3) + !write(*,*) 'DOT', dot + deallocate(w_exp, w_thr) + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! masses and intensity scalement for the second term ! m**0 ! int**1 - - w=0.0_wp - - do i = 1, 10000 - !do j = 1, 10000 - do j = 1, list_length - if ( j == exp_mass(i) ) then - w(2,j) = sorted_intensities(j) - w(1,j) = exp_int(i) - endif - enddo + allocate(w_exp(exp_entries), w_thr(new_length)) + + norm=0.0_wp + + do i=1, exp_entries + !norm = norm + w_exp(i)**2 ! + exp_int(i) !w(1,i)**2 + norm = norm + exp_int(i)**2 !w(1,i)**2 enddo - - !calculate the norm - + + norm = sqrt(norm) + + do i=1, exp_entries + w_exp(i) = exp_int(i) / norm + enddo + + !!!!!!!!!!!!!!!!!!!! norm=0.0_wp - do i=1, 10000 - norm(1) = norm(1) + w(1,i)**2 - norm(2) = norm(2) + w(2,i)**2 + do j=1, new_length + norm = norm + added_intensities(j)**2 !w(2,i)**2 enddo - + norm = sqrt(norm) - do i = 1, 10000 - w(1,i) = w(1,i) / norm(1) - w(2,i) = w(2,i) / norm(2) + do j=1, new_length + w_thr(j) = added_intensities(j) / norm enddo - pp = 0 + !pp = 0 + !p = 0.0_wp sum4 = 0.0_wp fr = 0.0_wp - p = 0.0_wp - - do i = 1, 10000 - if ( (w(1,i) * w(2,i) ) /= 0) then - pp = pp + 1 - p(1,pp) = w(1,i) - p(2,pp) = w(2,i) - ! print*,p(1,i),p(2,i) - endif - enddo + cnt = 0 + + allocate (p(2,pp)) + + do i = 1, exp_entries + do j = 1, new_length + if ( exp_mass(i) == added_masses(j)) then + cnt = cnt + 1 + p(1,cnt) = w_exp(i) + p(2,cnt) = w_thr(j) + endif + enddo + enddo ! ardous loop call calcfr(pp,p,sum4) - if (pp == numcalc) sum4 = sum4 + 1.0_wp + if (pp == new_length) sum4 = sum4 + 1.0_wp + !write(*,*) 'SUM4', sum4 + !write(*,*) 'numcalc', numcalc + !write(*,*) 'numcalc', new_length fr = sum4 / float(pp) - score = (numcalc * dot + pp * fr) / (numcalc + pp) + score = (new_length * dot + pp * fr) / (new_length + pp) return end subroutine match @@ -995,7 +1308,7 @@ subroutine calcfr(pp,pair,sum4) sum4 = 0.0_wp - do i = 1, pp + do i = 2, pp if (abs((pair(1,i) * pair(2,i-1)) / (pair(1,i-1) * pair(2,i))) < 1.0_wp)then sum4 = sum4 + (pair(1,i) * pair(2,i-1)) / (pair(1,i-1) * pair(2,i)) diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..b3bddf8 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,21 @@ +# This file is part of PlotMS. + + +srcs += files( + 'isotopes.f90', + 'boxmuller.f90', + 'entries.f90', + 'version.f90', + 'readcommon.f90', + 'readl.f90', + 'xtb_mctc_accuracy.f90', + 'xtb_mctc_constants.f90', + 'xtb_mctc_convert.f90', + 'xtb_mctc_symbols.f90', + 'xtb_mctc_resize.f90' + ) + +prog += files( + 'main.F90', +) + diff --git a/readcommon.f90 b/src/readcommon.f90 similarity index 55% rename from readcommon.f90 rename to src/readcommon.f90 index 179c9a6..fcef392 100644 --- a/readcommon.f90 +++ b/src/readcommon.f90 @@ -1,8 +1,10 @@ -module qcxms_readcommon +!module qcxms_readcommon +module readcommon use xtb_mctc_accuracy, only: wp implicit none integer :: iocheck = 0 character(len=80) :: line -end module qcxms_readcommon +!end module qcxms_readcommon +end module readcommon diff --git a/readl.f90 b/src/readl.f90 similarity index 100% rename from readl.f90 rename to src/readl.f90 diff --git a/src/version.f90 b/src/version.f90 new file mode 100644 index 0000000..5993a91 --- /dev/null +++ b/src/version.f90 @@ -0,0 +1,23 @@ +module version + implicit none + + contains + +subroutine print_version + + write(*,*) + write(*,'(6x,''* * * * * * * * * * * * * * * * * *'')') + write(*,'(6x,''* P l o t M S *'')') + write(*,'(6x,''* * * * * * * * * * * * * * * * * *'')') + write(*,'(6x,''* QCxMS spectra plotting tool *'')') + write(*,'(6x,''* - v. 6.1.0 - *'')') + write(*,'(6x,''* 25. Jul 2022 *'')') + write(*,'(6x,''* * * * *'')') + write(*,'(6x,''* S. Grimme *'')') + write(*,'(6x,''* J. Koopman *'')') + write(*,'(6x,''* * * * * * * * * * * * * * * * * *'')') + write(*,'(6x,'' Contributor: C.Bauer, T.Kind '')') + write(*,*) + +end subroutine print_version +end module version diff --git a/xtb_mctc_accuracy.f90 b/src/xtb_mctc_accuracy.f90 similarity index 100% rename from xtb_mctc_accuracy.f90 rename to src/xtb_mctc_accuracy.f90 diff --git a/src/xtb_mctc_constants.f90 b/src/xtb_mctc_constants.f90 new file mode 100644 index 0000000..4976e72 --- /dev/null +++ b/src/xtb_mctc_constants.f90 @@ -0,0 +1,38 @@ +! This file is part of xtb. +! +! Copyright (C) 2017-2020 Stefan Grimme +! +! xtb is free software: you can redistribute it and/or modify it under +! the terms of the GNU Lesser General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! xtb is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU Lesser General Public License for more details. +! +! You should have received a copy of the GNU Lesser General Public License +! along with xtb. If not, see . + +module xtb_mctc_constants + use xtb_mctc_accuracy, only : wp + implicit none + private + real(wp),public,parameter :: pi = 3.1415926535897932384626433832795029_wp + real(wp),public,parameter :: pi4 = 3.1415926535897932384626433832795029_wp*4._wp +! √π + real(wp),public,parameter :: sqrtpi = sqrt(pi) +! 2×π + real(wp),public,parameter :: twopi = 2.0_wp * pi +! 4×π + real(wp),public,parameter :: fourpi = 4.0_wp * pi +! π/2 + real(wp),public,parameter :: pihalf = 0.5_wp * pi +! 2π/3 + real(wp),public,parameter :: twothirdpi = 2.0_wp * pi / 3.0_wp +! Boltzmann constant in Eh/K + real(wp),public,parameter :: kB = 3.166808578545117e-06_wp +! speed of light c in vacuum in a.u. + real(wp),public,parameter :: lightspeed = 137.0359990740_wp +end module xtb_mctc_constants diff --git a/src/xtb_mctc_convert.f90 b/src/xtb_mctc_convert.f90 new file mode 100644 index 0000000..266a713 --- /dev/null +++ b/src/xtb_mctc_convert.f90 @@ -0,0 +1,64 @@ +! This file is part of xtb. +! +! Copyright (C) 2017-2020 Stefan Grimme +! +! xtb is free software: you can redistribute it and/or modify it under +! the terms of the GNU Lesser General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! xtb is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU Lesser General Public License for more details. +! +! You should have received a copy of the GNU Lesser General Public License +! along with xtb. If not, see . + +module xtb_mctc_convert + use xtb_mctc_accuracy, only : wp + use xtb_mctc_constants + implicit none + private +! convert bohr (a.u.) to Ångström and back + real(wp),public,parameter :: autoaa = 0.52917726_wp + real(wp),public,parameter :: aatoau = 1.0_wp/autoaa +! convert Hartree to eV and back + real(wp),public,parameter :: autoev = 27.21138505_wp + real(wp),public,parameter :: evtoau = 1.0_wp/autoev +! convert Hartree to kcal/mol and back + real(wp),public,parameter :: autokcal = 627.50947428_wp + real(wp),public,parameter :: kcaltoau = 1.0_wp/autokcal +! convert Hartree to kJ/mol and back + real(wp),public,parameter :: autokj = 2625.49964038_wp + real(wp),public,parameter :: kjtoau = 1.0_wp/autokj +! convert Hartree to reciproce centimeters/wavenumbers and back + real(wp),public,parameter :: autorcm = 219474.63067_wp + real(wp),public,parameter :: autowav = autorcm + real(wp),public,parameter :: rcmtoau = 1.0_wp/autorcm + real(wp),public,parameter :: wavtoau = 1.0_wp/autowav +! convert Hartree to nanometers and back + real(wp),public,parameter :: autonm = 45.56335266_wp + real(wp),public,parameter :: nmtoau = 1.0_wp/autonm +! masses +! amu -> kg :: conversion from atomic mass units to kg +! me -> kg :: electron mass (a.u.) in kg +! amu -> au :: conversion from a.u. to amu + real(wp),public,parameter :: amutokg = 1.660539040e-27_wp + real(wp),public,parameter :: kgtoamu = 1.0_wp/amutokg + real(wp),public,parameter :: metokg = 9.10938356e-31_wp + real(wp),public,parameter :: kgtome = 1.0_wp/metokg + real(wp),public,parameter :: amutoau = amutokg*kgtome + real(wp),public,parameter :: autoamu = kgtoamu*metokg +! femtosectons to atomic time units + real(wp),public,parameter :: fstoau = 41.3413733365614_wp + real(wp),public,parameter :: autofs = 1.0_wp/fstoau +! Coulomb to atomic charge units (electrons) + real(wp),public,parameter :: autoc = 1.6021766208e-19_wp + real(wp),public,parameter :: ctoau = 1.0_wp/autoc +! Debye to atomic units + real(wp),public,parameter :: autod = autoc * lightspeed * autoaa**2 * fstoau * 1.0e+16_wp + real(wp),public,parameter :: dtoau = 1.0_wp/autod +! convert meter/seconds to au units + real(wp),public,parameter :: mstoau = 1.0d0/2.18769126364D+06 +end module xtb_mctc_convert diff --git a/xtb_mctc_resize.f90 b/src/xtb_mctc_resize.f90 similarity index 100% rename from xtb_mctc_resize.f90 rename to src/xtb_mctc_resize.f90 diff --git a/xtb_mctc_symbols.f90 b/src/xtb_mctc_symbols.f90 similarity index 100% rename from xtb_mctc_symbols.f90 rename to src/xtb_mctc_symbols.f90 diff --git a/subprojects/.gitignore b/subprojects/.gitignore new file mode 100644 index 0000000..63ea916 --- /dev/null +++ b/subprojects/.gitignore @@ -0,0 +1 @@ +/*/ diff --git a/subprojects/mctc-lib.wrap b/subprojects/mctc-lib.wrap new file mode 100644 index 0000000..6b71458 --- /dev/null +++ b/subprojects/mctc-lib.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = mctc-lib +url = https://github.com/grimme-lab/mctc-lib +revision = v0.2.3