Commit 3e407b95 authored by Félix Hartmann's avatar Félix Hartmann
Browse files

First commit with all relevent existing files.

parents
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Defining and computing boundary conditions.
"""
from __future__ import (unicode_literals, absolute_import, division,
print_function)
import numpy as np
from traits.api import HasTraits, Instance, Int, Enum, Button
from traitsui.api import View, Item, Group
from evolution_function import (FunctionHandler, EvolutionFunction, Constant,
Exponential, LogNormal, UserDefined)
from plotwindow_qt import FunctionOfTimePlot
class BoundaryCondition(HasTraits):
"""
Boundary condition for a signal diffusing in a radial cell file.
A BoundaryCondition object is intended to be associated with a
Signal object and contains all the information necessary to define
a boundary condition during a simulation run.
A boundary condition can be imposed on two possible boundaries:
- the phloem side
- the xylem side
A boundary condition can be of two types:
- imposed concentration
- imposed flux
"""
# boundary condition end point
boundary = Enum(
"phloem",
"xylem",
label="Boundary",
desc="boundary on which the condition applies"
)
def get_node(self):
if self.boundary == "phloem":
return 0
elif self.boundary == "xylem":
return -1
else:
return None
# boundary condition type
kind = Enum(
"concentration",
"flux",
label="Type",
desc="type of boundary condition"
)
time_range = Int(0, label="Time range (hours)")
# type of evolution function for the boundary condition
evolution = Enum(
"constant",
"exponential decay",
"log-normal",
"user-defined",
label="Evolution",
desc="type of evolution function for the boundary condition"
)
_function_dict = {
"constant": Constant,
"log-normal": LogNormal,
"exponential decay": Exponential,
"user-defined": UserDefined
}
function = Instance(EvolutionFunction)
def _function_default(self):
return self._function_dict[self.evolution]()
# This dictionary is intended to the FunctionHandler instance assigned to
# the traits_view's handler attribute.
_dict_for_functionhandler = {
"evolution": "evolution",
"function": "function"
}
# boundary condition values
values = None
def compute_values(self, start, stop, num):
"""Compute the values taken by the boundary condition during the
simulation.
"""
X = np.linspace(start, stop, num)
return self.function.f(X)
def set_values(self, start, stop, num):
"""Set the values taken by the boundary condition during the
simulation.
"""
self.values = self.compute_values(start, stop, num)
function_of_time_plot = Instance(FunctionOfTimePlot)
plot_function = Button("Plot function")
def _plot_function_fired(self):
"""Callback of the "plot function" button."""
if self.function_of_time_plot is None:
self.function_of_time_plot = FunctionOfTimePlot(
parent=self, function=self.function)
self.function_of_time_plot.edit_traits()
else:
self.function_of_time_plot.update_plot()
general_group = Group(
Item('kind'),
Item('evolution'),
springy=True
)
function_group = Group(
Group(
Item('function',
style='custom',
springy=True,
show_label=False
),
show_labels=False
),
label="Definition of the evolution function",
springy=True,
show_border=True
)
traits_view = View(
Group(
general_group,
function_group,
Item('plot_function', show_label=False),
springy=True
),
handler=FunctionHandler(),
resizable=True
)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Cell objects simulate cells resulting from cambial activity.
"""
from __future__ import (unicode_literals, absolute_import,
division, print_function)
class Cell(object):
"""Cell class.
"""
def __init__(self, index=None, date=None, diameter=None):
self.indices = []
self.dates = []
self.diameters = []
self.identities = []
self.strain_rates = []
if index is not None:
self.indices.append(index)
if date is not None:
self.dates.append(date)
if diameter is not None:
self.diameters.append(diameter)
def get_identity(self, time):
if time not in self.dates:
return None
else:
i = self.dates.index(time)
return self.identities[i]
This diff is collapsed.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ControlPanel class and auxiliary stuff.
This is the main GUI window.
"""
from __future__ import (unicode_literals, absolute_import, division,
print_function)
import sys
import os
import time
from datetime import timedelta
from threading import Thread
import json
from cellfile import CellFile
from morphogen import Morphogen
from boundarycondition import (BoundaryCondition, LogNormal, Exponential,
Constant, UserDefined)
from transport import (UniformDiffusion, SimpleUnidirectionalActiveTransport,
SmithModel2006, ImposedCellConcentrations)
from plottingcontrol import PlottingControl
from traits.api import (HasTraits, Instance, Str, Int, Float, List, Enum, File,
Button)
from traitsui.api import (View, Item, Group, HGroup, TextEditor, FileEditor,
ListEditor, CheckListEditor, EnumEditor, OKButton,
CancelButton)
if sys.version_info[0] > 2:
getcwd = os.getcwd
else:
getcwd = os.getcwdu
time_units = {
"second": 1,
"minute": 60,
"hour": 60 * 60,
"day": 24 * 60 * 60}
class JSONCodec():
"""Serialization and deserialization methods.
Methods intended to be called from within 'json' module methods for saving
and loading settings.
"""
def __init__(self):
# class_dict specifies which instance types will be serialized
self.class_dict = {
ControlPanel: "ControlPanel",
Morphogen: "Morphogen",
BoundaryCondition: "BoundaryCondition",
LogNormal: "LogNormal",
Exponential: "Exponential",
Constant: "Constant",
UserDefined: "UserDefined",
UniformDiffusion: "UniformDiffusion",
SmithModel2006: "SmithModel2006",
SimpleUnidirectionalActiveTransport:
"SimpleUnidirectionalActiveTransport",
ImposedCellConcentrations: "ImposedCellConcentrations"
}
def coder(self, obj):
if obj.__class__ in self.class_dict:
traits_dict = obj.get()
traits_dict['__class__'] = self.class_dict[obj.__class__]
return traits_dict
def decoder(self, json_obj):
if "__class__" in json_obj:
if json_obj["__class__"] == "ControlPanel":
del json_obj["__class__"]
return json_obj
# the two following elif blocks are for backward campatibility
elif json_obj["__class__"] == "DiffusiveSignal":
del json_obj["__class__"]
D = json_obj["d_intracell"]
d = -json_obj["alpha"]
del json_obj["d_intracell"]
del json_obj["d_intercell"]
del json_obj["alpha"]
del json_obj["P"]
obj = Morphogen()
obj.set(**json_obj)
obj.transport_mechanism = "uniform diffusion"
obj.transport = UniformDiffusion()
obj.transport.D = D
obj.transport.d_function = Constant(constant_value=d)
return obj
elif json_obj["__class__"] == "UniformDiffusion" \
and "d" in json_obj:
del json_obj["__class__"]
d = json_obj["d"]
del json_obj["d"]
obj = UniformDiffusion()
obj.set(**json_obj)
obj.d_function = Constant(constant_value=d)
return obj
for key, value in self.class_dict.items():
if json_obj["__class__"] == value:
del json_obj["__class__"]
obj = key()
obj.set(**json_obj)
return obj
return json_obj
class SettingsFile(HasTraits):
"""GUI for selecting a settings file to load."""
file_name = File(getcwd())
file_editor = FileEditor(filter=["*.json"])
# Display specification (one Item per editor style):
file_group = Group(
Item('file_name',
style='custom',
show_label=False,
editor=file_editor),
Item('_'),
Item('file_name', style='text', show_label=False),
)
def _ConcelButton_fired(self):
pass # TODO
traits_view = View(
file_group,
kind='modal',
title="Select a file",
buttons=[OKButton, CancelButton],
resizable=True,
height=0.6, width=0.4
)
class TextDisplay(HasTraits):
"""Used for displaying the state of a simulation run."""
string = Str()
view = View(Item('string',
show_label=False,
editor=TextEditor(multi_line=False),
style='custom'))
def signal_from_name(name, signals):
"""From a name, return the corresponding signal."""
L = [signal for signal in signals if signal.name == name]
if L:
return L[0]
else:
return None
def growth(simu):
"""Simulation run.
simu : ControlPanel instance
"""
start_time = timedelta(days=simu.start_time)
stop_time = timedelta(days=simu.stop_time)
activation_time = timedelta(days=simu.activation_time)
total_time = (stop_time - start_time).total_seconds()
nb_input_samples = int((total_time + simu.snapshot_time_step)
/ simu.input_time_step) + 2
# precomputation of time-dependant values
signals = simu.signals
for signal in signals:
signal.set_time_dependant_values(
simu.start_time,
simu.start_time + (nb_input_samples - 1) *
simu.input_time_step / time_units["day"],
nb_input_samples)
# activation_time_for_simu = \
# (simu.activation_time - simu.start_time) * time_units["day"]
simu.cell_file = CellFile(
nb_cells=simu.nb_cells,
initial_CRD=simu.cell_diameter,
division_threshold=simu.division_threshold,
enlargement_threshold=simu.enlargement_threshold,
growth_prefactor=simu.growth_prefactor,
data_time_unit=time_units["day"]
)
identity_signal = signal_from_name(simu.identity_signal, signals)
growth_control_signals = [signal_from_name(name, signals)
for name in simu.growth_control_signals]
simu.cell_file.grow(
start_time,
stop_time,
activation_time=activation_time,
input_time_step=simu.input_time_step,
growth_time_step=simu.growth_time_step,
snapshot_time_step=simu.snapshot_time_step,
nodes_per_cell=simu.nodes_per_cell,
identity_signal=identity_signal,
control_signals=growth_control_signals
)
class GrowthThread(Thread):
"""Thread of a simulation run."""
def run(self):
self.display.string = "Simulation running..."
simu = self.simulation
simu.plotting_control = None
start = time.time()
growth(simu)
time_elapsed = time.time() - start
self.display.string = "Simulation finished after "\
"{0:.1f} s.".format(time_elapsed)
simu.plotting_control = PlottingControl(
simu.cell_file, simu.start_time, simu.stop_time,
activation_time=simu.activation_time)
class ControlPanel(HasTraits):
"""This object is the main window of the interface.
Its view is composed of tabs:
1) The simulation / drawing tab, implemented directly in this object.
2) The model tab (Model object), for defining the model.
3) The signal tab (a List of Morphogen objects), for parametrizing
the signals.
"""
# List of signals
signals = List(trait=Morphogen)
# List is signal names
signal_names = List(Str)
def __init__(self, nb_signals=1):
signals = []
for i in range(nb_signals):
signals.append(Morphogen(number=i+1))
self.signals = signals
self.signal_names = [signal.name for signal in self.signals]
##################
# Simulation tab #
##################
start_time = Int(
0,
label="Start (day of year)",
desc="the first day of simulation"
)
activation_time = Int(
0,
label="Cambium activation (day of year)",
desc="the date of cambium activation"
)
stop_time = Int(
30,
label="Stop (day of year)",
desc="the last day of simulation"
)
nb_cells = Int(
4,
label="Initial number of cells"
)
cell_diameter = Float(
6.,
label="Initial cell diameter (µm)"
)
simulation_mode = Enum(
"non-equilibrium",
"quasistatic",
label="Simulation mode",
desc="if the full non-equilibrium reaction-diffusion is to be "
"solved or if the quasistatic approximation can be made."
)
launch_simulation = Button("Jetzt geht's los!")
display = Instance(TextDisplay, ())
display.string = ""
growth_thread = Instance(GrowthThread)
# GUI for plotting outputs
plotting_control = Instance(PlottingControl)
def _launch_simulation_fired(self):
"""Callback of the "launch simulation" button.
This starts the simulation thread, or kills it.
"""
if self.growth_thread and self.growth_thread.isAlive():
self.growth_thread.wants_abort = True
else:
self.growth_thread = GrowthThread()
self.growth_thread.wants_abort = False
self.growth_thread.display = self.display
self.growth_thread.simulation = self
self.growth_thread.start()
json_codec = Instance(JSONCodec, ())
settings_file = Instance(SettingsFile, ())
save_settings = Button("Save settings")
load_settings = Button("Load settings")
def set_from_file(self, file_name):
"""Set traits from a configuration file."""
with open(file_name, "r") as f:
traits_dict = json.load(f, object_hook=self.json_codec.decoder)
keys_to_remove = []
for key, value in traits_dict.items():
if value is None:
keys_to_remove.append(key)
# The next two lines are for backward compatibility.
if key == "simulation_mode" and "non-equilibrium" in value:
traits_dict[key] = "non-equilibrium"
for key in keys_to_remove:
del traits_dict[key]
self.set(**traits_dict)
def save_to_file(self, file_name):
"""Save current settings into a configuration file."""
with open(file_name, 'w') as f:
json.dump(self, f, indent=4, encoding='utf-8',
default=self.json_codec.coder)
def _load_settings_fired(self):
"""Callback of the "load settings" button.
This opens a window for selecting a settings file and loads settings
from it.
"""
self.settings_file.edit_traits()
file_name = self.settings_file.file_name
if not file_name or os.path.isdir(file_name):
return False
if os.path.isfile(file_name):
self.set_from_file(file_name)
else:
raise IOError("Configuration file {} does not "
"exist!".format(file_name))
def _save_settings_fired(self):
"""Callback of the "load settings" button.
This opens a window for selecting a settings file and saves settings
into it.
"""
output = self.settings_file.edit_traits()
if output:
file_name = self.settings_file.file_name
if not file_name or file_name == os.getcwdu():
return False
if not file_name[-5:] == ".json":
file_name += ".json"
self.settings_file.file_name = file_name
self.save_to_file(file_name)
#############
# Model tab #
#############
division_threshold = Float(
1,
label="Division threshold",
desc="a threshold on the signal concentration. Above "
"threshold, cell division can occur when the cell reach "
"the critical size."
)
enlargement_threshold = Float(
0.5,
label="Enlargement threshold",
desc="a threshold on the signal concentration. Under "
"this threshold, enlargement ceases."
)
threshold_list = [division_threshold, enlargement_threshold]
identity_signal = Str(
label="Identity signal",
desc="the signal which determines the identities of cells."
)
def _identity_signal_default(self):
if len(self.signals) > 0:
return self.signals[0].name
else:
return None
growth_control_signals = List(
trait=Str,
label="Growth control signals",
desc="the signals which determine the growth rates of cells."
)
def _growth_control_signals_default(self):
if len(self.signals) > 0:
return [self.signals[0].name]
else:
return []
def _signals_items_changed(self, old, new):
"""Callback after removal or addition of signals to 'signals'.
Any signal removed from the 'signals' List must be also removed from
the 'growth_control_signals' List if needed. If the removed signal is
the 'identity_signal', tnen the 'identity_signal' must be changed.
"""
if self.identity_signal is None and len(new.added) > 0:
self.identity_signal = self.signals[0].number
if len(new.removed) > 0:
for signal in new.removed:
if self.identity_signal is signal.number:
if len(self.signals) > 0:
self.identity_signal = self.signals[0].number
else:
self.identity_signal = None
for growth_control_signal in self.growth_control_signals:
if growth_control_signal is signal.number:
self.growth_control_signals.remove(
growth_control_signal)
self.signal_names = [signal.name for signal in self.signals]
growth_prefactor = Float(