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
63 changes: 45 additions & 18 deletions spatialpy/core/species.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,6 @@ class Species():
:type restrict_to: int, str, list of ints or list of strs
"""
def __init__(self, name=None, diffusion_coefficient=None, restrict_to=None):
if name is None:
raise SpeciesError("Species must have a name")
if not isinstance(name, str):
raise SpeciesError("Species name must be a string")

if diffusion_coefficient is None:
raise SpeciesError("Species must have a diffusion_coefficient.")
if not (isinstance(diffusion_coefficient, (Parameter, str, float, int)) or \
type(diffusion_coefficient).__name__ == 'Parameter'):
raise SpeciesError("Diffusion coefficient must be a spatialpy.Parameter, str, float, or int.")
if isinstance(diffusion_coefficient, (float, int)) and diffusion_coefficient < 0:
raise SpeciesError("Diffusion coefficient must be non-negative.")

if not (restrict_to is None or isinstance(restrict_to, (str, int, list))):
raise SpeciesError("Restrict_to must be an int, str or list of ints or strs.")
if restrict_to is not None and isinstance(restrict_to, (int, str)):
Expand All @@ -58,6 +45,8 @@ def __init__(self, name=None, diffusion_coefficient=None, restrict_to=None):
for type_id in restrict_to:
self.restrict_to.append(f"type_{type_id}")

self.validate()

def __str__(self):
print_string = f"{self.name}: {str(self.diffusion_coefficient)}"
return print_string
Expand All @@ -71,10 +60,48 @@ def set_diffusion_coefficient(self, diffusion_coefficient):

:raises SpeciesError: If diffusion_coefficient is negative or not a valid type.
"""
if not (isinstance(diffusion_coefficient, (Parameter, str, float, int)) or \
type(diffusion_coefficient).__name__ == 'Parameter'):
raise SpeciesError("Diffusion coefficient must be a spatialpy.Parameter, str, float, or int.")
if diffusion_coefficient < 0:
raise SpeciesError("Diffusion coefficient must be non-negative.")
self.validate(diffusion_coefficient=diffusion_coefficient, coverage="diffusion_coefficient")

self.diffusion_coefficient = diffusion_coefficient

def validate(self, diffusion_coefficient=None, coverage="all"):
"""
Validate the species.

:param coverage: The scope of attributes to validate. Set to an attribute name to restrict validation \
to a specific attribute.
:type coverage: str

:raises SpeciesError: Attribute is of invalid type. Required attribute set to None. \
Attribute is value outside of accepted bounds.
"""
# Check name
if coverage in ("all", "name"):
if self.name is None:
raise SpeciesError("name can't be None type.")
if not isinstance(self.name, str):
raise SpeciesError(f"name must be of type str not {type(self.name)}.")
if self.name == "":
raise SpeciesError("name can't be an empty str.")

# Check diffusion coefficient
if coverage in ("all", "diffusion_coefficient"):
if coverage == "all" and diffusion_coefficient is None:
diffusion_coefficient = self.diffusion_coefficient

if diffusion_coefficient is None:
raise SpeciesError("diffusion_coefficient can't be None type.")
if not (isinstance(diffusion_coefficient, (Parameter, str, float, int)) or \
type(diffusion_coefficient).__name__ == 'Parameter'):
errmsg = "diffusion_coefficient must be of type SpatialPy.Parameter, "
errmsg += f"str, int, float not {type(diffusion_coefficient)}"
raise SpeciesError(errmsg)
if isinstance(diffusion_coefficient, (int, float)) and diffusion_coefficient < 0:
raise SpeciesError("diffusion_coefficient must be a positive value.")

# Check restrict_to
if coverage in ("all", "restrict_to"):
if not (self.restrict_to is None or isinstance(self.restrict_to, list)):
raise SpeciesError("restrict_to must be None or of type int, str, or list")
if self.restrict_to is not None and len(self.restrict_to) == 0:
raise SpeciesError("restrict_to can't be an empty list.")
119 changes: 81 additions & 38 deletions test/unit_tests/test_species.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,92 +25,135 @@ class TestSpecies(unittest.TestCase):
Unit tests for spatialpy.Species.
################################################################################################
'''
def setUp(self):
self.species = Species(name="test_species", diffusion_coefficient=0)

def test_constructor(self):
""" Test the Species constructor. """
species = Species(name="test_species", diffusion_coefficient=0)
self.assertEqual(species.name, "test_species")
self.assertEqual(species.diffusion_coefficient, 0)


def test_constructor__no_name(self):
""" Test the Species constructor without name. """
with self.assertRaises(SpeciesError):
species = Species(diffusion_coefficient=0)


def test_constructor__name_not_str(self):
""" Test the Species constructor with non-str name. """
with self.assertRaises(SpeciesError):
species = Species(name=0, diffusion_coefficient=0)

def test_constructor__invalid_name(self):
""" Test the Species constructor with an invalid name. """
test_names = ["", None, 0, 0.5, [0]]
for test_name in test_names:
with self.subTest(name=test_name):
with self.assertRaises(SpeciesError):
species = Species(name=test_name, diffusion_coefficient=0)

def test_constructor__no_diffusion_coefficient(self):
""" Test the Species constructor without diffusion_coefficient. """
with self.assertRaises(SpeciesError):
species = Species(name="test_species")


def test_constructor__negative_diffusion_coefficient(self):
""" Test the Species constructor with negative diffusion_coefficient. """
with self.assertRaises(SpeciesError):
species = Species(name="test_species", diffusion_coefficient=-1)


def test_constructor__parameter_diffusion_coefficient(self):
""" Test the Species constructor with a parameter for diffusion_coefficient. """
test_parameter = Parameter(name="test_parameter", expression=0.5)
species = Species(name="test_species", diffusion_coefficient=test_parameter)
self.assertIsInstance(species.diffusion_coefficient, Parameter)


def test_constructor__diffusion_coefficient_not_str_int_or_float(self):
""" Test the Species constructor with non-str, non-int, or non-float diffusion_coefficient. """
with self.assertRaises(SpeciesError):
species = Species(name="test_species", diffusion_coefficient=[0])


def test_constructor__restrict_to_not_accepted_type(self):
""" Test the Species constructor with a restrict_to that is not the proper type. """
with self.assertRaises(SpeciesError):
species = Species(name="test_species", diffusion_coefficient="0", restrict_to=1.5)

def test_constructor__invalid_diffusion_coefficient(self):
""" Test the Species constructor with an invalid diffusion_coefficient. """
test_dcs = [None, [0]]
for test_dc in test_dcs:
with self.subTest(initial_value=test_dc):
with self.assertRaises(SpeciesError):
species = Species(name="test_species", diffusion_coefficient=test_dc)

def test_constructor__invalid_restrict_to(self):
""" Test the Species constructor with an invalid restrict_to. """
test_rts = [0.5, [], (5, 2), {'x': 5}]
for test_rt in test_rts:
with self.subTest(restrict_to=test_rt):
with self.assertRaises(SpeciesError):
species = Species(name="test_species", diffusion_coefficient="0", restrict_to=test_rt)

def test_constructor__int_restrict_to(self):
""" Test the Species constructor with a int restrict_to. """
species = Species(name="test_species", diffusion_coefficient=0, restrict_to=1)
self.assertIsInstance(species.restrict_to, list)
self.assertEqual(species.restrict_to, ["type_1"])


def test_constructor__str_restrict_to(self):
""" Test the Species constructor with a string restrict_to. """
species = Species(name="test_species", diffusion_coefficient=0, restrict_to="Walls")
self.assertIsInstance(species.restrict_to, list)
self.assertEqual(species.restrict_to, ["type_Walls"])


def test___str___(self):
""" Test Species.__str__ method. """
species = Species(name="test_species", diffusion_coefficient=0)
self.assertIsInstance(str(species), str)

self.assertIsInstance(str(self.species), str)

def test_set_diffusion_coefficient(self):
""" Test Species.set_diffusion_coefficient method. """
species = Species(name="test_species", diffusion_coefficient=0)
species.set_diffusion_coefficient(diffusion_coefficient=1)
self.assertEqual(species.diffusion_coefficient, 1)
self.species.set_diffusion_coefficient(diffusion_coefficient=1)
self.assertEqual(self.species.diffusion_coefficient, 1)

def test_set_diffusion_coefficient__invalid_diffusion_coefficient(self):
""" Test Species.set_diffusion_coefficient method with an invalid diffusion_coefficient. """
test_dcs = [None, [1]]
for test_dc in test_dcs:
with self.subTest(diffusion_coefficient=test_dc):
with self.assertRaises(SpeciesError):
self.species.set_diffusion_coefficient(diffusion_coefficient=test_dc)

def test_set_diffusion_coefficient__negative_diffusion_coefficient(self):
""" Test Species.set_diffusion_coefficient method with negative diffusion_coefficient. """
species = Species(name="test_species", diffusion_coefficient=0)
with self.assertRaises(SpeciesError):
species.set_diffusion_coefficient(diffusion_coefficient=-1)


def test_set_diffusion_coefficient__diffusion_coefficient_not_str_int_or_float(self):
""" Test Species.set_diffusion_coefficient method with non-str, non-int, or non-float diffusion_coefficient. """
species = Species(name="test_species", diffusion_coefficient=0)
with self.assertRaises(SpeciesError):
species.set_diffusion_coefficient(diffusion_coefficient=[0])
self.species.set_diffusion_coefficient(diffusion_coefficient=-1)

def test_validate__invalid_name(self):
""" Test Species.validate with an invalid name. """
test_names = ["", None, 0, 0.5, [0]]
for test_name in test_names:
with self.subTest(name=test_name):
with self.assertRaises(SpeciesError):
self.species.name = test_name
self.species.validate()

def test_validate__invalid_diffusion_coefficient(self):
""" Test Species.validate with an invalid diffusion_coefficient. """
test_dcs = [None, [0]]
for test_dc in test_dcs:
with self.subTest(initial_value=test_dc):
with self.assertRaises(SpeciesError):
self.species.diffusion_coefficient = test_dc
self.species.validate()

def test_validate__invalid_diffusion_coefficient_value(self):
""" Test Species.validate with an invalid diffusion_coefficient value. """
test_dcs = [-0.5, -1, -3, -5]
for test_dc in test_dcs:
with self.subTest(initial_value=test_dc):
with self.assertRaises(SpeciesError):
self.species.diffusion_coefficient = test_dc
self.species.validate()

def test_validate__invalid_restrict_to(self):
""" Test Species.validate with an invalid restrict_to. """
test_rts = ["5", 1, 0.5, [], (1, 2), {'x': 1}]
for test_rt in test_rts:
with self.subTest(restrict_to=test_rt):
with self.assertRaises(SpeciesError):
self.species.restrict_to = test_rt
self.species.validate()

def test_comp_time_of_validate(self):
""" Check the computation time of validate. """
import time
from datetime import datetime
start = time.time()
self.species.validate()
tic = datetime.utcfromtimestamp(time.time() - start)
print(f"Total time to run validate: {tic.strftime('%M mins %S secs %f msecs')}")