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 spatialpy/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from .result import *
from .spatialpyerror import *
from .species import *
from .visualization import Visualization
from .vtkreader import *

_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
Expand Down
85 changes: 59 additions & 26 deletions spatialpy/core/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from plotly.offline import init_notebook_mode, iplot
from scipy.spatial import KDTree

from spatialpy.core.visualization import Visualization
from spatialpy.core.spatialpyerror import DomainError

class Domain():
Expand Down Expand Up @@ -204,8 +205,8 @@ def set_properties(self, geometry_ivar, type_id, vol=None, mass=None, nu=None, r
"""
Add a type definition to the domain. By default, all regions are set to type 0.

:param geometry_ivar: an instance of a :py:class:`spatialpy.core.geometry.Geometry` subclass. The 'inside()' method
of this object will be used to assign properties to points.
:param geometry_ivar: an instance of a :py:class:`spatialpy.core.geometry.Geometry` subclass. \
The 'inside()' method of this object will be used to assign properties to points.
:type geometry_ivar: spatialpy.core.geometry.Geometry

:param type_id: The identifier for this type.
Expand Down Expand Up @@ -270,8 +271,8 @@ def fill_with_particles(self, geometry_ivar, deltax, deltay=None, deltaz=None, x
"""
Fill a geometric shape with particles.

:param geometry_ivar: an instance of a :py:class:`spatialpy.core.geometry.Geometry` subclass. The 'inside()' method
of this object will be used to create add the particles.
:param geometry_ivar: an instance of a :py:class:`spatialpy.core.geometry.Geometry` subclass. \
The 'inside()' method of this object will be used to create add the particles.
:type geometry_ivar: spatialpy.core.geometry.Geometry

:param deltax: Distance between particles on the x-axis.
Expand Down Expand Up @@ -535,7 +536,7 @@ def calculate_vol(self):
self.vol[v3] += t_vol / 4
self.vol[v4] += t_vol / 4

def plot_types(self, width=None, height=None, colormap=None, size=5, title=None,
def plot_types(self, width=None, height=None, colormap=None, size=None, title=None,
included_types_list=None, use_matplotlib=False, return_plotly_figure=False):
'''
Plots the domain using plotly. Can only be viewed in a Jupyter Notebook.
Expand Down Expand Up @@ -574,13 +575,10 @@ def plot_types(self, width=None, height=None, colormap=None, size=5, title=None,
'''
from spatialpy.core.result import _plotly_iterate # pylint: disable=import-outside-toplevel

if use_matplotlib:
width = 6.4 if width in (None, "auto") else width
height = 4.8 if height in (None, "auto") else height
else:
if not use_matplotlib:
if width in (None, "auto"):
width = None if width == "auto" else 500
if height is None:
if height in (None, "auto"):
height = None if height == "auto" else 500

if not numpy.count_nonzero(self.vertices[:, 1]):
Expand All @@ -593,32 +591,67 @@ def plot_types(self, width=None, height=None, colormap=None, size=5, title=None,
self._get_type_name_mapping()

types = {}
# Normalize volumes to [0, 1]
vols = (self.vol - numpy.min(self.vol))/numpy.ptp(self.vol)
for i, type_id in enumerate(self.type_id):
name = type_id[5:]
if included_types_list is None or name in included_types_list:
if name in types:
types[name]['points'].append(self.vertices[i])
types[name]['data'].append(self.typeNdxMapping[type_id])
types[name]['size_scale'] = numpy.append(types[name]['size_scale'], vols[i])
else:
types[name] = {"points":[self.vertices[i]], "data":[self.typeNdxMapping[type_id]]}
types[name] = {
"points": [self.vertices[i]],
"data": [self.typeNdxMapping[type_id]],
"size_scale": numpy.array([vols[i]])
}

if use_matplotlib:
import matplotlib.pyplot as plt # pylint: disable=import-outside-toplevel

fig, ax = plt.subplots(figsize=(width, height))
for name, data in types.items():
x_coords = list(map(lambda point: point[0], data["points"]))
y_coords = list(map(lambda point: point[1], data["points"]))

ax.scatter(x_coords, y_coords, label=name)
ax.grid(linestyle='--', linewidth=1)
ax.legend(loc='upper right', fontsize=12)
if title is not None:
ax.set_title(title)

plt.axis('scaled')
if not isinstance(use_matplotlib, dict):
use_matplotlib = {}
use_matplotlib['limits'] = (
(self.xlim[0] - 0.25, self.xlim[1] + 0.25), (self.ylim[0] - 0.25, self.ylim[1] + 0.25)
)

# Support for width, height, and title args
if width not in (None, "auto") and height not in (None, "auto"):
# TODO: Deprecation warning for width and height
plot_args = {"figsize": (width, height)}

if "plot_args" in use_matplotlib:
for name, val in use_matplotlib['plot_args'].items():
plot_args[name] = val
use_matplotlib['plot_args'] = plot_args

base_group_args = {}
if colormap is not None:
base_group_args['cmap'] = colormap
base_group_args['vmin'] = 1 # minimum number of defined types
base_group_args['vmax'] = len(self.typeNdxMapping) # number of defined types
if size is not None:
base_group_args['s'] = size

if "scatter_args" not in use_matplotlib:
use_matplotlib['scatter_args'] = {}
for type_id in self.typeNdxMapping.keys():
type_id = type_id[5:]
group_args = base_group_args.copy()
if type_id in use_matplotlib['scatter_args']:
for name, val in use_matplotlib['scatter_args'][type_id].items():
group_args[name] = val
use_matplotlib['scatter_args'][type_id] = group_args

if title is not None:
use_matplotlib['title'] = title

vis_obj = Visualization(data=types)
vis_obj.plot_scatter(**use_matplotlib)
return

if size is None:
size = 5

is_2d = self.dimensions == 2

trace_list = _plotly_iterate(types, size=size, property_name="type",
Expand Down Expand Up @@ -860,7 +893,7 @@ def create_3D_domain(cls, xlim, ylim, zlim, nx, ny, nz, type_id=1, mass=1.0,
:type fixed: bool

:param \**kwargs: Additional keyword arguments passed to :py:class:`Domain`.

:returns: Uniform 3D SpatialPy Domain object.
:rtype: spatialpy.core.domain.Domain
"""
Expand Down
75 changes: 59 additions & 16 deletions spatialpy/core/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from plotly.offline import init_notebook_mode, iplot

# from spatialpy.core.model import *
from spatialpy.core.visualization import Visualization
from spatialpy.core.vtkreader import VTKReader
from spatialpy.core.spatialpyerror import ResultError

Expand Down Expand Up @@ -221,16 +222,25 @@ def __del__(self):
def __map_property_to_type(self, property_name, data, included_types_list, points, p_ndx):
types = {}
if property_name == 'type':
# Normalize volumes to [0, 1]
vol = data['mass'] / data['rho']
vols = (vol - numpy.min(vol))/numpy.ptp(vol)
for i, val in enumerate(data['type']):
name = self.model.domain.typeNameMapping[val][5:]

if included_types_list is None or name in included_types_list:
if name in types:
types[name]['points'].append(points[i])
types[name]['data'].append(data[property_name][i])
types[name]['size_scale'] = numpy.append(types[name]['size_scale'], vols[i])
else:
types[name] = {"points":[points[i]], "data":[data[property_name][i]]}
elif property_name == 'v':
types[name] = {
"points": [points[i]],
"data": [data[property_name][i]],
"size_scale": numpy.array([vols[i]])
}
return types
if property_name == 'v':
types[property_name] = {
"points": points,
"data" : [data[property_name][i][p_ndx] for i in range(0,len(data[property_name]))]
Expand Down Expand Up @@ -326,7 +336,8 @@ def get_species(self, species, timepoints=None, concentration=False, determinist
If set to True, the concentration (=copy_number/volume) is returned. Defaults to False
:type concentration: bool

:param deterministic: Whether or not the species is deterministic (True) or stochastic (False). Defaults to False
:param deterministic: Whether or not the species is deterministic (True) or stochastic (False). \
Defaults to False
:type deterministic: bool

:param debug: Whether or not debug information should be printed. Defaults to False
Expand Down Expand Up @@ -614,7 +625,6 @@ def get_property(self, property_name, timepoints=None):
t_index_arr = [t_index_arr]
num_timepoints = 1


if property_name == "v":
ret = numpy.zeros((num_timepoints, num_voxel, 3))
else:
Expand All @@ -630,7 +640,7 @@ def get_property(self, property_name, timepoints=None):
return ret

def plot_property(self, property_name, t_ndx=None, t_val=None, p_ndx=0, width=None, height=None,
colormap=None, size=5, title=None, animated=False, t_ndx_list=None, speed=1,
colormap=None, size=None, title=None, animated=False, t_ndx_list=None, speed=1,
f_duration=500, t_duration=300, included_types_list=None, return_plotly_figure=False,
use_matplotlib=False, debug=False):
"""
Expand Down Expand Up @@ -743,17 +753,47 @@ def plot_property(self, property_name, t_ndx=None, t_val=None, p_ndx=0, width=No
if use_matplotlib:
import matplotlib.pyplot as plt # pylint: disable=import-outside-toplevel

if not isinstance(use_matplotlib, dict):
use_matplotlib = {}
use_matplotlib['limits'] = (
(self.model.domain.xlim[0] - 0.25, self.model.domain.xlim[1] + 0.25),
(self.model.domain.ylim[0] - 0.25, self.model.domain.ylim[1] + 0.25)
)

# Support for width, height, and title args
if width not in (None, "auto") and height not in (None, "auto"):
# TODO: Deprecation warning for width and height
plot_args = {"figsize": (width, height)}

if "plot_args" in use_matplotlib:
for name, val in use_matplotlib['plot_args'].items():
plot_args[name] = val
use_matplotlib['plot_args'] = plot_args

base_group_args = {}
if colormap is not None:
base_group_args['cmap'] = colormap
base_group_args['vmin'] = 1 # minimum number of defined types
base_group_args['vmax'] = len(self.model.domain.typeNdxMapping) # number of defined types
if size is not None:
base_group_args['s'] = size

if "scatter_args" not in use_matplotlib:
use_matplotlib['scatter_args'] = {}
for type_id in self.model.domain.typeNdxMapping.keys():
type_id = type_id[5:]
group_args = base_group_args.copy()
if type_id in use_matplotlib['scatter_args']:
for name, val in use_matplotlib['scatter_args'][type_id].items():
group_args[name] = val
use_matplotlib['scatter_args'][type_id] = group_args

if title is not None:
use_matplotlib['title'] = title

if property_name == "type":
fig, ax = plt.subplots(figsize=(width, height))
for name, data in types.items():
x_coords = list(map(lambda point: point[0], data["points"]))
y_coords = list(map(lambda point: point[1], data["points"]))

ax.scatter(x_coords, y_coords, label=name)
ax.grid(linestyle='--', linewidth=1)
ax.legend(loc='upper right', fontsize=12)
if title is not None:
ax.set_title(title)
vis_obj = Visualization(data=types)
vis_obj.plot_scatter(**use_matplotlib)
else:
if property_name == 'v':
p_data = data[property_name]
Expand All @@ -769,9 +809,12 @@ def plot_property(self, property_name, t_ndx=None, t_val=None, p_ndx=0, width=No
if title is not None:
plt.title(title)
plt.grid(linestyle='--', linewidth=1)
plt.axis('scaled')
plt.axis('scaled')
return

if size is None:
size = 5

is_2d = self.model.domain.dimensions == 2
trace_list = _plotly_iterate(types, size=size, property_name=property_name,
colormap=colormap, is_2d=is_2d)
Expand Down
8 changes: 8 additions & 0 deletions spatialpy/core/spatialpyerror.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class ResultError(Exception):
Class for exceptions in the results module.
"""

class VisualizationError(Exception):
"""
Class for exceptions in the visualization module.
"""

class VTKReaderError(Exception):
"""
Bass class for exceptions in the vtkreader module.
Expand Down Expand Up @@ -81,6 +86,9 @@ class SpeciesError(ModelError):
# Result Exceptions


# Visualization Exceptions


# VTKReader Exceptions
class VTKReaderIOError(VTKReaderError):
"""
Expand Down
Loading