Skip to content

Commit

Permalink
Merge #3331
Browse files Browse the repository at this point in the history
3331: Testcase for ELC and MMM2D r=fweik a=reinaual

Analytical testcase for ELC and MMM2D

Co-authored-by: Alexander Reinauer <st144434@stud.uni-stuttgart.de>
  • Loading branch information
bors[bot] and reinaual committed Dec 5, 2019
2 parents 7cf42c4 + 06e8adc commit 4d8b68b
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
1 change: 1 addition & 0 deletions testsuite/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ python_test(FILE linear_momentum.py)
python_test(FILE linear_momentum_lb.py MAX_NUM_PROC 2 LABELS gpu)
python_test(FILE mmm1d.py MAX_NUM_PROC 2 LABELS gpu)
python_test(FILE elc.py MAX_NUM_PROC 2)
python_test(FILE elc_and_mmm2d_vs_analytic.py MAX_NUM_PROC 2)

if(PY_H5PY)
python_test(FILE h5md.py MAX_NUM_PROC 2)
Expand Down
124 changes: 124 additions & 0 deletions testsuite/python/elc_and_mmm2d_vs_analytic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Copyright (C) 2019 The ESPResSo project
#
# This file is part of ESPResSo.
#
# ESPResSo is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ESPResSo is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest as ut
import unittest_decorators as utx
import espressomd
import numpy as np
import espressomd.electrostatics
from espressomd import electrostatic_extensions


@utx.skipIfMissingFeatures(["P3M"])
class ELC_and_MMM2D_vs_analytic(ut.TestCase):
# Handle to espresso system
box_l = 200.
system = espressomd.System(box_l=[box_l, box_l, box_l])
accuracy = 1e-7
check_accuracy = 1e-4
elc_gap = 75.0
system.time_step = 0.01
delta_mid_top = 1.
delta_mid_bot = 39. / 41.
distance = 1.

number_samples = 25
zPos = np.linspace(0.1, 8, number_samples)
q = np.arange(-5.0, 5.1, 2.5)

def test_elc_and_mmm2d(self):
"""
Testing ELC and MMM2D against the analytic solution for an infinite large simulation box with dielectric contrast on the bottom of the box, which can be calculated analytically with image charges.
"""
# MMM2D
self.system.cell_system.skin = 0.1
buf_node_grid = self.system.cell_system.node_grid
self.system.cell_system.set_layered(
n_layers=10, use_verlet_lists=False)

self.system.periodicity = [1, 1, 0]

self.system.part.add(id=1, pos=self.system.box_l / 2., q=self.q[0])
self.system.part.add(id=2, pos=self.system.box_l / 2. + [0, 0, 1],
q=-self.q[0])

# MMM2D
mmm2d = espressomd.electrostatics.MMM2D(prefactor=1.0,
maxPWerror=self.accuracy,
delta_mid_bot=self.delta_mid_bot,
delta_mid_top=self.delta_mid_top,
dielectric_contrast_on=1)

self.system.actors.add(mmm2d)

mmm2d_results = self.scan()

self.system.actors.remove(mmm2d)

# ELC
self.system.box_l = [self.box_l, self.box_l, self.box_l + self.elc_gap]
self.system.cell_system.set_domain_decomposition(
use_verlet_lists=True)
self.system.cell_system.node_grid = buf_node_grid
self.system.periodicity = [1, 1, 1]
p3m = espressomd.electrostatics.P3M(prefactor=1.,
accuracy=self.accuracy,
mesh=[58, 58, 70],
cao=4)
self.system.actors.add(p3m)

elc = electrostatic_extensions.ELC(gap_size=self.elc_gap,
maxPWerror=self.accuracy,
delta_mid_bot=self.delta_mid_bot,
delta_mid_top=self.delta_mid_top)
self.system.actors.add(elc)

elc_results = self.scan()

# ANALYTIC SOLUTION
charge_reshaped = np.square(self.q.reshape(-1, 1))
analytic_force = charge_reshaped * (1 / self.distance ** 2 + self.delta_mid_bot * (
1 / np.square(2 * self.zPos) - 1 / np.square(2 * self.zPos + self.distance)))
analytic_energy = charge_reshaped * (-1 / self.distance + self.delta_mid_bot * (1 / (
4 * self.zPos) - 1 / (2 * self.zPos + self.distance) + 1 / (4 * (self.zPos + self.distance))))

analytic_results = np.dstack((analytic_force, analytic_energy))

self.assertTrue(np.testing.assert_allclose(
mmm2d_results, analytic_results, rtol=0, atol=self.check_accuracy) is None)

self.assertTrue(np.testing.assert_allclose(
elc_results, analytic_results, rtol=0, atol=self.check_accuracy) is None)

def scan(self):
result_array = np.empty((len(self.q), len(self.zPos), 2))
for chargeIndex, charge in enumerate(self.q):
self.system.part[1].q = charge
self.system.part[2].q = -charge
for i, z in enumerate(self.zPos):
pos = self.system.part[1].pos
self.system.part[1].pos = [pos[0], pos[1], z]
self.system.part[2].pos = [pos[0], pos[1], z + self.distance]

self.system.integrator.run(0)
result_array[chargeIndex, i, 0] = self.system.part[1].f[2]
result_array[chargeIndex, i, 1] = self.system.analysis.energy()[
"total"]
return result_array


if __name__ == "__main__":
ut.main()

0 comments on commit 4d8b68b

Please sign in to comment.