Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Unsupported ESPResSo features now raise a `NotImplementedError` instead of a `ValueError`. (#113)

### Removed
- `verbose` optional argument has been deprecated in most of pyMBE methods. Now the module `loggins` is used instead for handling pyMBE's logs. (#119)
- `lib.handy_functions.minimize_espresso_system_energy` because its name was confusing (it was not only minimizing the system energy) and was changing the parameters of the integrator under the hood. (#118)
- print statements and most of `verbose` in `lib.handy_functions`. (#118)
- `pmb.parse_sequence_from_file` has been removed since it is no longer necesary to parse the sequence from pmb.df (#110)
Expand Down
2 changes: 0 additions & 2 deletions lib/handy_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import logging
# Create a logger for this module
logger = logging.getLogger(__name__)

def setup_electrostatic_interactions(units, espresso_system, kT, c_salt=None, solvent_permittivity=78.5, method='p3m', tune_p3m=True, accuracy=1e-3,verbose=False):
"""
Expand Down
118 changes: 26 additions & 92 deletions pyMBE.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion samples/peptide_cpH.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@

# Create an instance of an espresso system
espresso_system=espressomd.System (box_l = [L.to('reduced_length').magnitude]*3)

espresso_system.time_step=dt
espresso_system.cell_system.skin=0.4
# Add all bonds to espresso system
pmb.add_bonds_to_espresso(espresso_system=espresso_system)

Expand Down
1 change: 1 addition & 0 deletions testsuite/CTestTestfile.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pymbe_add_test(PATH peptide_tests.py LABELS long beyer2024 THREADS 2)
pymbe_add_test(PATH weak_polyelectrolyte_dialysis_test.py LABELS long beyer2024)

# unit tests
pymbe_add_test(PATH test_in_out_pmb_df.py)
pymbe_add_test(PATH serialization_test.py)
pymbe_add_test(PATH test_global_variables.py)
pymbe_add_test(PATH lj_tests.py)
Expand Down
4 changes: 2 additions & 2 deletions testsuite/bond_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,9 @@ def test_bond_framework(self):

# check invalid bond index
pmb.add_value_to_df(key=('particle_id',''), new_value=10,
index=np.where(pmb.df['name']=='A')[0][0], verbose=False)
index=np.where(pmb.df['name']=='A')[0][0])
pmb.add_value_to_df(key=('particle_id',''), new_value=20,
index=np.where(pmb.df['name']=='B')[0][0], verbose=False)
index=np.where(pmb.df['name']=='B')[0][0])
self.assertIsNone(pmb.add_bond_in_df(10, 20, use_default_bond=False))
self.assertIsNone(pmb.add_bond_in_df(10, 20, use_default_bond=True))

Expand Down
75 changes: 66 additions & 9 deletions testsuite/define_and_create_molecules_unit_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,22 @@
desired=0,
verbose=True)

print("*** Unit test passed ***")

print("*** Unit test: check that define_particles() defines a set of particles correctly ***")
particle_parameters={"S1":{"name": "S1",
"sigma":1*pmb.units.nm,
"z":0},
"S2":{"name": "S2",
"sigma":2*pmb.units.nm,
"z": 1},
"S3":{"name": "S3",
"sigma":3*pmb.units.nm,
"z":2}}

particle_parameters={"S1":{"name":"S1",
"sigma":1*pmb.units.nm,
"offset":0.5*pmb.units.nm,
"z":1},
"S2":{"name":"S2",
"sigma":2*pmb.units.nm,
"offset":1.5*pmb.units.nm,
"z": 1},
"S3":{"name":"S3",
"sigma":3*pmb.units.nm,
"offset":2.5*pmb.units.nm,
"z":2}}
pmb.define_particles(parameters=particle_parameters)

for particle_name in particle_parameters.keys():
Expand Down Expand Up @@ -468,3 +474,54 @@
desired=starting_number_of_particles,
verbose=True)
print("*** Unit test passed ***")

print("*** Unit test: check that get_particle_id_map() raises a ValueError if the user provides the name of an object that is not a particle ***")

# If a bond_object is passed then the ValueError should be raised

input_parameters={"object_name": 'default' }
np.testing.assert_raises(ValueError, pmb.get_particle_id_map, **input_parameters)

print("*** Unit test passed ***")

print("*** Unit test: check that get_radius_map() provides the right amount of radii corresponding to the number of different particles in the simulation box ***")

np.testing.assert_equal(actual=len(pmb.get_radius_map()),
desired=len(particle_parameters.values()),
verbose=True)

print("*** Unit test passed ***")

print("*** Unit test: check that get_radius_map() provides the right values of the radii of the particles, which corresponds to (sigma+offset)/2 ***")

desired_radii=[]
for particle in particle_parameters.values():
desired_radii.append((particle['sigma'].magnitude+particle['offset'].magnitude)/2)

actual_radii=[pmb.get_radius_map()[0],
pmb.get_radius_map()[1],
pmb.get_radius_map()[2],]

np.testing.assert_equal(actual=actual_radii,
desired=desired_radii,
verbose=True)

print("*** Unit test passed ***")

print("*** Unit test: check that the default value for the argument 'dimensionless' in get_radius_map() is True ***")

np.testing.assert_equal(actual=isinstance(pmb.get_radius_map()[0],float),
desired=True,
verbose=True)

print("*** Unit test passed ***")

print("*** Unit test: check that if the argument 'dimensionless' is False in get_radius_map() then we obtain the corresponding units ***")


np.testing.assert_equal(actual=pmb.get_radius_map(dimensionless=False)[0].dimensionality,
desired=pmb.units.nm.dimensionality,
verbose=True)

print("*** Unit test passed ***")

70 changes: 0 additions & 70 deletions testsuite/delete_entries_in_df.py

This file was deleted.

3 changes: 1 addition & 2 deletions testsuite/parameter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@
print(f"Checking {path.stem}")
path_to_pka = pmb.get_resource(path.relative_to(pymbe_root).as_posix())
assert pathlib.Path(path_to_pka) == path
pmb.load_pka_set(path_to_pka,
verbose=False)
pmb.load_pka_set(path_to_pka)

print("*** Test passed ***")

Expand Down
4 changes: 2 additions & 2 deletions testsuite/seed_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ def build_peptide_in_espresso(seed):
# Load peptide parametrization from Lunkad, R. et al. Molecular Systems Design & Engineering (2021), 6(2), 122-131.
path_to_interactions=pmb.get_resource("parameters/peptides/Lunkad2021.json")
path_to_pka=pmb.get_resource("parameters/pka_sets/CRC1991.json")
pmb.load_interaction_parameters(filename=path_to_interactions, verbose=False)
pmb.load_pka_set(path_to_pka, verbose=False)
pmb.load_interaction_parameters(filename=path_to_interactions)
pmb.load_pka_set(path_to_pka)

# Defines the peptide in the pyMBE data frame
peptide_name = 'generic_peptide'
Expand Down
3 changes: 2 additions & 1 deletion testsuite/test_handy_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@
import logging
import io
import numpy as np

import io
# Create an in-memory log stream
log_stream = io.StringIO()
logging.basicConfig(level=logging.INFO,
format="%(levelname)s: %(message)s",
handlers=[logging.StreamHandler(log_stream)] )

# Create instances of espresso and pyMBE
espresso_system = espressomd.System(box_l=[60,60,60])
seed = 23
Expand Down
121 changes: 121 additions & 0 deletions testsuite/test_in_out_pmb_df.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#
# Copyright (C) 2025 pyMBE-dev team
#
# This file is part of pyMBE.
#
# pyMBE 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.
#
# pyMBE 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 pyMBE
import io
import pandas as pd
import unittest as ut
import logging
import re

# Create an in-memory log stream
log_stream = io.StringIO()
logging.basicConfig(level=logging.DEBUG,
format="%(levelname)s: %(message)s",
handlers=[logging.StreamHandler(log_stream)])

pmb = pyMBE.pymbe_library(seed=42)
particle_parameters={"S1":{"name": "S1",
"sigma":0.355*pmb.units.nm,
"epsilon":1*pmb.units('reduced_energy'),
"z":0},
"S2":{"name": "S2",
"sigma":0.355*pmb.units.nm,
"epsilon":1*pmb.units('reduced_energy'),
"z":1},
"S3":{"name": "S3",
"sigma":0.355*pmb.units.nm,
"epsilon":1*pmb.units('reduced_energy'),
"z":2}}

pmb.define_particles(parameters=particle_parameters)

generic_harmonic_constant = 400 * pmb.units('reduced_energy / reduced_length**2')
generic_bond_length = 0.355*pmb.units.nm
HARMONIC_parameters = {'r_0' : generic_bond_length,
'k' : generic_harmonic_constant}
pmb.define_bond(bond_type = 'harmonic',
bond_parameters = HARMONIC_parameters, particle_pairs = [["S1", "S2"]])

class Serialization(ut.TestCase):
def test_add_to_df(self):
print("*** Unit test: test that check if the add_value_to_df() raises two warnings in default mode (overwrite=False) ***")
index=0
key = ('name','')
old_value = pmb.df.loc[index,pd.IndexSlice[key]]
new_value='T2'
name=pmb.df.loc[index,key]
pmb_type=pmb.df.loc[index,('pmb_type','')]
pmb.add_value_to_df(index=index,key=key,new_value=new_value)
log_contents = log_stream.getvalue()
warning_msg1=f"You are attempting to redefine the properties of {name} of pmb_type {pmb_type}"
warning_msg2=f"pyMBE has preserved of the entry `{key}`: old_value = {old_value}. If you want to overwrite it with new_value = {new_value}, activate the switch overwrite = True"
# Check if the warnings are in the log
assert re.search(re.escape(warning_msg1), log_contents)
assert re.search(re.escape(warning_msg2), log_contents)
print("*** Unit passed ***")
print("*** Unit test: test that check if the add_value_to_df() raises one warning when overwrite=True ***")
index=0
key = ('name','')
old_value = pmb.df.loc[index,pd.IndexSlice[key]]
new_value='T2'
name=pmb.df.loc[index,key]
pmb_type=pmb.df.loc[index,('pmb_type','')]
pmb.add_value_to_df(index=index,key=key,new_value=new_value,overwrite=True)
log_contents = log_stream.getvalue()
warning_msg1=f"You are attempting to redefine the properties of {name} of pmb_type {pmb_type}"
warning_msg2=f"Overwritting the value of the entry `{key}`: old_value = {old_value} new_value = {new_value}"
# Check if the warnings are in the log
assert re.search(re.escape(warning_msg1), log_contents)
assert re.search(re.escape(warning_msg2), log_contents)
print("*** Unit passed ***")

def test_delete_entries_df(self):
print("*** Unit test: test that entries in df are deleted properly using `delete_entries_in_df` ***")

pmb.delete_entries_in_df(entry_name="S1-S2")
assert pmb.df[pmb.df["name"]=="S1-S2"].empty
pmb.delete_entries_in_df(entry_name="S1")
assert pmb.df[pmb.df["name"]=="S1"].empty

residue_parameters={"R1":{"name": "R1",
"central_bead": "S2",
"side_chains": []},
"R2":{"name": "R2",
"central_bead": "S2",
"side_chains": ["S2","S3"]}}

for parameter_set in residue_parameters.values():
pmb.define_residue(**parameter_set)

pmb.delete_entries_in_df(entry_name="R1")
assert pmb.df[pmb.df["name"]=="R1"].empty

molecule_parameters={"M1":{"name": "M1",
"residue_list": ["R2","R2","R2"]}}

for parameter_set in molecule_parameters.values():
pmb.define_molecule(**parameter_set)

pmb.delete_entries_in_df(entry_name="M1")
assert pmb.df[pmb.df["name"]=="M1"].empty
print("*** Unit passed ***")

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

6 changes: 4 additions & 2 deletions tutorials/pyMBE_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1093,8 +1093,10 @@
"metadata": {},
"outputs": [],
"source": [
"pmb.load_interaction_parameters(filename = pmb.get_resource('parameters/peptides/Lunkad2021.json'))\n",
"pmb.add_bonds_to_espresso(espresso_system = espresso_system)"
"path_to_interactions=pmb.get_resource(\"parameters/peptides/Lunkad2021.json\")\n",
"path_to_pka=pmb.get_resource(\"parameters/pka_sets/Hass2015.json\")\n",
"pmb.load_interaction_parameters (filename=path_to_interactions) \n",
"pmb.load_pka_set (path_to_pka)"
]
},
{
Expand Down