Commit 1a7c047a authored by Randy Heiland's avatar Randy Heiland
Browse files

initial commit

parent afc71a07
# Enable C++ support
language: cpp
os: linux
# Compiler selection
compiler:
- gcc
# Build steps
script:
- cd src
- make
notifications:
email:
recipients:
- randy.heiland@gmail.com
on_success: always # default: change (always, never)
on_failure: always # default: always
BSD 3-Clause License
Copyright (c) 2019, PhysiCell-Tools
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# pc4covid19
PhysiCell for COVID19 simulator app for nanoHUB
# pc4covid19 - COVID19 tissue simulator nanoHUB app
**Version:** 1.0
**Release date:** 26 March 2020
This repository contains code and data for the nanoHUB app https://nanohub.org/tools/pc4covid19
### Refererences
https://github.com/MathCancer/COVID19
https://github.com/MathCancer/COVID19/wiki/About
from ipywidgets import Output
from IPython.display import display, HTML
class AboutTab(object):
def __init__(self):
# self.tab = Output(layout={'height': '600px'})
self.tab = Output(layout={'height': 'auto'})
self.tab.append_display_data(HTML(filename='doc/about.html'))
from ipywidgets import Button, VBox, Output
from IPython.display import display, HTML
import os
import glob
import subprocess
import time
class AnimateTab(object):
def __init__(self):
# self.tab = Output(layout={'height': '600px'})
# self.tab = Output(layout={'height': 'auto'})
gen_button = Button(
description='Generate',
button_style='success', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Generate a MP4 video of cells',
)
gen_button.on_click(self.gen_button_cb)
animate_out = Output(layout={'height': 'auto'})
animate_out.append_display_data(HTML(filename='doc/animate.html'))
self.tab = VBox([gen_button, animate_out])
def gen_button_cb(self, b):
print('running...')
tdir = os.path.abspath('tmpdir')
os.chdir(tdir)
# os.system('rm -rf tmpdir*')
# subprocess.Popen(["../bin/myproj", "config.xml"])
# cmd = 'magick mogrify -format jpg snapshot*.svg'
# print(cmd)
# os.system(cmd)
# cmd = 'mencoder "mf://snapshot*.jpg" -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=10000:mbd=2:trell -mf fps=5:type=jpg -nosound -o out.avi'
# print(cmd)
# os.system(cmd)
# cmd = 'convert out.avi cells.mp4'
# print(cmd)
# os.system(cmd)
# print('done.')
num_svg = len(glob.glob('snap*.svg'))
idx_start = 0
idx_end = num_svg
for idx in range(idx_start,idx_end):
fname = "snapshot%08d" % idx
# cmd = "convert " + fname + ".svg -resize " + str(resize_pct) + "% " + fname + ".jpg &"
svg_file = fname + ".svg"
jpg_file = fname + ".jpg"
# print("convert " + svg_file + " " + jpg_file)
# print(cmd)
# os.system(cmd)
# subprocess.Popen(["../bin/myproj", "config.xml"])
subprocess.Popen(["convert", svg_file, jpg_file])
print("Converting all svg to jpg...")
delay = 15 # secs
# while True:
for idx in range(num_svg):
print ("%s" % ( time.ctime(time.time()) ))
# print(len(glob.glob(os.path.join(".", 'snap*.svg'))))
num_jpg = len(glob.glob('snap*.jpg'))
print("%d of %d" % (num_jpg, num_svg))
time.sleep(delay)
if num_jpg == num_svg:
break
cmd = 'mencoder "mf://snapshot*.jpg" -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=10000:mbd=2:trell -mf fps=5:type=jpg -nosound -o out.avi'
print(cmd)
os.system(cmd)
cmd = 'convert out.avi cells.mp4'
print(cmd)
os.system(cmd)
print('done.')
# %%HTML
# <video width=500 height=500 controls>
# <source src="out.mp4" type="video/mp4">
# </video>
# Config Tab
import os
from ipywidgets import Layout, Label, Text, Checkbox, Button, HBox, VBox, \
FloatText, BoundedIntText, BoundedFloatText, HTMLMath, Dropdown
class ConfigTab(object):
def __init__(self):
# micron_units = HTMLMath(value=r"$\mu M$")
micron_units = Label('micron') # use "option m" (Mac, for micro symbol)
# micron_units = Label('microns') # use "option m" (Mac, for micro symbol)
constWidth = '180px'
# tab_height = '400px'
tab_height = '500px'
# tab_layout = Layout(width='900px', # border='2px solid black',
# tab_layout = Layout(width='850px', # border='2px solid black',
# height=tab_height, overflow_y='scroll',)
# np_tab_layout = Layout(width='800px', # border='2px solid black',
# height='350px', overflow_y='scroll',)
# my_domain = [0,0,-10, 2000,2000,10, 20,20,20] # [x,y,zmin, x,y,zmax, x,y,zdelta]
# label_domain = Label('Domain ($\mu M$):')
label_domain = Label('Domain (micron):')
stepsize = 10
disable_domain = False
self.xmin = FloatText(step=stepsize,
# description='$X_{min}$',
description='Xmin',
disabled = disable_domain,
layout=Layout(width=constWidth),
)
self.ymin = FloatText(step=stepsize,
description='Ymin',
disabled = disable_domain,
layout=Layout(width=constWidth),
)
self.zmin = FloatText(step=stepsize,
description='Zmin',
disabled = disable_domain,
layout=Layout(width=constWidth),
)
self.xmax = FloatText(step=stepsize,
description='Xmax',
disabled = disable_domain,
layout=Layout(width=constWidth),
)
self.ymax = FloatText(step=stepsize,
description='Ymax',
disabled = disable_domain,
layout=Layout(width=constWidth),
)
self.zmax = FloatText(step=stepsize,
description='Zmax',
disabled = disable_domain,
layout=Layout(width=constWidth),
)
# description='$Time_{max}$',
self.tmax = BoundedFloatText(
min=0.,
max=100000000,
step=stepsize,
description='Max Time',
layout=Layout(width=constWidth),
)
self.xdelta = BoundedFloatText(
min=1.,
description='dx', # '∆x', # Mac: opt-j for delta
disabled = disable_domain,
layout=Layout(width=constWidth),
)
self.ydelta = BoundedFloatText(
min=1.,
description='dy',
disabled = True,
layout=Layout(width=constWidth),
)
self.zdelta = BoundedFloatText(
min=1.,
description='dz',
disabled = disable_domain,
layout=Layout(width=constWidth),
)
def xdelta_cb(b):
self.ydelta.value = self.xdelta.value
self.zdelta.value = 0.5 * (self.xdelta.value + self.ydelta.value)
self.zmin.value = -0.5 * self.zdelta.value
self.zmax.value = 0.5 * self.zdelta.value
self.xdelta.observe(xdelta_cb)
"""
self.tdelta = BoundedFloatText(
min=0.01,
description='$Time_{delta}$',
layout=Layout(width=constWidth),
)
"""
"""
self.toggle2D = Checkbox(
description='2-D',
layout=Layout(width=constWidth),
)
def toggle2D_cb(b):
if (self.toggle2D.value):
#zmin.disabled = zmax.disabled = zdelta.disabled = True
zmin.disabled = True
zmax.disabled = True
zdelta.disabled = True
else:
zmin.disabled = False
zmax.disabled = False
zdelta.disabled = False
self.toggle2D.observe(toggle2D_cb)
"""
x_row = HBox([self.xmin, self.xmax, self.xdelta])
y_row = HBox([self.ymin, self.ymax, self.ydelta])
z_row = HBox([self.zmin, self.zmax, self.zdelta])
self.omp_threads = BoundedIntText(
min=1,
max=4,
description='# threads',
layout=Layout(width=constWidth),
)
# self.toggle_prng = Checkbox(
# description='Seed PRNG', style={'description_width': 'initial'}, # e.g. 'initial' '120px'
# layout=Layout(width=constWidth),
# )
# self.prng_seed = BoundedIntText(
# min = 1,
# description='Seed',
# disabled=True,
# layout=Layout(width=constWidth),
# )
# def toggle_prng_cb(b):
# if (toggle_prng.value):
# self.prng_seed.disabled = False
# else:
# self.prng_seed.disabled = True
# self.toggle_prng.observe(toggle_prng_cb)
#prng_row = HBox([toggle_prng, prng_seed])
self.toggle_svg = Checkbox(
description='Cells', # SVG
layout=Layout(width='150px') ) # constWidth = '180px'
# self.svg_t0 = BoundedFloatText (
# min=0,
# description='$T_0$',
# layout=Layout(width=constWidth),
# )
self.svg_interval = BoundedIntText(
min=1,
max=99999999, # TODO: set max on all Bounded to avoid unwanted default
description='every',
layout=Layout(width='160px'),
)
self.mcds_interval = BoundedIntText(
min=1,
max=99999999,
description='every',
# disabled=True,
layout=Layout(width='160px'),
)
# don't let this be > mcds interval
def svg_interval_cb(b):
if (self.svg_interval.value > self.mcds_interval.value):
self.svg_interval.value = self.mcds_interval.value
self.svg_interval.observe(svg_interval_cb) # BEWARE: when fill_gui, this sets value = 1 !
# don't let this be < svg interval
def mcds_interval_cb(b):
if (self.mcds_interval.value < self.svg_interval.value):
self.mcds_interval.value = self.svg_interval.value
self.mcds_interval.observe(mcds_interval_cb) # BEWARE: see warning above
def toggle_svg_cb(b):
if (self.toggle_svg.value):
# self.svg_t0.disabled = False
self.svg_interval.disabled = False
else:
# self.svg_t0.disabled = True
self.svg_interval.disabled = True
self.toggle_svg.observe(toggle_svg_cb)
self.toggle_mcds = Checkbox(
# value=False,
description='Subtrates', # Full
layout=Layout(width='180px'),
)
# self.mcds_t0 = FloatText(
# description='$T_0$',
# disabled=True,
# layout=Layout(width=constWidth),
# )
def toggle_mcds_cb(b):
if (self.toggle_mcds.value):
# self.mcds_t0.disabled = False #False
self.mcds_interval.disabled = False
else:
# self.mcds_t0.disabled = True
self.mcds_interval.disabled = True
self.toggle_mcds.observe(toggle_mcds_cb)
svg_mat_output_row = HBox([Label('Plots:'),self.toggle_svg, HBox([self.svg_interval,Label('min')]),
self.toggle_mcds, HBox([self.mcds_interval,Label('min')]) ])
# to sync, do this
# svg_mat_output_row = HBox( [Label('Plots:'), self.svg_interval, Label('min')])
#write_config_row = HBox([write_config_button, write_config_file])
#run_sim_row = HBox([run_button, run_command_str, kill_button])
# run_sim_row = HBox([run_button, run_command_str])
# run_sim_row = HBox([run_button.w]) # need ".w" for the custom RunCommand widget
label_blankline = Label('')
# toggle_2D_seed_row = HBox([toggle_prng, prng_seed]) # toggle2D
box_layout = Layout(border='1px solid')
# domain_box = VBox([label_domain,x_row,y_row,z_row], layout=box_layout)
domain_box = VBox([label_domain,x_row,y_row], layout=box_layout)
self.tab = VBox([domain_box,
# label_blankline,
HBox([self.tmax, Label('min')]), self.omp_threads,
svg_mat_output_row,
# HBox([self.substrate[3], self.diffusion_coef[3], self.decay_rate[3] ]),
]) # output_dir, toggle_2D_seed_
# ], layout=tab_layout) # output_dir, toggle_2D_seed_
# Populate the GUI widgets with values from the XML
def fill_gui(self, xml_root):
self.xmin.value = float(xml_root.find(".//x_min").text)
self.xmax.value = float(xml_root.find(".//x_max").text)
self.xdelta.value = float(xml_root.find(".//dx").text)
self.ymin.value = float(xml_root.find(".//y_min").text)
self.ymax.value = float(xml_root.find(".//y_max").text)
self.ydelta.value = float(xml_root.find(".//dy").text)
self.zmin.value = float(xml_root.find(".//z_min").text)
self.zmax.value = float(xml_root.find(".//z_max").text)
self.zdelta.value = float(xml_root.find(".//dz").text)
self.tmax.value = float(xml_root.find(".//max_time").text)
self.omp_threads.value = int(xml_root.find(".//omp_num_threads").text)
if xml_root.find(".//full_data//enable").text.lower() == 'true':
self.toggle_mcds.value = True
else:
self.toggle_mcds.value = False
self.mcds_interval.value = int(xml_root.find(".//full_data//interval").text)
# NOTE: do this *after* filling the mcds_interval, directly above, due to the callback/constraints on them
if xml_root.find(".//SVG//enable").text.lower() == 'true':
self.toggle_svg.value = True
else:
self.toggle_svg.value = False
self.svg_interval.value = int(xml_root.find(".//SVG//interval").text)
# Read values from the GUI widgets and generate/write a new XML
def fill_xml(self, xml_root):
# print('config.py fill_xml() !!!!!')
# TODO: verify template .xml file exists!
# TODO: verify valid type (numeric) and range?
xml_root.find(".//x_min").text = str(self.xmin.value)
xml_root.find(".//x_max").text = str(self.xmax.value)
xml_root.find(".//dx").text = str(self.xdelta.value)
xml_root.find(".//y_min").text = str(self.ymin.value)
xml_root.find(".//y_max").text = str(self.ymax.value)
xml_root.find(".//dy").text = str(self.ydelta.value)
xml_root.find(".//z_min").text = str(self.zmin.value)
xml_root.find(".//z_max").text = str(self.zmax.value)
xml_root.find(".//dz").text = str(self.zdelta.value)
xml_root.find(".//max_time").text = str(self.tmax.value)
xml_root.find(".//omp_num_threads").text = str(self.omp_threads.value)
xml_root.find(".//SVG").find(".//enable").text = str(self.toggle_svg.value)
xml_root.find(".//SVG").find(".//interval").text = str(self.svg_interval.value)
xml_root.find(".//full_data").find(".//enable").text = str(self.toggle_mcds.value)
xml_root.find(".//full_data").find(".//interval").text = str(self.mcds_interval.value)
# user_details = ET.SubElement(root, "user_details")
# ET.SubElement(user_details, "PhysiCell_settings", name="version").text = "devel-version"
# ET.SubElement(user_details, "domain")
# ET.SubElement(user_details, "xmin").text = "-100"
# tree = ET.ElementTree(root)
# tree.write(write_config_file.value)
# tree.write("test.xml")
# TODO: verify can write to this filename
# tree.write(write_config_file.value)
def get_num_svg_frames(self):
if (self.toggle_svg.value):
return int(self.tmax.value/self.svg_interval.value)
else:
return 0
def get_num_substrate_frames(self):
if (self.toggle_mcds.value):
return int(self.tmax.value/self.mcds_interval.value)
else:
return 0
import ipywidgets as widgets
# Provides a simple output widget to contain debug output.
# Works in callbacks and threads.
# Does not mess up other outputs.
debug_view = widgets.Output(layout={'border': '1px solid black'})
This diff is collapsed.
File added
import ipywidgets as widgets
import xml.etree.ElementTree as ET # https://docs.python.org/2/library/xml.etree.elementtree.html
import os
import glob
import shutil
import math
import datetime
import tempfile
from about import AboutTab
from config import ConfigTab
from microenv_params import MicroenvTab
from user_params import UserTab
# from svg import SVGTab
from substrates import SubstrateTab
# from animate_tab import AnimateTab
from pathlib import Path
import platform
import subprocess
from debug import debug_view
hublib_flag = True
if platform.system() != 'Windows':
try:
# print("Trying to import hublib.ui")
from hublib.ui import RunCommand, Submit
# from hublib2.ui import RunCommand, Submit
except:
hublib_flag = False
else:
hublib_flag = False
# join_our_list = "(Join/ask questions at https://groups.google.com/forum/#!forum/physicell-users)\n"
# create the tabs, but don't display yet
about_tab = AboutTab()
config_tab = ConfigTab()
xml_file = os.path.join('data', 'PhysiCell_settings.xml')
full_xml_filename = os.path.abspath(xml_file)
tree = ET.parse(full_xml_filename) # this file cannot be overwritten; part of tool distro
xml_root = tree.getroot()
microenv_tab = MicroenvTab()
user_tab = UserTab()
# svg = SVGTab()
sub = SubstrateTab()
# animate_tab = AnimateTab()
nanoHUB_flag = False
if( 'HOME' in os.environ.keys() ):
nanoHUB_flag = "home/nanohub" in os.environ['HOME']
# callback when user selects a cached run in the 'Load Config' dropdown widget.
# HOWEVER, beware if/when this is called after a sim finishes and the Load Config dropdown widget reverts to 'DEFAULT'.
# In that case, we don't want to recompute substrate.py self.numx, self.numy because we're still displaying plots from previous sim.
def read_config_cb(_b):
# with debug_view:
# print("read_config_cb", read_config.value)
sub.first_time = True
if read_config.value is None: #occurs when a Run just finishes and we update pulldown with the new cache dir??
# with debug_view:
# print("NOTE: read_config_cb(): No read_config.value. Returning!")
# print("NOTE: read_config_cb(): No read_config.value. Returning!")
return
if os.path.isdir(read_config.value):
is_dir = True
config_file = os.path.join(read_config.value, 'config.xml')
# print("read_config_cb(): is_dir=True; config_file=",config_file)
else:
is_dir = False
config_file = read_config.value
# print("read_config_cb(): is_dir=False; --- config_file=",config_file)
if Path(config_file).is_file():
# with debug_view:
# print("read_config_cb(): calling fill_gui_params with ",config_file)
fill_gui_params(config_file) #should verify file exists!
# If cells or substrates toggled off in Config tab, toggle off in Plots tab
if config_tab.toggle_svg.value == False:
sub.cells_toggle.value = False
sub.cells_toggle.disabled = True
else:
sub.cells_toggle.disabled = False
if config_tab.toggle_mcds.value == False:
sub.substrates_toggle.value = False
sub.substrates_toggle.disabled = True
else:
sub.substrates_toggle.disabled = False
else:
# with debug_view:
# print("read_config_cb: ",config_file, " does not exist.")
return
# update visualization tabs
if is_dir: