Skip to content

Commit

Permalink
Add Codecov (#57)
Browse files Browse the repository at this point in the history
* Split workflows

* Add Codecov

* Add unit tests
  • Loading branch information
jngrad committed May 17, 2024
1 parent 9b6cc37 commit 37491e8
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 21 deletions.
26 changes: 26 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
codecov:
branch: main
notify:
require_ci_to_pass: yes

coverage:
precision: 0
round: down
range: "70...100"
status:
project:
default:
enabled: yes
threshold: 5
patch:
default:
enabled: yes
threshold: 5%
changes: no

comment: false

ignore:
- "samples"
- "maintainer"
25 changes: 25 additions & 0 deletions .github/actions/dependencies/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: 'dependencies'
description: 'Install pyMBE dependencies'
inputs:
extra-python-packages:
description: Newline-separated list of arguments for pip.
required: false
modules:
description: Newline-separated list of arguments for module load.
required: true
runs:
using: "composite"
steps:
- run: |
module load ${{ inputs.modules }}
module save pymbe
python3 -m venv --system-site-packages venv
source venv/bin/activate
python3 maintainer/configure_venv.py
echo -e "\n" >> requirements.txt
echo "${{ inputs.extra-python-packages }}" >> requirements.txt
python3 -m pip install -r requirements.txt
git checkout requirements.txt
deactivate
module purge
shell: bash
35 changes: 35 additions & 0 deletions .github/workflows/samples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: samples

on:
schedule:
- cron: '20 6 5,20 * *' # biweekly at 06:20 AM UTC+00 (on the 5th and 20th of the month)
workflow_dispatch: # manual trigger

permissions:
contents: read # to fetch code (actions/checkout)

jobs:
samples:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'schedule' && github.repository == 'pyMBE-dev/pyMBE' || github.event_name != 'schedule' }}
env:
FI_PROVIDER: "^psm3,psm3;ofi_rxd"
OMPI_MCA_mtl_ofi_provider_exclude: psm3
steps:
- name: Setup EESSI
uses: eessi/github-action-eessi@v3
with:
eessi_stack_version: "2023.06"
- name: Checkout repository
uses: actions/checkout@main
- name: Install dependencies
uses: ./.github/actions/dependencies
with:
modules: |-
ESPResSo/4.2.1-foss-2023a
- name: Run testsuite
run: |
module restore pymbe
source venv/bin/activate
make functional_tests
shell: bash
35 changes: 23 additions & 12 deletions .github/workflows/testsuite.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: run tests
name: testsuite

on:
push:
Expand All @@ -21,22 +21,22 @@ jobs:
- name: Checkout repository
uses: actions/checkout@main
- name: Install dependencies
run: |
module load ESPResSo/4.2.1-foss-2023a
python3 -m venv --system-site-packages venv
source venv/bin/activate
python3 maintainer/configure_venv.py
python3 -m pip install -r requirements.txt
python3 -m pip install "pdoc==14.3" "pylint==3.0.3"
deactivate
uses: ./.github/actions/dependencies
with:
modules: |-
ESPResSo/4.2.1-foss-2023a
extra-python-packages: |-
pdoc==14.3
pylint==3.0.3
coverage==7.4.4
- name: Run testsuite
run: |
module load ESPResSo/4.2.1-foss-2023a
module restore pymbe
source venv/bin/activate
make pylint
make tests
make unit_tests COVERAGE=1
make docs
deactivate
make coverage_xml
shell: bash
- name: Upload artifact
uses: actions/upload-artifact@v4
Expand All @@ -45,3 +45,14 @@ jobs:
name: documentation
retention-days: 2
if-no-files-found: error
- name: Upload coverage to Codecov
if: ${{ github.repository == 'pyMBE-dev/pyMBE' }}
uses: codecov/codecov-action@v4
with:
file: "./coverage.xml"
disable_search: true
env_vars: OS,PYTHON
fail_ci_if_error: false
flags: unittests
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
26 changes: 25 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@
.PHONY: visual
.PHONY: clean

# whether to run unit tests with code coverage
COVERAGE = 0

# output directory for the code coverage HTML files
COVERAGE_HTML = coverage

# Python executable or launcher, possibly with command line arguments
PYTHON = python3
ifeq ($(COVERAGE),1)
PYTHON := ${PYTHON} -m coverage run --parallel-mode --source=$(CURDIR)
endif

docs:
mkdir -p ./documentation
PDOC_ALLOW_EXEC=0 ${PYTHON} -m pdoc ./pyMBE.py -o ./documentation --docformat google

tests:
unit_tests:
${PYTHON} testsuite/serialization_test.py
${PYTHON} testsuite/lj_tests.py
${PYTHON} testsuite/set_particle_acidity_test.py
${PYTHON} testsuite/bond_tests.py
Expand All @@ -19,13 +30,26 @@ tests:
${PYTHON} testsuite/read-write-df_test.py
${PYTHON} testsuite/parameter_test.py
${PYTHON} testsuite/henderson_hasselbalch_tests.py

functional_tests:
${PYTHON} testsuite/cph_ideal_tests.py
${PYTHON} testsuite/grxmc_ideal_tests.py
${PYTHON} testsuite/peptide_tests.py
${PYTHON} testsuite/gcmc_tests.py
${PYTHON} testsuite/weak_polyelectrolyte_dialysis_test.py
${PYTHON} testsuite/globular_protein_tests.py

tests: unit_tests functional_tests

coverage_xml:
${PYTHON} -m coverage combine .
${PYTHON} -m coverage report
${PYTHON} -m coverage xml

coverage_html:
${PYTHON} -m coverage combine .
${PYTHON} -m coverage html --directory="${COVERAGE_HTML}"

sample:
${PYTHON} samples/peptide.py

Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# pyMBE: the Python-based Molecule Builder for ESPResSo

![GitHub Actions](https://github.com/pyMBE-dev/pyMBE/actions/workflows/testsuite.yml/badge.svg)
[![codecov](https://codecov.io/gh/pyMBE-dev/pyMBE/branch/codecov/graph/badge.svg)](https://codecov.io/gh/pyMBE-dev/pyMBE)

pyMBE provides tools to facilitate building up molecules with complex architectures in the Molecular Dynamics software [ESPResSo](https://espressomd.org/wordpress/). Some examples of molecules that can be set up with pyMBE are polyelectrolytes, peptides and proteins. pyMBE bookkeeps all the information about the molecule topology, permitting to link each particle to its corresponding residue and molecule. pyMBE uses the [Pint](https://pint.readthedocs.io/en/stable/) library to enable input parameters in any arbitrary unit system, which is later transformed in the reduced unit system used in ESPResSo.

## Dependencies
Expand Down Expand Up @@ -47,8 +50,8 @@ git clone [email protected]: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.

The pyMBE module uses its own Python virtual enviroment to avoid incompatibility issues when loading its requierements from other libraries.
The Python module (`venv`)[https://docs.python.org/3/library/venv.html#module-venv] from the Python Standard Library (starting with Python 3.3) is needed to set up pyMBE.
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:

Expand Down
3 changes: 1 addition & 2 deletions lib/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def get_params_from_dir_name(name):
entries = name.split('_')
params = {}
for entry in entries:
sp_entry = entry.split('-')
sp_entry = entry.split('-', 1)
params[sp_entry[0]] = sp_entry[-1] #float(sp_entry[-1]) # creates a dictionary of parameters and their values.
return params

Expand Down Expand Up @@ -343,7 +343,6 @@ def get_distribution_from_df(df, key):
distribution_list (`lst`): list stored under `key`
"""
import pandas as pd
distribution_list=[]
for row in df[key]:
if pd.isnull(row):
Expand Down
8 changes: 4 additions & 4 deletions pyMBE.py
Original file line number Diff line number Diff line change
Expand Up @@ -2157,10 +2157,10 @@ def print_reduced_units(self):
unit_length=self.units.Quantity(1,'reduced_length')
unit_energy=self.units.Quantity(1,'reduced_energy')
unit_charge=self.units.Quantity(1,'reduced_charge')
print(unit_length.to('nm'), "=", unit_length)
print(unit_energy.to('J'), "=", unit_energy)
print('Temperature:', (self.kT/self.Kb).to("K"))
print(unit_charge.to('C'), "=", unit_charge)
print(f"{unit_length.to('nm'):.5g} = {unit_length}")
print(f"{unit_energy.to('J'):.5g} = {unit_energy}")
print(f"{unit_charge.to('C'):.5g} = {unit_charge}")
print(f"Temperature: {(self.kT/self.Kb).to('K'):.5g}")
print()

def propose_unused_type(self):
Expand Down
52 changes: 52 additions & 0 deletions testsuite/serialization_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import io
import json
import contextlib
import unittest as ut
import numpy as np
import pandas as pd
import pyMBE
import lib.analysis


class Serialization(ut.TestCase):

def test_json_encoder(self):
encoder = pyMBE.pymbe_library.NumpyEncoder
# Python types
self.assertEqual(json.dumps(1, cls=encoder), "1")
self.assertEqual(json.dumps([1, 2], cls=encoder), "[1, 2]")
self.assertEqual(json.dumps((1, 2), cls=encoder), "[1, 2]")
self.assertEqual(json.dumps({1: 2}, cls=encoder), """{"1": 2}""")
# NumPy types
self.assertEqual(json.dumps(np.array([1, 2]), cls=encoder), "[1, 2]")
self.assertEqual(json.dumps(np.array(1), cls=encoder), "1")
self.assertEqual(json.dumps(np.int32(1), cls=encoder), "1")
# Pandas types
with self.assertRaisesRegex(TypeError, "Object of type Series is not JSON serializable"):
json.dumps(pd.Series([1, 2]), cls=encoder)

def test_parameters_to_path(self):
params = {"kT": 2., "phi": -np.pi, "n": 3, "fene": True, "name": "pep"}
name = lib.analysis.built_output_name(params)
self.assertEqual(name, "kT-2_phi--3.14_n-3_fene-True_name-pep")
params_out = lib.analysis.get_params_from_dir_name(name)
params_ref = {"kT": "2", "phi": "-3.14", "n": "3",
"fene": "True", "name": "pep"}
self.assertEqual(params_out, params_ref)

def test_pint_units(self):
ref_output = [
"Current set of reduced units:",
"0.355 nanometer = 1 reduced_length",
"4.1164e-21 joule = 1 reduced_energy",
"1.6022e-19 coulomb = 1 reduced_charge",
"Temperature: 298.15 kelvin",
]
pmb = pyMBE.pymbe_library(SEED=42)
with contextlib.redirect_stdout(io.StringIO()) as f:
pmb.print_reduced_units()
self.assertEqual(f.getvalue().strip("\n").split("\n"), ref_output)


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

0 comments on commit 37491e8

Please sign in to comment.