diff --git a/.github/workflows/samples.yml b/.github/workflows/samples.yml index 6f45ba6..3503e55 100644 --- a/.github/workflows/samples.yml +++ b/.github/workflows/samples.yml @@ -26,7 +26,7 @@ jobs: uses: ./.github/actions/dependencies with: modules: |- - ESPResSo/4.2.1-foss-2023a + ESPResSo/4.2.2-foss-2023a - name: Run testsuite run: | module restore pymbe diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 3c408d5..4fde10e 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -27,7 +27,7 @@ jobs: uses: ./.github/actions/dependencies with: modules: |- - ESPResSo/4.2.1-foss-2023a + ESPResSo/4.2.2-foss-2023a extra-python-packages: |- pdoc==14.3 pylint==3.0.3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b6075f..83d6d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `lib.analysis.get_dt` now raises a ValueError if the two first two rows of the dataframe have the same values for the time, which break the subsequent code. (#95) - Removed global state variables, instead they are now created by the constructor of `pyMBE.pymbe_library`. This prevents two instances of the pyMBE library to share the same memory address for their attributes. (#89) - Required Python dependency versions compatible with ESPResSo 4.2 (#84) +- NumPy 2, Pandas 2 and the development version of ESPResSo are now fully supported. (#106) - Fixed several deprecated paths and function names in `tutorials/pyMBE_tutorial.ipynb`. (#77, #78, #79, #80, #81) ## [0.8.0] - 2024-06-18 diff --git a/README.md b/README.md index b640914..ef3b6e5 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,22 @@ pyMBE provides tools to facilitate building up molecules with complex architectu ## Dependencies -- [ESPResSo](https://espressomd.org/wordpress/) =4.2.1 -- [Pint](https://pint.readthedocs.io/en/stable/) >=0.20.01 -- [Pandas](https://pandas.pydata.org/) >=1.5.3 -- [Pint-Pandas](https://pypi.org/project/Pint-Pandas/) >=0.3 -- [Numpy](https://numpy.org/) >=1.23 -- [SciPy](https://scipy.org/) +- [ESPResSo](https://espressomd.org/wordpress/) +- [Pint](https://pint.readthedocs.io/en/stable/) +- [Pandas](https://pandas.pydata.org/) +- [Pint-Pandas](https://pypi.org/project/Pint-Pandas/) +- [Numpy](https://numpy.org/) +- [SciPy](https://scipy.org/) - [pdoc](https://pdoc.dev/) (for building the docs) - [CMake](https://cmake.org/) (for running the testsuite) +- any virtual environment manager: [venv](https://docs.python.org/3/library/venv.html), + [virtualenv](https://virtualenv.pypa.io/en/latest/), + [conda](https://docs.conda.io/projects/conda/en/latest/user-guide/getting-started.html#managing-python), + [miniconda](https://docs.anaconda.com/free/miniconda), + [micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html), + etc. + +Version requirements are documented in [`requirements.txt`](requirements.txt). ## Contents @@ -50,33 +58,42 @@ git clone git@github.com:pyMBE-dev/pyMBE.git ``` Please, be aware that pyMBE is intended to be a supporting tool to setup simulations with ESPResSo. -Thus, for most of its functionalities ESPResSo must also be available. Following the NEP29 guidelines, we recommend the users of pyMBE to use Python3.10+ when using our module. +Thus, for most of its functionalities ESPResSo must also be available. +pyMBE supports ESPResSo 4.2 and ESPResSo 4.3-dev. +Following the NEP29 guidelines, we recommend using Python3.10+. +Both NumPy 1 and NumPy 2 are supported. -The pyMBE module uses its own Python virtual enviroment to avoid incompatibility issues when loading its requirements from other libraries. -The Python module [`venv`](https://docs.python.org/3/library/venv.html) is needed to set up pyMBE. -If `venv` is not in the Python distribution of the user, the user will need to first install 'venv' before setting up pyMBE. -For Ubuntu users, this can be done as follows: +The pyMBE module needs a Python virtual environment to avoid compatibility issues with its dependencies. +Any virtual environment manager should work, but this readme will use `venv`, which can be installed on Ubuntu as follows: ```sh sudo apt install python3-venv ``` -To set up pyMBE, the users need to install its virtual environment, install its Python dependencies and configure the path to the ESPResSo build folder as follows: +To set up pyMBE, users need to configure a virtual environment. This is achieved by installing the Python dependencies and setting the path to the ESPResSo build folder, as follows: ```sh -python3 -m venv pymbe # creates a local folder named pymbe, which contains the virtual environment -source pymbe/bin/activate # activates the pymbe venv -python3 maintainer/configure_venv.py --espresso_path=/home/user/espresso/build # please, adapt the espresso path accordingly -python3 -m pip install -r requirements.txt -python3 simulation_script.py # run the espresso simulation script +python3 -m venv pymbe # create a local folder named pymbe containing the environment files +source pymbe/bin/activate # activate the virtual environment +python3 -m pip install -r requirements.txt "numpy<2.0" "pandas<2.0" +python3 maintainer/configure_venv.py --espresso_path=/home/user/espresso/build # please adapt the espresso path accordingly +python3 simulation_script.py # run a simulation script deactivate # deactivate the virtual environment ``` +NumPy 2 users should adapt the pip command as follows: + +```sh +python3 -m pip install -r requirements.txt "numpy>=2.1" "pandas>=2.0" +``` + We highlight that the path `/home/user/espresso/build` is just an example of a possible path to the ESPResSo build folder. -The user should change this path to match the local absolute path were ESPResSo was installed. +The user should change this path to match the local absolute path where ESPResSo was built. +Also, ESPResSo must be built with the same NumPy version as the one installed in the environment to avoid API version mismatch. For more details on how to install ESPResSo, please consult the [ESPResSo installation guide](https://espressomd.github.io/doc4.2.2/installation.html). -The pyMBE virtual enviroment can be deactivated at any moment: +The pyMBE virtual environment can be deactivated at any moment as follows: + ```sh deactivate ``` @@ -85,7 +102,7 @@ Cluster users who rely on module files to load dependencies should opt for the following alternative: ```sh -module load ESPResSo/4.2.1-foss-2022a # adapt module name +module load ESPResSo/4.2.2-foss-2023a # adapt release if needed python3 -m venv --system-site-packages pymbe source pymbe/bin/activate python3 maintainer/configure_venv.py @@ -94,7 +111,7 @@ deactivate module purge ``` -We highlight that the module files need to be loaded before every activation +Please note the module files need to be loaded before every activation of the virtual environment. Now you can use pyMBE and ESPResSo by activating the virtual environment: @@ -108,11 +125,11 @@ $ source pymbe/bin/activate $ deactivate ``` -To use pyMBE in JupyterLab, register the virtual environment in a new kernel: +To use pyMBE in JupyterLab, install extra dependencies and register the virtual environment in a new kernel: ```sh source pymbe/bin/activate -python3 -m pip install ipykernel "jupyterlab>=4.0.8" "PyOpenGL>=3.1.5" +python3 -m pip install ipykernel "jupyterlab>=4.0.8" "PyOpenGL>=3.1.5" "ipympl>=0.9.3" python3 -m ipykernel install --user --name=pyMBE deactivate ``` @@ -129,24 +146,30 @@ jupyter kernelspec uninstall pymbe The JupyterLab main menu will now show a new Python kernel called "pyMBE" that uses the virtual environment. -### Use pyMBE in your simulation scripts +### Run simulation scripts + +You can run the branched polyampholyte sample with the following commands: ```sh source pymbe/bin/activate -python3 samples/peptide.py +python3 samples/branched_polyampholyte.py --pH 6 +python3 samples/analyze_time_series.py --data_folder samples/time_series/branched_polyampholyte +python3 samples/plot_branched_polyampholyte.py deactivate ``` -### Run the tutorial of pyMBE +### Run tutorials -You can run the interactive tutorial of pyMBE with the command: +You can run the interactive tutorials with the following commands: ```sh source pymbe/bin/activate -jupyter-lab tutorials/pyMBE_tutorial.ipynb +jupyter-lab deactivate ``` +In the Jupyter interface, open the `tutorials` folder and then the `pyMBE_tutorial` file. +It will guide you through the creation of polyelectrolytes with pyMBE. Be sure to use the pyMBE kernel instead of the default Python3 kernel. The currently active kernel is usually displayed in the top right corner of the notebook. diff --git a/lib/analysis.py b/lib/analysis.py index 1a34c7d..fa3009c 100644 --- a/lib/analysis.py +++ b/lib/analysis.py @@ -50,7 +50,7 @@ def analyze_time_series(path_to_datafolder, filename_extension= ".csv", minus_se path_to_datafolder(`str`): path to the folder with the files with the time series filename_extension(`str`): extension of the file. Defaults to ".csv" minus_separator(`bool`): switch to enable the minus as a separator. Defaults to False. - ignore_files(`lst`): list of filenames to be ignored for the bining analysis. + ignore_files(`lst`): list of filenames to be ignored for the binning analysis. Returns: data(`Pandas.Dataframe`): Dataframe with the time averages of all the time series in the datafolder. @@ -113,7 +113,7 @@ def block_analyze(full_data, n_blocks=16, time_col = "time", equil=0.1, columns print(f"Warning: looks like a repeated time value was encountered {n_warnings} times") drop_rows = int(full_data.shape[0]*equil) # calculate how many rows should be dropped as equilibration - # drop the rows that will be discarded as equlibration + # drop the rows that will be discarded as equilibration data = full_data.drop(range(0,drop_rows)) # drop the columns step, time and MC sweep if time_col in data.columns : diff --git a/lib/handy_functions.py b/lib/handy_functions.py index 83dfa66..b20e0a6 100644 --- a/lib/handy_functions.py +++ b/lib/handy_functions.py @@ -32,6 +32,7 @@ def setup_electrostatic_interactions (units, espresso_system, kT, c_salt=None, s verbose (`bool`): switch to activate/deactivate verbose. Defaults to True. """ import espressomd.electrostatics + import espressomd.version import numpy as np import scipy.constants @@ -71,12 +72,18 @@ def setup_electrostatic_interactions (units, espresso_system, kT, c_salt=None, s if tune_p3m: espresso_system.time_step=0.01 - espresso_system.actors.add(coulomb) + if espressomd.version.friendly() == "4.2": + espresso_system.actors.add(coulomb) + else: + espresso_system.electrostatics.solver = coulomb # save the optimal parameters and add them by hand p3m_params = coulomb.get_params() - espresso_system.actors.remove(coulomb) + if espressomd.version.friendly() == "4.2": + espresso_system.actors.remove(coulomb) + else: + espresso_system.electrostatics.solver = None coulomb = espressomd.electrostatics.P3M( prefactor = COULOMB_PREFACTOR.magnitude, accuracy = accuracy, @@ -94,7 +101,10 @@ def setup_electrostatic_interactions (units, espresso_system, kT, c_salt=None, s r_cut = KAPPA.to('reduced_length').magnitude) - espresso_system.actors.add(coulomb) + if espressomd.version.friendly() == "4.2": + espresso_system.actors.add(coulomb) + else: + espresso_system.electrostatics.solver = coulomb if verbose: print("\n Electrostatics successfully added to the system \n") @@ -211,3 +221,20 @@ def do_snapshot_espresso_system(espresso_system, filename): visualizer.screenshot(filename) return + +def get_number_of_particles(espresso_system, ptype): + import espressomd.version + if espressomd.version.friendly() == "4.2": + args = (ptype,) + kwargs = {} + else: + args = () + kwargs = {"type": ptype} + return espresso_system.number_of_particles(*args, **kwargs) + +def do_reaction(algorithm, steps): + import espressomd.version + if espressomd.version.friendly() == '4.2': + algorithm.reaction(reaction_steps=steps) + else: + algorithm.reaction(steps=steps) diff --git a/pyMBE.py b/pyMBE.py index b1fcdcb..534c7dc 100644 --- a/pyMBE.py +++ b/pyMBE.py @@ -1006,7 +1006,7 @@ def create_particle(self, name, espresso_system, number_of_particles, position=N created_pid_list(`list` of `float`): List with the ids of the particles created into `espresso_system`. """ if number_of_particles <=0: - return 0 + return [] self.check_if_name_is_defined_in_df(name=name, pmb_type_to_be_defined='particle') # Copy the data of the particle `number_of_particles` times in the `df` @@ -1015,6 +1015,7 @@ def create_particle(self, name, espresso_system, number_of_particles, position=N number_of_copies=number_of_particles) # Get information from the particle type `name` from the df z = self.df.loc[self.df['name']==name].state_one.z.values[0] + z = 0. if z is None else z es_type = self.df.loc[self.df['name']==name].state_one.es_type.values[0] # Get a list of the index in `df` corresponding to the new particles to be created index = np.where(self.df['name']==name) @@ -1033,11 +1034,10 @@ def create_particle(self, name, espresso_system, number_of_particles, position=N else: bead_id = max (espresso_system.part.all().id) + 1 created_pid_list.append(bead_id) - + kwargs = dict(id=bead_id, pos=particle_position, type=es_type, q=z) if fix: - espresso_system.part.add (id=bead_id, pos = particle_position, type = es_type, q = z,fix =[fix,fix,fix]) - else: - espresso_system.part.add (id=bead_id, pos = particle_position, type = es_type, q = z) + kwargs["fix"] = 3 * [fix] + espresso_system.part.add(**kwargs) self.add_value_to_df(key=('particle_id',''),index=df_index,new_value=bead_id, verbose=False) return created_pid_list diff --git a/requirements.txt b/requirements.txt index f07ecbb..4cb76e9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy>=1.23,<2.0 +numpy>=1.23 pandas>=1.5.3 pint>=0.20.01 pint-pandas>=0.3 diff --git a/samples/Beyer2024/README.md b/samples/Beyer2024/README.md index 1a78224..096dede 100644 --- a/samples/Beyer2024/README.md +++ b/samples/Beyer2024/README.md @@ -9,6 +9,6 @@ where the previous line will run the script to produce Fig. 7a in Ref.[^1] The u - globular_protein.py: for the globular protein benchmarks - weak_polyelectrolyte_dialysis.py: for the weak polyelectrolyte dialysis benchmarks -The optional argparse argument `--plot` controls if these scripts generate the corresponding plot or if the data is simply stored to file. We note that the format of the plots can differ from that of our publication [^1]. Theses scripts are part of the continous integration (CI) scheme of the pyMBE library and they are used to ensure that any stable version of the library reproduces the benchmarks. +The optional argparse argument `--plot` controls if these scripts generate the corresponding plot or if the data is simply stored to file. We note that the format of the plots can differ from that of our publication [^1]. Theses scripts are part of the continous integration (CI) pipeline to ensure that future pyMBE releases still reproduce the benchmarks. -[^1]: D. Beyer, P. B. Torres, S. P. Pineda, C. F. Narambuena, J. N. Grad, P. Košovan, P. M Blanco. J. Chem. Phys.(2024), 161 (2), 022502. doi: [10.1063/5.0216389](https://doi.org/10.1063/5.0216389). \ No newline at end of file +[^1]: D. Beyer, P. B. Torres, S. P. Pineda, C. F. Narambuena, J.-N. Grad, P. Košovan, P. M. Blanco. J. Chem. Phys.(2024), 161 (2), 022502. doi: [10.1063/5.0216389](https://doi.org/10.1063/5.0216389). diff --git a/samples/Beyer2024/globular_protein.py b/samples/Beyer2024/globular_protein.py index 4e9a312..9a7aec7 100644 --- a/samples/Beyer2024/globular_protein.py +++ b/samples/Beyer2024/globular_protein.py @@ -17,7 +17,7 @@ # along with this program. If not, see . from pathlib import Path -from tqdm import tqdm +import tqdm import espressomd import argparse import numpy as np @@ -31,6 +31,7 @@ from lib.handy_functions import setup_electrostatic_interactions from lib.handy_functions import minimize_espresso_system_energy from lib.handy_functions import setup_langevin_dynamics +from lib.handy_functions import do_reaction from lib import analysis # Here you can adjust the width of the panda columns displayed when running the code pd.options.display.max_colwidth = 10 @@ -38,8 +39,6 @@ #This line allows you to see the complete amount of rows in the dataframe pd.set_option('display.max_rows', None) -valid_modes=["short-run","long-run", "test"] - parser = argparse.ArgumentParser(description='Script to run globular protein simulation in espressomd') parser.add_argument('--pdb', @@ -67,7 +66,8 @@ parser.add_argument('--mode', type=str, default= "short-run", - help='sets for how long the simulation runs, valid modes are {valid_modes}') + choices=["short-run","long-run", "test"], + help='sets for how long the simulation runs') parser.add_argument('--output', type=str, @@ -141,7 +141,6 @@ exist_ok=True) espresso_system = espressomd.System(box_l=[Box_L.to('reduced_length').magnitude] * 3) -espresso_system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() #Reads the VTF file of the protein model path_to_cg=pmb.get_resource(args.path_to_cg) @@ -240,22 +239,22 @@ number_of_particles=N_ions, position=added_salt_ions_coords[N_ions:]) -#Here we calculated the ionisible groups +#Here we calculated the ionisable groups basic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='basic')].name.to_list() acidic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='acidic')].name.to_list() -list_ionisible_groups = basic_groups + acidic_groups -total_ionisible_groups = len (list_ionisible_groups) +list_ionisable_groups = basic_groups + acidic_groups +total_ionisable_groups = len (list_ionisable_groups) if verbose: - print('The box length of the system is', Box_L.to('reduced_length'), Box_L.to('nm')) - print('The ionisable groups in the protein are ', list_ionisible_groups) - print ('The total amount of ionizable groups are:',total_ionisible_groups) + print(f"The box length of the system is {Box_L.to('reduced_length')} {Box_L.to('nm')}") + print(f"The ionisable groups in the protein are {list_ionisable_groups}") + print(f"The total amount of ionisable groups is {total_ionisable_groups}") #Setup of the reactions in espresso cpH, labels = pmb.setup_cpH(counter_ion=cation_name, constant_pH= pH_value) if verbose: - print('The acid-base reaction has been sucessfully setup for ', labels) + print(f"The acid-base reaction has been sucessfully setup for {labels}") type_map = pmb.get_type_map() types = list (type_map.values()) @@ -265,7 +264,7 @@ non_interacting_type = max(type_map.values())+1 cpH.set_non_interacting_type (type=non_interacting_type) if verbose: - print('The non interacting type is set to ', non_interacting_type) + print(f"The non interacting type is set to {non_interacting_type}") #Save the initial state n_frame = 0 @@ -320,9 +319,9 @@ net_charge_amino_save[label] = [] time_series[label] = [] -for step in tqdm(range(N_samples),disable=not verbose): +for step in tqdm.trange(N_samples, disable=not verbose): espresso_system.integrator.run (steps = integ_steps) - cpH.reaction(reaction_steps = total_ionisible_groups) + do_reaction(cpH, steps=total_ionisable_groups) charge_dict=pmb.calculate_net_charge (espresso_system=espresso_system, molecule_name=protein_name, dimensionless=True) diff --git a/samples/Beyer2024/peptide.py b/samples/Beyer2024/peptide.py index 1e8eb7a..01ba6b1 100644 --- a/samples/Beyer2024/peptide.py +++ b/samples/Beyer2024/peptide.py @@ -27,11 +27,11 @@ import pyMBE from lib import analysis from lib import handy_functions as hf +from lib.handy_functions import do_reaction # Create an instance of pyMBE library pmb = pyMBE.pymbe_library(seed=42) -valid_modes=["short-run","long-run", "test"] parser = argparse.ArgumentParser(description='Script to run the peptide test cases for pyMBE') parser.add_argument('--sequence', type=str, @@ -44,7 +44,8 @@ parser.add_argument('--mode', type=str, default= "short-run", - help='sets for how long the simulation runs, valid modes are {valid_modes}') + choices=["short-run","long-run", "test"], + help='sets for how long the simulation runs') parser.add_argument('--output', type=str, required= False, @@ -60,8 +61,6 @@ mode=args.mode verbose=args.no_verbose -if mode not in valid_modes: - raise ValueError(f"Mode {mode} is not currently supported, valid modes are {valid_modes}") LANGEVIN_SEED = 100 dt = 0.01 @@ -218,7 +217,7 @@ # Run LD espresso_system.integrator.run(steps=MD_steps_per_sample) # Run MC - cpH.reaction(reaction_steps=len(sequence)) + do_reaction(cpH, steps=len(sequence)) # Sample observables charge_dict=pmb.calculate_net_charge(espresso_system=espresso_system, molecule_name=sequence, @@ -234,7 +233,7 @@ data_path = args.output if data_path is None: - data_path=pmb.get_resource(path="samples/Beyer2024")+"/time_series/peptides" + data_path=pmb.get_resource(path="samples/Beyer2024") / "time_series" / "peptides" Path(data_path).mkdir(parents=True, exist_ok=True) diff --git a/samples/Beyer2024/weak_polyelectrolyte_dialysis.py b/samples/Beyer2024/weak_polyelectrolyte_dialysis.py index 54cc003..41849d0 100644 --- a/samples/Beyer2024/weak_polyelectrolyte_dialysis.py +++ b/samples/Beyer2024/weak_polyelectrolyte_dialysis.py @@ -25,7 +25,7 @@ from pathlib import Path import numpy as np import pandas as pd -from tqdm import tqdm +import tqdm from scipy import interpolate import argparse @@ -36,10 +36,11 @@ # Create an instance of pyMBE library pmb = pyMBE.pymbe_library(seed=42) -# Load some functions from the handy_scripts library for convinience +# Load some functions from the handy_scripts library for convenience from lib.handy_functions import setup_electrostatic_interactions from lib.handy_functions import minimize_espresso_system_energy from lib.handy_functions import setup_langevin_dynamics +from lib.handy_functions import do_reaction ####################################################### @@ -194,9 +195,9 @@ tune_skin=False) if verbose: print("Running warmup without electrostatics") -for i in tqdm(range(100),disable=not verbose): +for i in tqdm.trange(100, disable=not verbose): espresso_system.integrator.run(steps=1000) - grxmc.reaction(reaction_steps=1000) + do_reaction(grxmc, steps=1000) setup_electrostatic_interactions(units=pmb.units, espresso_system=espresso_system, @@ -216,9 +217,9 @@ N_warmup_loops = 1000 else: N_warmup_loops = 100 -for i in tqdm(range(N_warmup_loops),disable=not verbose): +for i in tqdm.trange(N_warmup_loops, disable=not verbose): espresso_system.integrator.run(steps=1000) - grxmc.reaction(reaction_steps=100) + do_reaction(grxmc, steps=100) # Main loop @@ -234,9 +235,9 @@ N_production_loops = 5000 else: N_production_loops = 100 -for i in tqdm(range(N_production_loops),disable=not verbose): +for i in tqdm.trange(N_production_loops, disable=not verbose): espresso_system.integrator.run(steps=1000) - grxmc.reaction(reaction_steps=100) + do_reaction(grxmc, steps=100) # Measure time time_series["time"].append(espresso_system.time) diff --git a/samples/README.md b/samples/README.md index d5fbcc2..8b0382f 100644 --- a/samples/README.md +++ b/samples/README.md @@ -2,9 +2,9 @@ ## Production scripts Production scripts show examples on how to setup various systems with pyMBE and ESPResSo. -These scripts sample the systems for a specific set of conditions and take as argparse arguments various inputs for the simulations (for example, the pH of the solution). -When run, the production script collect the time series of various quantities and store them in CSV files for later postprocessing. -Such CSV files are systematically named using the input argparse arguments, allowing to backtrace from which specific system are the corresponding time series. +These scripts sample the systems for a specific set of conditions and take as command-line arguments various inputs for the simulations (for example, the pH of the solution). +When run, the production scripts collect the time series of various quantities and store them in CSV files for future postprocessing. +Such CSV files are systematically named using the input arguments, allowing to backtrace from which specific system are the corresponding time series. Examples of production scripts are: `branched_polyampholyte.py`, `peptide_cpH.py`, `peptide_mixture_grxmc_ideal.py` and `salt_solution_gcmc.py`. ## Analysis scripts @@ -17,7 +17,7 @@ Examples of analysis scripts are: `analyze_time_series.py`. Plotting scripts show examples on how to plot data post-processed with the analysis scripts of pyMBE and on how to use the toolbox of pyMBE to calculate various analytical solutions. Examples of plotting scripts are: `plot_branched_polyampholyte.py`, `plot_peptide_cpH.py`, and `plot_peptide_mixture_grxmc_ideal.py`. -[^1]: Janke, W. (2002). Statistical analysis of simulations: Data correlations and error estimation. Quantum simulations of complex many-body systems: from theory to algorithms, 10, 423-445. +[^1]: Janke, W. (2002). Statistical analysis of simulations: Data correlations and error estimation. In: Quantum simulations of complex many-body systems: from theory to algorithms, NIC Series volume 10, pp. 423-445. isbn: 3-00-009057-6. URL: ## Example on how to use the pipeline The sample scripts are designed to be used in the following order: (i) production script, (ii) analysis script and (iii) plotting script. For example: diff --git a/samples/branched_polyampholyte.py b/samples/branched_polyampholyte.py index 31929a0..57b3d92 100644 --- a/samples/branched_polyampholyte.py +++ b/samples/branched_polyampholyte.py @@ -20,12 +20,16 @@ from pathlib import Path import espressomd import argparse +import tqdm import pandas as pd from espressomd.io.writer import vtf import pyMBE -# Load some functions from the handy_scripts library for convinience -from lib.handy_functions import setup_langevin_dynamics,minimize_espresso_system_energy,setup_electrostatic_interactions +# Load some functions from the handy_scripts library for convenience +from lib.handy_functions import setup_langevin_dynamics +from lib.handy_functions import minimize_espresso_system_energy +from lib.handy_functions import setup_electrostatic_interactions +from lib.handy_functions import do_reaction from lib.analysis import built_output_name # Create an instance of pyMBE library @@ -47,7 +51,7 @@ default=False, action='store_true', help='to run a short simulation for testing the script') -parser.add_argument('--no_verbose', action='store_false', help="Switch to deactivate verbose",default=True) +parser.add_argument('--no_verbose', action='store_false', help="Switch to deactivate verbosity",default=True) args = parser.parse_args() # The trajectories of the simulations will be stored using espresso built-up functions in separed files in the folder 'frames' @@ -152,7 +156,7 @@ # Add all bonds to espresso system pmb.add_bonds_to_espresso(espresso_system=espresso_system) -# Create your molecules icph_ideal_tests.pynto the espresso system +# Create your molecules into the espresso system pmb.create_pmb_object(name="polyampholyte", number_of_objects=N_polyampholyte_chains, espresso_system=espresso_system, @@ -169,20 +173,20 @@ c_salt=c_salt, verbose=verbose) -#List of ionisible groups +#List of ionisable groups basic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='basic')].name.to_list() acidic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='acidic')].name.to_list() -list_ionisible_groups = basic_groups + acidic_groups -total_ionisible_groups = len (list_ionisible_groups) +list_ionisable_groups = basic_groups + acidic_groups +total_ionisable_groups = len(list_ionisable_groups) if verbose: - print("The box length of your system is", L.to('reduced_length'), L.to('nm')) - print('The polyampholyte concentration in your system is ', calculated_polyampholyte_concentration.to('mol/L') , 'with', N_polyampholyte_chains, 'molecules') - print('The ionisable groups in your polyampholyte are ', list_ionisible_groups) + print(f"The box length of your system is {L.to('reduced_length')}, {L.to('nm')}") + print(f"The polyampholyte concentration in your system is {calculated_polyampholyte_concentration.to('mol/L')} with {N_polyampholyte_chains} molecules") + print(f"The ionisable groups in your polyampholyte are {list_ionisable_groups}") cpH, labels = pmb.setup_cpH(counter_ion=cation_name, constant_pH=pH_value) if verbose: - print('The acid-base reaction has been sucessfully setup for ', labels) + print(f"The acid-base reaction has been successfully set up for {labels}") # Setup espresso to track the ionization of the acid/basic groups type_map = pmb.get_type_map() @@ -193,7 +197,7 @@ non_interacting_type = max(type_map.values())+1 cpH.set_non_interacting_type (type=non_interacting_type) if verbose: - print('The non interacting type is set to ', non_interacting_type) + print(f"The non interacting type is set to {non_interacting_type}") if not ideal: ##Setup the potential energy @@ -234,10 +238,10 @@ # Production loop N_frame=0 -for step in range(N_samples): +for step in tqdm.trange(N_samples): espresso_system.integrator.run(steps=MD_steps_per_sample) - cpH.reaction( reaction_steps = total_ionisible_groups) + do_reaction(cpH, steps=total_ionisable_groups) # Get polyampholyte net charge diff --git a/samples/peptide_cpH.py b/samples/peptide_cpH.py index bf054fd..023ebdf 100644 --- a/samples/peptide_cpH.py +++ b/samples/peptide_cpH.py @@ -19,7 +19,7 @@ # Load espresso, pyMBE and other necessary libraries import espressomd import pandas as pd -from tqdm import tqdm +import tqdm from espressomd.io.writer import vtf from pathlib import Path import pyMBE @@ -28,8 +28,11 @@ # Create an instance of pyMBE library pmb = pyMBE.pymbe_library(seed=42) -# Load some functions from the handy_scripts library for convinience -from lib.handy_functions import setup_electrostatic_interactions, minimize_espresso_system_energy,setup_langevin_dynamics +# Load some functions from the handy_scripts library for convenience +from lib.handy_functions import setup_electrostatic_interactions +from lib.handy_functions import minimize_espresso_system_energy +from lib.handy_functions import setup_langevin_dynamics +from lib.handy_functions import do_reaction from lib.analysis import built_output_name parser = argparse.ArgumentParser(description='Sample script to run the pre-made peptide models with pyMBE') @@ -50,7 +53,7 @@ default=False, action='store_true', help='to run a short simulation for testing the script') -parser.add_argument('--no_verbose', action='store_false', help="Switch to deactivate verbose",default=True) +parser.add_argument('--no_verbose', action='store_false', help="Switch to deactivate verbosity",default=True) args = parser.parse_args() # The trajectories of the simulations will be stored using espresso built-up functions in separed files in the folder 'frames' @@ -153,20 +156,20 @@ vtf.writevsf(espresso_system, coordinates) vtf.writevcf(espresso_system, coordinates) -#List of ionisible groups +#List of ionisable groups basic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='basic')].name.to_list() acidic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='acidic')].name.to_list() -list_ionisible_groups = basic_groups + acidic_groups -total_ionisible_groups = len (list_ionisible_groups) +list_ionisable_groups = basic_groups + acidic_groups +total_ionisable_groups = len(list_ionisable_groups) if verbose: - print("The box length of your system is", L.to('reduced_length'), L.to('nm')) - print('The peptide concentration in your system is ', calculated_peptide_concentration.to('mol/L') , 'with', N_peptide_chains, 'peptides') - print('The ionisable groups in your peptide are ', list_ionisible_groups) + print(f"The box length of your system is {L.to('reduced_length')} {L.to('nm')}") + print(f"The peptide concentration in your system is {calculated_peptide_concentration.to('mol/L')} with {N_peptide_chains} peptides") + print(f"The ionisable groups in your peptide are {list_ionisable_groups}") cpH, labels = pmb.setup_cpH(counter_ion=cation_name, constant_pH=pH_value) if verbose: - print('The acid-base reaction has been sucessfully setup for ', labels) + print(f"The acid-base reaction has been successfully setup for {labels}") # Setup espresso to track the ionization of the acid/basic groups in peptide type_map =pmb.get_type_map() @@ -220,12 +223,12 @@ # Main loop for performing simulations at different pH-values N_frame=0 -for sample in tqdm(range(N_samples)): +for sample in tqdm.trange(N_samples): # LD sampling of the configuration space espresso_system.integrator.run(steps=MD_steps_per_sample) # cpH sampling of the reaction space - cpH.reaction( reaction_steps = total_ionisible_groups) # rule of thumb: one reaction step per titratable group (on average) + do_reaction(cpH, steps=total_ionisable_groups) # rule of thumb: one reaction step per titratable group (on average) # Get peptide net charge charge_dict=pmb.calculate_net_charge(espresso_system=espresso_system, @@ -235,7 +238,7 @@ time_series["charge"].append(charge_dict["mean"]) if sample % N_samples_print == 0: N_frame+=1 - with open('frames/trajectory'+str(N_frame)+'.vtf', mode='w+t') as coordinates: + with open(f'frames/trajectory{N_frame}.vtf', mode='w+t') as coordinates: vtf.writevsf(espresso_system, coordinates) vtf.writevcf(espresso_system, coordinates) diff --git a/samples/peptide_mixture_grxmc_ideal.py b/samples/peptide_mixture_grxmc_ideal.py index a00a762..d0871f8 100644 --- a/samples/peptide_mixture_grxmc_ideal.py +++ b/samples/peptide_mixture_grxmc_ideal.py @@ -24,18 +24,19 @@ from espressomd.io.writer import vtf import pyMBE from lib.analysis import built_output_name +from lib.handy_functions import do_reaction # Create an instance of pyMBE library pmb = pyMBE.pymbe_library(seed=42) # Command line arguments -valid_modes=["standard", "unified"] parser = argparse.ArgumentParser(description='Script that runs a simulation of an ideal peptide mixture in the grand-reaction ensemble using pyMBE and ESPResSo.') parser.add_argument('--mode', type=str, default= "standard", - help='Set if the grand-reaction method is used with unified ions or not, valid modes are {valid_modes}') + choices=["standard", "unified"], + help='Set if the grand-reaction method is used with unified ions or not') parser.add_argument('--test', default=False, action='store_true', @@ -60,9 +61,6 @@ parser.add_argument('--no_verbose', action='store_false', help="Switch to deactivate verbose",default=True) args = parser.parse_args() -if args.mode not in valid_modes: - raise ValueError(f"Mode {args.mode} is not currently supported, valid modes are {valid_modes}") - # The trajectories of the simulations will be stored using espresso built-up functions in separed files in the folder 'frames' Path("./frames").mkdir(parents=True, exist_ok=True) @@ -228,8 +226,8 @@ #List of ionisable groups basic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='basic')].name.to_list() acidic_groups = pmb.df.loc[(~pmb.df['particle_id'].isna()) & (pmb.df['acidity']=='acidic')].name.to_list() -list_ionisible_groups = basic_groups + acidic_groups -total_ionisible_groups = len (list_ionisible_groups) +list_ionisable_groups = basic_groups + acidic_groups +total_ionisable_groups = len (list_ionisable_groups) # Get peptide net charge if verbose: print("The box length of your system is", L.to('reduced_length'), L.to('nm')) @@ -285,7 +283,7 @@ N_frame=0 for step in range(N_samples): espresso_system.integrator.run(steps=MD_steps_per_sample) - grxmc.reaction(reaction_steps = total_ionisible_groups) + do_reaction(grxmc, steps=total_ionisable_groups) time_series["time"].append(espresso_system.time) # Get net charge of peptide1 and peptide2 charge_dict_peptide1=pmb.calculate_net_charge(espresso_system=espresso_system, diff --git a/samples/plot_branched_polyampholyte.py b/samples/plot_branched_polyampholyte.py index 59e03e9..7a2c68e 100644 --- a/samples/plot_branched_polyampholyte.py +++ b/samples/plot_branched_polyampholyte.py @@ -41,13 +41,10 @@ type=str, required= False, default="plot", - help='mode to execute the script available options are: store_HH (stores the analytical HH solution, used for testing) and plot (produces the plots)') + choices=["plot", "store_HH"], + help='mode to execute the script; available options are: store_HH (stores the analytical HH solution, used for testing) and plot (produces the plots)') args = parser.parse_args() -valid_modes = ["plot","store_HH"] -if args.mode not in valid_modes: - raise ValueError(f"mode {args.mode} is not supported, supported modes are {valid_modes}. Please check the docs for more information.") - # Define the molecule (necessary to calculate the HH analytical solution with pyMBE) # Acidic particle diff --git a/samples/plot_peptide_cpH.py b/samples/plot_peptide_cpH.py index 2e7f8bc..db9004c 100644 --- a/samples/plot_peptide_cpH.py +++ b/samples/plot_peptide_cpH.py @@ -45,17 +45,14 @@ type=str, required= False, default="plot", - help='mode to execute the script available options are: store_HH (stores the analytical HH solution, used for testing) and plot (produces the plots)') + choices=["plot", "store_HH"], + help='mode to execute the script; available options are: store_HH (stores the analytical HH solution, used for testing) and plot (produces the plots)') args = parser.parse_args() -valid_modes = ["plot","store_HH"] -if args.mode not in valid_modes: - raise ValueError(f"mode {args.mode} is not supported, supported modes are {valid_modes}. Please check the docs for more information.") - # Define peptide parameters sequence = args.sequence # Define the peptide in the pyMBE dataframe and load the pka set -# This is necesary to calculate the analytical solution from the Henderson-Hasselbach equation +# This is necessary to calculate the analytical solution from the Henderson-Hasselbach equation peptide = 'generic_peptide' pmb.define_peptide (name=peptide, sequence=sequence, diff --git a/samples/plot_peptide_mixture_grxmc_ideal.py b/samples/plot_peptide_mixture_grxmc_ideal.py index 32222ea..6aff771 100644 --- a/samples/plot_peptide_mixture_grxmc_ideal.py +++ b/samples/plot_peptide_mixture_grxmc_ideal.py @@ -57,16 +57,14 @@ type=str, required= False, default="plot", - help='mode to execute the script available options are: store_HH (stores the analytical HH solution, used for testing) and plot (produces the plots)') + choices=["plot", "store_HH"], + help='mode to execute the script; available options are: store_HH (stores the analytical HH solution, used for testing) and plot (produces the plots)') args = parser.parse_args() + # Create an instance of pyMBE library pmb = pyMBE.pymbe_library(seed=42) c_salt=args.csalt * pmb.units.mol/ pmb.units.L -valid_modes = ["plot","store_HH"] -if args.mode not in valid_modes: - raise ValueError(f"mode {args.mode} is not supported, supported modes are {valid_modes}. Please check the docs for more information.") - # Define peptide parameters sequence1 = args.sequence1 pep1_concentration = args.pep1_conc *pmb.units.mol/pmb.units.L diff --git a/samples/salt_solution_gcmc.py b/samples/salt_solution_gcmc.py index 4247aaf..35ea2b4 100644 --- a/samples/salt_solution_gcmc.py +++ b/samples/salt_solution_gcmc.py @@ -23,7 +23,7 @@ import pandas as pd from scipy import interpolate import argparse -from tqdm import tqdm +import tqdm # Import pyMBE import pyMBE @@ -38,10 +38,12 @@ parser = argparse.ArgumentParser(description='Script that runs a simulation of a salt solution in the grand-canonical ensemble using pyMBE and ESPResSo.') parser.add_argument("--c_salt_res", type=float, + required=True, help="Concentration of salt in the reservoir in mol/l.") parser.add_argument('--mode', type=str, default= "ideal", + choices=["ideal", "interacting"], help='Set if an ideal or interacting system is simulated.') parser.add_argument('--output', type=str, @@ -57,17 +59,14 @@ inputs={"csalt": args.c_salt_res, "mode": args.mode} -mode=args.mode -valid_modes=["ideal", "interacting"] verbose=args.no_verbose -if args.mode not in valid_modes: - raise ValueError(f"Mode {mode} is not currently supported, valid modes are {valid_modes}") - #Import functions from handy_functions script from lib.handy_functions import minimize_espresso_system_energy from lib.handy_functions import setup_electrostatic_interactions from lib.handy_functions import setup_langevin_dynamics +from lib.handy_functions import get_number_of_particles +from lib.handy_functions import do_reaction @@ -140,9 +139,9 @@ if verbose: print("Running warmup without electrostatics") -for i in tqdm(range(100),disable=not verbose): +for i in tqdm.trange(100, disable=not verbose): espresso_system.integrator.run(steps=100) - RE.reaction(reaction_steps=100) + do_reaction(RE, steps=100) if args.mode == "interacting": setup_electrostatic_interactions(units=pmb.units, @@ -162,9 +161,9 @@ print("Running warmup with electrostatics") N_warmup_loops = 100 -for i in tqdm(range(N_warmup_loops),disable=not verbose): +for i in tqdm.trange(N_warmup_loops, disable=not verbose): espresso_system.integrator.run(steps=100) - RE.reaction(reaction_steps=100) + do_reaction(RE, steps=100) # Main loop print("Started production run.") @@ -176,15 +175,15 @@ time_series[label]=[] N_production_loops = 100 -for i in tqdm(range(N_production_loops),disable=not verbose): +for i in tqdm.trange(N_production_loops, disable=not verbose): espresso_system.integrator.run(steps=100) - RE.reaction(reaction_steps=100) + do_reaction(RE, steps=100) # Measure time time_series["time"].append(espresso_system.time) # Measure degree of ionization - number_of_ion_pairs = espresso_system.number_of_particles(type_map[cation_name]) + number_of_ion_pairs = get_number_of_particles(espresso_system, type_map[cation_name]) time_series["c_salt"].append((number_of_ion_pairs/(volume * pmb.N_A)).magnitude) data_path = args.output diff --git a/testsuite/define_and_create_molecules_unit_tests.py b/testsuite/define_and_create_molecules_unit_tests.py index 435309b..79060a0 100644 --- a/testsuite/define_and_create_molecules_unit_tests.py +++ b/testsuite/define_and_create_molecules_unit_tests.py @@ -125,11 +125,12 @@ # Create an instance of an espresso system espresso_system=espressomd.System(box_l = [10]*3) particle_positions=[[0,0,0],[1,1,1]] -pmb.create_particle(name="S1", - espresso_system=espresso_system, - number_of_particles=2, - fix=True, - position=particle_positions) +retval = pmb.create_particle(name="S1", + espresso_system=espresso_system, + number_of_particles=2, + fix=True, + position=particle_positions) +np.testing.assert_array_equal(retval, [0, 1]) particle_ids=pmb.get_particle_id_map(object_name="S1")["all"] type_map=pmb.get_type_map() @@ -153,12 +154,11 @@ print("*** Unit test: check that create_particle() does not create any particle for number_of_particles <= 0 ***") starting_number_of_particles=len(espresso_system.part.all()) -pmb.create_particle(name="S1", - espresso_system=espresso_system, - number_of_particles=0) -pmb.create_particle(name="S1", - espresso_system=espresso_system, - number_of_particles=-1) +for number_of_particles in [0, -1]: + retval = pmb.create_particle(name="S1", + espresso_system=espresso_system, + number_of_particles=number_of_particles) + np.testing.assert_equal(len(retval), 0) # If no particles have been created, only two particles should be in the system (from the previous test) np.testing.assert_equal(actual=len(espresso_system.part.all()), desired=starting_number_of_particles, diff --git a/testsuite/globular_protein_unit_tests.py b/testsuite/globular_protein_unit_tests.py index 60b0bdf..8f83125 100644 --- a/testsuite/globular_protein_unit_tests.py +++ b/testsuite/globular_protein_unit_tests.py @@ -147,7 +147,6 @@ def custom_deserializer(dct): print("*** Unit test: check that create_protein() creates all the particles in the protein into the espresso_system with the properties defined in pmb.df ***") espresso_system=espressomd.System(box_l = [Box_L.to('reduced_length').magnitude] * 3) -espresso_system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() # Here we upload the pka set from the reference_parameters folder path_to_pka=pmb.get_resource('parameters/pka_sets/Nozaki1967.json') diff --git a/testsuite/setup_salt_ions_unit_tests.py b/testsuite/setup_salt_ions_unit_tests.py index 1e98282..d2e8eba 100644 --- a/testsuite/setup_salt_ions_unit_tests.py +++ b/testsuite/setup_salt_ions_unit_tests.py @@ -18,6 +18,7 @@ import numpy as np import espressomd +from lib.handy_functions import get_number_of_particles # Create an instance of pyMBE library import pyMBE pmb = pyMBE.pymbe_library(seed=42) @@ -41,22 +42,22 @@ # Create an instance of an espresso system espresso_system=espressomd.System (box_l = [L.to('reduced_length').magnitude]*3) -espresso_system.setup_type_map(type_map.values()) +espresso_system.setup_type_map(type_list=type_map.values()) #### Unit tests for the added salt def check_salt_concentration(espresso_system,cation_name,anion_name,c_salt,N_SALT_ION_PAIRS, verbose=False): charge_number_map=pmb.get_charge_number_map() type_map=pmb.get_type_map() - espresso_system.setup_type_map(type_map.values()) + espresso_system.setup_type_map(type_list=type_map.values()) c_salt_calculated = pmb.create_added_salt(espresso_system=espresso_system, cation_name=cation_name, anion_name=anion_name, c_salt=c_salt, verbose=verbose) - np.testing.assert_equal(espresso_system.number_of_particles(type_map[cation_name]),N_SALT_ION_PAIRS*abs(charge_number_map[type_map[anion_name]])) - np.testing.assert_equal(espresso_system.number_of_particles(type_map[anion_name]),N_SALT_ION_PAIRS*abs(charge_number_map[type_map[cation_name]])) + np.testing.assert_equal(get_number_of_particles(espresso_system, type_map[cation_name]),N_SALT_ION_PAIRS*abs(charge_number_map[type_map[anion_name]])) + np.testing.assert_equal(get_number_of_particles(espresso_system, type_map[anion_name]),N_SALT_ION_PAIRS*abs(charge_number_map[type_map[cation_name]])) np.testing.assert_almost_equal(c_salt_calculated.m_as("mol/L"), c_salt.m_as("mol/L")) espresso_system.part.clear() print("*** Unit test: test that create_added_salt works for a 1:1 salt (NaCl-like). Should print the added salt concentration and number of ions ***") @@ -90,13 +91,13 @@ def check_salt_concentration(espresso_system,cation_name,anion_name,c_salt,N_SAL print("*** Unit test passed***") print("*** Unit test: check that create_added_salt works for an input c_salt in [particle/lenght**3]. Should print the concentration and number of ions") c_salt_part=c_salt_input*pmb.N_A -espresso_system.setup_type_map(type_map.values()) +espresso_system.setup_type_map(type_list=type_map.values()) c_salt_calculated = pmb.create_added_salt(espresso_system=espresso_system, cation_name="Na", anion_name="Cl", c_salt=c_salt_part) -np.testing.assert_equal(espresso_system.number_of_particles(type_map["Na"]),N_SALT_ION_PAIRS) -np.testing.assert_equal(espresso_system.number_of_particles(type_map["Cl"]),N_SALT_ION_PAIRS) +np.testing.assert_equal(get_number_of_particles(espresso_system, type_map["Na"]),N_SALT_ION_PAIRS) +np.testing.assert_equal(get_number_of_particles(espresso_system, type_map["Cl"]),N_SALT_ION_PAIRS) np.testing.assert_almost_equal(c_salt_calculated.m_as("reduced_length**-3"), c_salt_part.m_as("reduced_length**-3")) espresso_system.part.clear() @@ -181,9 +182,9 @@ def test_counterions(molecule_name, cation_name, anion_name, espresso_system, ex anion_name=anion_name, espresso_system=espresso_system, verbose=verbose) - espresso_system.setup_type_map(type_map.values()) - np.testing.assert_equal(espresso_system.number_of_particles(type_map[cation_name]),expected_numbers[cation_name]) - np.testing.assert_equal(espresso_system.number_of_particles(type_map[anion_name]),expected_numbers[anion_name]) + espresso_system.setup_type_map(type_list=type_map.values()) + np.testing.assert_equal(get_number_of_particles(espresso_system, type_map[cation_name]),expected_numbers[cation_name]) + np.testing.assert_equal(get_number_of_particles(espresso_system, type_map[anion_name]),expected_numbers[anion_name]) pmb.destroy_pmb_object_in_system(espresso_system=espresso_system, name=molecule_name) espresso_system.part.clear() @@ -276,7 +277,7 @@ def test_counterions(molecule_name, cation_name, anion_name, espresso_system, ex anion_name="Cl", espresso_system=espresso_system, verbose=False) -espresso_system.setup_type_map(type_map.values()) -np.testing.assert_equal(espresso_system.number_of_particles(type_map["Na"]),0) -np.testing.assert_equal(espresso_system.number_of_particles(type_map["Cl"]),0) +espresso_system.setup_type_map(type_list=type_map.values()) +np.testing.assert_equal(get_number_of_particles(espresso_system, type_map["Na"]),0) +np.testing.assert_equal(get_number_of_particles(espresso_system, type_map["Cl"]),0) print("*** Unit test passed ***") diff --git a/tutorials/lattice_builder.ipynb b/tutorials/lattice_builder.ipynb index 6acc6c2..1238153 100644 --- a/tutorials/lattice_builder.ipynb +++ b/tutorials/lattice_builder.ipynb @@ -7,13 +7,7 @@ "source": [ "# Lattice builder\n", "\n", - "Show the basic functionality of the lattice builder by creating a hydrogel network based on the diamond lattice.\n", - "\n", - "To run the 3D viewer in a Jupyter notebook, the following dependency is required:\n", - "\n", - "```sh\n", - "python3 -m pip install \"ipympl>=0.9.3\"\n", - "```" + "Show the basic functionality of the lattice builder by creating a hydrogel network based on the diamond lattice." ] }, { diff --git a/tutorials/pyMBE_tutorial.ipynb b/tutorials/pyMBE_tutorial.ipynb index 465a23e..70d173f 100644 --- a/tutorials/pyMBE_tutorial.ipynb +++ b/tutorials/pyMBE_tutorial.ipynb @@ -53,7 +53,7 @@ "\n", "# Only necesary to produce the pictures used in this tutorial\n", "from lib.handy_functions import do_snapshot_espresso_system\n", - "from PIL import Image\n" + "from PIL import Image" ] }, { @@ -284,6 +284,7 @@ "metadata": {}, "source": [ "pyMBE can be used to easily construct coarse-grained models of simple polymers. Let us consider a coarse grained model for polydehydroalanaline (PDha) (figure below) in which its monomeric unit can be represented by three beads, as depicted in the schematics below: a backbone bead (grey), a bead for the carboxylic acid group (red) and a bead for the amino group (blue).\n", + "\n", "\n" ] }, @@ -1167,7 +1168,7 @@ " model = model)\n", "\n", "print('one letter code', pmb.protein_sequence_parser(sequence=sequence))\n", - "print('defined peptide sequence ', sequence)\n" + "print('defined peptide sequence ', sequence)" ] }, {