Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide support for JSON output in tblite flavour #29

Merged
merged 11 commits into from
Aug 7, 2024
14 changes: 12 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ env:
cmake
ninja
gcovr
numpy
pytest
MACOS_BASEKIT_URL: >-
https://registrationcenter-download.intel.com/akdlm/irc_nas/17969/m_BaseKit_p_2021.3.0.3043.dmg
MACOS_HPCKIT_URL: >-
Expand Down Expand Up @@ -236,18 +238,26 @@ jobs:
meson test -C ${{ env.BUILD_DIR }} --print-errorlogs --no-rebuild
env:
OMP_NUM_THREADS: 1
MKL_NUM_THREADS: 1

- name: Run validation tests
if: ${{ matrix.build == 'meson' && ! contains(matrix.os, 'windows') }}
run: |
meson test -C ${{ env.BUILD_DIR }} --print-errorlogs --no-rebuild --suite multicharge --benchmark
env:
OMP_NUM_THREADS: 1
MKL_NUM_THREADS: 1

- name: Run unit tests
if: ${{ matrix.build == 'cmake' }}
run: |
ctest --output-on-failure --parallel 2
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
ninja -C ${{ env.BUILD_DIR }} coverage

- name: Install project
run: |
Expand Down
23 changes: 20 additions & 3 deletions app/main.f90
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,25 @@ program main
& write_ascii_model, write_ascii_properties, write_ascii_results, &
& get_coordination_number, get_covalent_rad, get_lattice_points, &
& get_multicharge_version
use multicharge_output, only : json_results
implicit none
character(len=*), parameter :: prog_name = "multicharge"
character(len=*), parameter :: json_output = "multicharge.json"

character(len=:), allocatable :: input, chargeinput
integer, allocatable :: input_format
integer :: stat, unit
type(error_type), allocatable :: error
type(structure_type) :: mol
type(mchrg_model_type) :: model
logical :: grad, exist
logical :: grad, json, exist
real(wp), parameter :: cn_max = 8.0_wp, cutoff = 25.0_wp
real(wp), allocatable :: cn(:), dcndr(:, :, :), dcndL(:, :, :), rcov(:), trans(:, :)
real(wp), allocatable :: energy(:), gradient(:, :), sigma(:, :)
real(wp), allocatable :: qvec(:), dqdr(:, :, :), dqdL(:, :, :)
real(wp), allocatable :: charge

call get_arguments(input, input_format, grad, charge, error)
call get_arguments(input, input_format, grad, charge, json, error)
if (allocated(error)) then
write(error_unit, '(a)') error%message
error stop
Expand Down Expand Up @@ -102,6 +104,14 @@ program main
call write_ascii_properties(output_unit, mol, model, cn, qvec)
call write_ascii_results(output_unit, mol, energy, gradient, sigma)

if (json) then
open(file=json_output, newunit=unit)
call json_results(unit, " ", energy=sum(energy), gradient=gradient, charges=qvec, cn=cn)
close(unit)
write(output_unit, '(a)') &
"[Info] JSON dump of results written to '"// json_output //"'"
end if

contains


Expand All @@ -121,6 +131,7 @@ subroutine help(unit)
"-i, --input <format>", "Hint for the format of the input file", &
"-c, --charge <value>", "Set the molecular charge", &
"-g, --grad", "Evaluate molecular gradient and virial", &
"-j, --json", "Provide output in JSON format to the file 'multicharge.json'", &
"-v, --version", "Print program version and exit", &
"-h, --help", "Show this help message"

Expand All @@ -140,7 +151,7 @@ subroutine version(unit)
end subroutine version


subroutine get_arguments(input, input_format, grad, charge, error)
subroutine get_arguments(input, input_format, grad, charge, json, error)

!> Input file name
character(len=:), allocatable :: input
Expand All @@ -151,6 +162,9 @@ subroutine get_arguments(input, input_format, grad, charge, error)
!> Evaluate gradient
logical, intent(out) :: grad

!> Provide JSON output
logical, intent(out) :: json

!> Charge
real(wp), allocatable, intent(out) :: charge

Expand All @@ -161,6 +175,7 @@ subroutine get_arguments(input, input_format, grad, charge, error)
character(len=:), allocatable :: arg

grad = .false.
json = .false.
iarg = 0
narg = command_argument_count()
do while(iarg < narg)
Expand Down Expand Up @@ -203,6 +218,8 @@ subroutine get_arguments(input, input_format, grad, charge, error)
end if
case("-grad", "--grad")
grad = .true.
case("-j", "--json")
json = .true.
end select
end do

Expand Down
3 changes: 3 additions & 0 deletions man/multicharge.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Electronegativity equilibration model for atomic partial charges.
*-i, --input* _format_::
Hint for the format of the input file

*--json*::
Provide output in JSON format to the file 'multicharge.json'

*--grad*::
Evaluate molecular gradient and virial

Expand Down
75 changes: 74 additions & 1 deletion src/multicharge/output.f90
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ module multicharge_output
use mctc_io_convert, only : autoaa
use mctc_io_constants, only : pi
use multicharge_model, only : mchrg_model_type
use multicharge_version, only : get_multicharge_version
implicit none
private

public :: write_ascii_model, write_ascii_properties, write_ascii_results
public :: write_ascii_model, write_ascii_properties, write_ascii_results, json_results

contains

Expand Down Expand Up @@ -139,4 +140,76 @@ subroutine write_ascii_results(unit, mol, energy, gradient, sigma)

end subroutine write_ascii_results

subroutine json_results(unit, indentation, energy, gradient, charges, cn)
integer, intent(in) :: unit
character(len=*), intent(in), optional :: indentation
real(wp), intent(in), optional :: energy
real(wp), intent(in), optional :: gradient(:, :)
real(wp), intent(in), optional :: charges(:)
real(wp), intent(in), optional :: cn(:)
character(len=:), allocatable :: indent, version_string
character(len=*), parameter :: jsonkey = "('""',a,'"":',1x)"
real(wp), allocatable :: array(:)

call get_multicharge_version(string=version_string)

if (present(indentation)) then
indent = indentation
else
indent = ""
end if

write(unit, '("{")', advance='no')
if (allocated(indent)) write(unit, '(/,a)', advance='no') repeat(indent, 1)
write(unit, jsonkey, advance='no') 'version'
write(unit, '(1x,a)', advance='no') '"'//version_string//'"'
if (present(energy)) then
write(unit, '(",")', advance='no')
if (allocated(indent)) write(unit, '(/,a)', advance='no') repeat(indent, 1)
write(unit, jsonkey, advance='no') 'energy'
write(unit, '(1x,es25.16)', advance='no') energy
end if
if (present(gradient)) then
write(unit, '(",")', advance='no')
if (allocated(indent)) write(unit, '(/,a)', advance='no') repeat(indent, 1)
write(unit, jsonkey, advance='no') 'gradient'
array = reshape(gradient, [size(gradient)])
call write_json_array(unit, array, indent)
end if
if (present(charges)) then
write(unit, '(",")', advance='no')
if (allocated(indent)) write(unit, '(/,a)', advance='no') repeat(indent, 1)
write(unit, jsonkey, advance='no') 'charges'
array = reshape(charges, [size(charges)])
call write_json_array(unit, array, indent)
end if
if (present(cn)) then
write(unit, '(",")', advance='no')
if (allocated(indent)) write(unit, '(/,a)', advance='no') repeat(indent, 1)
write(unit, jsonkey, advance='no') 'coordination numbers'
array = reshape(cn, [size(cn)])
call write_json_array(unit, array, indent)
end if
if (allocated(indent)) write(unit, '(/)', advance='no')
write(unit, '("}")')

end subroutine json_results


subroutine write_json_array(unit, array, indent)
integer, intent(in) :: unit
real(wp), intent(in) :: array(:)
character(len=:), allocatable, intent(in) :: indent
integer :: i
write(unit, '("[")', advance='no')
do i = 1, size(array)
if (allocated(indent)) write(unit, '(/,a)', advance='no') repeat(indent, 2)
write(unit, '(es23.16)', advance='no') array(i)
if (i /= size(array)) write(unit, '(",")', advance='no')
end do
if (allocated(indent)) write(unit, '(/,a)', advance='no') repeat(indent, 1)
write(unit, '("]")', advance='no')
end subroutine write_json_array


end module multicharge_output
32 changes: 1 addition & 31 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# Unit testing
set(
tests
"model"
"ncoord"
"pbc"
"wignerseitz"
)
set(
test-srcs
"main.f90"
)
foreach(t IN LISTS tests)
string(MAKE_C_IDENTIFIER ${t} t)
list(APPEND test-srcs "test_${t}.f90")
endforeach()

add_executable(
"${PROJECT_NAME}-tester"
"${test-srcs}"
)
target_link_libraries(
"${PROJECT_NAME}-tester"
PRIVATE
"${PROJECT_NAME}-lib"
"mstore::mstore"
)

foreach(t IN LISTS tests)
add_test("${t}" "${PROJECT_NAME}-tester" "${t}")
endforeach()
add_subdirectory("unit")
41 changes: 2 additions & 39 deletions test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# Find mstore dependency for testing
mstore_dep = dependency(
'mstore',
version: '>=0.1',
fallback: ['mstore', 'mstore_dep'],
required: not meson.is_subproject(),
default_options: [
'default_library=static',
],
)
# If we do not find mstore and are a subproject, we just skip testing
if not mstore_dep.found()
subdir_done()
endif

tests = [
'model',
'ncoord',
'pbc',
'wignerseitz',
]

test_srcs = files(
'main.f90',
)
foreach t : tests
test_srcs += files('test_@0@.f90'.format(t.underscorify()))
endforeach

tester = executable(
'tester',
sources: test_srcs,
dependencies: [multicharge_dep, mstore_dep],
link_language: 'fortran',
)

foreach t : tests
test(t, tester, args: t)
endforeach
subdir('validation')
subdir('unit')
46 changes: 46 additions & 0 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This file is part of multicharge.
# SPDX-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Unit testing
set(
tests
"model"
"ncoord"
"pbc"
"wignerseitz"
)
set(
test-srcs
"main.f90"
)
foreach(t IN LISTS tests)
string(MAKE_C_IDENTIFIER ${t} t)
list(APPEND test-srcs "test_${t}.f90")
endforeach()

add_executable(
"${PROJECT_NAME}-tester"
"${test-srcs}"
)
target_link_libraries(
"${PROJECT_NAME}-tester"
PRIVATE
"${PROJECT_NAME}-lib"
"mstore::mstore"
)

foreach(t IN LISTS tests)
add_test("${t}" "${PROJECT_NAME}-tester" "${t}")
endforeach()
File renamed without changes.
Loading
Loading