diff --git a/.github/workflows/apple.yml b/.github/workflows/apple.yml index 8ca2f4e..9e19b7e 100644 --- a/.github/workflows/apple.yml +++ b/.github/workflows/apple.yml @@ -7,9 +7,7 @@ run-name: ${{ github.actor }}::pytest pcdl library on mac os x; the latest pytho on: push: - branches: ["utest", "master"] # ["v3", "v4"] - pull_request: - branches: ["development", "master"] + branches: ["v3"] # only! jobs: build-macosx: @@ -33,7 +31,7 @@ jobs: run: | brew install ffmpeg imagemagick python -m pip install --upgrade pip - python -m pip install flake8 pytest anndata bioio matplotlib numpy pandas requests scipy vtk + python -m pip install flake8 pytest anndata matplotlib numpy pandas requests scipy vtk python -m pip install /Users/runner/work/physicelldataloader/physicelldataloader -v #if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: lint with flake8 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1a01ad7..d8445b7 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -7,9 +7,7 @@ run-name: ${{ github.actor }}::pytest pcdl library on linux os; all python3 vers on: push: - branches: ["utest", "master"] # ["v3","v4"] - pull_request: - branches: ["development", "master"] + branches: ["v3"] # only! jobs: build-linux: @@ -33,7 +31,7 @@ jobs: run: | sudo apt install ffmpeg imagemagick python -m pip install --upgrade pip - python -m pip install flake8 pytest anndata bioio matplotlib numpy pandas requests scipy vtk + python -m pip install flake8 pytest anndata matplotlib numpy pandas requests scipy vtk python -m pip install /home/runner/work/physicelldataloader/physicelldataloader -v #if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: lint with flake8 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 235e012..e355849 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -7,9 +7,7 @@ run-name: ${{ github.actor }}::pytest pcdl library on windows os; the latest pyt on: push: - branches: ["utest", "master"] # ["v3", "v4"] - pull_request: - branches: ["development", "master"] + branches: ["v3"] # only! jobs: build-windows: @@ -33,7 +31,7 @@ jobs: run: | choco install ffmpeg imagemagick python -m pip install --upgrade pip - python -m pip install flake8 pytest anndata bioio matplotlib numpy pandas requests scipy vtk + python -m pip install flake8 pytest anndata matplotlib numpy pandas requests scipy vtk python -m pip install D:\a\physicelldataloader\physicelldataloader -v #echo 'set PYTHONPATH=D:\a\physicelldataloader\physicelldataloader' >> $GITHUB_ENV #if [ -f requirements.txt ]; then pip install -r requirements.txt; fi diff --git a/man/HOWTO.md b/man/HOWTO.md index 1e72517..3cfe5d1 100644 --- a/man/HOWTO.md +++ b/man/HOWTO.md @@ -146,6 +146,5 @@ pyMCDS.py and the pyMCDS class is very lightweight. Besides the python3 core library, this code has only matplotlib, numpy, pandas, scipy, and vtk library dependencies.\ The pyMCDS class evolved into the pcdl.TimeStep class, which has additionally anndata dependency, which makes the library slightly heavier but much more powerful for downstream data analysis. Apart from that, pcdl offers the pcdl.TimeSeries class to handle the mcds snapshots from an entire PhysiCell run, and a set of functions that can be run straight from the command line, without even having to fire up a python3 shell. -Finally, branch version 4 broke with this ancient library structure because it is just out of time to run the code like this.\ -Stay assured, if you like pyMCDS.py, it is there to last. -We will keep on maintaining pyMCDS.py from branch version 3. +Future branch version 4 will abandon this ancient library structure to become more concise. +Don't fear. pyMCDS.py is there to last. We will keep on maintaining pyMCDS.py from branch version 3. diff --git a/man/REFERENCE.md b/man/REFERENCE.md index d583780..a4af6c7 100644 --- a/man/REFERENCE.md +++ b/man/REFERENCE.md @@ -34,7 +34,7 @@ Familiarize yourself well with their parameters! Basically, there are four types of functions: + set_ : set a python3 variable. + get_ : recall a python3 variable. -+ make_ : make functions generate file output (gml, ome.tiff, vtk). ++ make_ : make functions generate file output (gml, vtk). + plot_ : plot functions generate a matplotlib figure, or axis object, or file output (jpeg, png, tiff), depending on your parameter settings. ### TimeStep initialize @@ -80,6 +80,8 @@ Basically, there are four types of functions: + [help(mcds.get_substrate_list)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_substrate_list.md) #! workhorse function + [help(mcds.get_substrate_dict)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_substrate_dict.md) + [help(mcds.get_substrate_df)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_substrate_df.md) ++ [help(mcds.get_concentration)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_concentration.md) ++ [help(mcds.get_concentration_at)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_concentration_at.md) + [help(mcds.get_conc_df)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_conc_df.md) #! workhorse function + [help(mcds.plot_contour)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.plot_contour.md) #! workhorse function + [help(mcds.make_conc_vtk)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.make_conc_vtk.md) #! workhorse function @@ -88,6 +90,7 @@ Basically, there are four types of functions: + [help(mcds.get_celltype_list)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_celltype_list.md) #! workhorse function + [help(mcds.get_celltype_dict)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_celltype_dict.md) + [help(mcds.get_cell_df)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_cell_df.md) #! workhorse function ++ [help(mcds.get_cell_df_at)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_cell_df_at.md) + [help(mcds.plot_scatter)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.plot_scatter.md) #! workhorse function + [help(mcds.make_cell_vtk)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.make_cell_vtk.md) #! workhorse function + [help(mcds.get_anndata)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_anndata.md) #! workhorse function @@ -97,16 +100,12 @@ Basically, there are four types of functions: + [help(mcds.get_neighbor_graph_dict)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.get_neighbor_graph_dict.md) + [help(mcds.make_graph_gml)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.make_graph_gml.md) #! workhose function -### TimeStep microenvironment and cells -+ [help(mcds.make_ome_tiff)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.make_ome_tiff.md) #! workhose function -+ [help(mcds.make_neuroglancer)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcds.make_neuroglancer.md) #! workhose function - ### TimeStep internal functions + [help(pcdl.scaler)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/pcdl.scaler.md) # anndata + [help(pcdl.graphfile_parser)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/pcdl.graphfile_parser.md) # mcds ```python3 -help(pcdl.TimeStep._read_xml) -help(pcdl.TimeStep._anndextract) +help(pcdl.pyMCDS._read_xml) +help(pcdl.pyAnnData._anndextract) ``` @@ -115,7 +114,7 @@ help(pcdl.TimeStep._anndextract) Basically, there are four types of functions: + set_ : set a python3 variable. + get_ : recall a python3 variable. -+ make_ : make functions generate file output (gif, gml, mp4, ome.tiff, vtk). ++ make_ : make functions generate file output (gif, gml, mp4, vtk). + plot_ : plot functions generate a matplotlib figure, or axis object, or file output (jpeg, png, tiff), depending on your parameter settings. ### TimeSeries initialization @@ -143,10 +142,6 @@ Basically, there are four types of functions: ### TimeSeries cell graph + [help(mcdsts.get_graph_gml)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcdsts.make_graph_gml.md) #! workhose function -### TimeSteries microenvironment and cells -+ [help(mcdsts.make_ome_tiff)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcdsts.make_ome_tiff.md) #! workhose function -+ [help(mcdsts.make_neuroglancer)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcdsts.make_neuroglancer.md) #! workhose function - ### Timeseries timeseries + [help(mcdsts.plot_timeseries)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/mcdsts.plot_timeseries.md) #! workhorse function @@ -159,8 +154,8 @@ Basically, there are four types of functions: ### TimeSeries internal functions + [help(pcdl.scaler)](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/pcdl.scaler.md) # anndata ```python3 -help(pcdl.TimeSeries._handle_magick) -help(pcdl.TimeSeries._anndextract) +help(pcdl.pyMCDSts._handle_magick) +help(pcdl.pyAnnData._anndextract) ``` @@ -193,10 +188,6 @@ The command line interface functions mimic the name and parameter arguments as c ### Command line cell graph + [pcdl_make_graph_gml --help](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/pcdl_make_graph_gml.md) #! workhorse function -### Command line cells and microenvironment -+ [pcdl_make_ome_tiff --help](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/pcdl_make_ome_tiff.md) #! workhorse function -+ [pcdl_make_neuroglancer --help](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/pcdl_make_neuroglancer.md) #! workhorse function - ### Command line timeseries + [pcdl_plot_timeseries --help](https://github.com/elmbeech/physicelldataloader/tree/master/man/docstring/pcdl_plot_timeseries.md) #! workhorse function diff --git a/man/TUTORIAL_blender.md b/man/TUTORIAL_blender.md index d5e05ca..f9fd8eb 100644 --- a/man/TUTORIAL_blender.md +++ b/man/TUTORIAL_blender.md @@ -3,7 +3,6 @@ [Blender](https://www.blender.org/) is a modeling, rigging, animation, simulation, rendering, compositing, motion tracking, video editing, and game creation software. Blender is free and open source. There exists a vtk nodes plugin, that lets us load vtk polynomial data files. -And there exists a bioxel nodes plugin, that lets us load ome tiff files. ## ✨ Handle vtk files @@ -64,31 +63,4 @@ To learn more about Blender and BVTK Node plugin, please study the official docu + https://docs.blender.org/manual/en/latest/ -## ✨ Handle ome tiff files - -The blender bioxel nodes plugin allows us load single time step ome tiff files into blender. - -### Generate ome tiff files from the command line - -```bash -pcdl_make_ome_tiff output --collapse false -``` - -### Generate ome tiff files from within python - -```python -import pcdl - -mcdsts = pcdl.TimeSeries('output/') -mcdsts.make_ome_tiff(collapse=False) -``` - -### The blender bioxel nodes plugin - -Please follow the official bioxel nodes instructions for installation -and to learn how to use the plugin. - -+ https://omoolab.github.io/BioxelNodes/latest/ - - That's it! The rest is analysis within blender! diff --git a/man/TUTORIAL_commandline.md b/man/TUTORIAL_commandline.md index ae15560..61b0429 100644 --- a/man/TUTORIAL_commandline.md +++ b/man/TUTORIAL_commandline.md @@ -1,5 +1,7 @@ # PhysiCell Data Loader Tutorial: pcdl from the Commandline + + The most important commands for down stream data analysis, available in the pcdl TimeStep and TimeSeries class, can be run straight from a command line shell, like [bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)), [csh](https://en.wikipedia.org/wiki/C_shell), [dos](https://en.wikipedia.org/wiki/DOS), [fish](https://en.wikipedia.org/wiki/Fish_(Unix_shell)), [ksh](https://en.wikipedia.org/wiki/KornShell), [powershell](https://en.wikipedia.org/wiki/PowerShell), [sh](https://en.wikipedia.org/wiki/Bourne_shell), [tsh](https://en.wikipedia.org/wiki/Tcsh), or [zsh](https://en.wikipedia.org/wiki/Z_shell), to name a view. @@ -9,7 +11,7 @@ The command parameter mimics the related python3 function arguments as closely a You can always call the [help](https://en.wikipedia.org/wiki/Help!) parameter ( pcdl\_command -h), to access the man page for a pcdl command! -Please spend some time to learn about each of the about 20 commands, by studying its man page. +Please spend some time to learn about each of the 18 commands, by studying its man page. This will truly make you a power user! @@ -194,19 +196,6 @@ pcdl_get_celltype_list output/output00000000.xml pcdl_get_celltype_list -h ``` -### ✨ pcdl\_get\_cell\_attrribute\_list - -Output all recorded cell attributes. - -```bash -pcdl_get_cell_attribute_list output -``` -```bash -pcdl_get_cell_attribute_list output/output00000000.xml -``` -```bash -pcdl_get_cell_attribute_list -h -``` ### ✨ pcdl\_get\_cell\_attribute @@ -398,60 +387,21 @@ pcdl_plot_timeseries -h ``` -### ✨ pcdl\_make\_ome\_tiff - -Generate an [ome.tiff](https://ome-model.readthedocs.io/en/stable/index.html) file, -to analyze a single time step or the whole time series, -the same way as usually fluorescent microscopy data is analyzed. - -By default, the cell\_attribute outputted is the cell ID + 1. -However, any numerical (bool, int, float) cell\_attribute can be outputted. -For example: dead, cells\_per\_voxel, or pressure. - -These ome.tiff files can be further analyzed, -for example with the [Napari](https://napari.org/stable/) or [Fiji Imagej](https://fiji.sc/) or [Neuroglancer](https://research.google/blog/an-interactive-automated-3d-reconstruction-of-a-fly-brain/) or [Blender](https://www.blender.org/) or similar software, -as described in the extra tutorials. - -```bash -pcdl_make_ome_tiff output/output00000000.xml pressure -``` -```bash -pcdl_make_ome_tiff output -``` -```bash -pcdl_make_ome_tiff -h -``` - -Further readings: -+ [TUTORIAL_python3_napari.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_napari.md) -+ [TUTORIAL_fiji_imagej.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_fijiimagej.md) -+ [TUTORIAL_neuroglancer.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_neuroglancer.md) -+ [TUTORIAL_blender.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_blender.md) +## [Making movies](https://en.wikipedia.org/wiki/Making_Movies) -### ✨ pcdl\_render\_neuroglancer +### ✨ pcdl\_make\_movie -With this command, you can render a time step ome.tiff file or a time step from a whole time series ome.tiff file straight into [Neuroglancer](https://research.google/blog/an-interactive-automated-3d-reconstruction-of-a-fly-brain/), which is a [WebGL](https://en.wikipedia.org/wiki/WebGL)-based viewer that will render the ome.tiff straight in your browser. +Make a [mp4](https://en.wikipedia.org/wiki/MP4_file_format) movie from the jpeg plots from a time series. -Below, we render a time step into Neuroglancer, first utilizing the time step ome.tiff, then using the whole time series ome.tiff. -You can only render one time step at a time and not a entire time series, like, for example, in napari. ```bash -pcdl_make_ome_tiff output/output00000000.xml -pcdl_render_neuroglancer output_2d/output00000000_oxygen_water_default_blood_cells_ID.ome.tiff -``` -```bash -pcdl_make_ome_tiff output -pcdl_render_neuroglancer output_2d/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff 3 # render time step 3 from the time series +pcdl_plot_scatter output +pcdl_make_movie output/cell_cell_type_z0.0/ ``` ```bash -pcdl_make_ome_tiff -h +pcdl_make_movie -h ``` -Further readings: -+ [TUTORIAL_neuroglancer.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_neuroglancer.md) - - -## [Making movies](https://en.wikipedia.org/wiki/Making_Movies) ### ✨ pcdl\_make\_gif @@ -465,17 +415,7 @@ pcdl_make_gif output/cell_cell_type_z0.0/ pcdl_make_gif -h ``` -### ✨ pcdl\_make\_movie - -Make a [mp4](https://en.wikipedia.org/wiki/MP4_file_format) movie from the jpeg plots from a time series. -```bash -pcdl_plot_scatter output -pcdl_make_movie output/cell_cell_type_z0.0/ -``` -```bash -pcdl_make_movie -h -``` ## Data Clean Up diff --git a/man/TUTORIAL_fijiimagej.md b/man/TUTORIAL_fijiimagej.md deleted file mode 100644 index 1914369..0000000 --- a/man/TUTORIAL_fijiimagej.md +++ /dev/null @@ -1,50 +0,0 @@ -# PhysiCell Data Loader Tutorial: pcdl and Fiji, Imagej, Icy, QuPath. - -[Fiji](https://fiji.sc/) is a free and open source image processing library, -a "batteries-included" distribution of [ImageJ](https://en.wikipedia.org/wiki/ImageJ), -able to read [ome.tiff](https://www.openmicroscopy.org/ome-files/) files. - -[Icy](https://icy.bioimageanalysis.org/) and [QuPath](https://github.com/qupath/qupath/wiki/What-is-QuPath%3F) are similar bioimage analysis software applications. - -Fiji Imagej, Icy, and QuPath are used by wet lab scientists and bioinformatician to analyze fluorescent and bright field microscopy data. -All these software applications can open the ome.tiff file format. - - -## Install Fiji Imagej (JDK), Icy, or QuPath ~ the 64[bit] version! - -Please follow the installation instructions on the official homepages. -+ https://imagej.net/software/fiji/ -+ https://icy.bioimageanalysis.org/download/ -+ https://qupath.github.io/ - - -## Generate ome.tiff files from the command line - -```bash -pcdl_make_ome_tiff('output/') -``` - - -## Generate ome.tiff files from within python - -```python -import pcdl - -mcdsts = pcdl.TimeSeries('output/') -mcdsts.make_ome_tiff() -``` - - -## Open ome.tiff files - -Use the graphical user interface to open the ome.tiff files. - - -## Running Fiji Imagej, Icy, or QuPath - -Please work through the official documentation to learn how to run the software. -+ https://imagej.net/learn/ -+ https://icy.bioimageanalysis.org/ -+ https://qupath.readthedocs.io/en/stable/ - -That's it. The rest is analysis within fiij imagej! diff --git a/man/TUTORIAL_introduction.md b/man/TUTORIAL_introduction.md index ee8ea4a..85dd531 100644 --- a/man/TUTORIAL_introduction.md +++ b/man/TUTORIAL_introduction.md @@ -2,10 +2,8 @@ If you have not already done so, please install the latest version of physicelldataloader (pcdl), as described in the [HowTo](https://github.com/elmbeech/physicelldataloader/blob/master/man/HOWTO.md) section.\ -The current development happens in branch v4. -Branch v3 and v4 are maintained and [pip](https://pypi.org/project/pcdl/) installable. -Branch v3 is also installable through [bioconada](https://bioconda.github.io/). -Branch v1, v2 exists, if ever needed, for reproducibility of old results. +The current development happens in branch v3 and v4. +Branch v1 and v2 exists, if ever needed, for reproducibility of old results. ## Tutorial - branch v1 and v2 @@ -22,9 +20,9 @@ In the very early days, [PhysiCell](https://github.com/MathCancer/PhysiCell) out In 2019, a similar loader script was written for python3. The name of this script filed was pyMCDS.py and basically defined one class named pyMCDS. -In autumn 2022, an endeavor was undertaken to pack the original pyMCDS.py script into a pip installable python3 library and develop it further, but always in such a way that, if necessary, the code could still be run like in the early days.\ +In autumn 2022 an endeavor was undertaken to pack the original pyMCDS.py script into a pip installable python3 library and develop it further, but always in such a way that, if necessary, the code could still be run like in the early days.\ The result is the pcdl physicelldataloader library branch v2, v3. -In spring 2025, the code was stripped of some relics (mainly in the back end) from the early days to make the code more python3 than C++ like, which resulted in branch v4. +In autumn 2024 the code was stripped of some relics from the early days, to make the code more python3 than C++ like, which resulted in branch v4. The result from all of this is the pcdl physicelldataloader library here.\ In the big picture, the pyMCDS class evolved into the TimeStep class, which is slightly heavier but much more powerful for downstream data analysis than the original pyMCDS class. @@ -38,7 +36,7 @@ Naturally, the full-fledged pcdl library with the TimeSteps and TimeSeries class ### Concept PhysiCell data loader is not yet another analysis software, it is just an interface to analysis software! -Please work through the tutorials, and you will in in-depth grasp the meaning behind this sentence. +If you work through the tutorials, and you will in in-depth grasp the meaning behind this sentence. ### Understanding PhysiCell's Time Step Output: the MultiCellular Data Standard (MCDS) Format @@ -63,6 +61,6 @@ The files we care about most from this set consists of: + **output00000012_cells.mat**: This is a MATLAB matrix file that contains tracked information about the individual cells in the model. It tells us things like the cells' position, volume, secretion, cell cycle status, and user-defined cell parameters. + **output00000012_microenvironment0.mat**: This is a MATLAB matrix file that contains data about the microenvironment at this time step. -+ **output00000012_cell_neighbor_graph.txt**, **output00000012_attached_cells_graph.txt**, and **output00000012_spring_attached_cells_graph.txt**: These are files describing the cell neighborhood graph. ++ **output00000012_attached_cells_graph.txt** and **output00000036_cell_neighbor_graph.txt**: These are files describing the cell neighborhood graph. With pcdl we can load a **MCDS time step** or a whole **MCDS time series** for data analysis. diff --git a/man/TUTORIAL_julia.md b/man/TUTORIAL_julia.md index b766b79..189d733 100644 --- a/man/TUTORIAL_julia.md +++ b/man/TUTORIAL_julia.md @@ -166,9 +166,9 @@ Please study the Muon and AnnData documentation to learn how to analyze this dat + https://github.com/scverse/anndata -## ✨ Handle ome.tiff, tiff, png, and jpeg file format +## ✨ Handle tiff, png, and jpeg file format -### Save pcdl data structures as jpeg, png, tiff, and ome.tiff files from the command line +### Save pcdl data structures as jpeg, png, and tiff files from the command line ```bash pcdl_plot_contour output/output00000021.xml oxygen --ext tiff @@ -179,16 +179,10 @@ pcdl_plot_contour output/output00000021.xml oxygen ```bash pcdl_plot_scatter output/output00000021.xml ``` -```bash -pcdl_make_ome_tiff('output/') -``` - -### Load jpeg, png, tiff, and ometiff files into a julia data structures -⚠ **bue 2024-09-04:** ome.tiff files currently cannot be loaded ( github issue: https://github.com/tlnagy/OMETIFF.jl/issues/112 ). +### Load jpeg, png, and tiff files into a julia data structures -We will use the [Images](https://github.com/JuliaImages/Images.jl) library, and it's [OMETIFF](https://github.com/tlnagy/OMETIFF.jl) extension, -to load jpeg, png, tiff, and ome.tiff files +We will use the [Images](https://github.com/JuliaImages/Images.jl) library to load jpeg, png, and tiff files Package installation. @@ -196,7 +190,6 @@ Package installation. using Pkg Pkg.add("FileIO") Pkg.add("Images") -Pkg.add("OMETIFF") ``` Load image file. @@ -206,9 +199,6 @@ using FileIO using Images ``` ```julia -omeimg = load("output/timeseries_ID.ome.tiff") -``` -```julia img = load("output/cell_cell_type_z0.0/output00000021_cell_type.jpeg") ``` diff --git a/man/TUTORIAL_neuroglancer.md b/man/TUTORIAL_neuroglancer.md deleted file mode 100644 index 75a3563..0000000 --- a/man/TUTORIAL_neuroglancer.md +++ /dev/null @@ -1,99 +0,0 @@ -# PhysiCell Data Loader Tutorial: pcdl and Neuroglancer. - -[Neuroglancer](https://research.google/blog/an-interactive-automated-3d-reconstruction-of-a-fly-brain/) is a [WebGL](https://en.wikipedia.org/wiki/WebGL)-based viewer for volumetric data. -It is capable of displaying arbitrary (non axis-aligned) cross-sectional views of volumetric data, as well as 3-D meshes and line-segment based models (skeletons). - -[Ome.tiff](https://www.openmicroscopy.org/ome-files/) is the open microscopy image standard format used by wet lab scientists to store (fluorescent) microscopy data. - -In 2022, in the middle of the pandemic, a small group of programmers undertook at the Image Analysis Working Group of the Cancer Systems Biology Consortium & Physical Sciences-Oncology Network [hackathon](https://github.com/IAWG-CSBC-PSON/hack2022-10-neuroglancer) the endeavor to write a python3 script that directly can render ome.tiff files into Neurogalncer. -This script was adapted for the pcdl project. - - -### ✨ command line - -With pcdl it is very easy to generate time step and time series ome.tiff files from regular PhysiCell output straight from the command line and render them into Neuroglancer. - -#### command line time step -Generate time step 6 ome.tiff - -```bash -pcdl_make_ome_tiff pcdl_make_ome_tiff output_2d/output00000006.xml -``` - -Render time step 6 ome.tiff - -```bash -pcdl_render_neuroglancer output_2d/output00000006_oxygen_water_default_blood_cells_ID.ome.tiff -``` - -#### command line time series - -Generate time series ome.tiff - -```bash -pcdl_make_ome_tiff output/ -``` - -Render timer series time step 0. - -```bash -pcdl_render_neuroglancer output/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff -``` - -Render time series time step 12. - -```bash -pcdl_render_neuroglancer output/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff 12 -``` - -#### command line man pages - -```bash -pcdl_make_ome_tiff -h -``` -```bash -pcdl_render_neuroglancer -h -``` - -### ✨ python -With pcdl it is very easy to generate within python3 time steps and time series ome.tiff files from regular PhysiCell output and render them into Neuroglancer. - -#### python time step - -Generate and directly render time step 6. -```python -import pcdl - -mcds = pcdl.TimeStep('output_2d/output00000006.xml') -mcds.render_neuroglancer(mcds.make_ome_tiff()) -``` - -#### python time series - -Generate time series, then render. -```python -import pcdl - -mcdsts = pcdl.TimeSeries('output_2d/') -s_pathfile = mcdsts.make_ome_tiff() -mcdsts.render_neuroglancer(s_pathfile) # time step 0 -``` -```python -mcdsts.render_neuroglancer(s_pathfile, 12) # time step 12 -``` - -#### python docstrings - -```python -import pcdl -help(pcdl.render_neuroglancer) -``` - -### ✨ Further readings - -Please work through the official documentation to learn how to run the Neuroglancer software. -+ https://github.com/google/neuroglancer -+ https://neuroglancer-docs.web.app/index.html -+ https://research.google/blog/an-interactive-automated-3d-reconstruction-of-a-fly-brain/ - -That's it. The rest is analysis within neuroglancer! diff --git a/man/TUTORIAL_paraview.md b/man/TUTORIAL_paraview.md index d9d9b2b..fda8707 100644 --- a/man/TUTORIAL_paraview.md +++ b/man/TUTORIAL_paraview.md @@ -1,13 +1,7 @@ # PhysiCell Data Loader Tutorial: pcdl and Paraview - - [Paraview](https://www.paraview.org/) is a free and open source scientific visualization software, -that lets us load and analyze vtk rectilinear grid data and vtk polynomial data, and even ome.tiff data files. +that lets us load and analyze vtk rectilinear grid data and vtk polynomial data. ## Install paraview @@ -37,13 +31,12 @@ mcdsts.make_cell_vtk() ## Load vtk files with paraview -1. **View** / **Pipeline Browser** has to be checked! -2. **View** / **Properties** has to be checked! -3. **File** / **Open...** path/to/PhysiCell/output/output..\_conc.vtr [OK] -4. **File** / **Open...** path/to/PhysiCell/output/output..\_cell.vtp [OK] -5. In the **Pipeline Browser**, click the closed **eyes**, so that they open. -6. In the **Pipeline Browser**, click the output00000000\_cell.vtp\* and under **Properties Coloring** select cell\_type in the dropdown menu. -7. In the **Pipeline Browser**, click the output00000000\_conc.vtr\* and under **Properties Coloring** select the oxygen in the dropdown menu. + +1. **File** / **Open...** path/to/PhysiCell/output/output..\_conc.vtr [OK] +2. **File** / **Open...** path/to/PhysiCell/output/output..\_cell.vtp [OK] +3. In the **Pipeline Browser**, click the closed **eyes**, so that they open. +4. In the **Pipeline Browser**, click the output00000000\_cell.vtp\* and under **Coloring** select cell\_type in the dropdown menu. +5. In the **Pipeline Browser**, click the output00000000\_conc.vtr\* and under **Coloring** select the oxygen in the dropdown menu. For learning more about how to run the software, please work through the official documentation. diff --git a/man/TUTORIAL_python3_json.md b/man/TUTORIAL_python3_json.md index 0dcebad..eae2a53 100644 --- a/man/TUTORIAL_python3_json.md +++ b/man/TUTORIAL_python3_json.md @@ -7,10 +7,10 @@ In pcdl output from the [TimeSeries](https://github.com/elmbeech/physicelldatalo The [json library](https://docs.python.org/3/library/json.html) is a part of core python. Json is the ideal data format for unstructured data constructs that not can be stored in csv file format, like a dictionary of lists. -Please note, python objects are not per se json comaptible. +Please note, python objecta are not per se json comaptible. For example: -Python dictionary keys can be of almost any data type, but json object keys have to be strings. Complex numbers are a standard data type in python, but complex numbers cannot be stored in json. +Python dictionary keys can be of almost any data type, but json object keys have to be strings. ### Dump pcdl data construct from the command line into a json file diff --git a/man/TUTORIAL_python3_matplotlib.md b/man/TUTORIAL_python3_matplotlib.md index 8cd5772..3c8be7e 100644 --- a/man/TUTORIAL_python3_matplotlib.md +++ b/man/TUTORIAL_python3_matplotlib.md @@ -126,10 +126,11 @@ df_cell = mcds.get_cell_df() Generate a dataframe with some categorical data + ```pandas df_cat = df_cell.loc[:, ['cell_type', 'cycle_model', 'current_phase', 'dead']] df_cat['count'] = 1 -df_catplot = df_cat.loc[:, ['cell_type','count']].groupby('cell_type').count() +df_catplot = df_cat.loc[:, ['dead','count']].groupby('dead').count() df_catplot.info() ``` @@ -151,7 +152,7 @@ df_catplot.plot( y = 'count', ylabel = '', legend = False, - title = 'cell_type' + title = 'dead' ) ``` diff --git a/man/TUTORIAL_python3_napari.md b/man/TUTORIAL_python3_napari.md deleted file mode 100644 index e6e6908..0000000 --- a/man/TUTORIAL_python3_napari.md +++ /dev/null @@ -1,54 +0,0 @@ -# PhysiCell Data Loader Tutorial: pcdl and Python and Napari - -[Napari](https://napari.org/stable/) is both a python library and a GUI software. -Napari is used by wetlab scientists and bioinformatician to analyze fluorescent microscopy data. -Napari can read [ome.tiff](https://www.openmicroscopy.org/ome-files/) files. -https://github.com/AllenCellModeling/napari-aicsimageio - - -## Install napari - -```bash -pip3 install napari[all] -``` - - -### Generate ome.tiff files from the command line - -```bash -pcdl_make_ome_tiff('output/') -``` - - -### Generate ome.tiff files from within python - -```python -import pcdl - -mcdsts = pcdl.TimeSeries('output/') -mcdsts.make_ome_tiff() -``` - - -### Open ome.tiff files in napari from within python - -```python -import napari - -viewer = napari.Viewer() -viewer.open('output/timeseries_ID.ome.tiff') -``` - - -### Open ome.tiff files in napari from the command line - -```bash -napari output/timeseries_ID.ome.tiff -``` - -## Running napari - -Please work through the official documentation to learn how to run the software. -+ https://napari.org/stable/tutorials/start_index.html - -That's it. The rest is analysis within napari! diff --git a/man/TUTORIAL_python3_ometiff.md b/man/TUTORIAL_python3_ometiff.md index 1dc2d61..58b91e2 100644 --- a/man/TUTORIAL_python3_ometiff.md +++ b/man/TUTORIAL_python3_ometiff.md @@ -1,28 +1,14 @@ # PhysiCell Data Loader Tutorial: pcdl and Python and the Ome.tiff, Tiff, Png, and Jpeg File Format -In pcdl output from [TimeSteps](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_timestep.md) and [TimeSeries](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_timeseries.md) can be stotred as [ome.tiff](https://www.openmicroscopy.org/ome-files/), [tiff](https://www.loc.gov/preservation/digital/formats/fdd/fdd000022.shtml), [png](http://libpng.org/pub/png/), and [jpeg](https://jpeg.org/jpeg/) files. +In pcdl output from [TimeSteps](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_timestep.md) and [TimeSeries](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_timeseries.md) can be stotred as [tiff](https://www.loc.gov/preservation/digital/formats/fdd/fdd000022.shtml), [png](http://libpng.org/pub/png/), and [jpeg](https://jpeg.org/jpeg/) files. Tiff, png, and jpeg are raster graphic file formats. -Ome.tiff is the open microscopy image standard. -This means, being able to export PhysiCell output in ome.tiff files format -enables us to study PhysiCell output the same way -as commonly fluorescent microscopy data is analyzed by wetlab scientists. -Please have a look at [TUTORIAL_python3_napari.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_napari.md), -[TUTORIAL_fiji_imagej.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_fijiimagej.md), and -[TUTORIAL_neuroglancer.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_neuroglancer.md) to learn more. +Additionally tiff, png, and jpeg files can as well be loaded back in to python as [numpy](https://numpy.org/) array, for example with the [sci-kit image](https://scikit-image.org/) library (image data only). -Additionally ome.tiff, tiff, png, and jpeg files can as well be loaded back in to python as [numpy](https://numpy.org/) array, for example with the [sci-kit image](https://scikit-image.org/) library (image data only). +### Save pcdl data constructs from the command line into tiff files -Besides that, ome.tiff files can be loaded with the [bioio](https://github.com/bioio-devs/bioio) library (image and metadata). - - -### Save pcdl data constructs from the command line into tiff and ome.tiff files - -```bash -pcdl_make_ome_tiff('output/') -``` ```bash pcdl_plot_contour output/output00000012.xml oxygen ``` @@ -31,7 +17,7 @@ pcdl_plot_scatter output/output00000012.xml ``` -### Save pcdl data constructs from within python into tiff and ome.tiff files +### Save pcdl data constructs from within python into tiff files ```python import pcdl @@ -39,7 +25,6 @@ import pcdl mcdsts = pcdl.TimeSeries('output/') mcdsts.get_mcds_list()[12].plot_contour(focus='oxygen', ext='tiff') mcdsts.get_mcds_list()[12].plot_scatter(ext='tiff') -mcdsts.make_ome_tiff() ``` @@ -57,27 +42,6 @@ from skimage import io a_cell = io.imread('output/cell_cell_type_z0.0/output00000012_cell_type.tiff') a_cell.shape # (480, 640, 4) ``` -```python -from skimage import io - -a_ome = io.imread('output/timeseries_ID.ome.tiff') -a_ome.shape # (25, 2, 200, 300) -``` -### Load ome.tiff files as BioImage object into python - -```python -from bioio import BioImage - -img = BioImage('output/timeseries_ID.ome.tiff') -img.shape # (25, 2, 1, 200, 300) -``` -```python -img.dims # -``` -```python -img.channel_names # [np.str_('oxygen'), np.str_('cancer_cell')] -``` - That's it. The rest is analysis! diff --git a/man/TUTORIAL_python3_scverse.md b/man/TUTORIAL_python3_scverse.md index ef55b99..a73dd43 100644 --- a/man/TUTORIAL_python3_scverse.md +++ b/man/TUTORIAL_python3_scverse.md @@ -62,8 +62,8 @@ Load the data. ```python mcdsts = pcdl.TimeSeries('output/') -adata = mcdsts.get_anndata(values=2, scale='maxabs', collapse=True) -print(adata) +annts = mcdsts.get_anndata(values=2, scale='maxabs', collapse=True) +print(annts) ``` Let's do an interactive data analysis.\ @@ -78,22 +78,22 @@ Please note, sub-library abbreviations used in the scanpy and squidpy library ar Principal component analysis: ```python -sc.tl.pca(adata) # process anndata object with the pca tool. -sc.pl.pca(adata) # plot pca result. +sc.tl.pca(annts) # process anndata object with the pca tool. +sc.pl.pca(annts) # plot pca result. ``` ```python -sc.pl.pca(adata, color=['current_phase','oxygen']) # plot the pca results colored by some attributes. +sc.pl.pca(annts, color=['current_phase','oxygen']) # plot the pca results colored by some attributes. ``` ```python -sc.pl.pca_variance_ratio(adata) # plot how much of the variation each principal component captures. +sc.pl.pca_variance_ratio(annts) # plot how much of the variation each principal component captures. ``` Neighborhood graph clustering: ```python -sc.pp.neighbors(adata, n_neighbors=15) # compute the neighborhood graph with the neighbors preprocess step. -sc.tl.leiden(adata, resolution=0.01) # cluster the neighborhood graph with the leiden tool. -sc.pl.pca(adata, color='leiden') # plot the pca results colored by leiden clusters. +sc.pp.neighbors(annts, n_neighbors=15) # compute the neighborhood graph with the neighbors preprocess step. +sc.tl.leiden(annts, resolution=0.01) # cluster the neighborhood graph with the leiden tool. +sc.pl.pca(annts, color='leiden') # plot the pca results colored by leiden clusters. ``` -```python -mcdsts.get_conc_df() -``` -```python -mcdsts.plot_contour('substrate') -``` -```python -mcdsts.make_conc_vtk() -``` ++ mcdsts.get_conc_df() ++ mcdsts.plot_contour('substrate') ++ mcdsts.make_conc_vtk() For cell data, these are the functions: - -```python -mcdsts.get_cell_df() -``` -```python -mcdsts.get_anndata() -``` -```python -mcdsts.make_graph_gml() -``` -```python -mcdsts.plot_scatter() -``` -```python -mcdsts.make_cell_vtk() -``` - -For microenvironment and cell data, these are the functions: -```python -mcdsts.make_ome_tiff() -``` -```python -mcdsts.render_neuroglancer(mcdsts.make_ome_tiff(), timestep=0) -``` ++ mcdsts.get_cell_df() ++ mcdsts.get_anndata() ++ mcdsts.make_graph_gml() ++ mcdsts.plot_scatter() ++ mcdsts.make_cell_vtk() Yet, there are additional functions, that only make sense for TimeSeries, and those functions will be discussed in this chapter. For handling TimeSeries, these are the functions: -+ `mcdsts.get_xmlfile_list()` -+ `mcdsts.read_mcds()` -+ `mcdsts.get_mcds_list()` -+ `mcdsts.get_annmcds_list()` ++ mcdsts.get_xmlfile_list() ++ mcdsts.read_mcds() ++ mcdsts.get_mcds_list() ++ mcdsts.get_annmcds_list() For microenvironment data, this is the function: -+ `mcdsts.get_conc_attribute()` ++ mcdsts.get_conc_attribute() For cell data, this is the function: -+ `mcdsts.get_cell_attribute()` ++ mcdsts.get_cell_attribute() For microenvironment and cell data, this is the function: -+ `mcdsts.plot_timeseries()` ++ mcdsts.plot_timeseries() Besides, there are functions to render a set of jpeg, png, or tiff images into a movie. -+ `mcdsts.make_movie()` and `pcdl.make_movie()` -+ `mcdsts.make_gif()` and `pcdl.make_gif()` ++ mcdsts.make_movie() and pcdl.make_movie() ++ mcdsts.make_gif() and pcdl.make_gif() @@ -101,7 +70,7 @@ python3 -c"import pathlib, pcdl, shutil; pcdl.install_data(); s_ipath=str(pathli ## Loading an MCDS Time Series -Like in the TimeStep class, for memory consumption and processing speed control, +Like in the pyMCDs class, for memory consumption and processing speed control, we can specify if we want to load microenvironment data and graph data from the snapshots we later on analyze. Additionally, we can specify, if for first even want to load data at all, or if we only would like to load the output xml file list, which we will see, can be manipulated before actual data is loaded. diff --git a/man/TUTORIAL_python3_timestep.md b/man/TUTORIAL_python3_timestep.md index 0f58a4f..3bce765 100644 --- a/man/TUTORIAL_python3_timestep.md +++ b/man/TUTORIAL_python3_timestep.md @@ -1,6 +1,6 @@ # PhysiCell Data Loader Tutorial: pcdl and python and MCDS TimeSteps -In this chapter, we will load the pcdl library and use its TimeStep class to load the data snapshot output/00000012, from the 2D time series test dataset (https://github.com/elmbeech/physicelldataloader/blob/master/output_2d.tar.gz). +In this chapter, we will load the pcdl library and use its TimeStep class to load the data snapshot 00000012, from [data\_timeseries\_ 2d](https://github.com/elmbeech/physicelldataloader/tree/master/pcdl/output_2d) from the 2D time series test dataset. First, please install the latest version of physicelldataloader (pcdl), as described in the [HowTo](https://github.com/elmbeech/physicelldataloader/blob/master/man/HOWTO.md) chapter. @@ -12,7 +12,7 @@ And, if not already done so, have a quick read through the pcdl [background](htt ## Preparation To runs this tutorial, -you can either work with the data that is currently in your output folder, +you can either work the data that is currently in your output folder, or you can install the 2D unit test dataset into your PhysiCell output folder, by executing the following command sequence. @@ -53,7 +53,7 @@ print('pcdl version:', pcdl.__version__) # it is easy to figure out which pcdl mcds = pcdl.TimeStep(s_pathfile, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) ``` -The verbosity for pcdl shell output you can tune, even after loading the data. +The verbosity for pcdl output you can tune, even after loading the data. ```python3 mcds.set_verbose_false() @@ -74,34 +74,31 @@ Regarding the original python-loader, the structure has slightly changed.\ Anyhow, let's take a look at what we actually have in here. +![mcds.data dictionary blueprint](img/physicelldataloader_data_dictionary_v3.2.2.png) + ```python # main data branches -sorted(mcds.data.keys()) # metadata, mesh, substrate (microenvironment), cell (agent), and raw_substrate and raw_cell data. +sorted(mcds.data.keys()) # metadata, mesh, substrate microenvironment (continuum_variables), and cell agent (discrete_cells) # metadata -sorted(mcds.data['metadata'].keys()) # multicellds_version, physicell_version, simulation time (current_time), runtime (current_runtime), time stamp (created), time unit ('time_units', 'runtime_units'), spatial unit ('spatial_unit'), and all units specified (ds_unit). +sorted(mcds.data['metadata'].keys()) # multicellds version, physicell version, simulation time, runtime, time stamp, time unit, spatial unit, and substrate and cell type ID label mappings # mesh -sorted(mcds.data['mesh'].keys()) # voxel (ijk), mesh (mnp), and position (xyz) range, axis, spacing, voxel volume, coordinate, and grid. +sorted(mcds.data['mesh'].keys()) # voxel (ijk), mesh (nmp), and position (xyz) range, axis, coordinate, grid objects, and voxel volume # microenvironment -sorted(mcds.data['substrate'].keys()) # df_conc, df_substarte, ds_substrate, ls_substarte -print(mcds.data['substrate']['ls_substarte'] # list of all processed substrates -print(mcds.data['substrate']['ds_substrate'] # dictionary with substrate ID label mapping -print(mcds.data['substrate']['df_substarte'] # pandas data frame with substrate decay_rate and diffusion_coefficient. -print(mcds.data['substrate']['df_conc'] # pandas data frame with the actual substrate concentrations in each voxel. +sorted(mcds.data['continuum_variables'].keys()) # list of all processed substrates, e.g. oxygen +sorted(mcds.data['continuum_variables']['oxygen'].keys()) # substrate related data values, unit, diffusion coefficient, and decay rate # cell -sorted(mcds.data['cell'].keys()) # dei_graph, df_cell, ds_celltype, ls_cellattr, ls_celltype -print(mcds.data['cell']['ls_celltype']) # list of all processed cell types -print(mcds.data['cell']['ls_cellattr']) # list of all outputted (df_cell) cell type related attributes -print(mcds.data['cell']['ds_celltype']) # dictionary with cell type ID label mapping -print(mcds.data['cell']['df_cell']) # pandas data frame with each cell's attribute values -sorted(mcds.data['cell']['dei_graph'].keys()) # dictionary with neighbor_cells, attached_cells, and spring_attached_cells graph information. +sorted(mcds.data['discrete_cells'].keys()) # data, units, and graph dictionaries +sorted(mcds.data['discrete_cells']['data'].keys()) # all cell related, tracked data +sorted(mcds.data['discrete_cells']['units'].keys()) # all units from the cell related, tracked data +sorted(mcds.data['discrete_cells']['graph'].keys()) # neighbor_cells and attached_cells graph dictionaries ``` **Once again, loud, for the ones in the back, in pcdl >= version 3, all data is accessible by functions. -There should be no need to fetch data directly from the `mcds.data` dictionaries!** +There should be no need to fetch data directly from the `mcds.data` dictionaries.** We will explore these functions in the upcoming sections. @@ -200,6 +197,39 @@ df_conc.loc[(df_conc.voxel_i == 2) & (df_conc.voxel_j == 1) & (df_conc.voxel_k = Please have a look at [TUTORIAL_python3_pandas.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_pandas.md) to learn more. +Additionally, there is a less often used function to retrieve substrate specific 3D or 2D meshgrid [numpy](https://numpy.org/) arrays. +To get a 2D meshgrids you can slice though any z stack value, the function will always pick the closest mesh center coordinate, the smaller coordinate, if you hit the saddle point between two voxels. +(This function might become deprecated in a future pcdl version.) + +```python +# concentration meshgrid for a particular substrate +oxygen_2d = mcds.get_concentration('oxygen', z_slice=0) +oxygen_2d.shape # (11, 11) +``` +```python +# concentration meshgrid for a particular substrate +oxygen_3d = mcds.get_concentration('oxygen') +oxygen_3d.shape # (11, 11, 1) +``` + + +Additionally, there is a less often used functions to retrieve a [numpy](https://numpy.org/) array of all substrate concentrations at a particular xyz coordinate, ordered alphabetically by substrate name, like the list retrieved by the get\_substrate\_names function. +(This function might become deprecated in a future pcdl version.) + +```python +# all concentration values at a particular coordinate +mcds.get_concentration_at(x=0, y=0, z=0) # array([34.4166271]) +``` +```python +# all concentration values at a particular coordinate +mcds.get_concentration_at(x=111, y=22, z=-5) # array([18.80652216]) +``` +```python +# all concentration values at a particular coordinate +mcds.get_concentration_at(x=111, y=22, z=-5.1) # None and Warning @ pyMCDS.is_in_mesh : z = -5.1 out of bounds: z-range is (-5.0, 5.0) +``` + + ### ✨ Microenvironment Data Analysis with [Matplotlib](https://matplotlib.org/) For substrate concentration visualization, **matplotlib contour and contourf plots**, @@ -250,12 +280,6 @@ We can retrieve a dictionary that maps cell type IDs to labels. mcds.get_celltype_dict() # {'0': 'cancer_cell'} ``` -And we can retrieve an alphabetically ordered list with all cell agent attributes outputted by PhysiCell, loadable with mcds.get\_cell\_df(). - -```python -mcds.get_cell_attribute_list() # ['apoptotic_phagocytosis_rate', ...] -``` - ### ✨ Cell Data Analysis with [Pandas](https://pandas.pydata.org/) @@ -291,6 +315,15 @@ df_cell.loc[(df_cell.voxel_i == 2) & (df_cell.voxel_j == 1) & (df_cell.voxel_k = Please have a look at [TUTORIAL_python3_pandas.md](https://github.com/elmbeech/physicelldataloader/blob/master/man/TUTORIAL_python3_pandas.md) to learn more. +There exist an additional, less often used function, +to filter for cells in xyz position plus minus (voxel spacing / 2). +(This function might become deprecated in a future pcdl version.) + +```python +mcds.get_cell_df_at(x=45, y=10, z=0) # cells: 5, 7, 39 +``` + + ### ✨ Cell Data Analysis within the [Scverse](https://scverse.org/) To be able to analyze cell agent data the same way as single cell RNA seq data is analyzed, @@ -300,8 +333,8 @@ Anndata is the backbone of the scverse (single cell universe) project. ```python ann = mcds.get_anndata(values=2) -print(ann) # AnnData object with n_obs × n_vars = 81 × 50 - # obs: 'z_layer', 'time', 'cell_type', 'chemotaxis_index', 'current_phase', 'cycle_model' +print(ann) # AnnData object with n_obs × n_vars = 992 × 26 + # obs: 'z_layer', 'time', 'current_phase', 'cycle_model' # uns: 'neighbor' # obsm: 'spatial' # obsp: 'physicell_neighbor_conectivities', 'physicell_neighbor_distances' @@ -310,16 +343,16 @@ print(ann) # AnnData object with n_obs × n_vars = 81 × 50 variables: ```python -ann.var_names # numerical cell attributes: Index(['attachment_rate', ...], dtype='object') +ann.var_names # numerical cell attributes: Index(['cell_BM_repulsion_strength', ... , 'total_volume'], dtype='object') ``` observation: ```python -ann.obs_names # cell IDs: Index(['10', ...], dtype='object', name='ID', length=992) +ann.obs_names # cell IDs: Index(['0', ..., '994'], dtype='object', name='ID', length=992) ``` ```python -ann.obs_keys() # categorical cell attributes: ['z_layer', 'time', 'cell_type', 'chemotaxis_index', 'current_phase', 'cycle_model'] +ann.obs_keys() # categorical cell attributes: ['z_layer', 'time', 'current_phase', 'cycle_model'] ``` ```python ann.obsm_keys() # cell coordinates: ['spatial'] @@ -340,8 +373,8 @@ ann.uns_keys() # ['neighbor'] ann.uns['neighbor'] # metadata about the neighborhood graph. ``` -The output tells us that we have loaded a time step with 81 cell agents and 50 numerical attributes (vars). -Further, we have 6 categorical cell agent attributes (obs). +The output tells us that we have loaded a time step with 992 cell agents and 26 numerical attributes (vars). +Further, we have 4 categorical cell agent attributes (obs). We have each cell agent's xy spatial coordinate information (obsm). And we have cell neighbor graph infromation (obsp, uns). @@ -359,19 +392,13 @@ This file format can be read by graph libraries, like network and igraph, for do Cell neighbor touching graph ```python -mcds.make_graph_gml('neighbor') +mcds.make_graph_gml('touch') ``` Cell neighbor attached graph ```python mcds.make_graph_gml('attached') ``` - -Cell neighbor spring attached graph -```python -mcds.make_graph_gml('spring') -``` - +```python3 import vtk # load file -reader = vtk.vtkXMLRectilinearGridReader() -reader.SetFileName('output/output00000012_conc.vtr') -reader.Update() -grid = reader.GetOutput() - -# use scalar field for coloring the grid -scalar_name = 'oxygen' # 'water' -scalar_array = grid.GetPointData().GetArray(scalar_name) -if scalar_array is None: - raise RuntimeError(f'Scalar array "{scalar_name}" not found.') -grid.GetPointData().SetActiveScalars(scalar_name) -scalar_range = scalar_array.GetRange() - -# generate color lookup table -lut = vtk.vtkLookupTable() -lut.SetNumberOfTableValues(256) -lut.SetTableRange(scalar_range) -lut.SetHueRange(0.667, 0.0) # blue to red rainbow -lut.Build() - -# convert grid to z slice to data -# generate xy plane at z center -bounds = grid.GetBounds() -z_center = 0.5 * (bounds[4] + bounds[5]) -plane = vtk.vtkPlane() -plane.SetOrigin(0.5 * (bounds[0] + bounds[1]), 0.5 * (bounds[2] + bounds[3]), center_z) -plane.SetNormal(0, 0, 1) # slice in z -# cut xy plain at z center -cutter = vtk.vtkCutter() -cutter.SetCutFunction(plane) -cutter.SetInputData(grid) -cutter.Update() -# get xy plain data at z center -polydata_zslice = cutter.GetOutput() - -# mapper: map data to geometry -mapper = vtk.vtkPolyDataMapper() -mapper.SetInputData(polydata_zslice) -mapper.SetLookupTable(lut) -mapper.SetScalarRange(scalar_range) -mapper.SetColorModeToMapScalars() -mapper.SetScalarModeToUsePointData() -mapper.ScalarVisibilityOn() - -# actor: map actor to data mapped geometry -actor = vtk.vtkActor() -actor.SetMapper(mapper) - -# actor: color bar -scalar_bar = vtk.vtkScalarBarActor() -scalar_bar.SetLookupTable(lut) -scalar_bar.SetTitle(scalar_name) - -# renderer: director -renderer = vtk.vtkRenderer() -renderer.SetBackground(1/3, 1/3, 1/3) # gray background -renderer.AddActor(actor) -renderer.AddActor2D(scalar_bar) +vtr_reader = vtk.vtkXMLRectilinearGridReader() +vtr_reader.SetFileName('output/output00000012_conc.vtr') +vtr_reader.Update() -# render window: stage -render_window = vtk.vtkRenderWindow() -render_window.AddRenderer(renderer) -render_window.SetSize(800, 600) - -# interactor: 4th wall -interactor = vtk.vtkRenderWindowInteractor() -interactor.SetRenderWindow(render_window) - -# initialize and start the rendering loop -renderer.ResetCamera() -render_window.Render() -interactor.Start() -``` - -Load and display cell.vtp **polydata** files ~ **no coloring**. - -```python -import vtk - -# load file -vtp_reader = vtk.vtkXMLPolyDataReader() -vtp_reader.SetFileName('output/output00000012_cell.vtp') -vtp_reader.Update() -polydatapipe = vtp_reader.GetOutputPort() - -# mapper: map data to geometry -mapper = vtk.vtkPolyDataMapper() -mapper.SetInputConnection(polydatapipe) - -# actor: map actor to data mapped geometry -actor = vtk.vtkActor() -actor.SetMapper(mapper) - -# renderer: director -renderer = vtk.vtkRenderer() -renderer.SetBackground(1/3, 1/3, 1/3) # gray background -renderer.AddActor(actor) - -# render window: stage -render_window = vtk.vtkRenderWindow() -render_window.AddRenderer(renderer) -render_window.SetSize(800, 600) - -# interactor: 4th wall -interactor = vtk.vtkRenderWindowInteractor() -interactor.SetRenderWindow(render_window) - -# initialize and start the rendering loop -render_window.Render() -interactor.Start() -``` - -Load and display cell.vtp **polydata** files ~ **numerical data coloring**. - -```python -import vtk - -# load file -vtp_reader = vtk.vtkXMLPolyDataReader() -vtp_reader.SetFileName('output/output00000012_cell.vtp') -vtp_reader.Update() -polydata = vtp_reader.GetOutput() - -# get color data -scalar_name = 'pressure' # 'positions_and_radii' -scalar_array = polydata.GetPointData().GetArray(scalar_name) -if scalar_array is None: - raise RuntimeError(f'Scalar array "{scalar_name}" not found.') -scalar_range = scalar_array.GetRange() - -# set color data -polydata.GetPointData().SetActiveScalars(scalar_name) - -# mapper: map data to geometry -mapper = vtk.vtkPolyDataMapper() -mapper.SetInputData(polydata) -mapper.SetScalarRange(scalar_range) # min and max value -mapper.SetScalarModeToUsePointFieldData() -mapper.SelectColorArray(scalar_name) -mapper.SetColorModeToMapScalars() -mapper.ScalarVisibilityOn() - -# actor: map actor to data mapped geometry -actor = vtk.vtkActor() -actor.SetMapper(mapper) - -# actor: map actor to color bar -scalar_bar = vtk.vtkScalarBarActor() -lut = mapper.GetLookupTable() -scalar_bar.SetLookupTable(lut) -scalar_bar.SetTitle(scalar_name) - -# renderer: director -renderer = vtk.vtkRenderer() -renderer.SetBackground(1/3, 1/3, 1/3) # gray background -renderer.AddActor(actor) -renderer.AddActor2D(scalar_bar) # color bar ~ if color mapping - -# render window: stage -render_window = vtk.vtkRenderWindow() -render_window.AddRenderer(renderer) -render_window.SetSize(800, 600) - -# interactor: 4th wall -interactor = vtk.vtkRenderWindowInteractor() -interactor.SetRenderWindow(render_window) - -# initialize and start the rendering loop -render_window.Render() -interactor.Start() +# actor +# scene +# show ``` -Load and display cell.vtp **polydata** files ~ **categorical data coloring**. +Load and display cell.vtp ploydata files. -```python -import matplotlib.pyplot as plt + +```python3 import vtk # load file vtp_reader = vtk.vtkXMLPolyDataReader() vtp_reader.SetFileName('output/output00000012_cell.vtp') vtp_reader.Update() -polydata = vtp_reader.GetOutput() - -# get color data -scalar_name_cat = 'cell_type' -scalar_array_str = polydata.GetPointData().GetAbstractArray(scalar_name_cat) -scalar_name_num = scalar_name_cat + '_numeric' -scalar_array = vtk.vtkIntArray() -scalar_array.SetName(scalar_name_num) - -# unique categoris -es_cat = set() -for i_cat in range(scalar_array_str.GetNumberOfValues()): - s_cat = scalar_array_str.GetValue(i_cat) - es_cat.add(s_cat) - -# index unique categories -dsi_cat = {} -for i_cat, s_cat in enumerate(sorted(es_cat)): - dsi_cat.update({s_cat: i_cat}) - -# map each element of the array to the category index -for i in range(scalar_array_str.GetNumberOfValues()): - s_cat = scalar_array_str.GetValue(i) - scalar_array.InsertNextValue(dsi_cat[s_cat]) -scalar_range = scalar_array.GetRange() - -# set color data -polydata.GetPointData().AddArray(scalar_array) -polydata.GetPointData().SetActiveScalars(scalar_name_num) - -# look up table -lut = vtk.vtkLookupTable() -lut.SetNumberOfTableValues(len(es_cat)) -lut.Build() -cmap = plt.get_cmap('turbo', len(es_cat)) -for s_cat, i_cat in dsi_cat.items(): - r, g, b, _ = cmap(i_cat) - lut.SetTableValue(i_cat, r, g, b, 1.0) - lut.SetAnnotation(i_cat, s_cat) - -# mapper: map data to geometry -mapper = vtk.vtkPolyDataMapper() -mapper.SetInputData(polydata) -mapper.SetLookupTable(lut) -mapper.SetScalarRange(scalar_range) # min and max value -mapper.SetScalarModeToUsePointFieldData() -mapper.SelectColorArray(scalar_name_num) -mapper.SetColorModeToMapScalars() -mapper.ScalarVisibilityOn() - -# actor: map actor to data mapped geometry -actor = vtk.vtkActor() -actor.SetMapper(mapper) - -# actor: color bar -scalar_bar = vtk.vtkScalarBarActor() -scalar_bar.SetLookupTable(lut) -scalar_bar.SetTitle(scalar_name_cat) -scalar_bar.DrawTickLabelsOff() - -# renderer: director -renderer = vtk.vtkRenderer() -renderer.SetBackground(1/3, 1/3, 1/3) # gray background -renderer.AddActor(actor) -renderer.AddActor2D(scalar_bar) # color bar ~ if color mapping -# render window: stage -render_window = vtk.vtkRenderWindow() -render_window.AddRenderer(renderer) -render_window.SetSize(800, 600) - -# interactor: 4th wall -interactor = vtk.vtkRenderWindowInteractor() -interactor.SetRenderWindow(render_window) - -# initialize and start the rendering loop -render_window.Render() -interactor.Start() +# actor +# scene +# show ``` Official documentation: @@ -373,38 +56,6 @@ Official documentation: + https://examples.vtk.org/site/PythonicAPIComments/ -### How to load and visualize a vtk file with the pyvista python library - -Installation. - -```bash -pip3 install pyvista -``` - -Load, color, and display **cell.vtr rectilinear grid** files. - -```python -import pyvista as pv - -grid = pv.read('output/output00000012_conc.vtr') -grid.plot(scalars='oxygen', cmap='turbo', opacity=1/3) # 'water' -``` - -Load, color, and display **cell.vtp polydata** files. - -```python -import pyvista as pv - -poly = pv.read('output/output00000012_cell.vtp') -poly.plot(scalars='cell_type', cmap='turbo', opacity=2/3) # 'pressure', 'positions_and_radii' -``` - -Official documentation: -+ https://docs.pyvista.org/ - -Hasta la vista, baby! - - ### How to load and visualize a vtk file with the fury python library Installation. @@ -413,12 +64,10 @@ Installation. pip3 install fury ``` -Unfortunately, **conc.vtr rectilinear grid** files are not yet supported (v0.12.0 and 2.0.0a1); conc.vtr files cannot be loaded. -Although this issue might be resolved any time now. -+ https://github.com/fury-gl/fury/issues/971 - + +Unfortunately, rectiliniar grid files are not supported, conc.vtr files cannot be loaded. -Load and display **cell.vtp polydata** files ~ **no coloring**. +Load and display cell.vtp ploydata files. ```python import fury @@ -433,7 +82,7 @@ actor = fury.get_actor_from_polydata(v_cell) scene = fury.window.Scene() scene.add(actor) -# initialize and start the rendering loop +# show showm = fury.window.ShowManager(scene) showm.initialize() showm.start() diff --git a/man/TUTORIAL_r.md b/man/TUTORIAL_r.md index 33d1d9a..5dac49f 100644 --- a/man/TUTORIAL_r.md +++ b/man/TUTORIAL_r.md @@ -136,7 +136,7 @@ pcdl_get_anndata output/ We will use the [schard](https://github.com/cellgeni/schard) R package to translate the h5ad file into R data structures that can be analyzed by [singlecellexperiment](https://bioconductor.org/packages/release/bioc/html/SingleCellExperiment.html) -and [seurat](https://satijalab.org/seurat/). +and [seurat](https://satijalab.org/seurat/). Special thanks to Marcello Hurtado from the Pancald Lab, who told me that such translation software exist! diff --git a/man/docstring/mcds.__init__.md b/man/docstring/mcds.__init__.md index f760d95..7fdb6ec 100644 --- a/man/docstring/mcds.__init__.md +++ b/man/docstring/mcds.__init__.md @@ -11,19 +11,21 @@ relative or absolute path to the directory where the PhysiCell output files are stored. - custom_dtype: dictionary; default is {} - variable to specify custom_data variable types other than - floats (namely: int, bool, str) like this: {var: dtype, ...}. + custom_data_type: dictionary; default is {} + variable to specify custom_data variable types + besides float (int, bool, str) like this: {var: dtype, ...}. downstream float and int will be handled as numeric, bool as Boolean, and str as categorical data. microenv: boole; default True should the microenvironment data be loaded? - setting microenv to False will use less memory and speed up processing. + setting microenv to False will use less memory and speed up + processing, similar to the original pyMCDS_cells.py script. graph: boole; default True - should the graphs, like cell_neighbor_graph.txt, be loaded? - setting graph to False will use less memory and speed up processing. + should neighbor garph, attached graph, and spring attached graph + be loaded? setting graph to False will use less memory and + speed up processing. physiboss: boole; default True should physiboss state data be loaded, if found? @@ -36,7 +38,7 @@ set to None or False if the xml file is missing! verbose: boole; default True - setting verbose to False for less text output, while processing. + setting verbose to False for less text output while processing. ``` @@ -49,13 +51,12 @@ ## description: ``` - TimeStep.__init__ will generate a class instance with a - dictionary of dictionaries data structure that contains all - output from a single PhysiCell model time step. furthermore, - this class, and as such it's instances, offers functions - to access the stored data. - the code assumes that all related output files are stored in - the same directory. data is loaded by reading the xml file - for a particular time step and the therein referenced files. + TimeStep.__init__ will call pyMCDS.__init__ that generates a mcds + class instance, a dictionary of dictionaries data structure that + contains all output from a single PhysiCell model time step. + furthermore, the mcds object offers functions to access the stored data. + the code assumes that all related output files are stored + in the same directory. data is loaded by reading the xml file for + a particular time step and the therein referenced files. ``` \ No newline at end of file diff --git a/man/docstring/mcds.get_cell_attribute_list.md b/man/docstring/mcds.get_cell_attribute_list.md deleted file mode 100644 index cf4cdc1..0000000 --- a/man/docstring/mcds.get_cell_attribute_list.md +++ /dev/null @@ -1,21 +0,0 @@ -# mcds.get_cell_attribute_list() - - -## input: -``` - -``` - -## output: -``` - ls_cellattr: list of strings - alphabetically ordered list of all tracked cell attributes. - -``` - -## description: -``` - function returns a list with all cell attribute labels, - alphabetically ordered. - -``` \ No newline at end of file diff --git a/man/docstring/mcds.get_cell_df_at.md b/man/docstring/mcds.get_cell_df_at.md new file mode 100644 index 0000000..03dd1ed --- /dev/null +++ b/man/docstring/mcds.get_cell_df_at.md @@ -0,0 +1,47 @@ +# mcds.get_cell_df_at() + + +## input: +``` + x: floating point number + position x-coordinate. + + y: floating point number + position y-coordinate. + + z: floating point number; default is 0 + position z-coordinate. + + values: integer; default is 1 + minimal number of values a variable has to have to be outputted. + variables that have only 1 state carry no information. + None is a state too. + + drop: set of strings; default is an empty set + set of column labels to be dropped for the dataframe. + don't worry: essential columns like ID, coordinates + and time will never be dropped. + Attention: when the keep parameter is given, then + the drop parameter has to be an empty set! + + keep: set of strings; default is an empty set + set of column labels to be kept in the dataframe. + set values=1 to be sure that all variables are kept. + don't worry: essential columns like ID, coordinates + and time will always be kept. + +``` + +## output: +``` + df_voxel: pandas dataframe + x, y, z voxel filtered cell dataframe. + +``` + +## description: +``` + function returns the cell dataframe for the voxel + specified with the x, y, z position coordinate. + +``` \ No newline at end of file diff --git a/man/docstring/mcds.get_concentration.md b/man/docstring/mcds.get_concentration.md new file mode 100644 index 0000000..03e6cef --- /dev/null +++ b/man/docstring/mcds.get_concentration.md @@ -0,0 +1,36 @@ +# mcds.get_concentration() + + +## input: +``` + substrate: string + substrate name. + + z_slice: floating point number; default is None + z-axis position to slice a 2D xy-plain out of the + 3D substrate concentration mesh. if None the + whole 3D mesh will be returned. + + halt: boolean; default is False + should program execution break or just spit out a warning, + if z_slice position is not an exact mesh center coordinate? + if False, z_slice will be adjusted to the nearest + mesh center value, the smaller one, if the coordinate + lies on a saddle point. + +``` + +## output: +``` + ar_conc: numpy array of floating point numbers + substrate concentration meshgrid or xy-plain slice + through the meshgrid. + +``` + +## description: +``` + function returns the concentration meshgrid, or a xy-plain slice + out of the whole meshgrid, for the specified chemical species. + +``` \ No newline at end of file diff --git a/man/docstring/mcds.get_concentration_at.md b/man/docstring/mcds.get_concentration_at.md new file mode 100644 index 0000000..88d306a --- /dev/null +++ b/man/docstring/mcds.get_concentration_at.md @@ -0,0 +1,31 @@ +# mcds.get_concentration_at() + + +## input: +``` + x: floating point number + position x-coordinate. + + y: floating point number + position y-coordinate. + + z: floating point number; default is 0 + position z-coordinate. + +``` + +## output: +``` + ar_concs: numpy array of floating point numbers + array of substrate concentrations in the order + given by get_substrate_list(). + +``` + +## description: +``` + function return concentrations of each chemical species + inside a particular voxel that contains the point specified + in the arguments. + +``` \ No newline at end of file diff --git a/man/docstring/mcds.get_spring_graph_dict.md b/man/docstring/mcds.get_spring_graph_dict.md deleted file mode 100644 index 1227ae6..0000000 --- a/man/docstring/mcds.get_spring_graph_dict.md +++ /dev/null @@ -1,20 +0,0 @@ -# mcds.get_spring_graph_dict() - - -## input: -``` - -``` - -## output: -``` - dei_graph: dictionary of sets of integers - maps each cell ID to the attached connected cell IDs. - -``` - -## description: -``` - function returns the attached spring cell graph as a dictionary object. - -``` \ No newline at end of file diff --git a/man/docstring/mcds.make_cell_vtk.md b/man/docstring/mcds.make_cell_vtk.md index 53e2da0..3784fe3 100644 --- a/man/docstring/mcds.make_cell_vtk.md +++ b/man/docstring/mcds.make_cell_vtk.md @@ -6,6 +6,9 @@ attribute: list of strings; default is ['cell_type'] column name within cell dataframe. + visualize: boolean; default is True + additionally, visualize cells using vtk renderer. + ``` ## output: diff --git a/man/docstring/mcds.make_conc_vtk.md b/man/docstring/mcds.make_conc_vtk.md index dc40043..660500b 100644 --- a/man/docstring/mcds.make_conc_vtk.md +++ b/man/docstring/mcds.make_conc_vtk.md @@ -3,6 +3,8 @@ ## input: ``` + visualize: boolean; default is True + additionally, visualize cells using vtk renderer. ``` diff --git a/man/docstring/mcds.make_ome_tiff.md b/man/docstring/mcds.make_ome_tiff.md deleted file mode 100644 index 9d78c3a..0000000 --- a/man/docstring/mcds.make_ome_tiff.md +++ /dev/null @@ -1,50 +0,0 @@ -# mcds.make_ome_tiff() - - -## input: -``` - cell_attribute: strings; default is 'ID', which will result in a - cell segmentation mask. - column name within the cell dataframe. - the column data type has to be numeric (bool, int, float) - and cannot be string. - the result will be stored as 32 bit float. - - conc_cutoff: dictionary string to real; default is an empty dictionary. - if a contour from a substrate not should be cut by greater - than zero (shifted to integer 1), another cutoff value can be - specified here. - - focus: set of strings; default is a None - set of substrate and cell_type names to specify what will be - translated into ome tiff format. - if None, all substrates and cell types will be processed. - - file: boolean; default True - if True, an ome tiff file is the output. - if False, a numpy array with shape czyx is the output. - -``` - -## output: -``` - a_tczyx_img: numpy array or ome tiff file. - -``` - -## description: -``` - function to transform chosen mcds output into an 1[um] spaced - czyx (channel, z-axis, y-axis, x-axis) ome tiff file or numpy array, - one substrate or cell_type per channel. - an ome tiff file is more or less: - a numpy array, containing the image information - and a xml, containing the microscopy metadata information, - like the channel labels. - the ome tiff file format can for example be read by the napari - or fiji (imagej) software. - - https://napari.org/stable/ - https://fiji.sc/ - -``` \ No newline at end of file diff --git a/man/docstring/mcds.plot_contour.md b/man/docstring/mcds.plot_contour.md index 8b2dd3a..1b94656 100644 --- a/man/docstring/mcds.plot_contour.md +++ b/man/docstring/mcds.plot_contour.md @@ -62,6 +62,11 @@ None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is None output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -70,11 +75,6 @@ or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the matplotlib contour and contourf function. - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contour.html - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html - ``` ## output: diff --git a/man/docstring/mcds.plot_scatter.md b/man/docstring/mcds.plot_scatter.md index 332fbbd..1c81c19 100644 --- a/man/docstring/mcds.plot_scatter.md +++ b/man/docstring/mcds.plot_scatter.md @@ -18,7 +18,7 @@ depending on the focus column variable dtype, default extracts labels or min and max values from data. - alpha: floating point number; default is 1.0 + alpha: floating point number; default is 1 alpha channel transparency value between 1 (not transparent at all) and 0 (totally transparent). @@ -67,6 +67,11 @@ None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is None output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -75,10 +80,6 @@ or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the pandas dataframe plot function. - + https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.html - ``` ## output: diff --git a/man/docstring/mcdsts.__init__.md b/man/docstring/mcdsts.__init__.md index fade05a..1581c00 100644 --- a/man/docstring/mcdsts.__init__.md +++ b/man/docstring/mcdsts.__init__.md @@ -9,7 +9,7 @@ custom_data_type: dictionary; default is {} variable to specify custom_data variable types - other than float (int, bool, str) like this: {var: dtype, ...}. + besides float (int, bool, str) like this: {var: dtype, ...}. downstream float and int will be handled as numeric, bool as Boolean, and str as categorical data. @@ -19,11 +19,13 @@ microenv: boole; default True should the microenvironment data be loaded? - setting microenv to False will use less memory and speed up processing. + setting microenv to False will use less memory and speed up + processing, similar to the original pyMCDS_cells.py script. graph: boole; default True - should the graphs, like cell_neighbor_graph.txt, be loaded? - setting graph to False will use less memory and speed up processing. + should neighbor garph, attached graph, and spring attached graph + be loaded? setting graph to False will use less memory and + speed up processing. physiboss: boole; default True should physiboss state data be loaded, if found? @@ -36,13 +38,13 @@ set to None or False if the xml file is missing! verbose: boole; default True - setting verbose to False for less text output, while processing. + setting verbose to False for less text output while processing. ``` ## output: ``` - mcdsts: TimeSeries class instance + mcdsts: pyMCDSts class instance this instance offers functions to process all stored time steps from a simulation. @@ -50,7 +52,8 @@ ## description: ``` - TimeSeries.__init__ generates a class instance the instance offers - functions to process all time steps in the output_path directory. + TimeSeries.__init__ will call pyMCDSts.__init__ that generates a mcdsts + class instance. this instance offers functions to process all time steps + in the output_path directory. ``` \ No newline at end of file diff --git a/man/docstring/mcdsts.get_cell_attribute.md b/man/docstring/mcdsts.get_cell_attribute.md index fb7ce0d..1db519d 100644 --- a/man/docstring/mcdsts.get_cell_attribute.md +++ b/man/docstring/mcdsts.get_cell_attribute.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have diff --git a/man/docstring/mcdsts.get_cell_df.md b/man/docstring/mcdsts.get_cell_df.md index b9313ec..b15b4ab 100644 --- a/man/docstring/mcdsts.get_cell_df.md +++ b/man/docstring/mcdsts.get_cell_df.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have to be outputted. diff --git a/man/docstring/mcdsts.get_conc_attribute.md b/man/docstring/mcdsts.get_conc_attribute.md index fabb81e..1642975 100644 --- a/man/docstring/mcdsts.get_conc_attribute.md +++ b/man/docstring/mcdsts.get_conc_attribute.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have diff --git a/man/docstring/mcdsts.get_conc_df.md b/man/docstring/mcdsts.get_conc_df.md index fcc0013..74e5d3e 100644 --- a/man/docstring/mcdsts.get_conc_df.md +++ b/man/docstring/mcdsts.get_conc_df.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have to be outputted. diff --git a/man/docstring/mcdsts.get_mcds_list.md b/man/docstring/mcdsts.get_mcds_list.md index 10db3d9..b018caf 100644 --- a/man/docstring/mcdsts.get_mcds_list.md +++ b/man/docstring/mcdsts.get_mcds_list.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDSts class instance. ``` diff --git a/man/docstring/mcdsts.get_xmlfile_list.md b/man/docstring/mcdsts.get_xmlfile_list.md index 50ea8c8..4602b9b 100644 --- a/man/docstring/mcdsts.get_xmlfile_list.md +++ b/man/docstring/mcdsts.get_xmlfile_list.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDSts class instance. ``` diff --git a/man/docstring/mcdsts.make_cell_vtk.md b/man/docstring/mcdsts.make_cell_vtk.md index 1de2295..9c3adcc 100644 --- a/man/docstring/mcdsts.make_cell_vtk.md +++ b/man/docstring/mcdsts.make_cell_vtk.md @@ -6,6 +6,9 @@ attribute: list of strings; default is ['cell_type'] column name within cell dataframe. + visualize: boolean; default is False + additionally, visualize cells using vtk renderer. + ``` ## output: diff --git a/man/docstring/mcdsts.make_conc_vtk.md b/man/docstring/mcdsts.make_conc_vtk.md index 23a854c..753e1e7 100644 --- a/man/docstring/mcdsts.make_conc_vtk.md +++ b/man/docstring/mcdsts.make_conc_vtk.md @@ -3,6 +3,8 @@ ## input: ``` + visualize: boolean; default is False + additionally, visualize cells using vtk renderer. ``` diff --git a/man/docstring/mcdsts.make_graph_gml.md b/man/docstring/mcdsts.make_graph_gml.md index c4e4bb3..c28b163 100644 --- a/man/docstring/mcdsts.make_graph_gml.md +++ b/man/docstring/mcdsts.make_graph_gml.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDS class instance. graph_type: string to specify which physicell output data should be processed. diff --git a/man/docstring/mcdsts.make_ome_tiff.md b/man/docstring/mcdsts.make_ome_tiff.md deleted file mode 100644 index f9b9ca9..0000000 --- a/man/docstring/mcdsts.make_ome_tiff.md +++ /dev/null @@ -1,55 +0,0 @@ -# mcdsts.make_ome_tiff() - - -## input: -``` - cell_attribute: strings; default is 'ID', which will result in a - cell segmentation mask. - column name within the cell dataframe. - the column data type has to be numeric (bool, int, float) - and cannot be string. - the result will be stored as 32 bit float. - - conc_cutoff: dictionary string to real; default is an empty dictionary. - if a contour from a substrate not should be cut by greater - than zero (shifted to integer 1), another cutoff value can be specified here. - - focus: set of strings; default is a None - set of substrate and cell_type names to specify what will be - translated into ome tiff format. - if None, all substrates and cell types will be processed. - - file: boolean; default True - if True, an ome tiff file is the output. - if False, a numpy array with shape tczyx is the output. - - collapse: boole; default True - should all mcds time steps from the time series be collapsed - into one ome tiff file (numpy array), - or an ome tiff file (numpy array) for each time step? - -``` - -## output: -``` - a_tczyx_img: numpy array or ome tiff file. - - -``` - -## description: -``` - function to transform chosen mcdsts output into an 1[um] spaced - tczyx (time, channel, z-axis, y-axis, x-axis) ome tiff file or numpy array, - one substrate or cell_type per channel. - a ome tiff file is more or less: - a numpy array, containing the image information - and a xml, containing the microscopy metadata information, - like the channel labels. - the ome tiff file format can for example be read by the napari - or fiji (imagej) software. - - https://napari.org/stable/ - https://fiji.sc/ - -``` \ No newline at end of file diff --git a/man/docstring/mcdsts.plot_contour.md b/man/docstring/mcdsts.plot_contour.md index 659e205..9078146 100644 --- a/man/docstring/mcdsts.plot_contour.md +++ b/man/docstring/mcdsts.plot_contour.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance + self: pyMCDSts class instance focus: string column name within conc dataframe, for example. @@ -54,6 +54,11 @@ None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is jpeg output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -62,11 +67,6 @@ or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the matplotlib contour and contourf function. - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contour.html - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html - ``` ## output: diff --git a/man/docstring/mcdsts.plot_scatter.md b/man/docstring/mcdsts.plot_scatter.md index f284c0d..03f0049 100644 --- a/man/docstring/mcdsts.plot_scatter.md +++ b/man/docstring/mcdsts.plot_scatter.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance + self: pyMCDSts class instance focus: string; default is 'cell_type' column name within cell dataframe. @@ -64,6 +64,11 @@ None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is jpeg output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -72,10 +77,6 @@ or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the pandas dataframe plot function. - + https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame - ``` ## output: diff --git a/man/docstring/mcdsts.plot_timeseries.md b/man/docstring/mcdsts.plot_timeseries.md index 09385d5..67906fe 100644 --- a/man/docstring/mcdsts.plot_timeseries.md +++ b/man/docstring/mcdsts.plot_timeseries.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance + self: pyMCDSts class instance focus_cat: string; default is None categorical or boolean data column within dataframe specified under frame. @@ -101,10 +101,6 @@ figure background color. only relevant if ext not is None. - **kwargs: possible additional keyword arguments input, - handled by the pandas series plot function. - + https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.plot.html - ``` ## output: diff --git a/man/docstring/mcdsts.read_mcds.md b/man/docstring/mcdsts.read_mcds.md index 8167f20..61fe678 100644 --- a/man/docstring/mcdsts.read_mcds.md +++ b/man/docstring/mcdsts.read_mcds.md @@ -3,7 +3,7 @@ ## input: ``` - self: TimeSeries class instance. + self: pyMCDSts class instance. xmlfile_list: list of strings; default None list of physicell output*.xml strings. @@ -19,6 +19,6 @@ ## description: ``` the function returns a list of mcds objects loaded by - TimeStep calls. + pyMCDS calls. ``` \ No newline at end of file diff --git a/man/docstring/pcdl.render_neuroglancer.md b/man/docstring/pcdl.render_neuroglancer.md deleted file mode 100644 index 5bf8132..0000000 --- a/man/docstring/pcdl.render_neuroglancer.md +++ /dev/null @@ -1,33 +0,0 @@ -# mcdsts.render_neuroglancer('path/to/ome.tiff') - - -## input: -``` - tiffpathfile: string. - path to ome tiff file. - - timestep: integer, default is 0. - variable to specify the specific time step to render. - useful for time series ome.tiff files. - the default is compatible with single time step ome.tiff files. - - intensity_cmap: string; default is 'gray'. - matlab color map label, used to display expression intensity values. - if None, no intensity layers will be generated. - + https://matplotlib.org/stable/users/explain/colors/colormaps.html - -``` - -## output: -``` - viewer: local url where the loaded, neuroglancer rendered ome tiff file - can be viewed. - -``` - -## description: -``` - function to load a time step from an ome tiff files, generated - with make_ome_tiff, into neuroglancer. - -``` \ No newline at end of file diff --git a/man/docstring/pcdl_get_anndata.md b/man/docstring/pcdl_get_anndata.md index cb59d2f..54e4763 100644 --- a/man/docstring/pcdl_get_anndata.md +++ b/man/docstring/pcdl_get_anndata.md @@ -28,8 +28,9 @@ options: empty string. --microenv MICROENV should the microenvironment be extracted and loaded into the anndata object? setting microenv to False - will use less memory and speed up processing. default - is True. + will use less memory and speed up processing, similar + to the original pyMCDS_cells.py script. default is + True. --graph GRAPH should neighbor graph, attach graph, and attached spring graph be extracted and loaded into the anndata object? default is True. diff --git a/man/docstring/pcdl_get_cell_attribute.md b/man/docstring/pcdl_get_cell_attribute.md index 2b1eb98..63628ab 100644 --- a/man/docstring/pcdl_get_cell_attribute.md +++ b/man/docstring/pcdl_get_cell_attribute.md @@ -34,7 +34,8 @@ options: empty string. --microenv MICROENV should the microenvironment data be loaded? setting microenv to False will use less memory and speed up - processing. default is True. + processing, similar to the original pyMCDS_cells.py + script. default is True. --physiboss PHYSIBOSS if found, should physiboss state data be extracted and loaded into df_cell dataframe? default is True. diff --git a/man/docstring/pcdl_get_cell_attribute_list.md b/man/docstring/pcdl_get_cell_attribute_list.md deleted file mode 100644 index 8acf2f2..0000000 --- a/man/docstring/pcdl_get_cell_attribute_list.md +++ /dev/null @@ -1,33 +0,0 @@ -``` -usage: pcdl_get_cell_attribute_list [-h] [--microenv MICROENV] - [--physiboss PHYSIBOSS] - [--settingxml SETTINGXML] [-v VERBOSE] - [path] - -this function is returns a list with all cell attribute labels, alphabetically -ordered. - -positional arguments: - path path to the PhysiCell output directory or a - outputnnnnnnnn.xml file. default is . . - -options: - -h, --help show this help message and exit - --microenv MICROENV should the microenvironment data be loaded? setting - microenv to False will use less memory and speed up - processing. default is True. - --physiboss PHYSIBOSS - if found, should physiboss state data be extracted and - loaded into df_cell dataframe? default is True. - --settingxml SETTINGXML - the settings.xml that is loaded, from which the cell - type ID label mapping, is extracted, if this - information is not found in the output xml file. set - to None or False if the xml file is missing! default - is PhysiCell_settings.xml. - -v VERBOSE, --verbose VERBOSE - setting verbose to True for more text output, while - processing. default is False. - -homepage: https://github.com/elmbeech/physicelldataloader -``` diff --git a/man/docstring/pcdl_get_cell_df.md b/man/docstring/pcdl_get_cell_df.md index 2b31c79..f810166 100644 --- a/man/docstring/pcdl_get_cell_df.md +++ b/man/docstring/pcdl_get_cell_df.md @@ -20,7 +20,8 @@ options: -h, --help show this help message and exit --microenv MICROENV should the microenvironment data be loaded? setting microenv to False will use less memory and speed up - processing. default is True. + processing, similar to the original pyMCDS_cells.py + script. default is True. --physiboss PHYSIBOSS if found, should physiboss state data be extracted and loaded into the df_cell dataframe? default is True. diff --git a/man/docstring/pcdl_get_unit_dict.md b/man/docstring/pcdl_get_unit_dict.md index 710b081..3f52826 100644 --- a/man/docstring/pcdl_get_unit_dict.md +++ b/man/docstring/pcdl_get_unit_dict.md @@ -13,7 +13,8 @@ options: -h, --help show this help message and exit --microenv MICROENV should the microenvironment data be loaded? setting microenv to False will use less memory and speed up - processing. default is True. + processing, similar to the original pyMCDS_cells.py + script. default is True. --settingxml SETTINGXML the settings.xml that is loaded, from which the cell type ID label mapping, is extracted, if this diff --git a/man/docstring/pcdl_make_cell_vtk.md b/man/docstring/pcdl_make_cell_vtk.md index 0043ac5..a067d57 100644 --- a/man/docstring/pcdl_make_cell_vtk.md +++ b/man/docstring/pcdl_make_cell_vtk.md @@ -26,7 +26,8 @@ options: empty string. --microenv MICROENV should the microenvironment data be loaded? setting microenv to False will use less memory and speed up - processing. default is True. + processing, similar to the original pyMCDS_cells.py + script. default is True. --physiboss PHYSIBOSS if found, should physiboss state data be extracted and loaded into the df_cell dataframe? default is True. diff --git a/man/docstring/pcdl_make_graph_gml.md b/man/docstring/pcdl_make_graph_gml.md index c47ebf9..20dfbd0 100644 --- a/man/docstring/pcdl_make_graph_gml.md +++ b/man/docstring/pcdl_make_graph_gml.md @@ -34,7 +34,8 @@ options: empty string. --microenv MICROENV should the microenvironment data be loaded? setting microenv to False will use less memory and speed up - processing. default is True. + processing, similar to the original pyMCDS_cells.py + script. default is True. --physiboss PHYSIBOSS if found, should physiboss state data be extracted and loaded into the df_cell dataframe? default is True. diff --git a/man/docstring/pcdl_make_ome_tiff.md b/man/docstring/pcdl_make_ome_tiff.md deleted file mode 100644 index 50e7d6c..0000000 --- a/man/docstring/pcdl_make_ome_tiff.md +++ /dev/null @@ -1,55 +0,0 @@ -``` -usage: pcdl_make_ome_tiff [-h] [--microenv MICROENV] [--physiboss PHYSIBOSS] - [--settingxml SETTINGXML] [-v VERBOSE] - [--conc_cutoff [CONC_CUTOFF ...]] - [--focus FOCUS [FOCUS ...]] [--collapse COLLAPSE] - [path] [cell_attribute] - -function to transform chosen mcdsts output into an 1[um] spaced tczyx (time, -channel, z-axis, y-axis, x-axis) ome tiff file, one substrate or cell_type per -channel. the ome tiff file format can for example be read by the napari -(https://napari.org/stable/) or fiji imagej (https://fiji.sc/) software. - -positional arguments: - path path to the PhysiCell output directory or a - outputnnnnnnnn.xml file. default is . . - cell_attribute mcds.get_cell_df dataframe column, used for - cell_attribute. the column data type has to be numeric - (bool, int, float) and cannot be string. the result - will be stored as 32 bit float. default is ID, with - will result in a segmentation mask. - -options: - -h, --help show this help message and exit - --microenv MICROENV should the microenvironment data be loaded? setting - microenv to False will use less memory and speed up - processing. default is True. - --physiboss PHYSIBOSS - if found, should physiboss state data be extracted and - loaded into the df_cell dataframe? default is True. - --settingxml SETTINGXML - the settings.xml that is loaded, from which the cell - type ID label mapping, is extracted, if this - information is not found in the output xml file. set - to None or False if the xml file is missing! default - is PhysiCell_settings.xml. - -v VERBOSE, --verbose VERBOSE - setting verbose to False for less text output, while - processing. default is True. - --conc_cutoff [CONC_CUTOFF ...] - if a contour from a substrate not should be cut by - greater than zero (shifted to integer 1), another - cutoff value can be specified here like this: - substarte:value substrate:value substarte:value . - default is and empty string. - --focus FOCUS [FOCUS ...] - set of substrate and cell_type names to specify what - will be translated into ome tiff format. if None, all - substrates and cell types will be processed. default - is a None. - --collapse COLLAPSE should all mcds time steps from the time series be - collapsed into one big ome.tiff, or a many ome.tiff, - one ome.tiff for each time step?, default is True. - -homepage: https://github.com/elmbeech/physicelldataloader -``` diff --git a/man/docstring/pcdl_plot_contour.md b/man/docstring/pcdl_plot_contour.md index c368910..b5cb596 100644 --- a/man/docstring/pcdl_plot_contour.md +++ b/man/docstring/pcdl_plot_contour.md @@ -4,7 +4,8 @@ usage: pcdl_plot_contour [-h] [-v VERBOSE] [--z_slice Z_SLICE] [--fill FILL] [--cmap CMAP] [--title TITLE] [--grid GRID] [--xlim XLIM [XLIM ...]] [--ylim YLIM [YLIM ...]] [--xyequal XYEQUAL] - [--figsizepx FIGSIZEPX [FIGSIZEPX ...]] [--ext EXT] + [--figsizepx FIGSIZEPX [FIGSIZEPX ...]] + [--directory DIRECTORY] [--ext EXT] [--figbgcolor FIGBGCOLOR] [path] [focus] @@ -53,6 +54,11 @@ options: be able to generate movies from the images. None tries to take the values from the initial.svg file. fall back setting is 640 480. default is None. + --directory DIRECTORY + if none, a meaningful output directory name will be + generated, based on focus and z_slice parameters, else + the resulting plots will be moved to the explicit name + directory. --ext EXT output image format. possible formats are jpeg, png, and tiff. default is jpeg. --figbgcolor FIGBGCOLOR diff --git a/man/docstring/pcdl_plot_scatter.md b/man/docstring/pcdl_plot_scatter.md index b97563b..11b3f12 100644 --- a/man/docstring/pcdl_plot_scatter.md +++ b/man/docstring/pcdl_plot_scatter.md @@ -7,7 +7,8 @@ usage: pcdl_plot_scatter [-h] [--custom_data_type [CUSTOM_DATA_TYPE ...]] [--grid GRID] [--legend_loc LEGEND_LOC] [--xlim XLIM [XLIM ...]] [--ylim YLIM [YLIM ...]] [--xyequal XYEQUAL] [--s S] - [--figsizepx FIGSIZEPX [FIGSIZEPX ...]] [--ext EXT] + [--figsizepx FIGSIZEPX [FIGSIZEPX ...]] + [--directory DIRECTORY] [--ext EXT] [--figbgcolor FIGBGCOLOR] [path] [focus] @@ -30,7 +31,8 @@ options: empty string. --microenv MICROENV should the microenvironment data be loaded? setting microenv to False will use less memory and speed up - processing. default is True. + processing, similar to the original pyMCDS_cells.py + script. default is True. --physiboss PHYSIBOSS if found, should physiboss state data be extracted and loaded into the df_cell dataframe? default is True. @@ -84,6 +86,11 @@ options: be able to generate movies from the images. None tries to take the values from the initial.svg file. fall back setting is 640 480. default is None. + --directory DIRECTORY + if none, a meaningful output directory name will be + generated, based on focus and z_slice parameters, else + the resulting plots will be moved to the explicit name + directory. --ext EXT output image format. possible formats are jpeg, png, and tiff. default is jpeg. --figbgcolor FIGBGCOLOR diff --git a/man/docstring/pcdl_plot_timeseries.md b/man/docstring/pcdl_plot_timeseries.md index e93a2ca..c529514 100644 --- a/man/docstring/pcdl_plot_timeseries.md +++ b/man/docstring/pcdl_plot_timeseries.md @@ -40,7 +40,8 @@ options: empty string. --microenv MICROENV should the microenvironment data be loaded? setting microenv to False will use less memory and speed up - processing. default is True. + processing, similar to the original pyMCDS_cells.py + script. default is True. --physiboss PHYSIBOSS if found, should physiboss state data be extracted and loaded into the df_cell dataframe? default is True. diff --git a/man/docstring/pcdl_render_neuroglancer.md b/man/docstring/pcdl_render_neuroglancer.md deleted file mode 100644 index 939d206..0000000 --- a/man/docstring/pcdl_render_neuroglancer.md +++ /dev/null @@ -1,23 +0,0 @@ -``` -usage: pcdl_render_neuroglancer [-h] [--intensity_cmap INTENSITY_CMAP] - [tiffpathfile] [timestep] - -function to load a time step from an ome tiff files, generated with -make_ome_tiff, into neuroglancer. - -positional arguments: - tiffpathfile path to ome tiff file. - timestep time step, within a possibly collapsed ome tiff file, - to render. the default will work with single time step - ome tiff files. - -options: - -h, --help show this help message and exit - --intensity_cmap INTENSITY_CMAP - matlab color map label, used to display expression - intensity values. if None, no intensity layers will be - generated. https://matplotlib.org/stable/users/explain - /colors/colormaps.html - -homepage: https://github.com/elmbeech/physicelldataloader -``` diff --git a/man/img/physicelldataloafder_github_qr.png b/man/img/physicelldataloafder_github_qr.png deleted file mode 100644 index 86d3d92..0000000 Binary files a/man/img/physicelldataloafder_github_qr.png and /dev/null differ diff --git a/man/scarab.py b/man/scarab.py index 5ed20a8..a138386 100644 --- a/man/scarab.py +++ b/man/scarab.py @@ -99,13 +99,13 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): # load data mcds = pcdl.TimeStep( 'pcdl/output_2d/output00000000.xml', - #custom_data_type={'oncoprotein': str}, + custom_data_type={'oncoprotein': str}, verbose=False, ) mcdsts = pcdl.TimeSeries( 'pcdl/output_2d/', - #custom_data_type={'oncoprotein': str}, + custom_data_type={'oncoprotein': str}, verbose=False, ) @@ -121,7 +121,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ) -# write TimeStep initialize function makdown files +# write pyMCDS initialize function makdown files docstring_md( s_function = 'mcds.__init__', ls_doc = pcdl.TimeStep.__init__.__doc__.split('\n'), @@ -136,7 +136,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeStep.set_verbose_true.__doc__.split('\n'), ) -# write TimeStep metadata function makdown files +# write pyMCDS metadata function makdown files docstring_md( s_function = 'mcds.get_multicellds_version', ls_doc = pcdl.TimeStep.get_multicellds_version.__doc__.split('\n'), @@ -163,7 +163,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeStep.get_unit_dict.__doc__.split('\n'), ) -# write TimeStep mesh function markdown files +# write pyMCDS mesh function markdown files # range and axis docstring_md( s_function = 'mcds.get_voxel_ijk_range', @@ -224,7 +224,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeStep.get_voxel_ijk.__doc__.split('\n'), ) -# write TimeStep microenv function markdown files +# write pyMCDS microenv function markdown files docstring_md( s_function = 'mcds.get_substrate_list', ls_doc = pcdl.TimeStep.get_substrate_list.__doc__.split('\n'), @@ -237,6 +237,14 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): s_function = 'mcds.get_substrate_df', ls_doc = pcdl.TimeStep.get_substrate_df.__doc__.split('\n'), ) +docstring_md( + s_function = 'mcds.get_concentration', + ls_doc = pcdl.TimeStep.get_concentration.__doc__.split('\n'), +) +docstring_md( + s_function = 'mcds.get_concentration_at', + ls_doc = pcdl.TimeStep.get_concentration_at.__doc__.split('\n'), +) docstring_md( s_function = 'mcds.get_conc_df', ls_doc = pcdl.TimeStep.get_conc_df.__doc__.split('\n'), @@ -250,7 +258,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeStep.make_conc_vtk.__doc__.split('\n'), ) -# write TimeStep cell agent function markdown files +# write pyMCDS cell agent function markdown files docstring_md( s_function = 'mcds.get_celltype_list', ls_doc = pcdl.TimeStep.get_celltype_list.__doc__.split('\n'), @@ -264,8 +272,8 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeStep.get_cell_df.__doc__.split('\n'), ) docstring_md( - s_function = 'mcds.get_cell_attribute_list', - ls_doc = pcdl.TimeStep.get_cell_attribute_list.__doc__.split('\n'), + s_function = 'mcds.get_cell_df_at', + ls_doc = pcdl.TimeStep.get_cell_df_at.__doc__.split('\n'), ) docstring_md( s_function = 'mcds.plot_scatter', @@ -280,7 +288,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeStep.get_anndata.__doc__.split('\n'), ) -# write TimeStep graph function markdown files +# write pyMCDS graph function markdown files docstring_md( s_function = 'mcds.get_attached_graph_dict', ls_doc = pcdl.TimeStep.get_attached_graph_dict.__doc__.split('\n'), @@ -289,28 +297,12 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): s_function = 'mcds.get_neighbor_graph_dict', ls_doc = pcdl.TimeStep.get_neighbor_graph_dict.__doc__.split('\n'), ) -docstring_md( - s_function = 'mcds.get_spring_graph_dict', - ls_doc = pcdl.TimeStep.get_spring_graph_dict.__doc__.split('\n'), -) docstring_md( s_function = 'mcds.make_graph_gml', ls_doc = pcdl.TimeStep.make_graph_gml.__doc__.split('\n'), ) -# write TimeStep microenvironment and cells function markdown files -docstring_md( - s_function = 'mcds.make_ome_tiff', - ls_doc = pcdl.TimeStep.make_ome_tiff.__doc__.split('\n'), -) - -docstring_md( - s_function = 'pcdl.render_neuroglancer', - ls_doc = pcdl.render_neuroglancer.__doc__.split('\n'), - s_header = "mcds.render_neuroglancer('path/to/ome.tiff')" -) - -# write TimeStep internal function makdown files +# write pyMCDS internal function makdown files docstring_md( s_function = 'pcdl.scaler', ls_doc = pcdl.scaler.__doc__.split('\n'), @@ -321,7 +313,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ) -# write TimeSeries initialize function makdown files +# write pyMCDSts initialize function makdown files docstring_md( s_function = 'mcdsts.__init__', ls_doc = pcdl.TimeSeries.__init__.__doc__.split('\n'), @@ -353,7 +345,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeSeries.set_verbose_true.__doc__.split('\n'), ) -# write TimeSeries microenv function makdown files +# write pyMCDSts microenv function makdown files docstring_md( s_function = 'mcdsts.get_conc_df', ls_doc = pcdl.TimeSeries.get_conc_df.__doc__.split('\n'), @@ -371,7 +363,7 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeSeries.make_conc_vtk.__doc__.split('\n'), ) -# write TimeSeries cell agent function makdown files +# write pyMCDSts cell agent function makdown files docstring_md( s_function = 'mcdsts.get_cell_df', ls_doc = pcdl.TimeSeries.get_cell_df.__doc__.split('\n'), @@ -393,28 +385,19 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): ls_doc = pcdl.TimeSeries.get_anndata.__doc__.split('\n'), ) -# write TimeSeries graph function makdown files +# write pyMCDSts graph function makdown files docstring_md( s_function = 'mcdsts.make_graph_gml', ls_doc = pcdl.TimeSeries.make_graph_gml.__doc__.split('\n'), ) -# write TimeSeries microenvironment and cells function makdown files -docstring_md( - s_function = 'mcdsts.make_ome_tiff', - ls_doc = pcdl.TimeSeries.make_ome_tiff.__doc__.split('\n'), -) -docstring_md( - s_function = 'pcdl.render_neuroglancer', - ls_doc = pcdl.render_neuroglancer.__doc__.split('\n'), - s_header = "mcdsts.render_neuroglancer('path/to/ome.tiff')" -) +# write pyMCDSts microenvironment and cells function makdown files docstring_md( s_function = 'mcdsts.plot_timeseries', ls_doc = pcdl.TimeSeries.plot_timeseries.__doc__.split('\n'), ) -# write TimeSeries making movies function markdown files +# write pyMCDSts making movies function markdown files docstring_md( s_function = 'pcdl.make_gif', ls_doc = pcdl.make_gif.__doc__.split('\n'), @@ -439,7 +422,6 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): help_md(s_command='pcdl_make_conc_vtk') # cell agent help_md(s_command='pcdl_get_celltype_list') -help_md(s_command='pcdl_get_cell_attribute_list') help_md(s_command='pcdl_get_cell_attribute') help_md(s_command='pcdl_get_cell_df') help_md(s_command='pcdl_get_anndata') @@ -448,8 +430,6 @@ def docstring_md(s_function, ls_doc, s_header=None, s_opath='man/docstring/'): help_md(s_command='pcdl_make_cell_vtk') # substrate and cell agent help_md(s_command='pcdl_plot_timeseries') -help_md(s_command='pcdl_make_ome_tiff') -help_md(s_command='pcdl_render_neuroglancer') # making movies help_md(s_command='pcdl_make_gif') help_md(s_command='pcdl_make_movie') diff --git a/pcdl/VERSION.py b/pcdl/VERSION.py index d5f9f2a..a8cd31d 100644 --- a/pcdl/VERSION.py +++ b/pcdl/VERSION.py @@ -1 +1 @@ -__version__ = '4.0.4' +__version__ = '3.3.8' diff --git a/pcdl/__init__.py b/pcdl/__init__.py index d0d0da2..a0dc1d8 100644 --- a/pcdl/__init__.py +++ b/pcdl/__init__.py @@ -1,4 +1,5 @@ -from pcdl.timestep import TimeStep, graphfile_parser, render_neuroglancer, scaler -from pcdl.timeseries import TimeSeries, make_gif, make_movie +from pcdl.pyAnnData import TimeStep, TimeSeries, scaler +from pcdl.pyMCDS import pyMCDS, graphfile_parser +from pcdl.pyMCDSts import pyMCDSts, make_gif, make_movie from pcdl.VERSION import __version__ -from pcdl.output_data import install_data, uninstall_data +from pcdl.data_timeseries import install_data, uninstall_data diff --git a/pcdl/output_data.py b/pcdl/data_timeseries.py similarity index 97% rename from pcdl/output_data.py rename to pcdl/data_timeseries.py index 21ed8cd..9358cd6 100644 --- a/pcdl/output_data.py +++ b/pcdl/data_timeseries.py @@ -86,7 +86,7 @@ def __init__(self): # for each obsolet timeserie for s_file in sorted(os.listdir(s_path)): - if s_file.startswith('output_2d') or s_file.startswith('output_3d'): + if s_file.startswith('output_'): # get path and filename s_pathfile = s_path + s_file diff --git a/pcdl/imagine.py b/pcdl/imagine.py deleted file mode 100644 index 232475f..0000000 --- a/pcdl/imagine.py +++ /dev/null @@ -1,745 +0,0 @@ -### -# title: biotransistor.imagine.py -# -# language Python3 -# license: GPLv3 -# author: bue, jenny eng -# date: 2019-01-31 -# -# run: -# form pcdl import imagine -# -# description: -# my image analysis library -# -# note: -# load basins tiff into numpy array -# -# import matplotlib.pyplot as plt -# from skimage import io -# -# a_tiff = ski.io.imread('segmentation_label_file.tiff') -# io.imshow(a_tiff) -# plt.imshow(a_tiff) -#### - -# library -import numpy as np -import pandas as pd -import sys - -# function -def slide_up(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one row up. - top row get deleted, - bottom row of zeros is inserted. - - description: - inspired by np.roll function, though elements that roll - beyond the last position are not re-introduced at the first. - """ - a = np.delete(np.insert(a, a.shape[0], 0, axis=0), 0, axis=0) - return(a) - - -def slide_down(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one row down. - top row of zeros is inserted. - bottom row get deleted, - - description: - inspired by np.roll function, though elements that roll - beyond the last position are not re-introduced at the first. - """ - a = np.delete(np.insert(a, 0, 0, axis=0), -1, axis=0) - return(a) - - -def slide_left(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one column left. - left most column gets deleted, - right most a column of zeros is inserted. - - description: - inspired by np.roll function, though elements that roll - beyond the last position are not re-introduced at the first. - """ - a = np.delete(np.insert(a, a.shape[1], 0, axis=1), 0, axis=1) - return(a) - - -def slide_right(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one column right. - left most a column of zeros is inserted. - right most column gets deleted, - - description: - inspired by np.roll function, though elements that roll - beyond the last position are not re-introduced at the first. - """ - a = np.delete(np.insert(a, 0, 0, axis=1), -1, axis=1) - return(a) - - -def slide_upleft(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one row up and one column left. - - description: - inspired by np.roll function. - """ - a = slide_left(slide_up(a)) - return(a) - - -def slide_upright(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one row up and one column right. - - description: - inspired by np.roll function. - """ - a = slide_right(slide_up(a)) - return(a) - - -def slide_downleft(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one row down and one column left. - - description: - inspired by np.roll function. - """ - a = slide_left(slide_down(a)) - return(a) - - -def slide_downright(a): - """ - input: - a: numpy array - - output: - a: input numpy array shifted one row down and one column right. - - description: - inspired by np.roll function. - """ - a = slide_right(slide_down(a)) - return(a) - - -def border(ai_segment): - """ - input: - ai_segment: numpy array representing a cells or nuclei basin file. - it is assumed that basin borders are represented by 0 values, - and basins are represented with any values different from 0. - ai_segment = skimage.io.imread("cells_basins.tif") - - output: - ai_border: numpy array containing only the cell or nuclei basin border. - border value will be 1, non border value will be 0. - - description: - algorithm to extract the basin borders form basin numpy arrays. - """ - ab_border_up = (ai_segment - slide_up(ai_segment)) != 0 - ab_border_down = (ai_segment - slide_down(ai_segment)) != 0 - ab_border_left = (ai_segment - slide_left(ai_segment)) != 0 - ab_border_right = (ai_segment - slide_right(ai_segment)) != 0 - ab_border_upleft = (ai_segment - slide_upleft(ai_segment)) != 0 - ab_border_upright = (ai_segment - slide_upright(ai_segment)) != 0 - ab_border_downleft = (ai_segment - slide_downleft(ai_segment)) != 0 - ab_border_downright = (ai_segment - slide_downright(ai_segment)) != 0 - ab_border = ab_border_up | ab_border_down | ab_border_left | ab_border_right | ab_border_upleft | ab_border_upright | ab_border_downleft | ab_border_downright - ai_border = ab_border * 1 - return(ai_border) - - -def grow(ai_segment, i_step=1, b_verbose=True): - """ - input: - ai_segment: numpy array representing a cells basin file. - it is assumed that basin borders are represented by 0 values, - and basins are represented with any values different from 0. - ai_segment = skimage.io.imread("cells_basins.tif") - - i_step: integer which specifies how many pixels the basin - to each direction should grow. - function can handle shrinking. enter negative steps like -1. - - b_verbose: boolean which specifies if, while processing, - text should be outputted. - - output: - ai_grown: numpy array with the grown basins - - description: - algorithm to grow the basins in a given basin numpy array. - growing happens counterclockwise, starting at noon. - """ - ai_tree = ai_segment.copy() # initialize output - if (i_step > -1): - # growing - for i in range(i_step): - # next grow cycle - if b_verbose: - print(f'grow {i+1}[px] ring ...') - ai_treering = ai_tree.copy() - for o_slide in [slide_up, slide_upleft, slide_left, slide_downleft, slide_down, slide_downright, slide_right, slide_upright]: - ai_evolve = o_slide(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - #print(ai_treering) - # update output - ai_tree = ai_treering - else: - # shrinking - ai_border = border(ai_segment) - ai_membrane = grow(ai_border, i_step=abs(i_step) - 1) - ai_tree[ai_membrane != 0] = 0 - - # output - return(ai_tree) - - -def grow_seed(ai_segment, i_step=1, b_verbose=True): - """ - input: - ai_segment: numpy array representing a cells center basin file. - it is assumed that basin borders are represented by 0 values, - and basins are represented with any values different from 0. - ai_segment = skimage.io.imread("cells_basins.tif") - - i_step: integer which specifies how many pixels the seed pixel - should grow. - - b_verbose: boolean which specifies if, while processing, - text should be outputted. - - output: - ai_grown: numpy array with the grown basins - - description: - algorithm to grow the basins in a given cell center seed numpy array. - growing happens counterclockwise, starting at noon. - """ - # initialize output - ai_tree = ai_segment.copy() - - # growing - for i in range(i_step): - # next grow cycle - if b_verbose: - print(f'grow {i+1}[px] ring ...') - ai_treering = ai_tree.copy() - - # calculate pythagoras - b_circle = ((i + 1) * 2**(1/2)) < (i_step + 1) - - # up - ai_evolve = slide_up(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - # upleft - if b_circle: - ai_evolve = slide_upleft(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - # left - ai_evolve = slide_left(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - # downleft - if b_circle: - ai_evolve = slide_downleft(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - # down - ai_evolve = slide_down(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - # downright - if b_circle: - ai_evolve = slide_downright(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - # right - ai_evolve = slide_right(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - # upright - if b_circle: - ai_evolve = slide_upright(ai_tree) - ai_treering[(ai_evolve != ai_tree) & (ai_treering == 0)] = ai_evolve[(ai_evolve != ai_tree) & (ai_treering == 0)] - - # update output - #print(ai_treering) - ai_tree = ai_treering - # output - return(ai_tree) - - -def collision(ai_segment, i_step_size=1): - """ - input: - ai_segment: numpy array representing a cells basin file. - it is assumed that basin borders are represented by 0 values, - and basins are represented with any values different from 0. - ai_segment = skimage.io.imread("cells_basins.tif") - - i_step_size: integer that specifies the distance from a basin - where collisions with other basins are detected. - increasing the step size behind > 1 will result in faster processing - but less certain results. step size < 1 make no sense. - default step size is 1. - - output: - eti_collision: a set of tuples representing colliding basins. - - description: - algorithm to detect which basin collide a given step size away. - """ - eti_collision = set() - for o_slide in [slide_up, slide_down, slide_left, slide_right, slide_upleft, slide_upright, slide_downleft, slide_downright]: - ai_walk = ai_segment.copy() - for _ in range(i_step_size): - ai_walk = o_slide(ai_walk) - ai_alice = ai_walk[(ai_segment != 0) & (ai_walk != 0)] - ai_bob = ai_segment[(ai_segment != 0) & (ai_walk != 0)] - eti_collision = eti_collision.union(set( - zip( - ai_alice[(ai_alice != ai_bob)], - ai_bob[(ai_bob != ai_alice)] - ) - )) - # return - return(eti_collision) - - -def touching_cells(ai_segment, i_border_width=0, i_step_size=1): - """ - input: - ai_segment: numpy array representing a cells basin file. - it is assumed that basin borders are represented by 0 values, - and basins are represented with any values different from 0. - ai_segment = skimage.io.imread("cells_basins.tif") - - i_border_width: maximal acceptable border with in pixels. - this is half of the range how far two the adjacent cell maximal - can be apart and still are regarded as touching each other. - - i_step_size: step size by which the border width is sampled for - touching cells. - increase the step size behind > 1 will result in faster processing - but less certain results. step size < 1 make no sense. - default step size is 1. - - output: - dei_touch: a dictionary that for each basin states - which other basins are touching. - - description: - algorithm to extract the touching basins from a cell basin numpy array. - algorithm inspired by C=64 computer games with sprit collision. - """ - - # detect neighbors - eti_collision = set() - ai_evolve = ai_segment.copy() - for _ in range(-1, i_border_width, i_step_size): - # detect cell border collision - eti_collision = eti_collision.union( - collision(ai_segment=ai_evolve, i_step_size=i_step_size) - ) - # grow basin - ai_evolve = grow(ai_segment=ai_evolve, i_step=i_step_size) - - # transform set of tuple of alice and bob collision to dictionary of sets - dei_touch = {} - ei_alice = set(np.ndarray.flatten(ai_segment)) - ei_alice.remove(0) - for i_alice in ei_alice: - dei_touch.update({i_alice : set()}) - for i_alice, i_bob in eti_collision: - ei_bob = dei_touch[i_alice] - ei_bob.add(i_bob) - dei_touch.update({i_alice : ei_bob}) - - # output - return(dei_touch) - - -def detouch_to_df(deo_touch, ls_column=["cell_center","cell_touch"]): - """ - input: - deo_touch: touching_cells generated dictionary - ls_column: future dictionary_key dictionary_value column name - - output: - df_touch: dataframe which contains the same information - as the input deo_touch dictionary. - - description: - transforms dei_touch dictionary into a two column dataframe. - """ - lo_key_total= [] - lo_value_total = [] - for o_key, eo_value in deo_touch.items(): - try: - lo_value = sorted(eo_value, key=int) - except ValueError: - lo_value = sorted(eo_value) - # extract form dictionary - if (len(lo_value) == 0): - lo_key_total.append(o_key) - lo_value_total.append(0) - else: - lo_key_total.extend([o_key] * len(lo_value)) - lo_value_total.extend(lo_value) - # generate datafarme - df_touch = pd.DataFrame([lo_key_total,lo_value_total], index=ls_column).T - return(df_touch) - - -# bue: 202021016: maybe refracture this into segment_px and membrane_px function -# the segement px function can then be used too on simple cell and nucles data -def membrane_px(ai_segment, dai_value, i_step=1, b_approximation=False): - ''' - input: - ai_segment: numpy array representing a cells basin tiff file. - it is assumed that basin borders are represented by 0 values, - and basins are represented with any values different from 0. - ai_segement = skimage.io.imread("cells_basins.tif") - - dai_value: dictionary of numpy array representing a - protein expression value tiff file. - the dictionary key should be the protein label. - - i_step: number of pixel the cell border, which is 2 pixel, - in both direction is grown, to cover the membrane segment. - default is 1. - - b_approximation: if set True, calculation works with - non-overlapping membranes and will as such be much faster. - - output: - df_membrane_px: dictionary of pandas datafarame whit cell_id, - image absolute xy coordinate, extended cell relative coordinate, - and membrane pixel related expression values for all proteins. - - description: - function extracts protein membrane expression values, - for each protein submitted to the function. - ''' - # get a fix sensor order - ls_sensor = sorted(dai_value.keys()) - - # get cell border - ab_border = border(ai_segment).astype(bool) - ai_segment_border = ai_segment.copy() - ai_segment_border[~ab_border] = 0 - - # aproximative algorithm - if (b_approximation) or (i_step <= 0): - - # membrane segment - ai_membrane = grow(ai_segment_border, i_step=i_step) - ab_membrane = ai_membrane.astype(bool) - ai_cell = ai_membrane[ab_membrane] - - # coordinates - tai_coor_absolute = np.where(ab_membrane) - ai_ycoor_absolute = tai_coor_absolute[0] - ai_xcoor_absolute = tai_coor_absolute[1] - - ai_membrane_coor = np.stack([ - ai_cell, - ai_ycoor_absolute, - ai_xcoor_absolute, - ], axis=1) - - # values - lai_membrane_value = [] - for s_sensor in ls_sensor: - ai_value = dai_value[s_sensor] - lai_membrane_value.append( - ai_value[ab_membrane] - ) - ai_membrane_value = np.stack( - lai_membrane_value, - axis=1, - ) - - # concatenate coor and value - ai_membrane_px = np.concatenate([ - ai_membrane_coor, - ai_membrane_value, - ], axis=1) - - # pack output - df_membrane_px = pd.DataFrame( - ai_membrane_px, - columns = ['cell', 'y_absolute', 'x_absolute'] + ls_sensor, - )#.astype({}) - df_membrane_px.index.name = f'approximation_membrane_{2*i_step + 1}px' - print(df_membrane_px.info()) - - # exact algorithm - else: - # empty result object - ai_membrane_px = None - - # for each cell - ei_cell = set(ai_segment.flatten()) - ei_cell.discard(0) # kick cell0 which is background - i_total = len(ei_cell) - #for i, i_cell in enumerate(sorted(ei_cell)[0:16]): - for i, i_cell in enumerate(sorted(ei_cell)): - #print(f'processing cell{i_cell}: {i} / {i_total}') - - # membrane segment - tai_coor = np.where(ai_segment_border == i_cell) - ai_ycoor = tai_coor[0] - ai_xcoor = tai_coor[1] - i_ymin = np.array([ai_ycoor.min() - i_step]).clip(min=0)[0] - i_xmin = np.array([ai_xcoor.min() - i_step]).clip(min=0)[0] - i_ymax = np.array([ai_ycoor.max() + i_step + 1]).clip(max=ai_segment_border.shape[0])[0] - i_xmax = np.array([ai_xcoor.max() + i_step + 1]).clip(max=ai_segment_border.shape[1])[0] - ai_border = (ai_segment_border == i_cell)[i_ymin:i_ymax,i_xmin:i_xmax].astype(int) - ab_membrane = grow(ai_border, i_step=i_step).astype(bool) - - # coordiantes - tai_coor_relative = np.where(ab_membrane) - ai_ycoor_relative = tai_coor_relative[0] - ai_xcoor_relative = tai_coor_relative[1] - ai_ycoor_absolute = ai_ycoor_relative + i_ymin - ai_xcoor_absolute = ai_xcoor_relative + i_xmin - ai_cell = np.array([i_cell] * len(ai_ycoor_relative)) - ai_membrane_coor = np.stack([ - ai_cell, - ai_ycoor_relative, - ai_xcoor_relative, - ai_ycoor_absolute, - ai_xcoor_absolute, - ], axis=1) - - # values - lai_membrane_value = [] - for s_sensor in ls_sensor: - ai_value = dai_value[s_sensor] - lai_membrane_value.append( - ai_value[i_ymin:i_ymax,i_xmin:i_xmax][ab_membrane] - ) - ai_membrane_value = np.stack( - lai_membrane_value, - axis=1, - ) - - # concatenate coor and value - ai_membrane_coorvalue = np.concatenate([ - ai_membrane_coor, - ai_membrane_value, - ], axis=1) - - # update ouput - if (ai_membrane_px is None): - ai_membrane_px = ai_membrane_coorvalue - else: - ai_membrane_px = np.concatenate([ - ai_membrane_px, - ai_membrane_coorvalue, - ], axis=0) - - # sanity check - print(f'cell {i_cell} / {i_total}: min {ai_ycoor_relative.min()},{ai_xcoor_relative.min()} x max {ai_ycoor_relative.max()},{ai_xcoor_relative.max()}') - - # pack output - df_membrane_px = pd.DataFrame( - ai_membrane_px, - columns = ['cell', 'y_relative', 'x_relative', 'y_absolute', 'x_absolute'] + ls_sensor, - )#.astype({}) - df_membrane_px.index.name = f'membrane_{2*i_step + 2}px' - - # output - return(df_membrane_px) - - -# bue 20201016: i should add all statistical moments -# bue 20201024: this should become dfpx_stats and be applicable to cell, nucleus, cytoplasm, and membrane -def dfmembranepx_stats(df_membrane_px, di_threshold_raw=None): - ''' - input: - df_membrane_px: membrane_px output which is a - pandas datafarame whit cell_id, - image absolute and cell relative xy coordinate and - membrane pixel related expression values for all proteins. - - di_threshold_raw: dictionary of raw intensity threshold values - for each protein. if set to none, cell membrane positive fraction - will not be calculated and be missing from the output. - default is None. - - output: - ddf_out: dictionary of pandas datafarames. one dataframe per protein. - each dataframe stores mean, min, max and whole range of quantile values - measured for each cell membrane. - - description: - function calculates statistical key numbers for membrane protein - expression values, for each protein submitted to the function. - ''' - # handle input - es_sensor = set(df_membrane_px.columns) - es_sensor = es_sensor.difference( - {'cell', 'y_relative', 'x_relative', 'y_absolute', 'x_absolute'} - ) - - # empty result object - ddlr_membrane = {} - for s_sensor in es_sensor: - ddlr_membrane.update({s_sensor: {}}) - - # for each cell - ei_cell = set(df_membrane_px.cell.unique()) - i_total = len(ei_cell) - 1 - - #for i, i_cell in enumerate(sorted(ei_cell)[0:16]): - for i, i_cell in enumerate(ei_cell): - print(f'processing cell{i_cell}: {i} / {i_total}') - - # for each sensor - df_membrane_cell_px = df_membrane_px.loc[df_membrane_px.cell == i_cell,:] - for s_sensor in es_sensor: - ai_membrane = df_membrane_cell_px.loc[:,s_sensor].values - - # quantile calculation - lr_membrane = [ - np.mean(ai_membrane), - np.std(ai_membrane, ddof=0), - np.min(ai_membrane), - np.quantile(ai_membrane, 0.01), - np.quantile(ai_membrane, 0.05), - np.quantile(ai_membrane, 0.1), - np.quantile(ai_membrane, 0.2), - np.quantile(ai_membrane, 0.3), - np.quantile(ai_membrane, 0.4), - np.quantile(ai_membrane, 0.5), - np.quantile(ai_membrane, 0.6), - np.quantile(ai_membrane, 0.7), - np.quantile(ai_membrane, 0.8), - np.quantile(ai_membrane, 0.9), - np.quantile(ai_membrane, 0.95), - np.quantile(ai_membrane, 0.99), - np.max(ai_membrane), - ai_membrane.shape[0], - ] - - # membrane fraction positive calculation - if not (di_threshold_raw is None): - ab_membrane = ai_membrane > di_threshold_raw[s_sensor] - i_sumpos = ab_membrane.sum() - r_fractpos = i_sumpos / ab_membrane.shape[0] - lr_membrane.extend([i_sumpos, r_fractpos]) - - # update result object - ddlr_membrane[s_sensor].update({i_cell: lr_membrane}) - - # pack output - ls_index = [ - 'mean','std', - 'min','q0_01','q0_05', - 'q0_1','q0_2','q0_3','q0_4','q0_5', - 'q0_6','q0_7','q0_8','q0_9','q0_95', - 'q0_99','max', - 'membrane_size_px' - ] - if not (di_threshold_raw is None): - ls_index.extend(['px_pos','fract_pos']) - ddf_out = {} - for s_sensor, dlr_membrane in ddlr_membrane.items(): - df_membrane = pd.DataFrame( - dlr_membrane, - index=ls_index, - ).T - ddf_out.update({s_sensor: df_membrane}) - df_membrane.index.name = f'cell' - - # output - return(ddf_out) - - -def imgfuse(laaai_in): - """ - input: - laaai_in: list of 3 channel (RGB) images - - output: - aaai_out: fused 3 channel image - - description: - code to fuse many RGB images into one. - """ - # check shape - ti_shape = None - for aaai_in in laaai_in: - if (ti_shape is None): - ti_shape = aaai_in.shape - else: - if (aaai_in.shape != ti_shape): - sys.exit(f"Error: input images have not the same shape. {aaai_in.shape} != {aaai_in}.") - - # fuse images - llli_channel = [] - for i_channel in range(ti_shape[0]): - lli_matrix = [] - for i_y in range(ti_shape[1]): - li_row = [] - for i_x in range(ti_shape[2]): - #print(f"{i_channel} {i_y} {i_x}") - li_px = [] - for aaai_in in laaai_in: - i_in = aaai_in[i_channel,i_y,i_x] - if (i_in != 0): - li_px.append(i_in) - if (len(li_px) != 0): - i_out = np.mean(li_px) - else: - i_out = 0 - li_row.append(int(i_out)) - lli_matrix.append(li_row) - llli_channel.append(lli_matrix) - - # output - aaai_out = np.array(llli_channel) - return(aaai_out) - - -# main code -#if __name__ == "__main__": - diff --git a/pcdl/neuromancer.py b/pcdl/neuromancer.py deleted file mode 100644 index 7110c64..0000000 --- a/pcdl/neuromancer.py +++ /dev/null @@ -1,206 +0,0 @@ -######### -# title: ometiff2neuro.py -# -# language: python3 -# date: 2022-02-16 -# license: MIT -# author: jason lu, viviana kwong, elmar bucher -# -# installation: -# pip install neuroglancer # the basics -# pip install ipython # for coding -# pip install matplotlib # for expression value color maps -# pip install numpy # for basic code manipulation -# pip install scikit-image # signal thresh and image manipulation. -# pip install bioio bioio-ome-tiff # for loading the image and extracting ometiff metadata -# -# run: -# python3 -i neuromancer.py -# -# description: -# script to render multi channel multi slice ome.tiff files into the -# neuroglancer software. -# -# the script here makes use of the neuroglancer python library and is based -# on the neuroglancer/python/examples/example.py code. -# with this script, it is possible to load three-dimensional single -# time step ome-tiff files straight into the neuroglancer software. -# for each channel, mesh generation, rendering, and expression intensity -# coloring is done on the fly. -# channels can be viewed together or alone by toggling them on or off in -# the neuroglancer user interface. -# -# references: -# + https://www.openmicroscopy.org/ome-files/ -# + https://github.com/google/neuroglancer -# + https://github.com/google/neuroglancer/tree/master/python -######### - - -# library -import argparse -from bioio import BioImage -import matplotlib as mpl -import neuroglancer -import neuroglancer.cli -import numpy as np -from skimage import exposure, util -import sys - - -# functions -def ometiff2neuro( - o_state, - s_pathfile_tiff, - i_timestep = 0, - s_intensity_cmap = 'gray', # None - ): - ''' - input: - o_state: neuroglancer viewer state object. - - s_pathfile_tiff: file name and path to input tiff file. - - s_intensity_cmap: matlab color map object, used to display expression intensity values. - default is cm.gray. - if None, no intensity layers will be generated. - - output: - local url where the rendered data can be viewed. - - description: - function to render multi channel multi slice (ome)tiff files into the neuroglancer software. - ''' - # off we go # - print(f'\nprocessing: {s_pathfile_tiff}') - - # load tiff image as numpy array - o_img = BioImage(s_pathfile_tiff) - - # check dimensionality - if (o_img.dims.order.lower() != 'tczyx'): - sys.exit(f'Error @ ometiff2neuro : cannot handle ome tiff file with dimension {o_img.dims.order.lower()}') - - # generate neuroglancer layers - for i_channel in range(o_img.shape[1]): - - # extract channel label - s_label = o_img.channel_names[i_channel] - print(f'check channel {i_channel}/{o_img.shape[1]}: {s_label}') - - # extract data - a_channel = o_img.data[i_timestep, i_channel, :, :, :] - - # 3D rendering # - # thresh data - # shape - a_shape = np.zeros(a_channel.shape, dtype=np.uint32) - a_shape[a_channel > 0] = i_channel + 1 - - # generate neuroglancer object - ls_name = ['z', 'y', 'x'] - li_scale = [ - o_img.physical_pixel_sizes.Z, - o_img.physical_pixel_sizes.Y, - o_img.physical_pixel_sizes.X, - ] - - o_state.layers.append( - name = s_label, - layer = neuroglancer.LocalVolume( - data = a_shape, - dimensions = neuroglancer.CoordinateSpace( - # rgb, x, y, z - names = ls_name, - scales = li_scale, - units = ['nm', 'nm', 'nm'], - ), - ), - visible = False, # special thanks to Jeremy Maitin-Shepard. - ) - - # expression intensity rendering # - if not (s_intensity_cmap is None): - a_channel = exposure.rescale_intensity(a_channel, in_range='image') # 16 or 8[bit] normalized - - # translate intensity by color map - a_8bit = util.img_as_ubyte(a_channel) - a_intensity = mpl.colormaps[s_intensity_cmap](a_8bit, alpha=None, bytes=True)[:,:,:,0:3] - - # generate neuroglancer object - ls_name = ['z', 'y', 'x','c^'] - li_scale = [ - o_img.physical_pixel_sizes.Z, - o_img.physical_pixel_sizes.Y, - o_img.physical_pixel_sizes.X, - 3 - ] - - o_state.layers.append( - name = s_label + '_intensity', - layer = neuroglancer.LocalVolume( - data = a_intensity, - dimensions = neuroglancer.CoordinateSpace( - # rgb, x, y, z - names = ls_name, - scales = li_scale, - units = ['nm', 'nm', 'nm', ''], - ), - ), - visible = False, # special thanks to Jeremy Maitin-Shepard. - shader= -""" -void main() { - emitRGB( - vec3(toNormalized(getDataValue(0)), - toNormalized(getDataValue(1)), - toNormalized(getDataValue(2))) - ); -} -""", - ) - - -# run the code from the command line -if __name__ == '__main__': - import neuroglancer - o_parser = argparse.ArgumentParser( - description='script to render an ome.tiff file into the neuroglancer software.', - epilog = 'homepage: https://github.com/elmbeech/physicelldataloader', - ) - - # request path to ometiff and file name as command line argument - o_parser.add_argument( - 'ometiff', - type=str, - nargs=1, - help='ome.tiff path/filename' - ) - # time step - o_parser.add_argument( - '--timestep', - default = 0, - type = int, - help = 'time step, within a possibly collapsed ome tiff file, to render. the default will work with single time step ome tiff files.', - ) - # intensity colormap - o_parser.add_argument( - '--intensity_cmap', - default = 'gray', - help = 'matlab color map label, used to display expression intensity values. if None, no intensity layers will be generated. https://matplotlib.org/stable/users/explain/colors/colormaps.html', - ) - - # start neuroglancer - neuroglancer.cli.add_server_arguments(o_parser) - neuroglancer.cli.handle_server_arguments(o_parser.parse_args()) - viewer = neuroglancer.Viewer() - with viewer.txn() as state: - # render ometiff - ometiff2neuro( - o_state = state, - s_pathfile_tiff = o_parser.parse_args().ometiff[0], - i_timestep = o_parser.parse_args().timestep, - s_intensity_cmap = None if (o_parser.parse_args().intensity_cmap.lower() == 'none') else o_parser.parse_args().intensity_cmap, - ) - # print neuroglancer viewer url - print(viewer) diff --git a/pcdl/pdplt.py b/pcdl/pdplt.py deleted file mode 100644 index c45ed4f..0000000 --- a/pcdl/pdplt.py +++ /dev/null @@ -1,132 +0,0 @@ -#### -# title: biotransistor.pdplt.py -# -# language: python3 -# date: 2019-06-29 -# license: GPL>=v3 -# author: Elmar Bucher -# -# description: -# library with the missing pandas plot features. -# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.html -#### - - -# library -import matplotlib.pyplot as plt -from matplotlib import cm -from matplotlib import colors -import matplotlib.patches as mpatches -import numpy as np -import random - - -# pandas to matplotlib -#fig, ax = plt.subplots() -#ax = ax.ravel() -#ax.axis('equal') -#df.plot(ax=ax) -#plt.tight_layout() -#fig.savefig(s_filename, facecolor='white') -#plt.close() - - -# plot stuff -def df_label_to_color(df_abc, s_focus, es_label=None, s_nolabel='gray', s_cmap='viridis', b_shuffle=False): - ''' - input: - df_abc: dataframe to which the color column will be added. - s_focus: column name with sample labels for which a color column will be generated. - es_label: set of labels to color. if None, es_label will be extracted for the s_focus column. - s_nolabel: color for labels not defined in es_label. - s_cmap: matplotlib color map label. - https://matplotlib.org/stable/tutorials/colors/colormaps.html - b_shuffle: should colors be given by alphabetical order, - or should the label color mapping order be random. - - output: - df_abc: dataframe updated with color column. - ds_color: lable to hex color string mapping dictionary - - description: - function adds for the selected label column - a color column to the df_abc dataframe. - ''' - if (es_label is None): - es_label = set(df_abc.loc[:,s_focus]) - if b_shuffle: - ls_label = list(es_label) - random.shuffle(ls_label) - else: - ls_label = sorted(es_label) - a_color = plt.get_cmap(s_cmap)(np.linspace(0, 1, len(ls_label))) - do_color = dict(zip(ls_label, a_color)) - df_abc[f'{s_focus}_color'] = s_nolabel - ds_color = {} - for s_category, o_color in do_color.items(): - s_color = colors.to_hex(o_color) - ds_color.update({s_category : s_color}) - df_abc.loc[(df_abc.loc[:,s_focus] == s_category), f'{s_focus}_color'] = s_color - # output - return(ds_color) - -def ax_colorlegend(ax, ds_color, s_loc='lower left', s_fontsize='small'): - ''' - input: - ax: matplotlib axis object to which a color legend will be added. - ds_color: lables to color strings mapping dictionary - s_loc: the location of the legend. - possible strings are: best, - upper right, upper center, upper left, center left, - lower left, lower center, lower right, center right, - center. - s_fontsize: font size used for the legend. known are: - xx-small, x-small, small, medium, large, x-large, xx-large. - - output: - ax: matplotlib axis object updated with color legend. - - description: - function to add color legend to a figure. - ''' - lo_patch = [] - for s_label, s_color in sorted(ds_color.items()): - o_patch = mpatches.Patch(color=s_color, label=s_label) - lo_patch.append(o_patch) - ax.legend( - handles = lo_patch, - loc = s_loc, - fontsize = s_fontsize - ) - -def ax_colorbar(ax, r_vmin, r_vmax, s_cmap='viridis', s_text=None, o_fontsize='medium', b_axis_erase=False): - ''' - input: - ax: matplotlib axis object to which a colorbar will be added. - r_vmin: colorbar min value. - r_vmax: colorbar max value. - s_cmap: matplotlib color map label. - https://matplotlib.org/stable/tutorials/colors/colormaps.html - s_text: to label the colorbar axis. - o_fontsize: font size used for the legend. known are: - xx-small, x-small, small, medium, large, x-large, xx-large. - b_axis_erase: should the axis ruler be erased? - - output: - ax: matplotlib axis object updated with colorbar. - - description" - function to add colorbar to a figure. - ''' - if b_axis_erase: - ax.axis('off') - if not (s_text is None): - ax.text(0.5,0.5, s_text, fontsize=o_fontsize) - plt.colorbar( - cm.ScalarMappable( - norm=colors.Normalize(vmin=r_vmin, vmax=r_vmax, clip=False), - cmap=s_cmap, - ), - ax=ax, - ) - diff --git a/pcdl/pyAnnData.py b/pcdl/pyAnnData.py new file mode 100644 index 0000000..51757ea --- /dev/null +++ b/pcdl/pyAnnData.py @@ -0,0 +1,616 @@ +### +# title: pyAnnData.py +# +# language: python3 +# date: 2023-06-24 +# license: BSD-3-Clause +# author: Elmar Bucher +# +# description: +# pyAnnData.py spices up the pyMCDS (TimeStep) and pyMCDSts (TimeSeries) +# classes with a function to transform mcds time steps and time series +# into anndata objects. +# anndata is the de facto python3 single cell data standard. +# In other words, these functions enable us to analyze PhysiCell output +# the same way that bioinformatician analyze their data retrieved from +# single cell wet lab experiments. +# +# + https://www.biorxiv.org/content/10.1101/2021.12.16.473007v1 +# + https://anndata.readthedocs.io/en/latest/ +# + https://scverse.org/ +#### + + +import anndata as ad +import numpy as np +import pandas as pd +from pcdl.pyMCDS import pyMCDS, es_coor_cell +from pcdl.pyMCDSts import pyMCDSts +from scipy import sparse +import warnings + + +def scaler(df_x, scale='maxabs'): + """ + input: + df_x: pandas dataframe + one attribute per column, one sample per row. + + scale: string; default 'maxabs' + None: no scaling. set scale to None if you would like to have + raw data or scale, transform, and normalize the data later. + + maxabs: maximum absolute value distance scaler will linearly map + all values into a [-1, 1] interval. if the original data + has no negative values, the result will be the same as with + the minmax scaler (except with attributes with only one value). + if the attribute has only zeros, the value will be set to 0. + + minmax: minimum maximum distance scaler will map all values + linearly into a [0, 1] interval. + if the attribute has only one value, the value will be set to 0. + + std: standard deviation scaler will result in sigmas. + each attribute will be mean centered around 0. + ddof delta degree of freedom is set to 1 because it is assumed + that the values are samples out of the population + and not the entire population. it is incomprehensible to me + that the equivalent sklearn method has ddof set to 0. + if the attribute has only one value, the value will be set to 0. + + output: + df_x: pandas dataframe + scaled df_x dataframe. + + description: + inspired by scikit-learn's preprocessing scaling method, this function + offers a re-implementation of the linear re-scaling methods maxabs, + minmax, and scale. + + the robust scaler methods (quantile based) found in scikit-learn are + missing. since we deal with simulated data, we don't expect heavy + outliers, and if they exist, then they are of interest. + the power and quantile based transformation methods and unit circle + based normalizer methods found there are missing too. + if you need to apply any such methods, you can do so to an anndata object + like this: + + from sklearn import preprocessing + adata.obsm["X_scaled"] = preprocessing.scale(adata.X) + + + https://scikit-learn.org/stable/auto_examples/preprocessing/plot_all_scaling.html + + https://scikit-learn.org/stable/modules/classes.html#module-sklearn.preprocessing + + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.maxabs_scale.html + + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.minmax_scale.html + + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.scale.html + """ + if scale is None: + pass + # -1,1 + elif scale == 'maxabs': + a_x = df_x.values + warnings.filterwarnings('ignore', category=RuntimeWarning) + a_maxabs = a_x / abs(a_x).max(axis=0) + warnings.simplefilter('default') + a_maxabs[np.isnan(a_maxabs)] = 0 # fix if entier column is 0 + df_x = pd.DataFrame(a_maxabs, columns=df_x.columns, index=df_x.index) + # 0,1 + elif scale == 'minmax': + a_x = df_x.values + warnings.simplefilter("ignore") + warnings.filterwarnings('ignore', category=RuntimeWarning) + a_minmax = (a_x - a_x.min(axis=0)) / (a_x.max(axis=0) - a_x.min(axis=0)) + warnings.simplefilter('default') + a_minmax[np.isnan(a_minmax)] = 0 # fix if entier column has same value + df_x = pd.DataFrame(a_minmax, columns=df_x.columns, index=df_x.index) + # sigma + elif scale == 'std': + a_x = df_x.values + warnings.filterwarnings('ignore', category=RuntimeWarning) + a_std = (a_x - a_x.mean(axis=0)) / a_x.std(axis=0, ddof=1) + warnings.simplefilter('default') + a_std[np.isnan(a_std)] = 0 # fix if entier column has same value + df_x = pd.DataFrame(a_std, columns=df_x.columns, index=df_x.index) + else: + raise ValueError(f"Error @ scaler : unknown scale algorithm {scale} detected. known are [None, 'maxabs', 'minmax', 'std'].") + + return df_x + + +def _anndextract(df_cell, scale='maxabs', graph_attached={}, graph_neighbor={}, graph_spring={}, graph_method='PhysiCell'): + """ + input: + df_cell: pandas dataframe + data frame retrieved with the mcds.get_cell_df function. + + scale: string; default maxabs + specify how the data should be scaled. + possible values are None, maxabs, minmax, std. + for more input, check out: help(pcdl.scaler). + + graph_attached: dict; default {} + attached graph dictionary, retrieved with + with the mcds.get_attched_graph() function. + + graph_neighbor: dict; default {} + neighbor graph dictionary, retrieved + with the mcds.get_neighbor_graph() function. + + graph_spring: dict; default {} + spring_attached graph dictionary, retrieved + with the mcds.get_spring_graph_dict() function. + + graph_method: string; default PhysiCell + method how the graphs were generated. + + output: + df_count, df_obs, d_obsm, d_obsp, d_uns dataframes and dictionaries, + ready to be backed into an anndata object. + + description: + this function takes a pcdl df_cell pandas dataframe and re-formats + it into a set of two dataframes (df_count, df_obs), + two dictionary of numpy array (d_obsm, d_obsp), + and one dictionary of string (d_uns), + which downstream might be transformed into an anndata object. + """ + # transform index to string + df_coor = df_cell.loc[:,['position_x','position_y','position_z']].copy() + df_cell.index = df_cell.index.astype(str) + + # build obs anndata object (annotation of observations) + df_obs = df_cell.loc[:,['mesh_center_p','time']].copy() + df_obs.columns = ['z_layer', 'time'] + + # buil obsm anndata object spatial (multi-dimensional annotation of observations) + if (len(set(df_cell.position_z)) == 1): + df_obsm = df_cell.loc[:,['position_x','position_y']].copy() + else: + df_obsm = df_cell.loc[:,['position_x','position_y','position_z']].copy() + d_obsm = {"spatial": df_obsm.values} + + # build obsp and uns anndata object graph (pairwise annotation of obeservation) and (unstructured data) + #### + # acknowledgement: + # this code is inspired from the tysserand add_to_AnnData impelmentation + # from Alexis Coullomb form the Pancaldi Lab. + # https://github.com/VeraPancaldiLab/tysserand/blob/main/tysserand/tysserand.py#L1546 + #### + # extract cell_id to index mapping (i always loved perl) + di_ididx = df_cell.reset_index().loc[:,'ID'].reset_index().astype(int).set_index('ID').squeeze().to_dict() + # transform cell id graph dict to index matrix and pack for anndata + d_obsp = {} # pairwise annotation of obeservation + d_uns = {} # unstructured data + for s_graph, dei_graph in [('neighbor', graph_neighbor), ('attached', graph_attached), ('spring', graph_spring)]: + lli_edge = [] + lr_distance = [] + for i_src, ei_dst in dei_graph.items(): + for i_dst in ei_dst: + # extract edge + lli_edge.append([di_ididx[i_src], di_ididx[i_dst]]) + r_distance = ((df_coor.loc[i_src,:].values - df_coor.loc[i_dst,:].values)**2).sum()**(1/2) + lr_distance.append(r_distance) + # if there is a graph + if (len(lli_edge) > 0): + # handle edge data + ai_edge = np.array(lli_edge, dtype=np.uint) + # handle connection data + ai_conectivity = np.ones(ai_edge.shape[0], dtype=np.uint16) + ai_conectivity_sparse = sparse.csr_matrix( + (ai_conectivity, (ai_edge[:,0], ai_edge[:,1])), + shape = (df_cell.shape[0], df_cell.shape[0]), + dtype = np.uint + ) + # handle distance data + ar_distance = np.array(lr_distance, dtype=np.float64) + ar_distance_sparse = sparse.csr_matrix( + (ar_distance, (ai_edge[:,0], ai_edge[:,1])), + shape = (df_cell.shape[0], df_cell.shape[0]), + dtype = np.float64 + ) + # pack obsp + d_obsp.update({ + f'physicell_{s_graph}_conectivities': ai_conectivity_sparse, + f'physicell_{s_graph}_distances': ar_distance_sparse, + }) + # pack uns + d_uns.update({ + s_graph : { + 'connectivities_key': f'physicell_{s_graph}_conectivities', + 'distances_key': f'physicell_{s_graph}_distances', + 'params': { + 'metric': 'euclidean', + 'method': graph_method, + } + } + }) + + # extract discrete cell data + es_drop = set(df_cell.columns).intersection({ + 'voxel_i', 'voxel_j', 'voxel_k', + 'mesh_center_m', 'mesh_center_n', 'mesh_center_p', + 'position_x', 'position_y','position_z', + 'time', 'runtime', 'xmlfile', + }) + df_cell.drop(es_drop, axis=1, inplace=True) # maybe obs? + + # dectect variable types + des_type = {'float': set(), 'int': set(), 'bool': set(), 'str': set()} + for _, se_cell in df_cell.items(): + if str(se_cell.dtype).startswith('float'): + des_type['float'].add(se_cell.name) + elif str(se_cell.dtype).startswith('int'): + des_type['int'].add(se_cell.name) + elif str(se_cell.dtype).startswith('bool'): + des_type['bool'].add(se_cell.name) + elif str(se_cell.dtype).startswith('object'): + des_type['str'].add(se_cell.name) + else: + print(f'Error @ TimeSeries.get_anndata : column {se_cell.name} detected with unknown dtype {str(se_cell.dtype)}.') + + # build on obs and X anndata object + df_cat = df_cell.loc[:,sorted(des_type['str'])].copy() + df_obs = pd.merge(df_obs, df_cat, left_index=True, right_index=True) + es_num = des_type['float'].union(des_type['int'].union(des_type['bool'])) + df_count = df_cell.loc[:,sorted(es_num)].copy() + for s_col in des_type['bool']: + df_count[s_col] = df_count[s_col].astype(int) + df_count = scaler(df_count, scale=scale) + + # return + return(df_count, df_obs, d_obsm, d_obsp, d_uns) + + +# class definition +class TimeStep(pyMCDS): + def __init__(self, xmlfile, output_path='.', custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True): + """ + input: + xmlfile: string + name of the xml file with or without path. + in the with path case, output_path has to be set to the default! + + output_path: string; default '.' + relative or absolute path to the directory where + the PhysiCell output files are stored. + + custom_data_type: dictionary; default is {} + variable to specify custom_data variable types + besides float (int, bool, str) like this: {var: dtype, ...}. + downstream float and int will be handled as numeric, + bool as Boolean, and str as categorical data. + + microenv: boole; default True + should the microenvironment data be loaded? + setting microenv to False will use less memory and speed up + processing, similar to the original pyMCDS_cells.py script. + + graph: boole; default True + should neighbor garph, attached graph, and spring attached graph + be loaded? setting graph to False will use less memory and + speed up processing. + + physiboss: boole; default True + should physiboss state data be loaded, if found? + setting physiboss to False will use less memory and speed up processing. + + settingxml: string; default PhysiCell_settings.xml + the settings.xml that is loaded, from which the cell type ID + label mapping, is extracted, if this information is not found + in the output xml file. + set to None or False if the xml file is missing! + + verbose: boole; default True + setting verbose to False for less text output while processing. + + output: + mcds: TimeStep class instance + all fetched content is stored at mcds.data. + + description: + TimeStep.__init__ will call pyMCDS.__init__ that generates a mcds + class instance, a dictionary of dictionaries data structure that + contains all output from a single PhysiCell model time step. + furthermore, the mcds object offers functions to access the stored data. + the code assumes that all related output files are stored + in the same directory. data is loaded by reading the xml file for + a particular time step and the therein referenced files. + """ + pyMCDS.__init__( + self, + xmlfile = xmlfile, + output_path = output_path, + custom_data_type = custom_data_type, + microenv = microenv, + graph = graph, + physiboss = physiboss, + settingxml = settingxml, + verbose = verbose + ) + + + def get_anndata(self, values=1, drop=set(), keep=set(), scale='maxabs'): + """ + input: + values: integer; default is 1 + minimal number of values a variable has to have to be outputted. + variables that have only 1 state carry no information. + None is a state too. + + drop: set of strings; default is an empty set + set of column labels to be dropped for the dataframe. + don't worry: essential columns like ID, coordinates + and time will never be dropped. + Attention: when the keep parameter is given, then + the drop parameter has to be an empty set! + + keep: set of strings; default is an empty set + set of column labels to be kept in the dataframe. + set values=1 to be sure that all variables are kept. + don't worry: essential columns like ID, coordinates + and time will always be kept. + + scale: string; default 'maxabs' + specify how the data should be scaled. + possible values are None, maxabs, minmax, std. + for more input, check out: help(pcdl.scaler) + + output: + annmcds: anndata object + for this one time step. + + description: + function to transform a mcds time step into an anndata object + for downstream analysis. + """ + # processing + if self.verbose: + print(f'processing: 1/1 {round(self.get_time(),9)}[min] mcds into anndata obj.') + df_cell = self.get_cell_df(values=values, drop=drop, keep=keep) + df_count, df_obs, d_obsm, d_obsp, d_uns = _anndextract( + df_cell = df_cell, + scale = scale, + graph_attached = self.get_attached_graph_dict(), + graph_neighbor = self.get_neighbor_graph_dict(), + graph_method = self.get_physicell_version(), + ) + annmcds = ad.AnnData( + X = df_count, + obs = df_obs, + obsm = d_obsm, + obsp = d_obsp, + uns = d_uns + ) + # output + return annmcds + + +class TimeSeries(pyMCDSts): + def __init__(self, output_path='.', custom_data_type={}, load=True, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True): + """ + input: + output_path: string, default '.' + relative or absolute path to the directory where + the PhysiCell output files are stored. + + custom_data_type: dictionary; default is {} + variable to specify custom_data variable types + besides float (int, bool, str) like this: {var: dtype, ...}. + downstream float and int will be handled as numeric, + bool as Boolean, and str as categorical data. + + load: boole; default True + should the whole time series data, all time steps, straight at + object initialization be read and stored to mcdsts.l_mcds? + + microenv: boole; default True + should the microenvironment data be loaded? + setting microenv to False will use less memory and speed up + processing, similar to the original pyMCDS_cells.py script. + + graph: boole; default True + should neighbor garph, attached graph, and spring attached graph + be loaded? setting graph to False will use less memory and + speed up processing. + + physiboss: boole; default True + should physiboss state data be loaded, if found? + setting physiboss to False will use less memory and speed up processing. + + settingxml: string; default PhysiCell_settings.xml + the settings.xml that is loaded, from which the cell type ID + label mapping, is extracted, if this information is not found + in the output xml file. + set to None or False if the xml file is missing! + + verbose: boole; default True + setting verbose to False for less text output while processing. + + output: + mcdsts: pyMCDSts class instance + this instance offers functions to process all stored time steps + from a simulation. + + description: + TimeSeries.__init__ will call pyMCDSts.__init__ that generates a mcdsts + class instance. this instance offers functions to process all time steps + in the output_path directory. + """ + pyMCDSts.__init__( + self, + output_path = output_path, + custom_data_type = custom_data_type, + load = load, + microenv = microenv, + graph = graph, + physiboss = physiboss, + settingxml = settingxml, + verbose = verbose + ) + self.l_annmcds = None + + + def get_anndata(self, values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True): + """ + input: + values: integer; default is 1 + minimal number of values a variable has to have to be outputted. + variables that have only 1 state carry no information. + None is a state too. + + drop: set of strings; default is an empty set + set of column labels to be dropped for the dataframe. + don't worry: essential columns like ID, coordinates + and time will never be dropped. + Attention: when the keep parameter is given, then + the drop parameter has to be an empty set! + + keep: set of strings; default is an empty set + set of column labels to be kept in the dataframe. + don't worry: essential columns like ID, coordinates + and time will always be kept. + + scale: string; default 'maxabs' + specify how the data should be scaled. + possible values are None, maxabs, minmax, std. + for more input, check out: help(pcdl.scaler) + + collapse: boole; default True + should all mcds time steps from the time series be collapsed + into one single anndata object, or a list of anndata objects + for each time step? + + keep_mcds: boole; default True + should the loaded original mcds be kept in memory + after transformation? + + output: + annmcds or self.l_annmcds: anndata object or list of anndata objects. + what is returned depends on the collapse setting. + + description: + function to transform mcds time steps into one or many + anndata objects for downstream analysis. + """ + # initialize vaiable + l_annmcds = [] + df_anncount = None + df_annobs = None + ar_annobsm = None + + # variable triage + if (values < 2): + ls_column = list(self.l_mcds[0].get_cell_df(drop=drop, keep=keep).columns) + else: + ls_column = sorted(es_coor_cell.difference({'ID'})) + ls_column.extend(sorted(self.get_cell_attribute(values=values, drop=drop, keep=keep, allvalues=False).keys())) + + # collapse warning + if collapse and self.verbose: + print('Warning @ mcdsts.get_anndata : only df_cell data, but not graph data, can be collapsed.') + + # processing + lann_mcds = [] + i_mcds = len(self.l_mcds) + for i in range(i_mcds): + # fetch mcds + if keep_mcds: + mcds = self.l_mcds[i] + else: + mcds = self.l_mcds.pop(0) + # extract physicell version + s_physicellv = mcds.get_physicell_version(), + # extract time and dataframes + r_time = round(mcds.get_time(),9) + if self.verbose: + print(f'processing: {i+1}/{i_mcds} {r_time}[min] mcds into anndata obj.') + df_cell = mcds.get_cell_df() + df_cell = df_cell.loc[:,ls_column] + + # pack collapsed + if collapse: + # extract + df_count, df_obs, d_obsm, d_obsp, d_uns = _anndextract( + df_cell=df_cell, + scale = scale, + #graph_attached = {}, + #graph_neighbor = {}, + #graph_spring = {}, + #graph_method = s_physicellv, + ) + # count + df_count.reset_index(inplace=True) + df_count.index = df_count.ID + f'id_{r_time}min' + df_count.index.name = 'id_time' + df_count.drop('ID', axis=1, inplace=True) + if df_anncount is None: + df_anncount = df_count + else: + df_anncount = pd.concat([df_anncount, df_count], axis=0) + # obs + df_obs.reset_index(inplace=True) + df_obs.index = df_obs.ID + f'id_{r_time}min' + df_obs.index.name = 'id_time' + if df_annobs is None: + df_annobs = df_obs + else: + df_annobs = pd.concat([df_annobs, df_obs], axis=0) + # obsm (spatial) + if ar_annobsm is None: + ar_annobsm = d_obsm['spatial'] + else: + ar_annobsm = np.vstack([ar_annobsm, d_obsm['spatial']]) + # obsp: nop (graph) + # uns: nop (graph) + + # pack not collapsed + else: + # extract + df_count, df_obs, d_obsm, d_obsp, d_uns = _anndextract( + df_cell=df_cell, + scale = scale, + graph_attached = mcds.get_attached_graph_dict(), + graph_neighbor = mcds.get_neighbor_graph_dict(), + graph_spring = mcds.get_spring_graph_dict(), + graph_method = s_physicellv, + ) + # annmcds + ann_mcds = ad.AnnData( + X = df_count, + obs = df_obs, + obsm = d_obsm, + obsp = d_obsp, + uns = d_uns, + ) + lann_mcds.append(ann_mcds) + + # output + if collapse: + ann_mcdsts = ad.AnnData( + X = df_anncount, + obs = df_annobs, + obsm = {'spatial': ar_annobsm}, + #obsp = d_obsp, + #uns = d_uns + ) + return ann_mcdsts + else: + self.l_annmcds = lann_mcds + return self.l_annmcds + + + def get_annmcds_list(self): + """ + input: + self: TimeSeries class instance. + + output: + self.l_annmcds: list of chronologically ordered anndata mcds objects. + watch out, this is a pointer to the + self.l_annmcds list of anndata mcds objects, not a copy of self.l_annmcds! + + description: + function returns a binding to the self.l_annmcds list of anndata mcds objects. + """ + return self.l_annmcds + diff --git a/pcdl/commandline.py b/pcdl/pyCLI.py similarity index 87% rename from pcdl/commandline.py rename to pcdl/pyCLI.py index f77ec5e..2dd4f0c 100644 --- a/pcdl/commandline.py +++ b/pcdl/pyCLI.py @@ -1,5 +1,5 @@ ### -# title: commandline.py +# title: pyCLI.py # # language: python3 # date: 2024-02-21 @@ -7,7 +7,7 @@ # author: Elmar Bucher # # description: -# commandline.py provides command line interface commands for appropriate pcdl functions. +# pyCLI.py provides command line interface commands for appropriate pcdl functions. # all clis mirror the related python function interface as close as possible. # i like to thank Miguel Ponce-de-Leon for making me aware of the # entry point implementation technic which makes all of this possible. @@ -23,10 +23,8 @@ import numpy as np import os import pandas as pd -import pathlib import pcdl from scipy import stats -import subprocess import sys @@ -83,10 +81,10 @@ def get_version(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_version : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_version : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', #custom_data_type, @@ -123,7 +121,7 @@ def get_unit_dict(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.', + help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.', ) # TimeSeries graph False # TimeSeries physiboss False @@ -156,10 +154,10 @@ def get_unit_dict(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_unit_dict : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_unit_dict : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', #custom_data_type, @@ -228,10 +226,10 @@ def get_substrate_list(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_substrate_list : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_substrate_list : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', #custom_data_type, @@ -318,10 +316,10 @@ def get_conc_attribute(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_conc_attribute : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.pyCLI.get_conc_attribute : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, #custom_data_type, load = True, @@ -341,8 +339,7 @@ def get_conc_attribute(): keep = set(args.keep), allvalues = b_allvalues, ) - s_ofile = f"timeseries_conc_attribute_{s_values.replace(' ','_')}.json" - s_opathfile = s_path + '/' + s_ofile + s_opathfile = f'{s_path}/timeseries_conc_attribute_{s_values}.json' json.dump(dl_variable, open(s_opathfile, 'w'), sort_keys=True) # going home print(s_opathfile) @@ -421,11 +418,11 @@ def get_conc_df(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_conc_df : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_conc_df : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', #custom_data_type, @@ -446,7 +443,7 @@ def get_conc_df(): print(s_opathfile) else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, #custom_data_type, load = True, @@ -474,7 +471,6 @@ def get_conc_df(): for i, df_conc in enumerate(ldf_conc): df_conc.to_csv(ls_opathfile[i]) print(ls_opathfile) - # going home return 0 @@ -585,6 +581,12 @@ def plot_contour(): default = ['none'], help = 'size of the figure in pixels (integer), x y. the given x and y will be rounded to the nearest even number, to be able to generate movies from the images. None tries to take the values from the initial.svg file. fall back setting is 640 480. default is None.', ) + # plot_contour directory + parser.add_argument( + '--directory', + default = 'none', + help = 'if none, a meaningful output directory name will be generated, based on focus and z_slice parameters, else the resulting plots will be moved to the explicit name directory.', + ) # plot_contour ext parser.add_argument( '--ext', @@ -614,15 +616,15 @@ def plot_contour(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_plot_contour : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.plot_contour : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # focus if (args.focus == None): - sys.exit(f'Error @ pcdl_plot_contour : input for positional argument focus is missung! this has to be a column name from the conc dataframe.') + sys.exit(f'Error @ pyCLI.plot_contour : input for positional argument focus is missung! this has to be a column name from the conc dataframe.') # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', #custom_data_type, @@ -658,6 +660,7 @@ def plot_contour(): xyequal = False if args.xyequal.lower().startswith('f') else True, ax = None, figsizepx = None if (args.figsizepx[0].lower() == 'none') else [int(n) for n in args.figsizepx], + directory = None if (args.directory.lower() == 'none') else args.directory, ext = args.ext, figbgcolor = None if (args.figbgcolor.lower() == 'none') else args.figbgcolor, ) @@ -665,7 +668,7 @@ def plot_contour(): print(s_opathfile) else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, #custom_data_type, load = True, @@ -689,13 +692,13 @@ def plot_contour(): ylim = None if (args.ylim[0].lower() == 'none') else args.ylim, xyequal = False if args.xyequal.lower().startswith('f') else True, figsizepx = None if (args.figsizepx[0].lower() == 'none') else [int(n) for n in args.figsizepx], + directory = None if (args.directory.lower() == 'none') else args.directory, ext = args.ext, figbgcolor = None if (args.figbgcolor.lower() == 'none') else args.figbgcolor, ) # going home s_opath = '/'.join(ls_opathfile[0].split('/')[:-1]) print(s_opath) - # going home return 0 @@ -744,11 +747,11 @@ def make_conc_vtk(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_make_conc_vtk : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.make_conc_vtk : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', custom_data_type = {}, @@ -758,12 +761,14 @@ def make_conc_vtk(): settingxml = None, verbose = False if args.verbose.lower().startswith('f') else True ) - s_opathfile = mcds.make_conc_vtk() + s_opathfile = mcds.make_conc_vtk( + visualize = False, + ) # going home print(s_opathfile) else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, custom_data_type = {}, load = True, @@ -773,13 +778,15 @@ def make_conc_vtk(): settingxml = None, verbose = False if args.verbose.lower().startswith('f') else True, ) - ls_opathfile = mcdsts.make_conc_vtk() + ls_opathfile = mcdsts.make_conc_vtk( + visualize = False, + ) # going home print(ls_opathfile) - # going home return 0 + ############################################ # cell agent relatd command line functions # ############################################ @@ -833,10 +840,10 @@ def get_celltype_list(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_celltype_list : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_celltype_list : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', #custom_data_type, @@ -926,6 +933,7 @@ def get_cell_attribute_list(): # going home print(mcds.get_cell_attribute_list()) + return 0 def get_cell_attribute(): @@ -955,7 +963,7 @@ def get_cell_attribute(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.', + help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.', ) # TimeSeries graph False # TimeSeries physiboss @@ -1021,7 +1029,7 @@ def get_cell_attribute(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_cell_attribute : {s_pathfile} path does not look like a physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_cell_attribute : {s_pathfile} path does not look like a physicell output directory ({s_path}/initial.xml is missing).') # custom_data_type d_vartype = {} @@ -1032,12 +1040,12 @@ def get_cell_attribute(): elif s_type in {'float'}: o_type = float elif s_type in {'str'}: o_type = str else: - sys.exit(f'Error @ pcdl_get_cell_attribute : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') + sys.exit(f'Error @ pyCLI.get_cell_attribute : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') d_vartype.update({s_var : o_type}) # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', custom_data_type = d_vartype, @@ -1052,10 +1060,10 @@ def get_cell_attribute(): b_allvalues = True if args.allvalues.lower().startswith('t') else False if b_allvalues: s_values = 'all' - s_opathfile = f"{s_pathfile.replace('.xml','')}_{s_values.replace(' ','_')}.json" + s_opathfile = f"{s_pathfile.replace('.xml','')}_{s_values}.json" else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, custom_data_type = d_vartype, load = True, @@ -1070,8 +1078,7 @@ def get_cell_attribute(): b_allvalues = True if args.allvalues.lower().startswith('t') else False if b_allvalues: s_values = 'all' - s_ofile = f"timeseries_cell_attribute_{s_values.replace(' ','_')}.json" - s_opathfile = s_path + '/' + s_ofile + s_opathfile = f'{s_path}/timeseries_cell_attribute_{s_values}.json' # going home dl_variable = mcdsts.get_cell_attribute( @@ -1106,7 +1113,7 @@ def get_cell_df(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.' + help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.' ) # TimeSeries graph False # TimeSeries physiboss @@ -1172,11 +1179,11 @@ def get_cell_df(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_cell_df : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_cell_df : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', #custom_data_type, @@ -1197,7 +1204,7 @@ def get_cell_df(): print(s_opathfile) else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, #custom_data_type, load = True, @@ -1225,7 +1232,6 @@ def get_cell_df(): for i, df_cell in enumerate(ldf_cell): df_cell.to_csv(ls_opathfile[i]) print(ls_opathfile) - # going home return 0 @@ -1257,7 +1263,7 @@ def get_anndata(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment be extracted and loaded into the anndata object? setting microenv to False will use less memory and speed up processing. default is True.' + help = 'should the microenvironment be extracted and loaded into the anndata object? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.' ) # TimeSeries graph parser.add_argument( @@ -1334,7 +1340,7 @@ def get_anndata(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_get_anndata : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.get_anndata : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # custom_data_type d_vartype = {} @@ -1345,7 +1351,7 @@ def get_anndata(): elif s_type in {'float'}: o_type = float elif s_type in {'str'}: o_type = str else: - sys.exit(f'Error @ pcdl_get_anndata : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') + sys.exit(f'Error @ pyCLI.get_anndata : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') d_vartype.update({s_var : o_type}) # run @@ -1393,15 +1399,14 @@ def get_anndata(): ) # going home if b_collapse : - s_opathfile = f'{s_path}/timeseries_cell_{args.scale}.h5ad' + s_opathfile = f'{s_path}/timeseries_cell_{args.scale.lower()}.h5ad' ann_mcdsts.write_h5ad(s_opathfile) print(s_opathfile) else: - ls_opathfile = [f"{s_path}/{s_xmlfile.replace('.xml', '_cell_{}.h5ad'.format(args.scale))}" for s_xmlfile in mcdsts.get_xmlfile_list()] + ls_opathfile = [f"{s_path}/{s_xmlfile.replace('.xml', '_cell_{}.h5ad'.format(args.scale.lower()))}" for s_xmlfile in mcdsts.get_xmlfile_list()] for i, ann_mcds in enumerate(ann_mcdsts): ann_mcds.write_h5ad(ls_opathfile[i]) print(ls_opathfile) - # going home return 0 @@ -1433,7 +1438,7 @@ def make_graph_gml(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.' + help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.' ) # TimeSeries graph True # TimeSeries physiboss @@ -1490,7 +1495,7 @@ def make_graph_gml(): else: s_path = '/'.join(s_path.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_make_graph_gml : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.make_graph_gml : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # custom_data_type d_vartype = {} @@ -1501,12 +1506,12 @@ def make_graph_gml(): elif s_type in {'float'}: o_type = float elif s_type in {'str'}: o_type = str else: - sys.exit(f'Error @ pcdl_make_graph_gml : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') + sys.exit(f'Error @ pyCLI.make_graph_gml : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') d_vartype.update({s_var : o_type}) # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', custom_data_type = d_vartype, @@ -1525,7 +1530,7 @@ def make_graph_gml(): print(s_opathfile) else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, custom_data_type = d_vartype, load = True, @@ -1542,11 +1547,9 @@ def make_graph_gml(): ) # going home print(ls_opathfile) - # going home return 0 - def plot_scatter(): # argv parser = argparse.ArgumentParser( @@ -1574,7 +1577,7 @@ def plot_scatter(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.', + help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.', ) # TimeSeries graph False # TimeSeries physiboss @@ -1682,6 +1685,12 @@ def plot_scatter(): default = ['none'], help = 'size of the figure in pixels (integer), x y. the given x and y will be rounded to the nearest even number, to be able to generate movies from the images. None tries to take the values from the initial.svg file. fall back setting is 640 480. default is None.', ) + # plot_scatter directory + parser.add_argument( + '--directory', + default = 'none', + help = 'if none, a meaningful output directory name will be generated, based on focus and z_slice parameters, else the resulting plots will be moved to the explicit name directory.', + ) # plot_scatter ext parser.add_argument( '--ext', @@ -1711,7 +1720,7 @@ def plot_scatter(): else: s_path = '/'.join(s_pathfile.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_plot_scatter : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.plot_scatter : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # custom_data_type d_vartype = {} @@ -1722,12 +1731,12 @@ def plot_scatter(): elif s_type in {'float'}: o_type = float elif s_type in {'str'}: o_type = str else: - sys.exit(f'Error @ pcdl_plot_scatter : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') + sys.exit(f'Error @ pyCLI.plot_scatter : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') d_vartype.update({s_var : o_type}) # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', custom_data_type = d_vartype, @@ -1753,6 +1762,7 @@ def plot_scatter(): s = args.s, ax = None, figsizepx = None if (args.figsizepx[0].lower() == 'none') else [int(i) for i in args.figsizepx], + directory = None if (args.directory.lower() == 'none') else args.directory, ext = args.ext, figbgcolor = None if (args.figbgcolor.lower() == 'none') else args.figbgcolor, ) @@ -1760,7 +1770,7 @@ def plot_scatter(): print(s_opathfile) else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, custom_data_type = d_vartype, load = True, @@ -1785,13 +1795,13 @@ def plot_scatter(): xyequal = False if args.xyequal.lower().startswith('f') else True, s = args.s, figsizepx = None if (args.figsizepx[0].lower() == 'none') else [int(i) for i in args.figsizepx], + directory = None if (args.directory.lower() == 'none') else args.directory, ext = args.ext, figbgcolor = None if (args.figbgcolor.lower() == 'none') else args.figbgcolor, ) # going home - s_opath = '/'.join(ls_opathfile[0].split('/')[:-1]) - print(s_opath) - + s_opathfile = '/'.join(ls_opathfile[0].split('/')[:-1]) + print(s_opathfile) # going home return 0 @@ -1823,7 +1833,7 @@ def make_cell_vtk(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.', + help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.', ) # TimeSeries graph False # TimeSeries physiboss @@ -1868,7 +1878,7 @@ def make_cell_vtk(): else: s_path = '/'.join(s_pathfile.split('/')[:-1]) if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_make_cell_vtk : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.make_cell_vtk : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') # custom_data_type d_vartype = {} @@ -1879,12 +1889,12 @@ def make_cell_vtk(): elif s_type in {'float'}: o_type = float elif s_type in {'str'}: o_type = str else: - sys.exit(f'Error @ pcdl_make_cell_vtk : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') + sys.exit(f'Error @ pyCLI.make_cell_vtk : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') d_vartype.update({s_var : o_type}) # run if os.path.isfile(args.path): - mcds = pcdl.TimeStep( + mcds = pcdl.pyMCDS( xmlfile = s_pathfile, output_path = '.', custom_data_type = d_vartype, @@ -1896,12 +1906,13 @@ def make_cell_vtk(): ) s_opathfile = mcds.make_cell_vtk( attribute = args.attribute, + visualize = False, ) # going home print(s_opathfile) else: - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = s_path, custom_data_type = d_vartype, load = True, @@ -1913,10 +1924,10 @@ def make_cell_vtk(): ) ls_opathfile = mcdsts.make_cell_vtk( attribute = args.attribute, + visualize = False, ) # going home print(ls_opathfile) - # going home return 0 @@ -1951,7 +1962,7 @@ def plot_timeseries(): parser.add_argument( '--microenv', default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.', + help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing, similar to the original pyMCDS_cells.py script. default is True.', ) # TimeSeries graph # nop @@ -2132,7 +2143,7 @@ def plot_timeseries(): # path if not os.path.exists(args.path + '/initial.xml'): - sys.exit(f'Error @ pcdl_plot_timeseries : path does not look like a physicell output directory ({args.path}/initial.xml is missing).') + sys.exit(f'Error @ pyCLI.plot_timeseries : path does not look like a physicell output directory ({args.path}/initial.xml is missing).') # custom_data_type d_vartype = {} @@ -2143,7 +2154,7 @@ def plot_timeseries(): elif s_type in {'float'}: o_type = float elif s_type in {'str'}: o_type = str else: - sys.exit(f'Error @ pcdl_plot_timeseries : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') + sys.exit(f'Error @ pyCLI.plot_timeseries : {s_var} {s_type} has an unknowen data type. knowen are bool, int, float, str.') d_vartype.update({s_var : o_type}) # aggregate_num @@ -2154,7 +2165,7 @@ def plot_timeseries(): elif (args.aggregate_num == 'min'): o_aggregate_num = np.nanmin elif (args.aggregate_num == 'std'): o_aggregate_num = np.nanstd elif (args.aggregate_num == 'var'): o_aggregate_num = np.nanvar - else: sys.exit(f'Error @ pcdl_plot_timeseries : unknowen aggregate_num {args.aggregate_num}. knowen are entropy, max, mean, median, min, std, var.') + else: sys.exit(f'Error @ pyCLI.plot_timeseries : unknowen aggregate_num {args.aggregate_num}. knowen are entropy, max, mean, median, min, std, var.') # secondary_y if (args.secondary_y[0].lower() == 'false'): ls_secondary_y = False @@ -2167,7 +2178,7 @@ def plot_timeseries(): else: b_legend = True # run - mcdsts = pcdl.TimeSeries( + mcdsts = pcdl.pyMCDSts( output_path = args.path, custom_data_type = d_vartype, load = True, @@ -2207,203 +2218,6 @@ def plot_timeseries(): return 0 -def make_ome_tiff(): - # argv - parser = argparse.ArgumentParser( - prog = 'pcdl_make_ome_tiff', - description = 'function to transform chosen mcdsts output into an 1[um] spaced tczyx (time, channel, z-axis, y-axis, x-axis) ome tiff file, one substrate or cell_type per channel. the ome tiff file format can for example be read by the napari (https://napari.org/stable/) or fiji imagej (https://fiji.sc/) software.', - epilog = 'homepage: https://github.com/elmbeech/physicelldataloader', - ) - - # TimeSeries path - parser.add_argument( - 'path', - nargs = '?', - default = '.', - help = 'path to the PhysiCell output directory or a outputnnnnnnnn.xml file. default is . .', - ) - # TimeSeries output_path '.' - # TimeSeries custom_data_type {} - # TimeSeries microenv - parser.add_argument( - '--microenv', - default = 'true', - help = 'should the microenvironment data be loaded? setting microenv to False will use less memory and speed up processing. default is True.' - ) - # TimeSeries graph False - # TimeSeries physiboss - parser.add_argument( - '--physiboss', - default = 'true', - help = 'if found, should physiboss state data be extracted and loaded into the df_cell dataframe? default is True.' - ) - # TimeSeries settingxml - parser.add_argument( - '--settingxml', - default = 'PhysiCell_settings.xml', - help = 'the settings.xml that is loaded, from which the cell type ID label mapping, is extracted, if this information is not found in the output xml file. set to None or False if the xml file is missing! default is PhysiCell_settings.xml.', - ) - # TimeSeries verbose - parser.add_argument( - '-v', '--verbose', - default = 'true', - help = 'setting verbose to False for less text output, while processing. default is True.', - ) - # make_ome_tiff cell_attribute - parser.add_argument( - 'cell_attribute', - nargs = '?', - default = 'ID', - help = 'mcds.get_cell_df dataframe column, used for cell_attribute. the column data type has to be numeric (bool, int, float) and cannot be string. the result will be stored as 32 bit float. default is ID, with will result in a segmentation mask.', - ) - # make_ome_tiff conc_cutoff - parser.add_argument( - '--conc_cutoff', - nargs = '*', - default = [], - help = 'if a contour from a substrate not should be cut by greater than zero (shifted to integer 1), another cutoff value can be specified here like this: substarte:value substrate:value substarte:value . default is and empty string.', - ) - # make_ome_tiff focus - parser.add_argument( - '--focus', - nargs = '+', - default = ['none'], - help = 'set of substrate and cell_type names to specify what will be translated into ome tiff format. if None, all substrates and cell types will be processed. default is a None.', - ) - # make_ome_tiff file True - # make_ome_tiff collapse - parser.add_argument( - '--collapse', - default = 'true', - help = 'should all mcds time steps from the time series be collapsed into one big ome.tiff, or a many ome.tiff, one ome.tiff for each time step?, default is True.' - ) - - # parse arguments - args = parser.parse_args() - print(args) - - # path - s_path = args.path - while (s_path.find('//') > -1): - s_path = s_path.replace('//','/') - if (s_path.endswith('/')) and (len(s_path) > 1): - s_path = s_path[:-1] - s_pathfile = s_path - if not s_pathfile.endswith('.xml'): - s_pathfile = s_pathfile + '/initial.xml' - else: - s_path = '/'.join(s_pathfile.split('/')[:-1]) - if not os.path.exists(s_pathfile): - sys.exit(f'Error @ pcdl_make_ome_tiff : {s_pathfile} path does not look like a outputnnnnnnnn.xml file or physicell output directory ({s_path}/initial.xml is missing).') - - # conc_cutoff - d_conccutoff = {} - for s_conccutoff in args.conc_cutoff: - s_substrate, s_value = s_conccutoff.split(':') - if (s_value.find('.') > -1): - o_value = float(s_value) - else: - o_value = int(s_value) - d_conccutoff.update({s_substrate : o_value}) - - # focus - if (args.focus[0].lower() == 'none'): - es_focus = None - else: - es_focus = set( args.focus) - - # run - if os.path.isfile(args.path): - mcds = pcdl.TimeStep( - xmlfile = s_pathfile, - output_path = '.', - custom_data_type = {}, - microenv = False if args.microenv.lower().startswith('f') else True, - graph = False, - physiboss = False if args.physiboss.lower().startswith('f') else True, - settingxml = None if ((args.settingxml.lower() == 'none') or (args.settingxml.lower() == 'false')) else args.settingxml, - verbose = False if args.verbose.lower().startswith('f') else True - ) - s_opathfile = mcds.make_ome_tiff( - cell_attribute = args.cell_attribute, - conc_cutoff = d_conccutoff, - focus = es_focus, - file = True, - ) - # going home - print(s_opathfile) - - else: - mcdsts = pcdl.TimeSeries( - output_path = s_path, - custom_data_type = {}, - load = True, - microenv = False if args.microenv.lower().startswith('f') else True, - graph = False, - physiboss = False if args.physiboss.lower().startswith('f') else True, - settingxml = None if ((args.settingxml.lower() == 'none') or (args.settingxml.lower() == 'false')) else args.settingxml, - verbose = False if args.verbose.lower().startswith('f') else True, - ) - o_opathfile = mcdsts.make_ome_tiff( - cell_attribute = args.cell_attribute, - conc_cutoff = d_conccutoff, - focus = es_focus, - file = True, - collapse = False if args.collapse.lower().startswith('f') else True, - ) - # going home - print(o_opathfile) - - # going home - return 0 - - -####################### -# render neuroglancer # -####################### - -def render_neuroglancer(): - # argv - parser = argparse.ArgumentParser( - prog = 'pcdl_render_neuroglancer', - description = 'function to load a time step from an ome tiff files, generated with make_ome_tiff, into neuroglancer.', - epilog = 'homepage: https://github.com/elmbeech/physicelldataloader', - ) - # ome tiff path file - parser.add_argument( - 'tiffpathfile', - nargs = '?', - default = '.', - help = 'path to ome tiff file.', - ) - # time step - parser.add_argument( - 'timestep', - nargs = '?', - default = 0, - type = int, - help = 'time step, within a possibly collapsed ome tiff file, to render. the default will work with single time step ome tiff files.', - ) - # intensity colormap - parser.add_argument( - '--intensity_cmap', - default = 'gray', - help = 'matlab color map label, used to display expression intensity values. if None, no intensity layers will be generated. https://matplotlib.org/stable/users/explain/colors/colormaps.html', - ) - - # parse arguments - args = parser.parse_args() - print(args) - - # process arguments - s_neuromancerpath = str(pathlib.Path(pcdl.__file__).parent).replace('\\','/') + '/' - s_tiffpathfile = args.tiffpathfile.replace('\\','/') - - # run - # bue 20250623: use subprocess to run python3 in interactive mode to run the neuromancer script, which is needed to keep the neuroglancer web gl server running. - subprocess.run(['python3', '-i', f'{s_neuromancerpath}neuromancer.py', s_tiffpathfile, '--timestep', str(args.timestep), '--intensity_cmap', args.intensity_cmap]) - - ################# # making movies # ################# diff --git a/pcdl/timestep.py b/pcdl/pyMCDS.py similarity index 68% rename from pcdl/timestep.py rename to pcdl/pyMCDS.py index c727261..4f14a0c 100644 --- a/pcdl/timestep.py +++ b/pcdl/pyMCDS.py @@ -1,5 +1,5 @@ ######### -# title: timestep.py +# title: pyMCDS.py # # language: python3 # date: 2022-08-22 @@ -7,32 +7,25 @@ # authors: Patrick Wall, Randy Heiland, Furkan Kurtoglu, Paul Macklin, Elmar Bucher # # description: -# timestep.py definds an object class, able to load and access +# pyMCDS.py definds an object class, able to load and access # within python a single time step from the PhysiCell model output folder. -# timestep.py was forked from the original PhysiCell-Tools python-loader +# pyMCDS.py was forked from the original PhysiCell-Tools python-loader # implementation and further developed. ######### # load library -import anndata as ad -import bioio_base -from bioio.writers import OmeTiffWriter import matplotlib.pyplot as plt from matplotlib import cm from matplotlib import colors -import neuroglancer +import matplotlib.patches as mpatches import numpy as np import os import pandas as pd -from pcdl import imagine -from pcdl import pdplt -from pcdl import neuromancer +import random from scipy import io -from scipy import sparse import sys import vtk -import warnings import xml.etree.ElementTree as etree from pcdl.VERSION import __version__ @@ -175,43 +168,73 @@ # functions -def render_neuroglancer(tiffpathfile, timestep=0, intensity_cmap='gray'): - """ +def df_label_to_color(df_abc, s_focus, es_label=None, s_nolabel='gray', s_cmap='viridis', b_shuffle=False): + ''' input: - tiffpathfile: string. - path to ome tiff file. + df_abc: dataframe to which the color column will be added. + s_focus: column name with sample labels for which a color column will be generated. + es_label: set of labels to color. if None, es_label will be extracted for the s_focus column. + s_nolabel: color for labels not defined in es_label. + s_cmap: matplotlib color map label. + https://matplotlib.org/stable/tutorials/colors/colormaps.html + b_shuffle: should colors be given by alphabetical order, + or should the label color mapping order be random. + + output: + df_abc: dataframe updated with color column. + ds_color: lable to hex color string mapping dictionary + + description: + function adds for the selected label column + a color column to the df_abc dataframe. + ''' + if (es_label is None): + es_label = set(df_abc.loc[:,s_focus]) + if b_shuffle: + ls_label = list(es_label) + random.shuffle(ls_label) + else: + ls_label = sorted(es_label) + a_color = plt.get_cmap(s_cmap)(np.linspace(0, 1, len(ls_label))) + do_color = dict(zip(ls_label, a_color)) + df_abc[f'{s_focus}_color'] = s_nolabel + ds_color = {} + for s_category, o_color in do_color.items(): + s_color = colors.to_hex(o_color) + ds_color.update({s_category : s_color}) + df_abc.loc[(df_abc.loc[:,s_focus] == s_category), f'{s_focus}_color'] = s_color + # output + return(ds_color) - timestep: integer, default is 0. - variable to specify the specific time step to render. - useful for time series ome.tiff files. - the default is compatible with single time step ome.tiff files. - intensity_cmap: string; default is 'gray'. - matlab color map label, used to display expression intensity values. - if None, no intensity layers will be generated. - + https://matplotlib.org/stable/users/explain/colors/colormaps.html +def ax_colorlegend(ax, ds_color, s_loc='lower left', s_fontsize='small'): + ''' + input: + ax: matplotlib axis object to which a color legend will be added. + ds_color: lables to color strings mapping dictionary + s_loc: the location of the legend. + possible strings are: best, + upper right, upper center, upper left, center left, + lower left, lower center, lower right, center right, + center. + s_fontsize: font size used for the legend. known are: + xx-small, x-small, small, medium, large, x-large, xx-large. output: - viewer: local url where the loaded, neuroglancer rendered ome tiff file - can be viewed. + ax: matplotlib axis object updated with color legend. description: - function to load a time step from an ome tiff files, generated - with make_ome_tiff, into neuroglancer. - """ - # start neuroglancer - viewer = neuroglancer.Viewer() - with viewer.txn() as state: - # render ometiff into neuroglancer - neuromancer.ometiff2neuro( - o_state = state, - s_pathfile_tiff = tiffpathfile, - i_timestep = timestep, - s_intensity_cmap = intensity_cmap, - ) - - # print neuroglancer viewer url - return viewer + function to add color legend to a figure. + ''' + lo_patch = [] + for s_label, s_color in sorted(ds_color.items()): + o_patch = mpatches.Patch(color=s_color, label=s_label) + lo_patch.append(o_patch) + ax.legend( + handles = lo_patch, + loc = s_loc, + fontsize = s_fontsize + ) def graphfile_parser(s_pathfile): @@ -244,239 +267,8 @@ def graphfile_parser(s_pathfile): return dei_graph -def scaler(df_x, scale='maxabs'): - """ - input: - df_x: pandas dataframe - one attribute per column, one sample per row. - - scale: string; default 'maxabs' - None: no scaling. set scale to None if you would like to have - raw data or scale, transform, and normalize the data later. - - maxabs: maximum absolute value distance scaler will linearly map - all values into a [-1, 1] interval. if the original data - has no negative values, the result will be the same as with - the minmax scaler (except with attributes with only one value). - if the attribute has only zeros, the value will be set to 0. - - minmax: minimum maximum distance scaler will map all values - linearly into a [0, 1] interval. - if the attribute has only one value, the value will be set to 0. - - std: standard deviation scaler will result in sigmas. - each attribute will be mean centered around 0. - ddof delta degree of freedom is set to 1 because it is assumed - that the values are samples out of the population - and not the entire population. it is incomprehensible to me - that the equivalent sklearn method has ddof set to 0. - if the attribute has only one value, the value will be set to 0. - - output: - df_x: pandas dataframe - scaled df_x dataframe. - - description: - inspired by scikit-learn's preprocessing scaling method, this function - offers a re-implementation of the linear re-scaling methods maxabs, - minmax, and scale. - - the robust scaler methods (quantile based) found in scikit-learn are - missing. since we deal with simulated data, we don't expect heavy - outliers, and if they exist, then they are of interest. - the power and quantile based transformation methods and unit circle - based normalizer methods found there are missing too. - if you need to apply any such methods, you can do so to an anndata object - like this: - - from sklearn import preprocessing - adata.obsm["X_scaled"] = preprocessing.scale(adata.X) - - + https://scikit-learn.org/stable/auto_examples/preprocessing/plot_all_scaling.html - + https://scikit-learn.org/stable/modules/classes.html#module-sklearn.preprocessing - + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.maxabs_scale.html - + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.minmax_scale.html - + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.scale.html - """ - if scale is None: - pass - # -1,1 - elif scale == 'maxabs': - a_x = df_x.values - warnings.filterwarnings('ignore', category=RuntimeWarning) - a_maxabs = a_x / abs(a_x).max(axis=0) - warnings.simplefilter('default') - a_maxabs[np.isnan(a_maxabs)] = 0 # fix if entier column is 0 - df_x = pd.DataFrame(a_maxabs, columns=df_x.columns, index=df_x.index) - # 0,1 - elif scale == 'minmax': - a_x = df_x.values - warnings.simplefilter("ignore") - warnings.filterwarnings('ignore', category=RuntimeWarning) - a_minmax = (a_x - a_x.min(axis=0)) / (a_x.max(axis=0) - a_x.min(axis=0)) - warnings.simplefilter('default') - a_minmax[np.isnan(a_minmax)] = 0 # fix if entier column has same value - df_x = pd.DataFrame(a_minmax, columns=df_x.columns, index=df_x.index) - # sigma - elif scale == 'std': - a_x = df_x.values - warnings.filterwarnings('ignore', category=RuntimeWarning) - a_std = (a_x - a_x.mean(axis=0)) / a_x.std(axis=0, ddof=1) - warnings.simplefilter('default') - a_std[np.isnan(a_std)] = 0 # fix if entier column has same value - df_x = pd.DataFrame(a_std, columns=df_x.columns, index=df_x.index) - else: - raise ValueError(f"Error @ scaler : unknown scale algorithm {scale} detected. known are [None, 'maxabs', 'minmax', 'std'].") - - return df_x - - -def _anndextract(df_cell, scale='maxabs', graph_attached={}, graph_neighbor={}, graph_spring={}, graph_method='PhysiCell'): - """ - input: - df_cell: pandas dataframe - data frame retrieved with the mcds.get_cell_df function. - - scale: string; default maxabs - specify how the data should be scaled. - possible values are None, maxabs, minmax, std. - for more input, check out: help(pcdl.scaler). - - graph_attached: dict; default {} - attached graph dictionary, retrieved with - with the mcds.get_attched_graph() function. - - graph_neighbor: dict; default {} - neighbor graph dictionary, retrieved - with the mcds.get_neighbor_graph() function. - - graph_spring: dict; default {} - spring_attached graph dictionary, retrieved - with the mcds.get_spring_graph_dict() function. - - graph_method: string; default PhysiCell - method how the graphs were generated. - - output: - df_count, df_obs, d_obsm, d_obsp, d_uns dataframes and dictionaries, - ready to be backed into an anndata object. - - description: - this function takes a pcdl df_cell pandas dataframe and re-formats - it into a set of two dataframes (df_count, df_obs), - two dictionary of numpy array (d_obsm, d_obsp), - and one dictionary of string (d_uns), - which downstream might be transformed into an anndata object. - """ - # transform index to string - df_coor = df_cell.loc[:,['position_x','position_y','position_z']].copy() - df_cell.index = df_cell.index.astype(str) - - # build obs anndata object (annotation of observations) - df_obs = df_cell.loc[:,['mesh_center_p','time']].copy() - df_obs.columns = ['z_layer', 'time'] - - # buil obsm anndata object spatial (multi-dimensional annotation of observations) - if (len(set(df_cell.position_z)) == 1): - df_obsm = df_cell.loc[:,['position_x','position_y']].copy() - else: - df_obsm = df_cell.loc[:,['position_x','position_y','position_z']].copy() - d_obsm = {"spatial": df_obsm.values} - - # build obsp and uns anndata object graph (pairwise annotation of obeservation) and (unstructured data) - #### - # acknowledgement: - # this code is inspired from the tysserand add_to_AnnData impelmentation - # from Alexis Coullomb form the Pancaldi Lab. - # https://github.com/VeraPancaldiLab/tysserand/blob/main/tysserand/tysserand.py#L1546 - #### - # extract cell_id to index mapping (i always loved perl) - di_ididx = df_cell.reset_index().loc[:,'ID'].reset_index().astype(int).set_index('ID').squeeze().to_dict() - # transform cell id graph dict to index matrix and pack for anndata - d_obsp = {} # pairwise annotation of obeservation - d_uns = {} # unstructured data - for s_graph, dei_graph in [('neighbor', graph_neighbor), ('attached', graph_attached), ('spring', graph_spring)]: - lli_edge = [] - lr_distance = [] - for i_src, ei_dst in dei_graph.items(): - for i_dst in ei_dst: - # extract edge - lli_edge.append([di_ididx[i_src], di_ididx[i_dst]]) - r_distance = ((df_coor.loc[i_src,:].values - df_coor.loc[i_dst,:].values)**2).sum()**(1/2) - lr_distance.append(r_distance) - # if there is a graph - if (len(lli_edge) > 0): - # handle edge data - ai_edge = np.array(lli_edge, dtype=np.uint) - # handle connection data - ai_conectivity = np.ones(ai_edge.shape[0], dtype=np.uint16) - ai_conectivity_sparse = sparse.csr_matrix( - (ai_conectivity, (ai_edge[:,0], ai_edge[:,1])), - shape = (df_cell.shape[0], df_cell.shape[0]), - dtype = np.uint - ) - # handle distance data - ar_distance = np.array(lr_distance, dtype=np.float64) - ar_distance_sparse = sparse.csr_matrix( - (ar_distance, (ai_edge[:,0], ai_edge[:,1])), - shape = (df_cell.shape[0], df_cell.shape[0]), - dtype = np.float64 - ) - # pack obsp - d_obsp.update({ - f'physicell_{s_graph}_conectivities': ai_conectivity_sparse, - f'physicell_{s_graph}_distances': ar_distance_sparse, - }) - # pack uns - d_uns.update({ - s_graph : { - 'connectivities_key': f'physicell_{s_graph}_conectivities', - 'distances_key': f'physicell_{s_graph}_distances', - 'params': { - 'metric': 'euclidean', - 'method': graph_method, - } - } - }) - - # extract discrete cell data - es_drop = set(df_cell.columns).intersection({ - 'voxel_i', 'voxel_j', 'voxel_k', - 'mesh_center_m', 'mesh_center_n', 'mesh_center_p', - 'position_x', 'position_y','position_z', - 'time', 'runtime', 'xmlfile', - }) - df_cell.drop(es_drop, axis=1, inplace=True) # maybe obs? - - # dectect variable types - des_type = {'float': set(), 'int': set(), 'bool': set(), 'str': set()} - for _, se_cell in df_cell.items(): - if str(se_cell.dtype).startswith('float'): - des_type['float'].add(se_cell.name) - elif str(se_cell.dtype).startswith('int'): - des_type['int'].add(se_cell.name) - elif str(se_cell.dtype).startswith('bool'): - des_type['bool'].add(se_cell.name) - elif str(se_cell.dtype).startswith('object'): - des_type['str'].add(se_cell.name) - else: - print(f'Error @ TimeSeries.get_anndata : column {se_cell.name} detected with unknown dtype {str(se_cell.dtype)}.') - - # build on obs and X anndata object - df_cat = df_cell.loc[:,sorted(des_type['str'])].copy() - df_obs = pd.merge(df_obs, df_cat, left_index=True, right_index=True) - es_num = des_type['float'].union(des_type['int'].union(des_type['bool'])) - df_count = df_cell.loc[:,sorted(es_num)].copy() - for s_col in des_type['bool']: - df_count[s_col] = df_count[s_col].astype(int) - df_count = scaler(df_count, scale=scale) - - # return - return(df_count, df_obs, d_obsm, d_obsp, d_uns) - - # object classes -class TimeStep: +class pyMCDS: def __init__(self, xmlfile, output_path='.', custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True): """ input: @@ -496,10 +288,11 @@ def __init__(self, xmlfile, output_path='.', custom_data_type={}, microenv=True, microenv: boole; default True should the microenvironment data be loaded? - setting microenv to False will use less memory and speed up processing. + setting microenv to False will use less memory and speed up + processing, similar to the original pyMCDS_cells.py script. graph: boole; default True - should the graphs, like cell_neighbor_graph.txt, be loaded? + should the graphs be loaded? setting graph to False will use less memory and speed up processing. physiboss: boole; default True @@ -516,11 +309,11 @@ def __init__(self, xmlfile, output_path='.', custom_data_type={}, microenv=True, setting verbose to False for less text output, while processing. output: - mcds: TimeStep class instance + mcds: pyMCDS class instance all fetched content is stored at mcds.data. description: - TimeStep.__init__ will generate a class instance with a + pyMCDS.__init__ will generate a class instance with a dictionary of dictionaries data structure that contains all output from a single PhysiCell model time step. furthermore, this class, and as such it's instances, offers functions @@ -541,7 +334,6 @@ def __init__(self, xmlfile, output_path='.', custom_data_type={}, microenv=True, self.verbose = verbose self.data = self._read_xml(xmlfile, output_path) - def set_verbose_false(self): """ input: @@ -555,7 +347,6 @@ def set_verbose_false(self): self.verbose = False #print(f'pcdl: set mcds.verbose = False.') - def set_verbose_true(self): """ input: @@ -662,7 +453,35 @@ def get_unit_dict(self): function returns a dictionary that stores all tracked variables and their units. """ - return self.data['metadata']['ds_unit'].copy() + # extract data + ds_unit = {} + # units for metadata parameters + ds_unit.update({'time': self.data['metadata']['time_units']}) + ds_unit.update({'runtime': self.data['metadata']['runtime_units']}) + ds_unit.update({'spatial_unit': self.data['metadata']['spatial_units']}) + + # microenvironment + if self.microenv: + for s_substrate in self.get_substrate_list(): + # unit from substrate parameters + s_unit = self.data['continuum_variables'][s_substrate]['units'] + ds_unit.update({s_substrate: s_unit}) + + # units from microenvironment parameters + s_diffusion_key = f'{s_substrate}_diffusion_coefficient' + s_diffusion_unit = self.data['continuum_variables'][s_substrate]['diffusion_coefficient']['units'] + ds_unit.update({s_diffusion_key: s_diffusion_unit}) + + s_decay_key = f'{s_substrate}_decay_rate' + s_decay_unit = self.data['continuum_variables'][s_substrate]['decay_rate']['units'] + ds_unit.update({s_decay_key: s_decay_unit}) + + # units from cell parameters + ds_unit.update(self.data['discrete_cells']['units']) + + # output + del ds_unit['ID'] + return ds_unit ## MESH RELATED FUNCTIONS ## @@ -813,7 +632,26 @@ def get_mesh_spacing(self): function returns the distance in between mesh centers, in the spacial unit defined in the PhysiCell_settings.xml file. """ - return self.data['mesh']['mnp_spacing'].copy() + tr_m_range, tr_n_range, tr_p_range = self.get_mesh_mnp_range() + ar_m_axis, ar_n_axis, ar_p_axis = self.get_mesh_mnp_axis() + + # m axis + if (len(set(tr_m_range)) == 1): + dm = np.float64(1.0) + else: + dm = (tr_m_range[1] - tr_m_range[0]) / (ar_m_axis.shape[0] - 1) + # n axis + if (len(set(tr_n_range)) == 1): + dn = np.float64(1.0) + else: + dn = (tr_n_range[1] - tr_n_range[0]) / (ar_n_axis.shape[0] - 1) + # p axis + if (len(set(tr_p_range)) == 1): + dp = np.float64(1.0) + else: + dp = (tr_p_range[1] - tr_p_range[0]) / (ar_p_axis.shape[0] - 1) + + return [dm, dn, dp] def is_in_mesh(self, x, y, z, halt=False): @@ -849,15 +687,15 @@ def is_in_mesh(self, x, y, z, halt=False): if (x < tr_x[0]) or (x > tr_x[1]): if self.verbose: - print(f'Warning @ TimeStep.is_in_mesh : x = {x} out of bounds: x-range is {tr_x}.') + print(f'Warning @ pyMCDS.is_in_mesh : x = {x} out of bounds: x-range is {tr_x}.') b_isinmesh = False elif (y < tr_y[0]) or (y > tr_y[1]): if self.verbose: - print(f'Warning @ TimeStep.is_in_mesh : y = {y} out of bounds: y-range is {tr_y}.') + print(f'Warning @ pyMCDS.is_in_mesh : y = {y} out of bounds: y-range is {tr_y}.') b_isinmesh = False elif (z < tr_z[0]) or (z > tr_z[1]): if self.verbose: - print(f'Warning @ TimeStep.is_in_mesh : z = {z} out of bounds: z-range is {tr_z}.') + print(f'Warning @ pyMCDS.is_in_mesh : z = {z} out of bounds: z-range is {tr_z}.') b_isinmesh = False # output @@ -919,7 +757,10 @@ def get_voxel_spacing(self): function returns the voxel width, height, depth measurement, in the spacial unit defined in the PhysiCell_settings.xml file. """ - return self.data['mesh']['mnp_spacing'].copy() + r_volume = self.get_voxel_volume() + dm, dn, _ = self.get_mesh_spacing() + dp = r_volume / (dm * dn) + return [dm, dn, dp] def get_voxel_volume(self): @@ -935,7 +776,11 @@ def get_voxel_volume(self): function returns the volume value for a single voxel, related to the spacial unit defined in the PhysiCell_settings.xml file. """ - return self.data['mesh']['volume'] + ar_volume = np.unique(self.data['mesh']['volumes']) + if ar_volume.shape != (1,): + sys.exit(f'Error @ pyMCDS.get_voxel_volume : mesh is not built out of a unique voxel volume {ar_volume}.') + r_volume = ar_volume[0] + return r_volume def get_voxel_ijk(self, x, y, z, is_in_mesh=True): @@ -996,7 +841,10 @@ def get_substrate_list(self): function returns all chemical species names, modeled in the microenvironment, ordered by chemical species ID. """ - return self.data['substrate']['ls_substarte'].copy() + # get substrate listing + ds_substrate = self.get_substrate_dict() + ls_substrate = [ds_substrate[s_key] for s_key in sorted(ds_substrate, key=int)] + return ls_substrate def get_substrate_dict(self): @@ -1012,7 +860,7 @@ def get_substrate_dict(self): microenvironment_setup variables, specified in the PhysiCell_settings.xml file. """ - return self.data['substrate']['ds_substrate'].copy() + return self.data['metadata']['substrate'] def get_substrate_df(self): @@ -1028,7 +876,114 @@ def get_substrate_df(self): function returns a dataframe with each substrate's decay_rate and difusion_coefficient. """ - return self.data['substrate']['df_substarte'].copy() + # extract data + ls_column = ['substrate','decay_rate','diffusion_coefficient'] + ll_sub = [] + for s_substrate in self.get_substrate_list(): + s_decay_value = self.data['continuum_variables'][s_substrate]['decay_rate']['value'] + s_diffusion_value = self.data['continuum_variables'][s_substrate]['diffusion_coefficient']['value'] + ll_sub.append([s_substrate, s_decay_value, s_diffusion_value]) + + # generate dataframe + df_substrate = pd.DataFrame(ll_sub, columns=ls_column) + df_substrate.set_index('substrate', inplace=True) + df_substrate.columns.name = 'attribute' + + # output + return df_substrate + + + def get_concentration(self, substrate, z_slice=None, halt=False): + """ + input: + substrate: string + substrate name. + + z_slice: floating point number; default is None + z-axis position to slice a 2D xy-plain out of the + 3D substrate concentration mesh. if None the + whole 3D mesh will be returned. + + halt: boolean; default is False + should program execution break or just spit out a warning, + if z_slice position is not an exact mesh center coordinate? + if False, z_slice will be adjusted to the nearest + mesh center value, the smaller one, if the coordinate + lies on a saddle point. + + output: + ar_conc: numpy array of floating point numbers + substrate concentration meshgrid or xy-plain slice + through the meshgrid. + + description: + function returns the concentration meshgrid, or a xy-plain slice + out of the whole meshgrid, for the specified chemical species. + """ + ar_conc = self.data['continuum_variables'][substrate]['data'].copy() + + # check if z_slice is a mesh center or None + if not (z_slice is None): + _, _, ar_p_axis = self.get_mesh_mnp_axis() + if not (z_slice in ar_p_axis): + if self.verbose: + print(f'Warning @ pyMCDS.get_concentration : specified z_slice {z_slice} is not an element of the z-axis mesh centers set {ar_p_axis}.') + if halt: + sys.exit('Processing stopped!') + else: + z_slice = ar_p_axis[abs(ar_p_axis - z_slice).argmin()] + print(f'z_slice set to {z_slice}.') + + # filter by z_slice + _, _, ar_p_grid = self.get_mesh() + mask = ar_p_grid == z_slice + ar_conc = ar_conc[mask].reshape((ar_p_grid.shape[0], ar_p_grid.shape[1])) + + # output + return ar_conc + + + def get_concentration_at(self, x, y, z=0): + """ + input: + x: floating point number + position x-coordinate. + + y: floating point number + position y-coordinate. + + z: floating point number; default is 0 + position z-coordinate. + + output: + ar_concs: numpy array of floating point numbers + array of substrate concentrations in the order + given by get_substrate_list(). + + description: + function return concentrations of each chemical species + inside a particular voxel that contains the point specified + in the arguments. + """ + ar_concs = None + + # is coordinate inside the domain? + b_calc = self.is_in_mesh(x=x, y=y, z=z, halt=False) + if b_calc: + + # get voxel coordinate and substrate names + i, j, k = self.get_voxel_ijk(x, y, z, is_in_mesh=False) + ls_substrate = self.get_substrate_list() + ar_concs = np.zeros(len(ls_substrate)) + + # get substrate concentrations + for n, s_substrate in enumerate(ls_substrate): + ar_concs[n] = self.get_concentration(s_substrate)[j, i, k] + if self.verbose: + print(f'pyMCD.get_concentration_at(x={x},y={y},z={z}) | jkl: [{i},{j},{k}] | substrate: {s_substrate} {ar_concs[n]}') + + # output + return ar_concs def get_conc_df(self, z_slice=None, halt=False, values=1, drop=set(), keep=set()): @@ -1075,22 +1030,55 @@ def get_conc_df(self, z_slice=None, halt=False, values=1, drop=set(), keep=set() """ # check keep and drop if (len(keep) > 0) and (len(drop) > 0): - sys.exit(f"Error @ TimeStep.get_conc_df : when keep is given {keep}, then drop has to be an empty set {drop}!") + sys.exit(f"Error @ pyMCDS.get_conc_df : when keep is given {keep}, then drop has to be an empty set {drop}!") # check if z_slice is a mesh center or None if not (z_slice is None): _, _, ar_p_axis = self.get_mesh_mnp_axis() if not (z_slice in ar_p_axis): if self.verbose: - print(f'Warning @ TimeStep.get_conc_df : specified z_slice {z_slice} is not an element of the z-axis mesh centers set {ar_p_axis}.') + print(f'Warning @ pyMCDS.get_conc_df : specified z_slice {z_slice} is not an element of the z-axis mesh centers set {ar_p_axis}.') if halt: sys.exit('Processing stopped!') else: z_slice = ar_p_axis[abs(ar_p_axis - z_slice).argmin()] print(f'z_slice set to {z_slice}.') - # fetch dataframe - df_conc = self.data['substrate']['df_conc'].copy() + # flatten mesh coordnates + ar_m, ar_n, ar_p = self.get_mesh() + ar_m = ar_m.flatten(order='C') + ar_n = ar_n.flatten(order='C') + ar_p = ar_p.flatten(order='C') + + # get mesh spacing + dm, dn, dp = self.get_voxel_spacing() + + # get voxel coordinates + ai_i = ((ar_m - ar_m.min()) / dm) + ai_j = ((ar_n - ar_n.min()) / dn) + ai_k = ((ar_p - ar_p.min()) / dp) + + # handle coordinates + ls_column = [ + 'voxel_i','voxel_j','voxel_k', + 'mesh_center_m','mesh_center_n','mesh_center_p' + ] + la_data = [ai_i, ai_j, ai_k, ar_m, ar_n, ar_p] + + # handle concentrations + for s_substrate in self.get_substrate_list(): + ls_column.append(s_substrate) + ar_conc = self.get_concentration(substrate=s_substrate, z_slice=None) + la_data.append(ar_conc.flatten(order='C')) + + # generate dataframe + aa_data = np.array(la_data) + df_conc = pd.DataFrame(aa_data.T, columns=ls_column) + df_conc['time'] = self.get_time() + df_conc['runtime'] = self.get_runtime() / 60 # in min + df_conc['xmlfile'] = self.xmlfile + d_dtype = {'voxel_i': int, 'voxel_j': int, 'voxel_k': int} + df_conc = df_conc.astype(d_dtype) # filter z_slice if not (z_slice is None): @@ -1118,7 +1106,7 @@ def get_conc_df(self, z_slice=None, halt=False, values=1, drop=set(), keep=set() return df_conc - def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=True, cmap='viridis', title=None, grid=True, xlim=None, ylim=None, xyequal=True, ax=None, figsizepx=None, ext=None, figbgcolor=None, **kwargs): + def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=True, cmap='viridis', title=None, grid=True, xlim=None, ylim=None, xyequal=True, ax=None, figsizepx=None, directory=None, ext=None, figbgcolor=None): """ input: focus: string @@ -1180,6 +1168,11 @@ def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=T None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is None output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -1188,11 +1181,6 @@ def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=T or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the matplotlib contour and contourf function. - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contour.html - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html - output: fig: matplotlib figure, depending on ext, either as object or as file. the figure containing the contour plot and color bar. @@ -1213,7 +1201,7 @@ def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=T figsizepx = [i_width, i_height] except FileNotFoundError: if self.verbose: - print(f'Warning @ TimeStep.plot_contour : could not load {s_pathfile} to auto detect figsizepx. take default.') + print(f'Warning @ pyMCDS.plot_contour : could not load {s_pathfile} to auto detect figsizepx. take default.') figsizepx = [640, 480] # handle figure size @@ -1274,9 +1262,9 @@ def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=T # get contour plot if fill: - ax.contourf(df_mesh.columns, df_mesh.index, df_mesh.values, vmin=vmin, vmax=vmax, alpha=alpha, cmap=cmap, **kwargs) + ax.contourf(df_mesh.columns, df_mesh.index, df_mesh.values, vmin=vmin, vmax=vmax, alpha=alpha, cmap=cmap) else: - ax.contour(df_mesh.columns, df_mesh.index, df_mesh.values, vmin=vmin, vmax=vmax, alpha=alpha, cmap=cmap, **kwargs) + ax.contour(df_mesh.columns, df_mesh.index, df_mesh.values, vmin=vmin, vmax=vmax, alpha=alpha, cmap=cmap) # set title if not (title is None): @@ -1305,9 +1293,12 @@ def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=T else: # handle output path and filename - s_path = self.path + f"/conc_{focus.replace(' ','_')}_z{round(z_slice,9)}/" + if (directory is None): + s_path = self.path + f'/conc_{focus}_z{round(z_slice,9)}/' + else: + s_path = f'{directory}/' os.makedirs(s_path, exist_ok=True) - s_file = self.xmlfile.replace('.xml', f"_{focus.replace(' ','_')}.{ext}") + s_file = self.xmlfile.replace('.xml', f'_{focus}.{ext}') s_pathfile = f'{s_path}{s_file}' # handle figure background color if figbgcolor is None: @@ -1320,9 +1311,11 @@ def plot_contour(self, focus, z_slice=0.0, vmin=None, vmax=None, alpha=1, fill=T return s_pathfile - def make_conc_vtk(self): + def make_conc_vtk(self, visualize=True): """ input: + visualize: boolean; default is True + additionally, visualize cells using vtk renderer. output: s_vtkpathfile: vtk rectilinear grid file that contains @@ -1393,13 +1386,118 @@ def make_conc_vtk(self): (df_conc.loc[:,'voxel_k'] == k) & (df_conc.loc[:,'voxel_j'] == j) & (df_conc.loc[:,'voxel_i'] == i), s_substrate ].values[0] + #vfa_value.InsertNextValue(r_conc) vfa_value.SetValue(i_index, r_conc) if b_first: + #vrg_data.GetCellData().SetScalars(vfa_value) vrg_data.GetPointData().SetScalars(vfa_value) b_first = False else: + #vrg_data.GetCellData().AddArray(vfa_value) vrg_data.GetPointData().AddArray(vfa_value) + # visualize on the fly + if (visualize): + # get scalar range + r_vmin = np.floor(df_conc.loc[:, s_substrate].min()) + r_vmax = np.ceil(df_conc.loc[:, s_substrate].max()) + + # generate the structured grid. + vsp_data = vtk.vtkStructuredPoints() + vsp_data.SetDimensions(ti_dim[0]+1, ti_dim[1]+1, ti_dim[2]+1) + vsp_data.SetSpacing( + self.get_voxel_spacing()[0], + self.get_voxel_spacing()[1], + self.get_voxel_spacing()[2], + ) + vsp_data.SetOrigin( + self.get_mesh_mnp_range()[0][0], + self.get_mesh_mnp_range()[1][0], + self.get_mesh_mnp_range()[2][0], + ) # lower-left-front point of domain bounding box + + # mapp grid and values + vdsm_data = vtk.vtkDataSetMapper() + vsp_data.GetCellData().SetScalars(vfa_value) + vdsm_data.SetInputData(vsp_data) + vdsm_data.Update() + vdsm_data.SetScalarRange(r_vmin, r_vmax) + vdsm_data.SetScalarModeToUseCellData() + + # build VTKLooktupTable (color scheme) + vlt_color = vtk.vtkLookupTable() + vlt_color.SetNumberOfTableValues(256) # number of color shades + vlt_color.SetHueRange(9/12, 0/12) # rainbow heat map + vlt_color.Build() + + # generate xy cutting plane actor + vp_canvas = vtk.vtkPlane() + vp_canvas.SetOrigin(0, 0, 0) # xyz + vp_canvas.SetNormal(0, 0, 1) + + vc_canvas = vtk.vtkCutter() + vc_canvas.SetInputData(vsp_data) + vc_canvas.SetCutFunction(vp_canvas) + vc_canvas.GeneratePolygons = 1 + + vpdm_canvas = vtk.vtkPolyDataMapper() + vpdm_canvas.SetInputConnection(vc_canvas.GetOutputPort()) + vpdm_canvas.ScalarVisibilityOn() + vpdm_canvas.SetScalarRange(r_vmin, r_vmax) + vpdm_canvas.SetLookupTable(vlt_color) + vpdm_canvas.SetScalarModeToUseCellData() + + va_canvas = vtk.vtkActor() + va_canvas.SetMapper(vpdm_canvas) + va_canvas.GetProperty().EdgeVisibilityOn() + + # generate outline actor + vof_frame = vtk.vtkOutlineFilter() + vof_frame.SetInputData(vsp_data) + + vpdm_frame = vtk.vtkPolyDataMapper() + vpdm_frame.SetInputConnection(vof_frame.GetOutputPort()) + + va_frame = vtk.vtkActor() + va_frame.SetMapper(vpdm_frame) + va_frame.GetProperty().SetColor(1, 1, 1) + + # generate scalar bar actor + vsba_spectrum = vtk.vtkScalarBarActor() + vsba_spectrum.SetTitle(s_substrate) + vsba_spectrum.GetPositionCoordinate().SetCoordinateSystemToNormalizedViewport() + vsba_spectrum.GetPositionCoordinate().SetValue(0.1, 0.01) + vsba_spectrum.SetOrientationToHorizontal() + vsba_spectrum.SetWidth(0.8) + vsba_spectrum.SetHeight(0.1) + vsba_spectrum.GetProperty().SetColor(0, 0, 0) + vsba_spectrum.GetTitleTextProperty().SetColor(0, 0, 0) + vsba_spectrum.GetTitleTextProperty().SetFontSize(22) + vsba_spectrum.SetLookupTable(vpdm_canvas.GetLookupTable()) + + # do render setup + ren = vtk.vtkRenderer() + renWin = vtk.vtkRenderWindow() + renWin.AddRenderer(ren) + renWin.SetSize(800, 600) + iren = vtk.vtkRenderWindowInteractor() + iren.SetRenderWindow(renWin) + + # add the actor to the renderer + #ren.ResetCamera() + ren.SetBackground(1/3, 1/3, 1/3) # gray + ren.AddActor(va_canvas) + ren.AddActor(va_frame) + ren.AddActor2D(vsba_spectrum) + + # render + iren.Initialize() + renWin.Render() + iren.Start() + + # free memory + #del vfa_value + # save vtk file s_vtkpathfile = self.path + '/' + s_vtkfile vw_writer = vtk.vtkXMLRectilinearGridWriter() @@ -1424,7 +1522,9 @@ def get_celltype_list(self): function returns a list with all celltype labels, ordered by cell_type ID. """ - return self.data['cell']['ls_celltype'].copy() + ds_celltype = self.get_celltype_dict() + ls_celltype = [ds_celltype[s_key] for s_key in sorted(ds_celltype, key=int)] + return ls_celltype def get_celltype_dict(self): @@ -1439,7 +1539,7 @@ def get_celltype_dict(self): function returns a dictionary that maps ID and name from all cell_definitions, specified in the PhysiCell_settings.xml file. """ - return self.data['cell']['ds_celltype'].copy() + return self.data['metadata']['cell_type'] def get_cell_df(self, values=1, drop=set(), keep=set()): @@ -1477,66 +1577,254 @@ def get_cell_df(self, values=1, drop=set(), keep=set()): """ # check keep and drop if (len(keep) > 0) and (len(drop) > 0): - sys.exit(f"Error @ TimeStep.get_cell_df : when keep is given {keep}, then drop has to be an empty set {drop}!") - - # fetch data frame - df_cell = self.data['cell']['df_cell'].copy() - - # filter - es_attribute = set(df_cell.columns).difference(es_coor_cell) - if (len(keep) > 0): - es_delete = es_attribute.difference(keep) - else: - es_delete = es_attribute.intersection(drop) - - if (values > 1): # by minimal number of states - for s_column in set(df_cell.columns).difference(es_coor_cell): - if len(set(df_cell.loc[:,s_column])) < values: - es_delete.add(s_column) - if self.verbose and (len(es_delete) > 0): - print('es_delete:', es_delete) - df_cell.drop(es_delete, axis=1, inplace=True) - - # output - df_cell = df_cell.loc[:,sorted(df_cell.columns)] - df_cell.sort_values('ID', axis=0, inplace=True) - return df_cell + sys.exit(f"Error @ pyMCDS.get_cell_df : when keep is given {keep}, then drop has to be an empty set {drop}!") + # get cell position and more + df_cell = pd.DataFrame(self.data['discrete_cells']['data']) + df_cell['time'] = self.get_time() + df_cell['runtime'] = self.get_runtime() / 60 # in min + df_cell['xmlfile'] = self.xmlfile + df_voxel = df_cell.loc[:,['position_x','position_y','position_z']].copy() - def get_cell_attribute_list(self): - """ - input: + # get mesh spacing + dm, dn, dp = self.get_voxel_spacing() - output: - ls_cellattr: list of strings - alphabetically ordered list of all tracked cell attributes. + # get mesh and voxel min max values + tr_m_range, tr_n_range, tr_p_range = self.get_mesh_mnp_range() + tr_i_range, tr_j_range, tr_k_range = self.get_voxel_ijk_range() - description: - function returns a list with all cell attribute labels, - alphabetically ordered. - """ - return self.data['cell']['ls_cellattr'].copy() + # get voxel for each cell + df_voxel.loc[:,'voxel_i'] = np.round((df_voxel.loc[:,'position_x'] - tr_m_range[0]) / dm).astype(int) + df_voxel.loc[:,'voxel_j'] = np.round((df_voxel.loc[:,'position_y'] - tr_n_range[0]) / dn).astype(int) + df_voxel.loc[:,'voxel_k'] = np.round((df_voxel.loc[:,'position_z'] - tr_p_range[0]) / dp).astype(int) + df_voxel.loc[(df_voxel.voxel_i > tr_i_range[1]), 'voxel_i'] = tr_i_range[1] # i_max + df_voxel.loc[(df_voxel.voxel_i < tr_i_range[0]), 'voxel_i'] = tr_i_range[0] # i_min + df_voxel.loc[(df_voxel.voxel_j > tr_j_range[1]), 'voxel_j'] = tr_j_range[1] # j_max + df_voxel.loc[(df_voxel.voxel_j < tr_j_range[0]), 'voxel_j'] = tr_j_range[0] # j_min + df_voxel.loc[(df_voxel.voxel_k > tr_k_range[1]), 'voxel_k'] = tr_k_range[1] # k_max + df_voxel.loc[(df_voxel.voxel_k < tr_k_range[0]), 'voxel_k'] = tr_k_range[0] # k_min + # merge voxel (inner join) + df_cell = pd.merge(df_cell, df_voxel, on=['position_x', 'position_y', 'position_z']) - def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cmap='viridis', title=None, grid=True, legend_loc='lower left', xlim=None, ylim=None, xyequal=True, s=1.0, ax=None, figsizepx=None, ext=None, figbgcolor=None, **kwargs): - """ - input: - focus: string; default is 'cell_type' - column name within cell dataframe. + # merge cell_density (left join) + df_cellcount = df_cell.loc[:,['voxel_i','voxel_j','voxel_k','ID']].groupby(['voxel_i','voxel_j','voxel_k']).count().reset_index() + ls_column = list(df_cellcount.columns) + ls_column[-1] = 'cell_count_voxel' + df_cellcount.columns = ls_column + s_density = f"cell_density_{self.data['metadata']['spatial_units']}3" + df_cellcount[s_density] = df_cellcount.loc[:,'cell_count_voxel'] / self.get_voxel_volume() + df_cell = pd.merge( + df_cell, + df_cellcount, + on = ['voxel_i', 'voxel_j', 'voxel_k'], + how = 'left', + ) - z_slice: floating point number; default is 0.0 - z-axis position to slice a 2D xy-plain out of the - 3D substrate concentration mesh. if z_slice position - is not an exact mesh center coordinate, then z_slice - will be adjusted to the nearest mesh center value, - the smaller one, if the coordinate lies on a saddle point. + # get column label set + es_column = set(df_cell.columns) - z_axis: for a categorical focus: set of labels; + # get vector length + for s_var_spatial in es_var_spatial: + es_vector = es_column.intersection({f'{s_var_spatial}_x',f'{s_var_spatial}_y',f'{s_var_spatial}_z'}) + if len(es_vector) > 0: + # linear algebra + #a_vector = df_cell.loc[:,ls_vector].values + #a_length = np.sqrt(np.diag(np.dot(a_vector, a_vector.T))) + # pythoagoras + a_length = None + for s_vector in es_vector: + a_vectorsq = df_cell.loc[:,s_vector].values**2 + if (a_length is None): + a_length = a_vectorsq + else: + a_length += a_vectorsq + a_length = a_length**(1/2) + # result + df_cell[f'{s_var_spatial}_vectorlength'] = a_length + + # physicell + if not (self.data['discrete_cells']['physiboss'] is None): + df_cell = pd.merge( + df_cell, + self.data['discrete_cells']['physiboss'], + left_index = True, + right_index = True, + how = 'left', + ) + + + # microenvironment + if self.microenv: + # merge substrate (left join) + df_sub = self.get_substrate_df() + for s_sub in df_sub.index: + for s_rate in df_sub.columns: + s_var = f'{s_sub}_{s_rate}' + df_cell[s_var] = df_sub.loc[s_sub,s_rate] + + # merge concentration (left join) + df_conc = self.get_conc_df(z_slice=None, values=1, drop=set(), keep=set()) + df_conc.drop({'time', 'runtime','xmlfile'}, axis=1, inplace=True) + df_cell = pd.merge( + df_cell, + df_conc, + on = ['voxel_i', 'voxel_j', 'voxel_k'], + how = 'left', + ) + + # variable typing + do_type = {} + [do_type.update({k:v}) for k,v in do_var_type.items() if k in es_column] + do_type.update(self.custom_data_type) + do_int = do_type.copy() + [do_int.update({k:int}) for k in do_int.keys()] + ls_int = sorted(do_int.keys()) + df_cell.loc[:,ls_int] = df_cell.loc[:,ls_int].round() + df_cell = df_cell.astype(do_int) + df_cell = df_cell.astype(do_type) + + # categorical translation + try: # bue 20240805: missing in MCDS version <= 0.5 (November 2021) + df_cell.loc[:,'current_death_model'] = df_cell.loc[:,'current_death_model'].replace(ds_death_model) # bue 20230614: this column looks like an artefact to me + except KeyError: + pass + df_cell.loc[:,'cycle_model'] = df_cell.loc[:,'cycle_model'].replace(ds_cycle_model) + df_cell.loc[:,'cycle_model'] = df_cell.loc[:,'cycle_model'].replace(ds_death_model) + df_cell.loc[:,'current_phase'] = df_cell.loc[:,'current_phase'].replace(ds_cycle_phase) + df_cell.loc[:,'current_phase'] = df_cell.loc[:,'current_phase'].replace(ds_death_phase) + df_cell.loc[:,'cell_type'] = df_cell.loc[:,'cell_type'].replace(self.data['metadata']['cell_type']) + df_cell.loc[:,'chemotaxis_index'] = df_cell.loc[:,'chemotaxis_index'].replace(self.data['metadata']['substrate']) + + # filter + es_attribute = set(df_cell.columns).difference(es_coor_cell) + if (len(keep) > 0): + es_delete = es_attribute.difference(keep) + else: + es_delete = es_attribute.intersection(drop) + + if (values > 1): # by minimal number of states + for s_column in set(df_cell.columns).difference(es_coor_cell): + if len(set(df_cell.loc[:,s_column])) < values: + es_delete.add(s_column) + if self.verbose and (len(es_delete) > 0): + print('es_delete:', es_delete) + df_cell.drop(es_delete, axis=1, inplace=True) + + # output + df_cell = df_cell.loc[:,sorted(df_cell.columns)] + df_cell.sort_values('ID', axis=0, inplace=True) + df_cell.set_index('ID', inplace=True) + df_cell = df_cell.copy() + return df_cell + + + def get_cell_df_at(self, x, y, z=0, values=1, drop=set(), keep=set()): + """ + input: + x: floating point number + position x-coordinate. + + y: floating point number + position y-coordinate. + + z: floating point number; default is 0 + position z-coordinate. + + values: integer; default is 1 + minimal number of values a variable has to have to be outputted. + variables that have only 1 state carry no information. + None is a state too. + + drop: set of strings; default is an empty set + set of column labels to be dropped for the dataframe. + don't worry: essential columns like ID, coordinates + and time will never be dropped. + Attention: when the keep parameter is given, then + the drop parameter has to be an empty set! + + keep: set of strings; default is an empty set + set of column labels to be kept in the dataframe. + set values=1 to be sure that all variables are kept. + don't worry: essential columns like ID, coordinates + and time will always be kept. + + output: + df_voxel: pandas dataframe + x, y, z voxel filtered cell dataframe. + + description: + function returns the cell dataframe for the voxel + specified with the x, y, z position coordinate. + """ + df_voxel = None + + # is coordinate inside the domain? + b_calc = self.is_in_mesh(x=x, y=y, z=z, halt=False) + if b_calc: + + # get mesh and mesh spacing + dm, dn, dp = self.get_voxel_spacing() + ar_m, ar_n, ar_p = self.get_mesh() + + # get voxel coordinate + i, j, k = self.get_voxel_ijk(x, y, z, is_in_mesh=False) + m = ar_m[j, i, k] + n = ar_n[j, i, k] + p = ar_p[j, i, k] + + # get voxel + df_cell = self.get_cell_df(values=values, drop=drop, keep=keep) + inside_voxel = ( + (df_cell['position_x'] <= m + dm / 2) & + (df_cell['position_x'] >= m - dm / 2) & + (df_cell['position_y'] <= n + dn / 2) & + (df_cell['position_y'] >= n - dn / 2) & + (df_cell['position_z'] <= p + dp / 2) & + (df_cell['position_z'] >= p - dp / 2) + ) + df_voxel = df_cell[inside_voxel] + + # output + return df_voxel + + + def get_cell_attribute_list(self): + """ + input: + + output: + ls_cellattr: list of strings + alphabetically ordered list of all tracked cell attributes. + + description: + function returns a list with all cell attribute labels, + alphabetically ordered. + """ + df_cell = self.get_cell_df() + ls_cellattr = sorted(set(df_cell.columns).difference(es_coor_cell)) + return ls_cellattr + + + def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cmap='viridis', title=None, grid=True, legend_loc='lower left', xlim=None, ylim=None, xyequal=True, s=1.0, ax=None, figsizepx=None, directory=None, ext=None, figbgcolor=None): + """ + input: + focus: string; default is 'cell_type' + column name within cell dataframe. + + z_slice: floating point number; default is 0.0 + z-axis position to slice a 2D xy-plain out of the + 3D substrate concentration mesh. if z_slice position + is not an exact mesh center coordinate, then z_slice + will be adjusted to the nearest mesh center value, + the smaller one, if the coordinate lies on a saddle point. + + z_axis: for a categorical focus: set of labels; for a numeric focus: tuple of two floats; default is None depending on the focus column variable dtype, default extracts labels or min and max values from data. - alpha: floating point number; default is 1.0 + alpha: floating point number; default is 1 alpha channel transparency value between 1 (not transparent at all) and 0 (totally transparent). @@ -1585,6 +1873,11 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is None output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -1593,10 +1886,6 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the pandas dataframe plot function. - + https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.html - output: fig: matplotlib figure, depending on ext, either as object or as file. the figure contains the scatter plot and color bar (numerical data) @@ -1625,7 +1914,7 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma figsizepx = [i_width, i_height] except FileNotFoundError: if self.verbose: - print(f'Warning @ TimeStepts.plot_scatter : could not load {s_pathfile}.') + print(f'Warning @ pyMCDSts.plot_scatter : could not load {s_pathfile}.') figsizepx = [640, 480] # handle figure size @@ -1713,7 +2002,7 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma df_cell.loc[(df_cell.loc[:,focus] == s_category), s_focus_color] = s_color # generate category color dictionary else: - ds_color = pdplt.df_label_to_color( + ds_color = df_label_to_color( df_abc = df_cell, s_focus = focus, es_label = es_category, @@ -1746,12 +2035,11 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma s = 's', grid = grid, ax = ax, - **kwargs, ) # plot categorical data legen if not (es_category is None): - pdplt.ax_colorlegend( + ax_colorlegend( ax = ax, ds_color = ds_color, s_loc = legend_loc, @@ -1765,9 +2053,12 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma else: # handle output path and filename - s_path = self.path + f"/cell_{focus.replace(' ','_')}_z{round(z_slice,9)}/" + if (directory is None): + s_path = self.path + f'/cell_{focus}_z{round(z_slice,9)}/' + else: + s_path = f'{directory}/' os.makedirs(s_path, exist_ok=True) - s_file = self.xmlfile.replace('.xml', f"_{focus.replace(' ','_')}.{ext}") + s_file = self.xmlfile.replace('.xml', f'_{focus}.{ext}') s_pathfile = f'{s_path}{s_file}' # handle figure background color if figbgcolor is None: @@ -1783,12 +2074,15 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma return fig - def make_cell_vtk(self, attribute=['cell_type']): + def make_cell_vtk(self, attribute=['cell_type'], visualize=True): """ input: attribute: list of strings; default is ['cell_type'] column name within cell dataframe. + visualize: boolean; default is True + additionally, visualize cells using vtk renderer. + output: s_vtkpathfile: vtk 3D glyph polynomial data file that contains cells. @@ -1839,18 +2133,15 @@ def make_cell_vtk(self, attribute=['cell_type']): # fill this grid with given attributes for s_attribute in attribute: b_bool = False - if pd.api.types.is_bool_dtype(df_cell.loc[:, s_attribute].dtype): + if (df_cell.loc[:, s_attribute].dtype == bool): #in {bool, np.bool_, np.bool}): b_bool = True voa_data = vtk.vtkStringArray() - print(f'boole: {s_attribute}') - elif pd.api.types.is_string_dtype(df_cell.loc[:, s_attribute].dtype): + elif (df_cell.loc[:, s_attribute].dtype == str) or (df_cell.loc[:, s_attribute].dtype == np.object_): # in {str, np.str_, np.object_}): voa_data = vtk.vtkStringArray() - print(f'string: {s_attribute}') - elif pd.api.types.is_integer_dtype(df_cell.loc[:, s_attribute].dtype) or pd.api.types.is_float_dtype(df_cell.loc[:, s_attribute].dtype): + elif (df_cell.loc[:, s_attribute].dtype == int) or (df_cell.loc[:, s_attribute].dtype == float): # in {int, np.int_, np.int8, np.int16, np.int32, np.int64, float, np.float16, np.float32, np.float64, np.float128}): voa_data = vtk.vtkFloatArray() - print(f'numeric: {s_attribute}') else: - sys.exit(f'Error @ TimeStep.make_cell_vtk : {s_attribute} {df_cell.loc[:, s_attribute].dtype} unknown df_cell column data type.') + sys.exit(f'Error @ pyMCDS.make_cell_vtk : {s_attribute} {df_cell.loc[:, s_attribute].dtype} unknown df_cell column data type.') voa_data.SetName(s_attribute) for i in df_cell.index: @@ -1863,6 +2154,8 @@ def make_cell_vtk(self, attribute=['cell_type']): voa_data.InsertNextValue(df_cell.loc[i, s_attribute]) vug_data.GetPointData().AddArray(voa_data) + # free memory + #del voa_data # generate sphere source vss_data = vtk.vtkSphereSource() @@ -1882,6 +2175,49 @@ def make_cell_vtk(self, attribute=['cell_type']): vg_data.SetColorModeToColorByScalar() vg_data.Update() + # visualize + if (visualize): + # select first attribute + s_attribute = attribute[0] + + # build VTKLooktupTable (color scheme) + vlt_color = vtk.vtkLookupTable() + i_element = df_cell.loc[:, s_attribute].unique().shape[0] + if (i_element > 256): + i_element = 256 + vlt_color.SetNumberOfTableValues(i_element) + vlt_color.SetHueRange(9/12, 0/12) # rainbow heat map + vlt_color.Build() + + # set up the mapper + vpdm_data = vtk.vtkPolyDataMapper() + vpdm_data.SetInputConnection(vg_data.GetOutputPort()) + vpdm_data.ScalarVisibilityOn() + vpdm_data.SetLookupTable(vlt_color) + vpdm_data.ColorByArrayComponent(s_attribute, 1) + + # set up the actor + actor = vtk.vtkActor() + actor.SetMapper(vpdm_data) + + # do renderer setup + ren = vtk.vtkRenderer() + renWin = vtk.vtkRenderWindow() + renWin.AddRenderer(ren) + renWin.SetSize(800, 600) + iren = vtk.vtkRenderWindowInteractor() + iren.SetRenderWindow(renWin) + + # add the actor to the renderer + #ren.ResetCamera() + ren.SetBackground(1/3, 1/3, 1/3) # gray + ren.AddActor(actor) + + # render + iren.Initialize() + renWin.Render() + iren.Start() + # write VTK s_vtkpathfile = self.path + '/' + s_vtkfile vw_writer = vtk.vtkXMLPolyDataWriter() @@ -1892,246 +2228,6 @@ def make_cell_vtk(self, attribute=['cell_type']): return s_vtkpathfile - ## MICROENVIRONMENT AND CELL AGENT RELATED FUNCTIONS ## - - def make_ome_tiff(self, cell_attribute='ID', conc_cutoff={}, focus=None, file=True): - """ - input: - cell_attribute: strings; default is 'ID', which will result in a - cell segmentation mask. - column name within the cell dataframe. - the column data type has to be numeric (bool, int, float) - and cannot be string. - the result will be stored as 32 bit float. - - conc_cutoff: dictionary string to real; default is an empty dictionary. - if a contour from a substrate not should be cut by greater - than zero (shifted to integer 1), another cutoff value can be - specified here. - - focus: set of strings; default is a None - set of substrate and cell_type names to specify what will be - translated into ome tiff format. - if None, all substrates and cell types will be processed. - - file: boolean; default True - if True, an ome tiff file is the output. - if False, a numpy array with shape czyx is the output. - - output: - a_tczyx_img: numpy array or ome tiff file. - - description: - function to transform chosen mcds output into an 1[um] spaced - czyx (channel, z-axis, y-axis, x-axis) ome tiff file or numpy array, - one substrate or cell_type per channel. - an ome tiff file is more or less: - a numpy array, containing the image information - and a xml, containing the microscopy metadata information, - like the channel labels. - the ome tiff file format can for example be read by the napari - or fiji (imagej) software. - - https://napari.org/stable/ - https://fiji.sc/ - """ - # handle channels - ls_substrate = self.get_substrate_list() - ls_celltype = self.get_celltype_list() - - if not (focus is None): - ls_substrate = [s_substrate for s_substrate in ls_substrate if s_substrate in set(focus)] - ls_celltype = [s_celltype for s_celltype in ls_celltype if s_celltype in set(focus)] - if (set(focus) != set(ls_substrate).union(set(ls_celltype))): - sys.exit(f'Error : {focus} not found in {ls_substrate} {ls_celltype}') - - # const - ls_coor_mnp = ['mesh_center_m', 'mesh_center_n', 'mesh_center_p'] # xyz - ls_coor_xyz = ['position_x', 'position_y', 'position_z'] # xyz - ls_coor = ['voxel_x', 'voxel_y', 'voxel_z'] - - # time step tensor - i_time = int(self.get_time()) - - # get xy coordinate dataframe - lr_axis_z = list(self.get_mesh_mnp_axis()[2] - self.get_voxel_spacing()[2] / 2) - lr_axis_z.append(self.get_mesh_mnp_axis()[2][-1] + self.get_voxel_spacing()[2] / 2) - lll_coor = [] - for i_x in range(int(round(self.get_voxel_ijk_range()[0][1] * self.get_voxel_spacing()[0]))): - for i_y in range(int(round(self.get_voxel_ijk_range()[1][1] * self.get_voxel_spacing()[1]))): - lll_coor.append([i_x, i_y]) - df_coor = pd.DataFrame(lll_coor, columns=ls_coor[:2]) - lr_axis_z[-1] += 1 - - # extract voxel radius - di_grow = {} - for s_substarte in ls_substrate: - di_grow.update({ - s_substarte : int(np.round(np.mean(self.get_voxel_spacing()[:2])) - 1) - }) - - # get and shift substrate xy data - df_conc = self.get_conc_df() - df_conc = df_conc.loc[:, ls_coor_mnp + ls_substrate] - df_conc.loc[:, 'mesh_center_m'] = (df_conc.loc[:, 'mesh_center_m'] - self.get_xyz_range()[0][0]).round() - df_conc.loc[:, 'mesh_center_n'] = (df_conc.loc[:, 'mesh_center_n'] - self.get_xyz_range()[1][0]).round() - df_conc.rename({'mesh_center_m':'voxel_x', 'mesh_center_n':'voxel_y', 'mesh_center_p':'voxel_z'}, axis=1, inplace=True) - df_conc = df_conc.astype({'voxel_x': int, 'voxel_y': int, 'voxel_z': float}) - # level the cake - for s_channel in conc_cutoff.keys(): - try: - df_conc.loc[:, s_channel] = df_conc.loc[:, s_channel] - conc_cutoff[s_channel] + 1 # positive values starting at > 0 - df_conc.loc[(df_conc.loc[:, s_channel] <= conc_cutoff[s_channel]), s_channel] = 0 - except KeyError: - pass - - - # get cell data - df_cell = self.get_cell_df().reset_index() - - # extract cell radius - for s_celltype in ls_celltype: - try: - i_cell_grow = int(round(df_cell.loc[(df_cell.cell_type == s_celltype), 'radius'].mean()) - 1) - except: - i_cell_grow = 0 - di_grow.update({s_celltype : i_cell_grow}) - - # filter and shift - df_cell = df_cell.loc[:, ls_coor_xyz + ['cell_type', cell_attribute]] - if (cell_attribute == 'cell_type'): - sys.exit(f'Error @ TimeStep.make_ome_tiff : cell_attribute cannot be cell_type.') - elif (df_cell.loc[:, cell_attribute].dtype == str) or (df_cell.loc[:, cell_attribute].dtype == np.object_): # in {str, np.str_, np.object_}): - sys.exit(f'Error @ TimeStep.make_ome_tiff : {cell_attribute} {df_cell.loc[:, cell_attribute].dtype} cell_attribute cannot be string or object. cell_attribute has to be boolean, integer, or float.') - elif (df_cell.loc[:, cell_attribute].dtype == bool): # in {bool, np.bool_, np.bool}): - df_cell = df_cell.astype({cell_attribute: int}) - df_cell.loc[:, 'position_x'] = (df_cell.loc[:, 'position_x'] - self.get_xyz_range()[0][0]).round() - df_cell.loc[:, 'position_y'] = (df_cell.loc[:, 'position_y'] - self.get_xyz_range()[1][0]).round() - df_cell.rename({'position_x':'voxel_x', 'position_y':'voxel_y', 'position_z':'voxel_z'}, axis=1, inplace=True) - df_cell = df_cell.astype({'voxel_x': int, 'voxel_y': int, 'voxel_z': float}) - # level the cake - df_cell.loc[:, cell_attribute] = df_cell.loc[:, cell_attribute] - df_cell.loc[:, cell_attribute].min() + 1 # positive values starting at > 0 - - # check for duplicates: two cell at exactelly the same xyz position. - #if self.verbose and df_cell.loc[:,['voxel_x', 'voxel_y', 'voxel_z']].duplicated().any(): - # df_duplicate = df_cell.loc[(df_cell.loc[:, ['voxel_x', 'voxel_y', 'voxel_z']].duplicated()), :] - # sys.exit(f"Error @ TimeStep.make_ome_tiff : {df_duplicate} cells at exactely the same xyz voxel position detected. cannot pivot!") - - # pivot cell_type - df_cell = df_cell.pivot_table(index=ls_coor, columns='cell_type', values=cell_attribute, aggfunc='sum').reset_index() # fill_value is na - for s_celltype in ls_celltype: - if not s_celltype in set(df_cell.columns): - df_cell[s_celltype] = 0 - - # each C channel - time step tensors - la_czyx_img = [] - ls_channel = ls_substrate + ls_celltype - for s_channel in ls_channel: - - # get channel dataframe - if s_channel in set(ls_substrate): - df_channel = df_conc.loc[:, ls_coor + [s_channel]] - elif s_channel in set(ls_celltype): - df_channel = df_cell.loc[:, ls_coor + [s_channel]] - else: - sys.exit(f'Error @ TimeStep.make_ome_tiff : {s_channel} unknown channel detected. not in substrate and cell type list {ls_substrate} {ls_celltype}!') - - # each z axis - la_zyx_img = [] - for i_zaxis in range(len(lr_axis_z)): - if (i_zaxis < (len(lr_axis_z) - 1)): - print(f'processing: {i_time} [min] {s_channel} [channel] {i_zaxis} [z_axis] ...') - # extract z layer - df_yxchannel = df_channel.loc[ - ((df_channel.loc[:, ls_coor[2]] >= lr_axis_z[i_zaxis]) & (df_channel.loc[:, ls_coor[2]] < lr_axis_z[i_zaxis + 1])), - ls_coor[:2] + [s_channel] - ] - - # drop row with na and duplicate entries - df_yxchannel = df_yxchannel.dropna(axis=0) - df_yxchannel = df_yxchannel.drop_duplicates() - - # merge with coooridnates and get image - # bue 20240811: df_coor left side merge will cut off reset cell that are out of the xyz domain range, which is what we want. - df_yxchannel = pd.merge(df_coor, df_yxchannel, on=ls_coor[:2], how='left').replace({np.nan: 0}) - try: - df_yxchannel = df_yxchannel.pivot(columns=ls_coor[0], index=ls_coor[1], values=s_channel) - except ValueError: # two cells from the same cell type very close to each other detetced. - if self.verbose: - df_duplicate = df_cell.loc[(df_yxchannel.loc[:, ['voxel_x', 'voxel_y']].duplicated()), :] - print(f'Warning: {s_channel} {df_duplicate} cells within 1[um] distance form each detected. cannot pivot. erase cell type from this timestep.') - df_yxchannel.loc[:,s_channel] = 0 # erase cells - df_yxchannel = df_yxchannel.drop_duplicates() - df_yxchannel = df_yxchannel.pivot(columns=ls_coor[0], index=ls_coor[1], values=s_channel) - a_yx_img = df_yxchannel.values - - # grow - a_yx_img = imagine.grow_seed(a_yx_img, i_step=di_grow[s_channel], b_verbose=False) - - # update output - la_zyx_img.append(a_yx_img) - a_zyx_img = np.array(la_zyx_img, np.float32) - la_czyx_img.append(np.array(a_zyx_img, np.float32)) - - # output - a_czyx_img = np.array(la_czyx_img, dtype=np.float32) - - # numpy array - if not file: - return a_czyx_img - - # write to file - else: - if self.verbose: - print('a_czyx_img shape:', a_czyx_img.shape) - # generate filename - s_channel = '' - for s_substrate in ls_substrate: - try: - r_value = conc_cutoff[s_substrate] - s_channel += f'_{s_substrate}{r_value}' - except KeyError: - s_channel += f'_{s_substrate}' - for s_celltype in ls_celltype: - s_channel += f'_{s_celltype}' - if len(ls_celltype) > 0: - s_channel += f'_{cell_attribute}' - s_tifffile = self.xmlfile.replace('.xml', f'{s_channel}.ome.tiff') - s_tifffile = s_tifffile.replace(' ','_') - if (len(s_tifffile) > 255): - print(f"Warning: filename {len(s_tifffile)} > 255 character.") - s_tifffile = self.xmlfile.replace('.xml', f'_channels.ome.tiff') - print(f"file name adjusted to {s_tifffile}.") - s_tiffpathfile = self.path + '/' + s_tifffile - - # save to file - OmeTiffWriter.save( - a_czyx_img, - s_tiffpathfile, - dim_order = 'CZYX', - #ome_xml=x_img, - channel_names = ls_channel, - image_names = [s_tifffile.replace('.ome.tiff','')], - physical_pixel_sizes = bioio_base.types.PhysicalPixelSizes(self.get_voxel_spacing()[2], 1.0, 1.0), # z,y,x [um] - #channel_colors=, - #fs_kwargs={}, - ) - return s_tiffpathfile - - - def render_neuroglancer(self, tiffpathfile, timestep=0, intensity_cmap='gray'): - """ - help(pcdl.render_neuroglancer) - try: mcds.render_neuroglancer(mcds.make_ome_tiff()) - """ - o_viewer = render_neuroglancer( - tiffpathfile = tiffpathfile, - timestep = timestep, - intensity_cmap = intensity_cmap, - ) - return o_viewer - - ## GRAPH RELATED FUNCTIONS ## def get_attached_graph_dict(self): @@ -2145,7 +2241,7 @@ def get_attached_graph_dict(self): description: function returns the attached cell graph as a dictionary object. """ - return self.data['cell']['dei_graph']['attached_cells'].copy() + return self.data['discrete_cells']['graph']['attached_cells'] def get_neighbor_graph_dict(self): @@ -2159,7 +2255,7 @@ def get_neighbor_graph_dict(self): description: function returns the cell neighbor graph as a dictionary object. """ - return self.data['cell']['dei_graph']['neighbor_cells'].copy() + return self.data['discrete_cells']['graph']['neighbor_cells'] def get_spring_graph_dict(self): @@ -2173,7 +2269,7 @@ def get_spring_graph_dict(self): description: function returns the attached spring cell graph as a dictionary object. """ - return self.data['cell']['dei_graph']['spring_attached_cells'].copy() + return self.data['discrete_cells']['graph']['spring_attached_cells'] def make_graph_gml(self, graph_type, edge_attribute=True, node_attribute=[]): @@ -2248,7 +2344,7 @@ def make_graph_gml(self, graph_type, edge_attribute=True, node_attribute=[]): elif (o_attribute.dtype == float): #in {float, np.float16, np.float32, np.float64, np.float128}): f.write(f' {s_attribute} {o_attribute}\n') else: - sys.exit(f'Error @ TimeStep.make_graph_gml : attribute {o_attribute}; type {o_attribute.dtype}; type seems not to be bool, int, float, or string.') + sys.exit(f'Error @ pyMCDS.make_graph_gml : attribute {o_attribute}; type {o_attribute.dtype}; type seems not to be bool, int, float, or string.') f.write(f' ]\n') # edge for i_dst in ei_dst: @@ -2273,70 +2369,12 @@ def make_graph_gml(self, graph_type, edge_attribute=True, node_attribute=[]): return s_gmlpathfile - ## ANNDATA RELATED FUNCTIONS ## - - def get_anndata(self, values=1, drop=set(), keep=set(), scale='maxabs'): - """ - input: - values: integer; default is 1 - minimal number of values a variable has to have to be outputted. - variables that have only 1 state carry no information. - None is a state too. - - drop: set of strings; default is an empty set - set of column labels to be dropped for the dataframe. - don't worry: essential columns like ID, coordinates - and time will never be dropped. - Attention: when the keep parameter is given, then - the drop parameter has to be an empty set! - - keep: set of strings; default is an empty set - set of column labels to be kept in the dataframe. - set values=1 to be sure that all variables are kept. - don't worry: essential columns like ID, coordinates - and time will always be kept. - - scale: string; default 'maxabs' - specify how the data should be scaled. - possible values are None, maxabs, minmax, std. - for more input, check out: help(pcdl.scaler) - - output: - annmcds: anndata object - for this one time step. - - description: - function to transform a mcds time step into an anndata object - for downstream analysis. - """ - # processing - if self.verbose: - print(f'processing: 1/1 {round(self.get_time(),9)}[min] mcds into anndata obj.') - df_cell = self.get_cell_df(values=values, drop=drop, keep=keep) - df_count, df_obs, d_obsm, d_obsp, d_uns = _anndextract( - df_cell = df_cell, - scale = scale, - graph_attached = self.get_attached_graph_dict(), - graph_neighbor = self.get_neighbor_graph_dict(), - graph_method = self.get_physicell_version(), - ) - annmcds = ad.AnnData( - X = df_count, - obs = df_obs, - obsm = d_obsm, - obsp = d_obsp, - uns = d_uns - ) - # output - return annmcds - - ## LOAD DATA ## def _read_xml(self, xmlfile, output_path='.'): """ input: - self: TimeStep class instance. + self: pyMCDS class instance. xmlfile: string name of the xml file with or without path @@ -2347,11 +2385,11 @@ def _read_xml(self, xmlfile, output_path='.'): the PhysiCell output files are stored. output: - self: TimeStep class instance with loaded data. + self: pyMCDS class instance with loaded data. description: internal function to load the data from the PhysiCell output files - into the TimeStep instance. + into the pyMCDS instance. """ ##################### # path and filename # @@ -2374,26 +2412,19 @@ def _read_xml(self, xmlfile, output_path='.'): b_celltype = False # generate output dictionary - d_mcds = { - 'metadata': {}, - 'mesh': {}, - 'substrate': { - 'ds_substrate': {}, - }, - 'metadata': {}, - 'cell': { - 'ds_celltype': {}, - }, - 'raw_substrate': {}, - 'raw_cell': { - 'units': {}, - }, - } + d_mcds = {} + d_mcds['metadata'] = {} + d_mcds['metadata']['substrate'] = {} + d_mcds['metadata']['cell_type'] = {} + d_mcds['mesh'] = {} + d_mcds['continuum_variables'] = {} + d_mcds['discrete_cells'] = {} + d_mcds['discrete_cells']['units'] = {} + ############################### # read PhysiCell_settings.xml # ############################### - ## get celltype dict # bue: used for cell_type label:id mapping for data generated with physicell versions < 3.15. if not ((self.settingxml is None) or (self.settingxml is False)): @@ -2408,9 +2439,8 @@ def _read_xml(self, xmlfile, output_path='.'): for x_celltype in self.x_settingxml.find('cell_definitions').findall('cell_definition'): # s_id = str(x_celltype.get('ID')) - # I don't like spaces in cell type names! - s_celltype = x_celltype.get('name') #.replace(' ','_') # ROH - d_mcds['cell']['ds_celltype'].update({s_id : s_celltype}) + s_celltype = x_celltype.get('name').replace(' ', '_') + d_mcds['metadata']['cell_type'].update({s_id : s_celltype}) b_celltype = True ####################################### @@ -2434,25 +2464,25 @@ def _read_xml(self, xmlfile, output_path='.'): ### find the metadata node ### x_metadata = x_root.find('metadata') - ## get multicellds xml version + # get multicellds xml version d_mcds['metadata']['multicellds_version'] = f"MultiCellDS_{x_root.get('version')}" - ## get physicell software version + # get physicell software version x_software = x_metadata.find('software') x_physicelln = x_software.find('name') x_physicellv = x_software.find('version') d_mcds['metadata']['physicell_version'] = f'{x_physicelln.text}_{x_physicellv.text}' - ## get timestamp + # get timestamp x_time = x_metadata.find('created') d_mcds['metadata']['created'] = x_time.text - ## get current simulated time + # get current simulated time x_time = x_metadata.find('current_time') d_mcds['metadata']['current_time'] = float(x_time.text) d_mcds['metadata']['time_units'] = x_time.get('units') - ## get current runtime + # get current runtime x_time = x_metadata.find('current_runtime') d_mcds['metadata']['current_runtime'] = float(x_time.text) d_mcds['metadata']['runtime_units'] = x_time.get('units') @@ -2468,7 +2498,7 @@ def _read_xml(self, xmlfile, output_path='.'): ### find the mesh node ### x_microenv = x_root.find('microenvironment').find('domain') # find the microenvironment node x_mesh = x_microenv.find('mesh') - d_mcds['metadata']['spatial_unit'] = x_mesh.get('units') + d_mcds['metadata']['spatial_units'] = x_mesh.get('units') # while we're at it, find the mesh s_x_coor = x_mesh.find('x_coordinates').text @@ -2483,38 +2513,38 @@ def _read_xml(self, xmlfile, output_path='.'): s_delim = x_mesh.find('z_coordinates').get('delimiter') ar_z_coor = np.array(s_z_coor.split(s_delim), dtype=np.float64) - ## get mesh grid + # reshape into a meshgrid d_mcds['mesh']['mnp_grid'] = np.array(np.meshgrid(ar_x_coor, ar_y_coor, ar_z_coor, indexing='xy')) - ## get mesh center axis + # get mesh center axis d_mcds['mesh']['mnp_axis'] = [ np.unique(ar_x_coor), np.unique(ar_y_coor), np.unique(ar_z_coor), ] - ## get mesh center range + # get mesh center range d_mcds['mesh']['mnp_range'] = [ (d_mcds['mesh']['mnp_axis'][0].min(), d_mcds['mesh']['mnp_axis'][0].max()), (d_mcds['mesh']['mnp_axis'][1].min(), d_mcds['mesh']['mnp_axis'][1].max()), (d_mcds['mesh']['mnp_axis'][2].min(), d_mcds['mesh']['mnp_axis'][2].max()), ] - ## get voxel range + # get voxel range d_mcds['mesh']['ijk_range'] = [ (0, len(d_mcds['mesh']['mnp_axis'][0]) - 1), (0, len(d_mcds['mesh']['mnp_axis'][1]) - 1), (0, len(d_mcds['mesh']['mnp_axis'][2]) - 1), ] - ## get voxel axis + # get voxel axis d_mcds['mesh']['ijk_axis'] = [ np.array(range(d_mcds['mesh']['ijk_range'][0][1] + 1)), np.array(range(d_mcds['mesh']['ijk_range'][1][1] + 1)), np.array(range(d_mcds['mesh']['ijk_range'][2][1] + 1)), ] - ## get mesh bounding box range [xmin, ymin, zmin, xmax, ymax, zmax] + # get mesh bounding box range [xmin, ymin, zmin, xmax, ymax, zmax] s_bboxcoor = x_mesh.find('bounding_box').text s_delim = x_mesh.find('bounding_box').get('delimiter') ar_bboxcoor = np.array(s_bboxcoor.split(s_delim), dtype=np.float64) @@ -2531,34 +2561,10 @@ def _read_xml(self, xmlfile, output_path='.'): if self.verbose: print(f'reading: {s_voxelpathfile}') - ## get voxle coordinates # center of voxel specified by first three rows [ x, y, z ] - d_mcds['mesh']['mnp_coordinate'] = ar_mesh_initial[:3, :] - - ## get voxel volume # volume specified by fourth row - ar_volume = ar_mesh_initial[3, :] - if (len(set(ar_volume)) != 1): - sys.exit(f'Error @ TimeStep._read_xml : mesh is not built out of a unique voxel volume {ar_volume}.') - d_mcds['mesh']['volume'] = ar_volume[0] - - ## get mesh voxel spacing - tr_m_range, tr_n_range, tr_p_range = d_mcds['mesh']['mnp_range'] - ar_m_axis, ar_n_axis, ar_p_axis = d_mcds['mesh']['mnp_axis'] - - if (len(set(tr_m_range)) == 1): # m axis - dm = np.float64(1.0) - else: - dm = (tr_m_range[1] - tr_m_range[0]) / (ar_m_axis.shape[0] - 1) - - if (len(set(tr_n_range)) == 1): # n axis - dn = np.float64(1.0) - else: - dn = (tr_n_range[1] - tr_n_range[0]) / (ar_n_axis.shape[0] - 1) - - dp = d_mcds['mesh']['volume'] / (dm * dn) # p axis - - d_mcds['mesh']['mnp_spacing'] = [dm, dn, dp] + d_mcds['mesh']['mnp_coordinate'] = ar_mesh_initial[:3, :] + d_mcds['mesh']['volumes'] = ar_mesh_initial[3, :] ################################ @@ -2578,37 +2584,37 @@ def _read_xml(self, xmlfile, output_path='.'): if self.verbose: print(f'reading: {s_microenvpathfile}') - # raw_substrate, unlike in the matlab version the individual chemical + # continuum_variables, unlike in the matlab version the individual chemical # species will be primarily accessed through their names e.g. - # d_mcds['raw_substrate']['oxygen']['units'] - # d_mcds['raw_substrate']['glucose']['data'] + # d_mcds['continuum_variables']['oxygen']['units'] + # d_mcds['continuum_variables']['glucose']['data'] # substrate loop for i_s, x_substrate in enumerate(x_microenv.find('variables').findall('variable')): # i don't like spaces in species names! - s_substrate = x_substrate.get('name') #.replace(' ','_') # ROH + s_substrate = x_substrate.get('name').replace(' ', '_') - d_mcds['raw_substrate'][s_substrate] = {} - d_mcds['raw_substrate'][s_substrate]['units'] = x_substrate.get('units') + d_mcds['continuum_variables'][s_substrate] = {} + d_mcds['continuum_variables'][s_substrate]['units'] = x_substrate.get('units') if self.verbose: print(f'parsing: {s_substrate} data') # update metadata substrate ID label dictionary - d_mcds['substrate']['ds_substrate'].update({str(i_s) : s_substrate}) + d_mcds['metadata']['substrate'].update({str(i_s) : s_substrate}) # initialize meshgrid shaped array for concentration data - d_mcds['raw_substrate'][s_substrate]['data'] = np.zeros(d_mcds['mesh']['mnp_grid'][0].shape) + d_mcds['continuum_variables'][s_substrate]['data'] = np.zeros(d_mcds['mesh']['mnp_grid'][0].shape) # diffusion data for each species - d_mcds['raw_substrate'][s_substrate]['diffusion_coefficient'] = {} - d_mcds['raw_substrate'][s_substrate]['diffusion_coefficient']['value'] = float(x_substrate.find('physical_parameter_set').find('diffusion_coefficient').text) - d_mcds['raw_substrate'][s_substrate]['diffusion_coefficient']['units'] = x_substrate.find('physical_parameter_set').find('diffusion_coefficient').get('units') + d_mcds['continuum_variables'][s_substrate]['diffusion_coefficient'] = {} + d_mcds['continuum_variables'][s_substrate]['diffusion_coefficient']['value'] = float(x_substrate.find('physical_parameter_set').find('diffusion_coefficient').text) + d_mcds['continuum_variables'][s_substrate]['diffusion_coefficient']['units'] = x_substrate.find('physical_parameter_set').find('diffusion_coefficient').get('units') # decay data for each species - d_mcds['raw_substrate'][s_substrate]['decay_rate'] = {} - d_mcds['raw_substrate'][s_substrate]['decay_rate']['value'] = float(x_substrate.find('physical_parameter_set').find('decay_rate').text) - d_mcds['raw_substrate'][s_substrate]['decay_rate']['units'] = x_substrate.find('physical_parameter_set').find('decay_rate').get('units') + d_mcds['continuum_variables'][s_substrate]['decay_rate'] = {} + d_mcds['continuum_variables'][s_substrate]['decay_rate']['value'] = float(x_substrate.find('physical_parameter_set').find('decay_rate').text) + d_mcds['continuum_variables'][s_substrate]['decay_rate']['units'] = x_substrate.find('physical_parameter_set').find('decay_rate').get('units') # store data from microenvironment file as numpy array # iterate over each voxel @@ -2622,70 +2628,13 @@ def _read_xml(self, xmlfile, output_path='.'): k = np.where(np.abs(ar_center[2] - d_mcds['mesh']['mnp_axis'][2]) < 1e-10)[0][0] # store value - d_mcds['raw_substrate'][s_substrate]['data'][j, i, k] = ar_microenv[4+i_s, i_voxel] - - ## get substrate listing - ds_substrate = d_mcds['substrate']['ds_substrate'] - ls_substrate = [ds_substrate[s_key] for s_key in sorted(ds_substrate, key=int)] - # store values - d_mcds['substrate']['ls_substarte'] = ls_substrate - - ## get substrate df - # extract data - ls_column = ['substrate','decay_rate','diffusion_coefficient'] - ll_sub = [] - for s_substrate in d_mcds['substrate']['ls_substarte']: - s_decay_value = d_mcds['raw_substrate'][s_substrate]['decay_rate']['value'] - s_diffusion_value = d_mcds['raw_substrate'][s_substrate]['diffusion_coefficient']['value'] - ll_sub.append([s_substrate, s_decay_value, s_diffusion_value]) - # generate dataframe - df_substrate = pd.DataFrame(ll_sub, columns=ls_column) - df_substrate.set_index('substrate', inplace=True) - df_substrate.columns.name = 'attribute' - # store values - d_mcds['substrate']['df_substarte'] = df_substrate - - ## get conc df - # flatten mesh coordnates - ar_m, ar_n, ar_p = d_mcds['mesh']['mnp_grid'] - ar_m = ar_m.flatten(order='C') - ar_n = ar_n.flatten(order='C') - ar_p = ar_p.flatten(order='C') - # get mesh spacing - dm, dn, dp = d_mcds['mesh']['mnp_spacing'] - # get voxel coordinates - ai_i = ((ar_m - ar_m.min()) / dm) - ai_j = ((ar_n - ar_n.min()) / dn) - ai_k = ((ar_p - ar_p.min()) / dp) - # handle coordinates - ls_column = [ - 'voxel_i','voxel_j','voxel_k', - 'mesh_center_m','mesh_center_n','mesh_center_p' - ] - la_data = [ai_i, ai_j, ai_k, ar_m, ar_n, ar_p] - # handle concentrations - for s_substrate in d_mcds['substrate']['ls_substarte']: - ls_column.append(s_substrate) - ar_conc = d_mcds['raw_substrate'][s_substrate]['data'].copy() - la_data.append(ar_conc.flatten(order='C')) - # generate dataframe - aa_data = np.array(la_data) - df_conc = pd.DataFrame(aa_data.T, columns=ls_column) - df_conc['time'] = d_mcds['metadata']['current_time'] - df_conc['runtime'] = d_mcds['metadata']['current_runtime'] / 60 # in min - df_conc['xmlfile'] = self.xmlfile - d_dtype = {'voxel_i': int, 'voxel_j': int, 'voxel_k': int} - df_conc = df_conc.astype(d_dtype) - # store values - df_conc.sort_values(['voxel_i', 'voxel_j', 'voxel_k', 'time'], axis=0, inplace=True) - df_conc.reset_index(drop=True, inplace=True) - df_conc.index.name = 'index' - d_mcds['substrate']['df_conc'] = df_conc + d_mcds['continuum_variables'][s_substrate]['data'][j, i, k] = ar_microenv[4+i_s, i_voxel] #################### # handle cell data # #################### + if self.verbose: print('working on discrete cell data ...') @@ -2703,9 +2652,8 @@ def _read_xml(self, xmlfile, output_path='.'): try: for x_celltype in x_celldata.find('cell_types').findall('type'): s_id = str(x_celltype.get('ID')) - # I don't like spaces in cell type names! - s_celltype = x_celltype.text #.replace(' ','_') # ROH - d_mcds['cell']['ds_celltype'].update({s_id : s_celltype}) + s_celltype = (x_celltype.text).replace(' ', '_') + d_mcds['metadata']['cell_type'].update({s_id : s_celltype}) b_celltype = True except AttributeError: pass @@ -2713,67 +2661,66 @@ def _read_xml(self, xmlfile, output_path='.'): # metadata cell_type label:id mapping detection ~ label information lost (silver quality) if not b_celltype: for x_label in x_celldata.find('labels').findall('label'): - # I don't like spaces in cell type names! - s_variable = x_label.tex #.replace(' ','_') # ROH + s_variable = x_label.text.replace(' ', '_') if s_variable in es_var_cell: for i_id in range(int(x_label.get('size'))): s_id = str(i_id) - d_mcds['cell']['ds_celltype'].update({s_id : s_id}) + d_mcds['metadata']['cell_type'].update({s_id : s_id}) b_celltype = True # iterate over labels which are children of labels these will be used to label data arrays ls_variable = [] for x_label in x_celldata.find('labels').findall('label'): # I don't like spaces in my dictionary keys! - s_variable = x_label.text #.replace(' ','_') # ROH + s_variable = x_label.text.replace(' ', '_') i_variable = int(x_label.get('size')) s_unit = x_label.get('units') # variable unique for each celltype substrate combination if s_variable in es_var_subs: - if (len(d_mcds['substrate']['ds_substrate']) > 0): + if (len(d_mcds['metadata']['substrate']) > 0): # continuum_variable id label sorting (becaus this is an id label mapping dict) - ls_substrate = [d_mcds['substrate']['ds_substrate'][o_key] for o_key in sorted(d_mcds['substrate']['ds_substrate'].keys(), key=int)] + ls_substrate = [d_mcds['metadata']['substrate'][o_key] for o_key in sorted(d_mcds['metadata']['substrate'].keys(), key=int)] for s_substrate in ls_substrate: s_variable_subs = s_substrate + '_' + s_variable ls_variable.append(s_variable_subs) - d_mcds['raw_cell']['units'].update({s_variable_subs : s_unit}) + d_mcds['discrete_cells']['units'].update({s_variable_subs : s_unit}) else: ls_substrate = [str(i_substrate) for i_substrate in range(i_variable)] for s_substrate in ls_substrate: s_variable_subs = s_variable + '_' + s_substrate ls_variable.append(s_variable_subs) - d_mcds['raw_cell']['units'].update({s_variable_subs : s_unit}) + d_mcds['discrete_cells']['units'].update({s_variable_subs : s_unit}) # variable unique for each celltype celltype combination elif s_variable in es_var_cell: - if (len(d_mcds['cell']['ds_celltype']) > 0): - # raw_cell id label sorting (becaus this is an id label mapping dict) - ls_celltype = [d_mcds['cell']['ds_celltype'][o_key] for o_key in sorted(d_mcds['cell']['ds_celltype'].keys(), key=int)] + if (len(d_mcds['metadata']['cell_type']) > 0): + # discrete_cells id label sorting (becaus this is an id label mapping dict) + ls_celltype = [d_mcds['metadata']['cell_type'][o_key] for o_key in sorted(d_mcds['metadata']['cell_type'].keys(), key=int)] for s_celltype in ls_celltype: s_variable_celltype = s_celltype + '_' + s_variable ls_variable.append(s_variable_celltype) - d_mcds['raw_cell']['units'].update({s_variable_celltype : s_unit}) + d_mcds['discrete_cells']['units'].update({s_variable_celltype : s_unit}) else: ls_celltype = [str(i_celltype) for i_celltype in range(i_variable)] for s_celltype in ls_celltype: s_variable_celltype = s_variable + '_' + s_celltype ls_variable.append(s_variable_celltype) - d_mcds['raw_cell']['units'].update({s_variable_celltype : s_unit}) + d_mcds['discrete_cells']['units'].update({s_variable_celltype : s_unit}) # variable unique for each dead model elif s_variable in es_var_death: for i_deathrate in range(i_variable): s_variable_deathrate = s_variable + '_' + str(i_deathrate) ls_variable.append(s_variable_deathrate) - d_mcds['raw_cell']['units'].update({s_variable_deathrate : s_unit}) + d_mcds['discrete_cells']['units'].update({s_variable_deathrate : s_unit}) # spatial variable elif s_variable in es_var_spatial: for s_axis in ['_x','_y','_z']: s_variable_spatial = s_variable + s_axis ls_variable.append(s_variable_spatial) - d_mcds['raw_cell']['units'].update({s_variable_spatial: s_unit}) + d_mcds['discrete_cells']['units'].update({s_variable_spatial: s_unit}) # simple variable and vectors else: @@ -2782,7 +2729,7 @@ def _read_xml(self, xmlfile, output_path='.'): ls_variable.append(f'{s_variable}_{str(i_n).zfill(3)}') else: ls_variable.append(s_variable) - d_mcds['raw_cell']['units'].update({s_variable : s_unit}) + d_mcds['discrete_cells']['units'].update({s_variable : s_unit}) # load the file s_cellpathfile = self.path + '/' + x_celldata.find('filename').text @@ -2792,99 +2739,76 @@ def _read_xml(self, xmlfile, output_path='.'): print(f'reading: {s_cellpathfile}') except ValueError: # hack: some old PhysiCell versions generates a corrupt cells.mat file, if there are zero cells. if self.verbose: - print(f'Warning @ TimeStep._read_xml : corrupt {s_cellpathfile} detected!\nassuming time step with zero cells because of a known bug in PhysiCell MultiCellDS version 0.5 output.') + print(f'Warning @ pyMCDS._read_xml : corrupt {s_cellpathfile} detected!\nassuming time step with zero cells because of a known bug in PhysiCell MultiCellDS version 0.5 output.') ar_cell = np.empty([len(ls_variable),0]) # check for column label mapping error (as good as it gets) if (ar_cell.shape[0] != len(ls_variable)): - sys.exit(f'Error @ TimeStep._read_xml : extracted column label list leng {len(ls_variable)} and data array shape {ar_cell.shape} are incompatible!') + sys.exit(f'Error @ pyMCDS._read_xml : extracted column label list leng {len(ls_variable)} and data array shape {ar_cell.shape} are incompatible!') # metadata cell_type label:id mapping detection ~ label information lost (bronze quality) if not b_celltype: for r_celltype in set(ar_cell[ls_variable.index('cell_type'),:]): s_celltype = str(int(r_celltype)) - d_mcds['cell']['ds_celltype'].update({s_celltype : s_celltype}) + d_mcds['metadata']['cell_type'].update({s_celltype : s_celltype}) b_celltype = True # store data - d_mcds['raw_cell']['data'] = {} + d_mcds['discrete_cells']['data'] = {} for i_col in range(len(ls_variable)): - d_mcds['raw_cell']['data'].update({ls_variable[i_col]: ar_cell[i_col,:]}) + d_mcds['discrete_cells']['data'].update({ls_variable[i_col]: ar_cell[i_col,:]}) - ## get celltype list - ds_celltype = d_mcds['cell']['ds_celltype'] - ls_celltype = [ds_celltype[s_key] for s_key in sorted(ds_celltype, key=int)] - # store values - d_mcds['cell']['ls_celltype'] = ls_celltype + ##################### + # handle graph data # + ##################### + d_mcds['discrete_cells']['graph'] = {} + d_mcds['discrete_cells']['graph'].update({'neighbor_cells': {}}) + d_mcds['discrete_cells']['graph'].update({'attached_cells': {}}) + d_mcds['discrete_cells']['graph'].update({'spring_attached_cells': {}}) - ## get cell df - # get cell position and more - df_cell = pd.DataFrame(d_mcds['raw_cell']['data']) - df_cell['time'] = d_mcds['metadata']['current_time'] - df_cell['runtime'] = d_mcds['metadata']['current_runtime'] / 60 # in min - df_cell['xmlfile'] = self.xmlfile - df_voxel = df_cell.loc[:,['position_x','position_y','position_z']].copy() + if self.graph: + if self.verbose: + print('working on graph data ...') - # get mesh spacing - dm, dn, dp = d_mcds['mesh']['mnp_spacing'] + # neighborhood cell graph + s_cellpathfile = self.path + '/' + x_cell.find('neighbor_graph').find('filename').text + dei_graph = graphfile_parser(s_pathfile=s_cellpathfile) + if self.verbose: + print(f'reading: {s_cellpathfile}') - # get mesh and voxel min max values - tr_m_range, tr_n_range, tr_p_range = d_mcds['mesh']['mnp_range'] - tr_i_range, tr_j_range, tr_k_range = d_mcds['mesh']['ijk_range'] + # store data + d_mcds['discrete_cells']['graph'].update({'neighbor_cells': dei_graph}) - # get voxel for each cell - df_voxel.loc[:,'voxel_i'] = np.round((df_voxel.loc[:,'position_x'] - tr_m_range[0]) / dm).astype(int) - df_voxel.loc[:,'voxel_j'] = np.round((df_voxel.loc[:,'position_y'] - tr_n_range[0]) / dn).astype(int) - df_voxel.loc[:,'voxel_k'] = np.round((df_voxel.loc[:,'position_z'] - tr_p_range[0]) / dp).astype(int) - df_voxel.loc[(df_voxel.voxel_i > tr_i_range[1]), 'voxel_i'] = tr_i_range[1] # i_max - df_voxel.loc[(df_voxel.voxel_i < tr_i_range[0]), 'voxel_i'] = tr_i_range[0] # i_min - df_voxel.loc[(df_voxel.voxel_j > tr_j_range[1]), 'voxel_j'] = tr_j_range[1] # j_max - df_voxel.loc[(df_voxel.voxel_j < tr_j_range[0]), 'voxel_j'] = tr_j_range[0] # j_min - df_voxel.loc[(df_voxel.voxel_k > tr_k_range[1]), 'voxel_k'] = tr_k_range[1] # k_max - df_voxel.loc[(df_voxel.voxel_k < tr_k_range[0]), 'voxel_k'] = tr_k_range[0] # k_min + # attached cell graph + s_cellpathfile = self.path + '/' + x_cell.find('attached_cells_graph').find('filename').text + dei_graph = graphfile_parser(s_pathfile=s_cellpathfile) + if self.verbose: + print(f'reading: {s_cellpathfile}') - # merge voxel (inner join) - df_cell = pd.merge(df_cell, df_voxel, on=['position_x', 'position_y', 'position_z']) + # store data + d_mcds['discrete_cells']['graph'].update({'attached_cells': dei_graph}) - # merge cell_density (left join) - df_cellcount = df_cell.loc[:,['voxel_i','voxel_j','voxel_k','ID']].groupby(['voxel_i','voxel_j','voxel_k']).count().reset_index() - ls_column = list(df_cellcount.columns) - ls_column[-1] = 'cell_count_voxel' - df_cellcount.columns = ls_column - s_density = f"cell_density_{d_mcds['metadata']['spatial_unit']}3" - df_cellcount[s_density] = df_cellcount.loc[:,'cell_count_voxel'] / d_mcds['mesh']['volume'] - df_cell = pd.merge( - df_cell, - df_cellcount, - on = ['voxel_i', 'voxel_j', 'voxel_k'], - how = 'left', - ) + # spring attached cell graph + try: + s_cellpathfile = self.path + '/' + x_cell.find('spring_attached_cells_graph').find('filename').text + dei_graph = graphfile_parser(s_pathfile=s_cellpathfile) + if self.verbose: + print(f'reading: {s_cellpathfile}') - # get column label set - es_column = set(df_cell.columns) + # store data + d_mcds['discrete_cells']['graph'].update({'spring_attached_cells': dei_graph}) + except AttributeError: + pass - # get vector length - for s_var_spatial in es_var_spatial: - es_vector = es_column.intersection({f'{s_var_spatial}_x',f'{s_var_spatial}_y',f'{s_var_spatial}_z'}) - if len(es_vector) > 0: - # linear algebra - #a_vector = df_cell.loc[:,ls_vector].values - #a_length = np.sqrt(np.diag(np.dot(a_vector, a_vector.T))) - # pythoagoras - a_length = None - for s_vector in es_vector: - a_vectorsq = df_cell.loc[:,s_vector].values**2 - if (a_length is None): - a_length = a_vectorsq - else: - a_length += a_vectorsq - a_length = a_length**(1/2) - # result - df_cell[f'{s_var_spatial}_vectorlength'] = a_length - # physiboss + ######################### + # handle physiboss data # + ######################### + + d_mcds['discrete_cells']['physiboss'] = None + if self.physiboss: if self.verbose: print('working on physiboss data ...') @@ -2908,155 +2832,14 @@ def _read_xml(self, xmlfile, output_path='.'): for s_node in sorted(es_node): df_physiboss[f'node_{s_node}'] = df_physiboss.state.str.find(s_node) > -1 - # store data - df_cell = pd.merge( - df_cell, - df_physiboss, - left_index = True, - right_index = True, - how = 'left', - ) - elif self.verbose: - print(f'Warning @ TimeStep._read_xml : physiboss file missing {s_intracellpathfile}.') + print(f'Warning @ pyMCDS._read_xml : physiboss file missing {s_intracellpathfile}.') else: pass - - # microenvironment - if self.microenv: - # merge substrate (left join) - df_sub = d_mcds['substrate']['df_substarte'] - for s_sub in df_sub.index: - for s_rate in df_sub.columns: - s_var = f'{s_sub}_{s_rate}' - df_cell[s_var] = df_sub.loc[s_sub,s_rate] - - # merge concentration (left join) - df_conc = d_mcds['substrate']['df_conc'].copy() # voxel and mesh coordinates - df_conc.drop({'time', 'runtime','xmlfile'}, axis=1, inplace=True) - df_cell = pd.merge( - df_cell, - df_conc, - on = ['voxel_i', 'voxel_j', 'voxel_k'], - how = 'left', - ) - - # variable typing - do_type = {} - [do_type.update({k:v}) for k,v in do_var_type.items() if k in es_column] - do_type.update(self.custom_data_type) - do_int = do_type.copy() - [do_int.update({k:int}) for k in do_int.keys()] - ls_int = sorted(do_int.keys()) - df_cell.loc[:,ls_int] = df_cell.loc[:,ls_int].round() - df_cell = df_cell.astype(do_int) - df_cell = df_cell.astype(do_type) - - # categorical translation - try: # bue 20240805: missing in MCDS version <= 0.5 (November 2021) - df_cell.loc[:,'current_death_model'] = df_cell.loc[:,'current_death_model'].replace(ds_death_model) # bue 20230614: this column looks like an artefact to me - except KeyError: - pass - df_cell.loc[:,'cycle_model'] = df_cell.loc[:,'cycle_model'].replace(ds_cycle_model) - df_cell.loc[:,'cycle_model'] = df_cell.loc[:,'cycle_model'].replace(ds_death_model) - df_cell.loc[:,'current_phase'] = df_cell.loc[:,'current_phase'].replace(ds_cycle_phase) - df_cell.loc[:,'current_phase'] = df_cell.loc[:,'current_phase'].replace(ds_death_phase) - df_cell.loc[:,'cell_type'] = df_cell.loc[:,'cell_type'].replace(d_mcds['cell']['ds_celltype']) - df_cell.loc[:,'chemotaxis_index'] = df_cell.loc[:,'chemotaxis_index'].replace(d_mcds['substrate']['ds_substrate']) - - # store - df_cell = df_cell.loc[:,sorted(df_cell.columns)] - df_cell.sort_values('ID', axis=0, inplace=True) - df_cell.set_index('ID', inplace=True) - d_mcds['cell']['df_cell'] = df_cell.copy() - - ## get cell attribute list - d_mcds['cell']['ls_cellattr'] = sorted(set(d_mcds['cell']['df_cell'].columns).difference(es_coor_cell)) - - - #################### - # handle unit data # - #################### - - if self.verbose: - print('working on unit data ...') - - # extract data - ds_unit = {} - - # units for metadata parameters - ds_unit.update({'time': d_mcds['metadata']['time_units']}) - ds_unit.update({'runtime': d_mcds['metadata']['runtime_units']}) - ds_unit.update({'spatial_unit': d_mcds['metadata']['spatial_unit']}) - - # microenvironment - if self.microenv: - for s_substrate in d_mcds['substrate']['ls_substarte']: - # unit from substrate parameters - s_unit = d_mcds['raw_substrate'][s_substrate]['units'] - ds_unit.update({s_substrate: s_unit}) - - # units from microenvironment parameters - s_diffusion_key = f'{s_substrate}_diffusion_coefficient' - s_diffusion_unit = d_mcds['raw_substrate'][s_substrate]['diffusion_coefficient']['units'] - ds_unit.update({s_diffusion_key: s_diffusion_unit}) - - s_decay_key = f'{s_substrate}_decay_rate' - s_decay_unit = d_mcds['raw_substrate'][s_substrate]['decay_rate']['units'] - ds_unit.update({s_decay_key: s_decay_unit}) - - # units from cell parameters - ds_unit.update(d_mcds['raw_cell']['units']) - - # output - del ds_unit['ID'] - d_mcds['metadata']['ds_unit'] = ds_unit - - - ##################### - # handle graph data # - ##################### - - d_mcds['cell']['dei_graph'] = {} - d_mcds['cell']['dei_graph'].update({'neighbor_cells': {}}) - d_mcds['cell']['dei_graph'].update({'attached_cells': {}}) - d_mcds['cell']['dei_graph'].update({'spring_attached_cells': {}}) - - if self.graph: - if self.verbose: - print('working on graph data ...') - - # neighborhood cell graph - s_cellpathfile = self.path + '/' + x_cell.find('neighbor_graph').find('filename').text - dei_graph = graphfile_parser(s_pathfile=s_cellpathfile) - if self.verbose: - print(f'reading: {s_cellpathfile}') - - # store data - d_mcds['cell']['dei_graph'].update({'neighbor_cells': dei_graph}) - - # attached cell graph - s_cellpathfile = self.path + '/' + x_cell.find('attached_cells_graph').find('filename').text - dei_graph = graphfile_parser(s_pathfile=s_cellpathfile) - if self.verbose: - print(f'reading: {s_cellpathfile}') - # store data - d_mcds['cell']['dei_graph'].update({'attached_cells': dei_graph}) - - # spring attached cell graph - try: - s_cellpathfile = self.path + '/' + x_cell.find('spring_attached_cells_graph').find('filename').text - dei_graph = graphfile_parser(s_pathfile=s_cellpathfile) - if self.verbose: - print(f'reading: {s_cellpathfile}') - - # store data - d_mcds['cell']['dei_graph'].update({'spring_attached_cells': dei_graph}) - except AttributeError: - pass + d_mcds['discrete_cells']['physiboss'] = df_physiboss ########## diff --git a/pcdl/timeseries.py b/pcdl/pyMCDSts.py similarity index 75% rename from pcdl/timeseries.py rename to pcdl/pyMCDSts.py index b3d1ae3..95ab9c8 100644 --- a/pcdl/timeseries.py +++ b/pcdl/pyMCDSts.py @@ -1,5 +1,5 @@ ######### -# title: timeseries.py +# title: pyMCDSts.py # # language: python3 # date: 2022-08-22 @@ -7,25 +7,25 @@ # authors: Patrick Wall, Randy Heiland, Paul Macklin, Elmar Bucher # # description: -# timeseries.py defines an object class, able to load and access +# pyMCDSts.py defines an object class, able to load and access # within python a time series of mcds objects loaded from a single -# PhysiCell model output directory. timeseries.py was first forked from +# PhysiCell model output directory. pyMCDSts.py was first forked from # PhysiCell-Tools python-loader, where it was implemented as # pyMCDS_timeseries.py, then totally rewritten and further developed. -#### +# +# the make_image and make_movie functions are cloned from the PhysiCell +# Makefile. note on difference image magick convert and mogrify: +# + https://graphicsmagick-tools.narkive.com/9Sowc4HF/gm-tools-mogrify-vs-convert +######### # load libraries -import anndata as ad -import bioio_base -from bioio.writers import OmeTiffWriter import glob import matplotlib.pyplot as plt import numpy as np import os import pandas as pd -from pcdl import render_neuroglancer -from pcdl.timestep import TimeStep, es_coor_cell, es_coor_conc, _anndextract +from pcdl.pyMCDS import pyMCDS, es_coor_cell, es_coor_conc import platform import sys @@ -80,15 +80,14 @@ def make_gif(path, interface='jpeg'): if path.endswith('/'): path = path[:-1] if not os.path.isdir(path): sys.exit(f'Error @ make_gif : {path} path does not exist.') - s_ofile = path.split('/')[-1] - if s_ofile.startswith('.'): s_ofile = s_ofile[1:] - if (len(s_ofile) == 0): s_ofile = 'movie' - s_ofile += f'_{interface}.gif' - s_ofile = s_ofile.replace(' ','_') - s_opathfile = path + '/' + s_ofile + s_file = path.split('/')[-1] + if s_file.startswith('.'): s_file = s_file[1:] + if (len(s_file) == 0): s_file = 'movie' + s_file += f'_{interface}.gif' + s_opathfile = f'{path}/{s_file}' s_ipathfiles = f'{path}/*.{interface}' # genaerate gif - s_cmd = f'{s_magick}convert "{s_ipathfiles}" "{s_opathfile}"' + s_cmd = f'{s_magick}convert {s_ipathfiles} {s_opathfile}' if (os.system(s_cmd) != 0): sys.exit("Error @ make_gif : imagemagick could not generatet the gif.") @@ -134,7 +133,6 @@ def make_movie(path, interface='jpeg', framerate=12): if s_ofile.startswith('.'): s_ofile = s_ofile[1:] if (len(s_ofile) == 0): s_ofile = 'movie' s_ofile += f'_{interface}{framerate}.mp4' - s_ofile = s_ofile.replace(' ','_') s_opathfile = f'{s_path}/{s_ofile}' # generate input file list @@ -146,7 +144,7 @@ def make_movie(path, interface='jpeg', framerate=12): f.close() # genearete movie - s_cmd = f'ffmpeg -y -r {framerate} -f concat -i ffmpeginput.txt -vcodec libx264 -pix_fmt yuv420p -strict -2 -tune animation -crf 15 -acodec none "{s_ofile}"' # -safe 0 + s_cmd = f'ffmpeg -y -r {framerate} -f concat -i ffmpeginput.txt -vcodec libx264 -pix_fmt yuv420p -strict -2 -tune animation -crf 15 -acodec none {s_ofile}' # -safe 0 if (os.system(s_cmd) != 0): sys.exit("Error @ make_movie : ffmpeg could not generatet the movie.") os.remove('ffmpeginput.txt') @@ -160,7 +158,7 @@ def make_movie(path, interface='jpeg', framerate=12): # classes # ########### -class TimeSeries: +class pyMCDSts: def __init__(self, output_path='.', custom_data_type={}, load=True, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True): """ input: @@ -180,10 +178,11 @@ def __init__(self, output_path='.', custom_data_type={}, load=True, microenv=Tru microenv: boole; default True should the microenvironment data be loaded? - setting microenv to False will use less memory and speed up processing. + setting microenv to False will use less memory and speed up + processing, similar to the original pyMCDS_cells.py script. graph: boole; default True - should the graphs, like cell_neighbor_graph.txt, be loaded? + should the graphs be loaded? setting graph to False will use less memory and speed up processing. physiboss: boole; default True @@ -200,12 +199,12 @@ def __init__(self, output_path='.', custom_data_type={}, load=True, microenv=Tru setting verbose to False for less text output, while processing. output: - mcdsts: TimeSeries class instance + mcdsts: pyMCDSts class instance this instance offers functions to process all stored time steps from a simulation. description: - TimeSeries.__init__ generates a class instance the instance offers + pyMCDSts.__init__ generates a class instance the instance offers functions to process all time steps in the output_path directory. """ output_path = output_path.replace('\\','/') @@ -214,7 +213,7 @@ def __init__(self, output_path='.', custom_data_type={}, load=True, microenv=Tru if (output_path.endswith('/')) and (len(output_path) > 1): output_path = output_path[:-1] if not os.path.isdir(output_path): - print(f'Error @ TimeSeries.__init__ : this is not a path! could not load {output_path}.') + print(f'Error @ pyMCDSts.__init__ : this is not a path! could not load {output_path}.') self.path = output_path self.ls_xmlfile = [s_pathfile.replace('\\','/').split('/')[-1] for s_pathfile in sorted(glob.glob(self.path + f'/output*.xml'))] # bue 2022-10-22: is output*.xml always the correct pattern? self.custom_data_type = custom_data_type @@ -227,7 +226,6 @@ def __init__(self, output_path='.', custom_data_type={}, load=True, microenv=Tru self.read_mcds() else: self.l_mcds = None - self.l_annmcds = None def set_verbose_false(self): @@ -259,8 +257,6 @@ def set_verbose_true(self): def make_gif(self, path, interface='jpeg'): """ help(pcdl.make_gif) - try: mcdsts.make_gif(mcdsts.plot_scatter()) - try: mcdsts.make_gif(mcdsts.plot_contour('substrate')) """ s_opathfile = make_gif(path=path, interface=interface) return s_opathfile @@ -269,32 +265,17 @@ def make_gif(self, path, interface='jpeg'): def make_movie(self, path, interface='jpeg', framerate=12): """ help(pcdl.make_movie) - try: mcdsts.make_movie(mcdsts.plot_scatter()) - try: mcdsts.make_movie(mcdsts.plot_contour('substrate')) """ s_opathfile = make_movie(path=path, interface=interface, framerate=framerate) return s_opathfile - def render_neuroglancer(self, tiffpathfile, timestep=0, intensity_cmap='gray'): - """ - help(pcdl.render_neuroglancer) - try: mcdsts.render_neuroglancer(mcdsts.make_ome_tiff(), 0) - """ - o_viewer = render_neuroglancer( - tiffpathfile = tiffpathfile, - timestep = timestep, - intensity_cmap = intensity_cmap, - ) - return o_viewer - - ## LOAD DATA ## def get_xmlfile_list(self): """ input: - self: TimeSeries class instance. + self: pyMCDSts class instance. output: xmlfile_list: list of strings @@ -312,7 +293,7 @@ def get_xmlfile_list(self): def read_mcds(self, xmlfile_list=None): """ input: - self: TimeSeries class instance. + self: pyMCDSts class instance. xmlfile_list: list of strings; default None list of physicell output*.xml strings. @@ -322,7 +303,7 @@ def read_mcds(self, xmlfile_list=None): description: the function returns a list of mcds objects loaded by - TimeStep calls. + pyMCDS calls. """ # handle input if (xmlfile_list is None): @@ -333,7 +314,7 @@ def read_mcds(self, xmlfile_list=None): # load mcds objects into list l_mcds = [] for s_xmlpathfile in ls_xmlpathfile: - mcds = TimeStep( + mcds = pyMCDS( xmlfile = s_xmlpathfile, custom_data_type = self.custom_data_type, microenv = self.microenv, @@ -355,7 +336,7 @@ def read_mcds(self, xmlfile_list=None): def get_mcds_list(self): """ input: - self: TimeSeries class instance. + self: pyMCDSts class instance. output: self.l_mcds: list of chronologically ordered mcds objects. @@ -373,7 +354,7 @@ def get_mcds_list(self): def get_conc_df(self, values=1, drop=set(), keep=set(), collapse=True): """ input: - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have to be outputted. @@ -458,7 +439,7 @@ def get_conc_df(self, values=1, drop=set(), keep=set(), collapse=True): def get_conc_attribute(self, values=1, drop=set(), keep=set(), allvalues=False): """ input: - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have @@ -521,10 +502,10 @@ def get_conc_attribute(self, values=1, drop=set(), keep=set(), allvalues=False): return dlr_variable_range - def plot_contour(self, focus, z_slice=0.0, extrema=None, alpha=1, fill=True, cmap='viridis', title='', grid=True, xlim=None, ylim=None, xyequal=True, figsizepx=None, ext='jpeg', figbgcolor=None, **kwargs): + def plot_contour(self, focus, z_slice=0.0, extrema=None, alpha=1, fill=True, cmap='viridis', title='', grid=True, xlim=None, ylim=None, xyequal=True, figsizepx=None, directory=None, ext='jpeg', figbgcolor=None): """ input: - self: TimeSeries class instance + self: pyMCDSts class instance focus: string column name within conc dataframe, for example. @@ -575,6 +556,11 @@ def plot_contour(self, focus, z_slice=0.0, extrema=None, alpha=1, fill=True, cma None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is jpeg output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -583,11 +569,6 @@ def plot_contour(self, focus, z_slice=0.0, extrema=None, alpha=1, fill=True, cma or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the matplotlib contour and contourf function. - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contour.html - + https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html - output: fig: matplotlib figures, depending on ext, either as files or as objects. the figures contains the contour plot and color bar. @@ -656,9 +637,9 @@ def plot_contour(self, focus, z_slice=0.0, extrema=None, alpha=1, fill=True, cma xyequal = xyequal, ax = None, figsizepx = figsizepx, + directory = directory, ext = ext, figbgcolor = figbgcolor, - **kwargs, ) lo_output.append(o_output) @@ -666,9 +647,11 @@ def plot_contour(self, focus, z_slice=0.0, extrema=None, alpha=1, fill=True, cma return lo_output - def make_conc_vtk(self): + def make_conc_vtk(self, visualize=True): """ input: + visualize: boolean; default is False + additionally, visualize cells using vtk renderer. output: ls_vtkpathfile: one vtk file per mcds time step that contains @@ -686,7 +669,7 @@ def make_conc_vtk(self): # processing ls_vtkpathfile = [] for mcds in self.get_mcds_list(): - s_vtkpathfile = mcds.make_conc_vtk() + s_vtkpathfile = mcds.make_conc_vtk(visualize=visualize) ls_vtkpathfile.append(s_vtkpathfile) # output @@ -698,7 +681,7 @@ def make_conc_vtk(self): def get_cell_df(self, values=1, drop=set(), keep=set(), collapse=True): """ input: - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have to be outputted. @@ -786,7 +769,7 @@ def get_cell_df(self, values=1, drop=set(), keep=set(), collapse=True): def get_cell_attribute(self, values=1, drop=set(), keep=set(), allvalues=False): """ input: - self: TimeSeries class instance. + self: pyMCDSts class instance. values: integer; default is 1 minimal number of values a variable has to have @@ -851,10 +834,10 @@ def get_cell_attribute(self, values=1, drop=set(), keep=set(), allvalues=False): return dl_variable_range - def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cmap='viridis', title='', grid=True, legend_loc='lower left', xlim=None, ylim=None, xyequal=True, s=1.0, figsizepx=None, ext='jpeg', figbgcolor=None, **kwargs): + def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cmap='viridis', title='', grid=True, legend_loc='lower left', xlim=None, ylim=None, xyequal=True, s=1.0, figsizepx=None, directory=None, ext='jpeg', figbgcolor=None): """ input: - self: TimeSeries class instance + self: pyMCDSts class instance focus: string; default is 'cell_type' column name within cell dataframe. @@ -915,6 +898,11 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma None tries to take the values from the initial.svg file. fall back setting is [640, 480]. + directory: string; default None + if None, a meaningful output directory name will be generated, + based on focus and z_slice parameters, else the resulting plots + will be moved to the explicit name directory. + ext: string; default is jpeg output image format. possible formats are jpeg, png, and tiff. None will return the matplotlib fig object. @@ -923,10 +911,6 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma or white (jpeg, tiff). figure background color. - **kwargs: possible additional keyword arguments input, - handled by the pandas dataframe plot function. - + https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame - output: fig: matplotlib figures, depending on ext, either as files or as objects. the figures contains the scatter plot and @@ -964,9 +948,9 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma s = s, ax = None, figsizepx = figsizepx, + directory = directory, ext = ext, figbgcolor = figbgcolor, - **kwargs, ) lo_output.append(o_output) @@ -974,12 +958,15 @@ def plot_scatter(self, focus='cell_type', z_slice=0.0, z_axis=None, alpha=1, cma return lo_output - def make_cell_vtk(self, attribute=['cell_type']): + def make_cell_vtk(self, attribute=['cell_type'], visualize=False): """ input: attribute: list of strings; default is ['cell_type'] column name within cell dataframe. + visualize: boolean; default is False + additionally, visualize cells using vtk renderer. + output: ls_vtkpathfile: one 3D glyph vtk file per mcds time step that contains cells. @@ -997,6 +984,7 @@ def make_cell_vtk(self, attribute=['cell_type']): for mcds in self.get_mcds_list(): s_vtkpathfile = mcds.make_cell_vtk( attribute = attribute, + visualize = visualize, ) ls_vtkpathfile.append(s_vtkpathfile) @@ -1004,147 +992,12 @@ def make_cell_vtk(self, attribute=['cell_type']): return ls_vtkpathfile - ## OME TIFF RELATED FUNCTIONS ## - - def make_ome_tiff(self, cell_attribute='ID', conc_cutoff={}, focus=None, file=True, collapse=True): - """ - input: - cell_attribute: strings; default is 'ID', which will result in a - cell segmentation mask. - column name within the cell dataframe. - the column data type has to be numeric (bool, int, float) - and cannot be string. - the result will be stored as 32 bit float. - - conc_cutoff: dictionary string to real; default is an empty dictionary. - if a contour from a substrate not should be cut by greater - than zero (shifted to integer 1), another cutoff value can be specified here. - - focus: set of strings; default is a None - set of substrate and cell_type names to specify what will be - translated into ome tiff format. - if None, all substrates and cell types will be processed. - - file: boolean; default True - if True, an ome tiff file is the output. - if False, a numpy array with shape tczyx is the output. - - collapse: boole; default True - should all mcds time steps from the time series be collapsed - into one ome tiff file (numpy array), - or an ome tiff file (numpy array) for each time step? - - output: - a_tczyx_img: numpy array or ome tiff file. - - - description: - function to transform chosen mcdsts output into an 1[um] spaced - tczyx (time, channel, z-axis, y-axis, x-axis) ome tiff file or numpy array, - one substrate or cell_type per channel. - a ome tiff file is more or less: - a numpy array, containing the image information - and a xml, containing the microscopy metadata information, - like the channel labels. - the ome tiff file format can for example be read by the napari - or fiji (imagej) software. - - https://napari.org/stable/ - https://fiji.sc/ - """ - # for each T time step - l_tczyx_img = [] - for i, mcds in enumerate(self.get_mcds_list()): - # processing - b_file = True # 10 - if (not file and not collapse) or (not file and collapse) or (file and collapse): # 00, 01, 11 - b_file = False - o_tczyx_img = mcds.make_ome_tiff( - cell_attribute = cell_attribute, - conc_cutoff = conc_cutoff, - focus = focus, - file = b_file - ) - l_tczyx_img.append(o_tczyx_img) - - # handle channels - ls_substrate = mcds.get_substrate_list() - ls_celltype = mcds.get_celltype_list() - - if not (focus is None): - ls_substrate = [s_substrate for s_substrate in ls_substrate if s_substrate in set(focus)] - ls_celltype = [s_celltype for s_celltype in ls_celltype if s_celltype in set(focus)] - if (set(focus) != set(ls_substrate).union(set(ls_celltype))): - sys.exit(f'Error : {focus} not found in {ls_substrate} {ls_celltype}') - - # output 00 list of numpy arrays - if (not file and not collapse): # 00 - if self.verbose: - print(f'la_tczyx_img shape: {len(l_tczyx_img)} * {l_tczyx_img[0].shape}') - return l_tczyx_img - - # output 01 numpy array - elif (not file and collapse): # 01 - # numpy array - a_tczyx_img = np.array(l_tczyx_img) - if self.verbose: - print('a_tczyx_img shape:', a_tczyx_img.shape) - return a_tczyx_img - - # output 10 list of pathfile strings - elif (file and not collapse): # 10 - return l_tczyx_img - - # output 11 ometiff file - elif (file and collapse): # 11 - a_tczyx_img = np.array(l_tczyx_img) - if self.verbose: - print('a_tczyx_img shape:', a_tczyx_img.shape) - - # generate filename - s_channel = '' - for s_substrate in ls_substrate: - try: - r_value = conc_cutoff[s_substrate] - s_channel += f'_{s_substrate}{r_value}' - except KeyError: - s_channel += f'_{s_substrate}' - for s_celltype in ls_celltype: - s_channel += f'_{s_celltype}' - if len(ls_celltype) > 0: - s_channel += f'_{cell_attribute}' - s_tifffile = f"timeseries{s_channel.replace(' ','_')}.ome.tiff" - if (len(s_tifffile) > 255): - print(f"Warning: filename {len(s_tifffile)} > 255 character.") - s_tifffile = 'timeseries_channels.ome.tiff' - print(f"file name adjusted to {s_tifffile}.") - s_tiffpathfile = self.path + '/' + s_tifffile - - # save to file - OmeTiffWriter.save( - a_tczyx_img, - s_tiffpathfile, - dim_order = 'TCZYX', - #ome_xml=x_img, - channel_names = ls_substrate + ls_celltype, - image_names = [f'timeseries_{cell_attribute}'], - physical_pixel_sizes = bioio_base.types.PhysicalPixelSizes(mcds.get_voxel_spacing()[2], 1.0, 1.0), # z,y,x [um] - #channel_colors=, - #fs_kwargs={}, - ) - return s_tiffpathfile - - # error case - else: - sys.exit(f'Error @ make_ome_tiff : {file} {collapse} strange file collapse combination.') - - ## TIME SERIES RELATED FUNCTIONS ## - def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanmean, frame='cell', z_slice=None, logy=False, ylim=None, secondary_y=None, subplots=False, sharex=False, sharey=False, linestyle='-', linewidth=None, cmap=None, color=None, grid=True, legend=True, yunit=None, title=None, ax=None, figsizepx=[640, 480], ext=None, figbgcolor=None, **kwargs): + def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanmean, frame='cell', z_slice=None, logy=False, ylim=None, secondary_y=None, subplots=False, sharex=False, sharey=False, linestyle='-', linewidth=None, cmap=None, color=None, grid=True, legend=True, yunit=None, title=None, ax=None, figsizepx=[640, 480], ext=None, figbgcolor=None): """ input: - self: TimeSeries class instance + self: pyMCDSts class instance focus_cat: string; default is None categorical or boolean data column within dataframe specified under frame. @@ -1242,10 +1095,6 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme figure background color. only relevant if ext not is None. - **kwargs: possible additional keyword arguments input, - handled by the pandas series plot function. - + https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.plot.html - output: if ext is None: a fig matplotlib figure, containing the ax axis object, is returned. else: an image file is generated under the returned path. @@ -1313,7 +1162,7 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme mcds.set_verbose_true() # error else: - sys.exit(f"Error @ TimeSeries.plot_timeseries : unknown frame {frame}. known are cell_df and conc_df.") + sys.exit(f"Error @ pyMCDSts.plot_timeseries : unknown frame {frame}. known are cell_df and conc_df.") # handle z_slize if not (z_slice is None): df_frame = df_frame.loc[(df_frame.mesh_center_p == z_slice),:] @@ -1328,7 +1177,7 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme df_aggregate = o_aggregate.loc[:,[focus_num]] df_aggregate.columns = [r_time] else: - sys.exit(f'Error @ TimeSeries.plot_timeseries : {aggregate_num} calculation returns unexpected variable type {type(o_aggregate)}.\nthe expected type is a pandas Series or DataFrame.') + sys.exit(f'Error @ pyMCDSts.plot_timeseries : {aggregate_num} calculation returns unexpected variable type {type(o_aggregate)}.\nthe expected type is a pandas Series or DataFrame.') # store result if (df_series is None): df_series = df_aggregate @@ -1349,9 +1198,9 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme elif (focus_num == 'count'): ylabel = f'focus_num [{yunit}]' elif (yunit is None): - ylabel = f"{aggregate_num.__name__.replace('nan','')} {focus_num}" + ylabel = f"{aggregate_num.__name__.replace('np.nan','')} {focus_num}" else: - ylabel = f"{aggregate_num.__name__.replace('nan','')} {focus_num} [{yunit}]" + ylabel = f"{aggregate_num.__name__.replace('np.nan','')} {focus_num} [{yunit}]" # generate series line plot if (ax is None): @@ -1385,8 +1234,7 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme ylabel = ylabel, xlabel = f"time [{mcds.get_unit_dict()['time']}]", title = title, - ax = ax, - **kwargs, + ax = ax ) else: # if color @@ -1406,8 +1254,7 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme ylabel = ylabel, xlabel = f"time [{mcds.get_unit_dict()['time']}]", title = title, - ax = ax, - **kwargs, + ax = ax ) # output @@ -1415,10 +1262,9 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme return fig else: if (focus_num == 'count'): - s_ofile = f'timeseries_{frame}_{focus_cat}_{focus_num}.{ext}'.replace(' ','_') + s_pathfile = self.path + f'/timeseries_{frame}_{focus_cat}_{focus_num}.{ext}' else: - s_ofile = f"timeseries_{frame}_{focus_cat}_{focus_num}_{aggregate_num.__name__.replace('np.nan','')}.{ext}".replace(' ','_') - s_pathfile = self.path + '/' + s_ofile + s_pathfile = self.path + f"/timeseries_{frame}_{focus_cat}_{focus_num}_{aggregate_num.__name__.replace('np.nan','')}.{ext}" if figbgcolor is None: figbgcolor = 'auto' plt.tight_layout() @@ -1432,7 +1278,7 @@ def plot_timeseries(self, focus_cat=None, focus_num=None, aggregate_num=np.nanme def make_graph_gml(self, graph_type, edge_attribute=True, node_attribute=[]): """ input: - self: TimeSeries class instance. + self: pyMCDS class instance. graph_type: string to specify which physicell output data should be processed. @@ -1479,170 +1325,3 @@ def make_graph_gml(self, graph_type, edge_attribute=True, node_attribute=[]): # outout return ls_pathfile - - - ## ANNDATA RELATED FUNCTIONS ## - - def get_anndata(self, values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True): - """ - input: - values: integer; default is 1 - minimal number of values a variable has to have to be outputted. - variables that have only 1 state carry no information. - None is a state too. - - drop: set of strings; default is an empty set - set of column labels to be dropped for the dataframe. - don't worry: essential columns like ID, coordinates - and time will never be dropped. - Attention: when the keep parameter is given, then - the drop parameter has to be an empty set! - - keep: set of strings; default is an empty set - set of column labels to be kept in the dataframe. - don't worry: essential columns like ID, coordinates - and time will always be kept. - - scale: string; default 'maxabs' - specify how the data should be scaled. - possible values are None, maxabs, minmax, std. - for more input, check out: help(pcdl.scaler) - - collapse: boole; default True - should all mcds time steps from the time series be collapsed - into one single anndata object, or a list of anndata objects - for each time step? - - keep_mcds: boole; default True - should the loaded original mcds be kept in memory - after transformation? - - output: - annmcds or self.l_annmcds: anndata object or list of anndata objects. - what is returned depends on the collapse setting. - - description: - function to transform mcds time steps into one or many - anndata objects for downstream analysis. - """ - # initialize vaiable - l_annmcds = [] - df_anncount = None - df_annobs = None - ar_annobsm = None - - # variable triage - if (values < 2): - ls_column = list(self.l_mcds[0].get_cell_df(drop=drop, keep=keep).columns) - else: - ls_column = sorted(es_coor_cell.difference({'ID'})) - ls_column.extend(sorted(self.get_cell_attribute(values=values, drop=drop, keep=keep, allvalues=False).keys())) - - # collapse warning - if collapse and self.verbose: - print('Warning @ mcdsts.get_anndata : only df_cell data, but not graph data, can be collapsed.') - - # processing - lann_mcds = [] - i_mcds = len(self.l_mcds) - for i in range(i_mcds): - # fetch mcds - if keep_mcds: - mcds = self.l_mcds[i] - else: - mcds = self.l_mcds.pop(0) - # extract physicell version - s_physicellv = mcds.get_physicell_version(), - # extract time and dataframes - r_time = round(mcds.get_time(),9) - if self.verbose: - print(f'processing: {i+1}/{i_mcds} {r_time}[min] mcds into anndata obj.') - df_cell = mcds.get_cell_df() - df_cell = df_cell.loc[:,ls_column] - - # pack collapsed - if collapse: - # extract - df_count, df_obs, d_obsm, d_obsp, d_uns = _anndextract( - df_cell=df_cell, - scale = scale, - #graph_attached = {}, - #graph_neighbor = {}, - #graph_spring = {}, - #graph_method = s_physicellv, - ) - # count - df_count.reset_index(inplace=True) - df_count.index = df_count.ID + f'id_{r_time}min' - df_count.index.name = 'id_time' - df_count.drop('ID', axis=1, inplace=True) - if df_anncount is None: - df_anncount = df_count - else: - df_anncount = pd.concat([df_anncount, df_count], axis=0) - # obs - df_obs.reset_index(inplace=True) - df_obs.index = df_obs.ID + f'id_{r_time}min' - df_obs.index.name = 'id_time' - if df_annobs is None: - df_annobs = df_obs - else: - df_annobs = pd.concat([df_annobs, df_obs], axis=0) - # obsm (spatial) - if ar_annobsm is None: - ar_annobsm = d_obsm['spatial'] - else: - ar_annobsm = np.vstack([ar_annobsm, d_obsm['spatial']]) - # obsp: nop (graph) - # uns: nop (graph) - - # pack not collapsed - else: - # extract - df_count, df_obs, d_obsm, d_obsp, d_uns = _anndextract( - df_cell=df_cell, - scale = scale, - graph_attached = mcds.get_attached_graph_dict(), - graph_neighbor = mcds.get_neighbor_graph_dict(), - graph_spring = mcds.get_spring_graph_dict(), - graph_method = s_physicellv, - ) - # annmcds - ann_mcds = ad.AnnData( - X = df_count, - obs = df_obs, - obsm = d_obsm, - obsp = d_obsp, - uns = d_uns, - ) - lann_mcds.append(ann_mcds) - - # output - if collapse: - ann_mcdsts = ad.AnnData( - X = df_anncount, - obs = df_annobs, - obsm = {'spatial': ar_annobsm}, - #obsp = d_obsp, - #uns = d_uns - ) - return ann_mcdsts - else: - self.l_annmcds = lann_mcds - return self.l_annmcds - - - def get_annmcds_list(self): - """ - input: - self: TimeSeries class instance. - - output: - self.l_annmcds: list of chronologically ordered anndata mcds objects. - watch out, this is a pointer to the - self.l_annmcds list of anndata mcds objects, not a copy of self.l_annmcds! - - description: - function returns a binding to the self.l_annmcds list of anndata mcds objects. - """ - return self.l_annmcds diff --git a/pyproject.toml b/pyproject.toml index 23190fc..039b19b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dynamic = ["version"] description = "physicell data loader (pcdl) provides a platform independent, python3 based, pip installable interface to load output, generated with the PhysiCell agent based modeling framework, into python3." readme = "README.md" -requires-python = ">=3.10, <4" +requires-python = ">=3.9, <4" license = "BSD-3-Clause" #license-files = {paths = ["LICENSE"]} @@ -71,14 +71,10 @@ classifiers = [ # bue 2024-12-06: enforcing some versions dependencies = [ "anndata>=0.10.8", - "bioio>=1.2.1", # needs numpy < 2.0.0 - "bioio-ome-tiff", "matplotlib", - "neuroglancer", "numpy", "pandas>=2.2.2", "requests", - "scikit-image>=0.24.0", "scipy>=1.13.0", "vtk", ] @@ -87,30 +83,28 @@ dependencies = [ [project.scripts] # special thanks to Miguel Ponce-de-Leon who introduced me to entry point scripts! # metadata -pcdl_get_version = "pcdl.commandline:get_version" -pcdl_get_unit_dict = "pcdl.commandline:get_unit_dict" +pcdl_get_version = "pcdl.pyCLI:get_version" +pcdl_get_unit_dict = "pcdl.pyCLI:get_unit_dict" # substrate -pcdl_get_substrate_list = "pcdl.commandline:get_substrate_list" -pcdl_get_conc_attribute = "pcdl.commandline:get_conc_attribute" -pcdl_get_conc_df = "pcdl.commandline:get_conc_df" -pcdl_plot_contour = "pcdl.commandline:plot_contour" -pcdl_make_conc_vtk = "pcdl.commandline:make_conc_vtk" +pcdl_get_substrate_list = "pcdl.pyCLI:get_substrate_list" +pcdl_get_conc_attribute = "pcdl.pyCLI:get_conc_attribute" +pcdl_get_conc_df = "pcdl.pyCLI:get_conc_df" +pcdl_plot_contour = "pcdl.pyCLI:plot_contour" +pcdl_make_conc_vtk = "pcdl.pyCLI:make_conc_vtk" # cell agent -pcdl_get_celltype_list = "pcdl.commandline:get_celltype_list" -pcdl_get_cell_attribute_list = "pcdl.commandline:get_cell_attribute_list" -pcdl_get_cell_attribute = "pcdl.commandline:get_cell_attribute" -pcdl_get_cell_df = "pcdl.commandline:get_cell_df" -pcdl_get_anndata = "pcdl.commandline:get_anndata" -pcdl_make_graph_gml = "pcdl.commandline:make_graph_gml" -pcdl_plot_scatter = "pcdl.commandline:plot_scatter" -pcdl_make_cell_vtk = "pcdl.commandline:make_cell_vtk" +pcdl_get_celltype_list = "pcdl.pyCLI:get_celltype_list" +pcdl_get_cell_attribute_list = "pcdl.pyCLI:get_cell_attribute_list" +pcdl_get_cell_attribute = "pcdl.pyCLI:get_cell_attribute" +pcdl_get_cell_df = "pcdl.pyCLI:get_cell_df" +pcdl_get_anndata = "pcdl.pyCLI:get_anndata" +pcdl_make_graph_gml = "pcdl.pyCLI:make_graph_gml" +pcdl_plot_scatter = "pcdl.pyCLI:plot_scatter" +pcdl_make_cell_vtk = "pcdl.pyCLI:make_cell_vtk" # substrate and cell agent -pcdl_plot_timeseries = "pcdl.commandline:plot_timeseries" -pcdl_make_ome_tiff = "pcdl.commandline:make_ome_tiff" -pcdl_render_neuroglancer = "pcdl.commandline:render_neuroglancer" +pcdl_plot_timeseries = "pcdl.pyCLI:plot_timeseries" # making movies -pcdl_make_gif = "pcdl.commandline:make_gif" -pcdl_make_movie = "pcdl.commandline:make_movie" +pcdl_make_gif = "pcdl.pyCLI:make_gif" +pcdl_make_movie = "pcdl.pyCLI:make_movie" [project.urls] diff --git a/test/pcmodel/Makefile b/test/pcmodel/Makefile index b0d63af..7aa7d18 100644 --- a/test/pcmodel/Makefile +++ b/test/pcmodel/Makefile @@ -3,11 +3,11 @@ PROGRAM_NAME := project CC := g++ # CC := g++-mp-7 # typical macports compiler name -# CC := g++-7 # typical homebrew compiler name +# CC := g++-7 # typical homebrew compiler name -# Check for environment definitions of compiler +# Check for environment definitions of compiler # e.g., on CC = g++-7 on OSX -ifdef PHYSICELL_CPP +ifdef PHYSICELL_CPP CC := $(PHYSICELL_CPP) endif @@ -18,8 +18,8 @@ endif ARCH := native # best auto-tuning # ARCH := core2 # a reasonably safe default for most CPUs since 2007 # ARCH := corei7 -# ARCH := corei7-avx # earlier i7 -# ARCH := core-avx-i # i7 ivy bridge or newer +# ARCH := corei7-avx # earlier i7 +# ARCH := core-avx-i # i7 ivy bridge or newer # ARCH := core-avx2 # i7 with Haswell or newer # ARCH := nehalem # ARCH := westmere @@ -31,7 +31,7 @@ ARCH := native # best auto-tuning # ARCH := bonnell # ARCH := silvermont # ARCH := skylake-avx512 -# ARCH := nocona #64-bit pentium 4 or later +# ARCH := nocona #64-bit pentium 4 or later # CFLAGS := -march=$(ARCH) -Ofast -s -fomit-frame-pointer -mfpmath=both -fopenmp -m64 -std=c++11 CFLAGS := -march=$(ARCH) -O3 -fomit-frame-pointer -mfpmath=both -fopenmp -m64 -std=c++11 @@ -53,7 +53,7 @@ COMPILE_COMMAND := $(CC) $(CFLAGS) $(EXTRA_FLAGS) LINK_COMMAND := $(CC) $(CFLAGS_LINK) $(EXTRA_FLAGS) BioFVM_OBJECTS := BioFVM_vector.o BioFVM_mesh.o BioFVM_microenvironment.o BioFVM_solvers.o BioFVM_matlab.o \ -BioFVM_utilities.o BioFVM_basic_agent.o BioFVM_MultiCellDS.o BioFVM_agent_container.o +BioFVM_utilities.o BioFVM_basic_agent.o BioFVM_MultiCellDS.o BioFVM_agent_container.o PhysiCell_core_OBJECTS := PhysiCell_phenotype.o PhysiCell_cell_container.o PhysiCell_standard_models.o \ PhysiCell_cell.o PhysiCell_custom.o PhysiCell_utilities.o PhysiCell_constants.o PhysiCell_basic_signaling.o \ @@ -71,10 +71,10 @@ pugixml_OBJECTS := pugixml.o PhysiCell_OBJECTS := $(BioFVM_OBJECTS) $(pugixml_OBJECTS) $(PhysiCell_core_OBJECTS) $(PhysiCell_module_OBJECTS) ALL_OBJECTS := $(PhysiCell_OBJECTS) $(PhysiCell_custom_module_OBJECTS) -# compile the project +# compile the project all: main.cpp $(ALL_OBJECTS) - $(COMPILE_COMMAND) -o $(PROGRAM_NAME) $(ALL_OBJECTS) main.cpp + $(COMPILE_COMMAND) -o $(PROGRAM_NAME) $(ALL_OBJECTS) main.cpp make name static: main.cpp $(ALL_OBJECTS) $(MaBoSS) @@ -85,70 +85,70 @@ name: @echo "Executable name is" $(PROGRAM_NAME) @echo "" -# PhysiCell core components +# PhysiCell core components PhysiCell_phenotype.o: ./core/PhysiCell_phenotype.cpp $(COMPILE_COMMAND) -c ./core/PhysiCell_phenotype.cpp - + PhysiCell_digital_cell_line.o: ./core/PhysiCell_digital_cell_line.cpp $(COMPILE_COMMAND) -c ./core/PhysiCell_digital_cell_line.cpp PhysiCell_cell.o: ./core/PhysiCell_cell.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_cell.cpp + $(COMPILE_COMMAND) -c ./core/PhysiCell_cell.cpp PhysiCell_cell_container.o: ./core/PhysiCell_cell_container.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_cell_container.cpp - + $(COMPILE_COMMAND) -c ./core/PhysiCell_cell_container.cpp + PhysiCell_standard_models.o: ./core/PhysiCell_standard_models.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_standard_models.cpp - + $(COMPILE_COMMAND) -c ./core/PhysiCell_standard_models.cpp + PhysiCell_utilities.o: ./core/PhysiCell_utilities.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_utilities.cpp - + $(COMPILE_COMMAND) -c ./core/PhysiCell_utilities.cpp + PhysiCell_custom.o: ./core/PhysiCell_custom.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_custom.cpp - + $(COMPILE_COMMAND) -c ./core/PhysiCell_custom.cpp + PhysiCell_constants.o: ./core/PhysiCell_constants.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_constants.cpp - + $(COMPILE_COMMAND) -c ./core/PhysiCell_constants.cpp + PhysiCell_signal_behavior.o: ./core/PhysiCell_signal_behavior.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_signal_behavior.cpp - + $(COMPILE_COMMAND) -c ./core/PhysiCell_signal_behavior.cpp + PhysiCell_rules.o: ./core/PhysiCell_rules.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_rules.cpp + $(COMPILE_COMMAND) -c ./core/PhysiCell_rules.cpp # BioFVM core components (needed by PhysiCell) - + BioFVM_vector.o: ./BioFVM/BioFVM_vector.cpp - $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_vector.cpp + $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_vector.cpp BioFVM_agent_container.o: ./BioFVM/BioFVM_agent_container.cpp - $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_agent_container.cpp - + $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_agent_container.cpp + BioFVM_mesh.o: ./BioFVM/BioFVM_mesh.cpp - $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_mesh.cpp + $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_mesh.cpp BioFVM_microenvironment.o: ./BioFVM/BioFVM_microenvironment.cpp - $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_microenvironment.cpp + $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_microenvironment.cpp BioFVM_solvers.o: ./BioFVM/BioFVM_solvers.cpp - $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_solvers.cpp + $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_solvers.cpp BioFVM_utilities.o: ./BioFVM/BioFVM_utilities.cpp - $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_utilities.cpp - + $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_utilities.cpp + BioFVM_basic_agent.o: ./BioFVM/BioFVM_basic_agent.cpp - $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_basic_agent.cpp - + $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_basic_agent.cpp + BioFVM_matlab.o: ./BioFVM/BioFVM_matlab.cpp $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_matlab.cpp BioFVM_MultiCellDS.o: ./BioFVM/BioFVM_MultiCellDS.cpp $(COMPILE_COMMAND) -c ./BioFVM/BioFVM_MultiCellDS.cpp - + pugixml.o: ./BioFVM/pugixml.cpp $(COMPILE_COMMAND) -c ./BioFVM/pugixml.cpp - + # standard PhysiCell modules PhysiCell_SVG.o: ./modules/PhysiCell_SVG.cpp @@ -165,158 +165,158 @@ PhysiCell_various_outputs.o: ./modules/PhysiCell_various_outputs.cpp PhysiCell_pugixml.o: ./modules/PhysiCell_pugixml.cpp $(COMPILE_COMMAND) -c ./modules/PhysiCell_pugixml.cpp - + PhysiCell_settings.o: ./modules/PhysiCell_settings.cpp $(COMPILE_COMMAND) -c ./modules/PhysiCell_settings.cpp - + PhysiCell_basic_signaling.o: ./core/PhysiCell_basic_signaling.cpp - $(COMPILE_COMMAND) -c ./core/PhysiCell_basic_signaling.cpp - + $(COMPILE_COMMAND) -c ./core/PhysiCell_basic_signaling.cpp + PhysiCell_geometry.o: ./modules/PhysiCell_geometry.cpp - $(COMPILE_COMMAND) -c ./modules/PhysiCell_geometry.cpp - + $(COMPILE_COMMAND) -c ./modules/PhysiCell_geometry.cpp + # user-defined PhysiCell modules -custom.o: ./custom_modules/custom.cpp +custom.o: ./custom_modules/custom.cpp $(COMPILE_COMMAND) -c ./custom_modules/custom.cpp # cleanup reset: - rm -f *.cpp - cp ./sample_projects/Makefile-default Makefile + rm -f *.cpp + cp ./sample_projects/Makefile-default Makefile rm -f ./custom_modules/* - touch ./custom_modules/empty.txt - touch ALL_CITATIONS.txt + touch ./custom_modules/empty.txt + touch ALL_CITATIONS.txt touch ./core/PhysiCell_cell.cpp - rm ALL_CITATIONS.txt - cp ./config/PhysiCell_settings-backup.xml ./config/PhysiCell_settings.xml + rm ALL_CITATIONS.txt + cp ./config/PhysiCell_settings-backup.xml ./config/PhysiCell_settings.xml touch ./config/empty.csv rm -f ./config/*.csv - + clean: rm -f *.o rm -f $(PROGRAM_NAME)* - + data-cleanup: rm -rf ./output mkdir ./output touch ./output/empty.txt - -# archival - -checkpoint: - zip -r $$(date +%b_%d_%Y_%H%M).zip Makefile *.cpp *.h config/*.xml custom_modules/* - + +# archival + +checkpoint: + zip -r $$(date +%b_%d_%Y_%H%M).zip Makefile *.cpp *.h config/*.xml custom_modules/* + zip: - zip -r latest.zip Makefile* *.cpp *.h BioFVM/* config/* core/* custom_modules/* matlab/* modules/* sample_projects/* + zip -r latest.zip Makefile* *.cpp *.h BioFVM/* config/* core/* custom_modules/* matlab/* modules/* sample_projects/* cp latest.zip $$(date +%b_%d_%Y_%H%M).zip - cp latest.zip VERSION_$(VERSION).zip + cp latest.zip VERSION_$(VERSION).zip mv *.zip archives/ - + tar: - tar --ignore-failed-read -czf latest.tar Makefile* *.cpp *.h BioFVM/* config/* core/* custom_modules/* matlab/* modules/* sample_projects/* + tar --ignore-failed-read -czf latest.tar Makefile* *.cpp *.h BioFVM/* config/* core/* custom_modules/* matlab/* modules/* sample_projects/* cp latest.tar $$(date +%b_%d_%Y_%H%M).tar cp latest.tar VERSION_$(VERSION).tar mv *.tar archives/ -unzip: - cp ./archives/latest.zip . - unzip latest.zip - -untar: +unzip: + cp ./archives/latest.zip . + unzip latest.zip + +untar: cp ./archives/latest.tar . tar -xzf latest.tar -# easier animation +# easier animation FRAMERATE := 24 OUTPUT := output -jpeg: - @magick identify -format "%h" $(OUTPUT)/initial.svg > __H.txt - @magick identify -format "%w" $(OUTPUT)/initial.svg > __W.txt - @expr 2 \* \( $$(grep . __H.txt) / 2 \) > __H1.txt - @expr 2 \* \( $$(grep . __W.txt) / 2 \) > __W1.txt - @echo "$$(grep . __W1.txt)!x$$(grep . __H1.txt)!" > __resize.txt +jpeg: + @magick identify -format "%h" $(OUTPUT)/initial.svg > __H.txt + @magick identify -format "%w" $(OUTPUT)/initial.svg > __W.txt + @expr 2 \* \( $$(grep . __H.txt) / 2 \) > __H1.txt + @expr 2 \* \( $$(grep . __W.txt) / 2 \) > __W1.txt + @echo "$$(grep . __W1.txt)!x$$(grep . __H1.txt)!" > __resize.txt @magick mogrify -format jpg -resize $$(grep . __resize.txt) $(OUTPUT)/s*.svg - rm -f __H*.txt __W*.txt __resize.txt - -gif: - magick convert $(OUTPUT)/s*.svg $(OUTPUT)/out.gif - + rm -f __H*.txt __W*.txt __resize.txt + +gif: + magick convert $(OUTPUT)/s*.svg $(OUTPUT)/out.gif + movie: ffmpeg -r $(FRAMERATE) -f image2 -i $(OUTPUT)/snapshot%08d.jpg -vcodec libx264 -pix_fmt yuv420p -strict -2 -tune animation -crf 15 -acodec none $(OUTPUT)/out.mp4 + +# upgrade rules -# upgrade rules - -SOURCE := PhysiCell_upgrade.zip -get-upgrade: - @echo $$(curl https://raw.githubusercontent.com/MathCancer/PhysiCell/master/VERSION.txt) > VER.txt - @echo https://github.com/MathCancer/PhysiCell/releases/download/$$(grep . VER.txt)/PhysiCell_V.$$(grep . VER.txt).zip > DL_FILE.txt +SOURCE := PhysiCell_upgrade.zip +get-upgrade: + @echo $$(curl https://raw.githubusercontent.com/MathCancer/PhysiCell/master/VERSION.txt) > VER.txt + @echo https://github.com/MathCancer/PhysiCell/releases/download/$$(grep . VER.txt)/PhysiCell_V.$$(grep . VER.txt).zip > DL_FILE.txt rm -f VER.txt $$(curl -L $$(grep . DL_FILE.txt) --output PhysiCell_upgrade.zip) - rm -f DL_FILE.txt + rm -f DL_FILE.txt -PhysiCell_upgrade.zip: - make get-upgrade +PhysiCell_upgrade.zip: + make get-upgrade upgrade: $(SOURCE) unzip $(SOURCE) PhysiCell/VERSION.txt - mv -f PhysiCell/VERSION.txt . - unzip $(SOURCE) PhysiCell/core/* - cp -r PhysiCell/core/* core - unzip $(SOURCE) PhysiCell/modules/* - cp -r PhysiCell/modules/* modules - unzip $(SOURCE) PhysiCell/sample_projects/* - cp -r PhysiCell/sample_projects/* sample_projects - unzip $(SOURCE) PhysiCell/BioFVM/* + mv -f PhysiCell/VERSION.txt . + unzip $(SOURCE) PhysiCell/core/* + cp -r PhysiCell/core/* core + unzip $(SOURCE) PhysiCell/modules/* + cp -r PhysiCell/modules/* modules + unzip $(SOURCE) PhysiCell/sample_projects/* + cp -r PhysiCell/sample_projects/* sample_projects + unzip $(SOURCE) PhysiCell/BioFVM/* cp -r PhysiCell/BioFVM/* BioFVM unzip $(SOURCE) PhysiCell/documentation/User_Guide.pdf mv -f PhysiCell/documentation/User_Guide.pdf documentation rm -f -r PhysiCell - rm -f $(SOURCE) + rm -f $(SOURCE) # use: make save PROJ=your_project_name PROJ := my_project -save: +save: echo "Saving project as $(PROJ) ... " mkdir -p ./user_projects mkdir -p ./user_projects/$(PROJ) mkdir -p ./user_projects/$(PROJ)/custom_modules - mkdir -p ./user_projects/$(PROJ)/config + mkdir -p ./user_projects/$(PROJ)/config cp main.cpp ./user_projects/$(PROJ) cp Makefile ./user_projects/$(PROJ) cp VERSION.txt ./user_projects/$(PROJ) cp ./config/* ./user_projects/$(PROJ)/config cp ./custom_modules/* ./user_projects/$(PROJ)/custom_modules -load: +load: echo "Loading project from $(PROJ) ... " cp ./user_projects/$(PROJ)/main.cpp . cp ./user_projects/$(PROJ)/Makefile . - cp ./user_projects/$(PROJ)/config/* ./config/ - cp ./user_projects/$(PROJ)/custom_modules/* ./custom_modules/ + cp ./user_projects/$(PROJ)/config/* ./config/ + cp ./user_projects/$(PROJ)/custom_modules/* ./custom_modules/ pack: @echo " " @echo "Preparing project $(PROJ) for sharing ... " - @echo " " + @echo " " cd ./user_projects && zip -r $(PROJ).zip $(PROJ) @echo " " @echo "Share ./user_projects/$(PROJ).zip ... " @echo "Other users can unzip $(PROJ).zip in their ./user_projects, compile, and run." - @echo " " + @echo " " unpack: @echo " " @echo "Preparing shared project $(PROJ).zip for use ... " - @echo " " - cd ./user_projects && unzip $(PROJ).zip + @echo " " + cd ./user_projects && unzip $(PROJ).zip @echo " " @echo "Load this project via make load PROJ=$(PROJ) ... " - @echo " " + @echo " " list-user-projects: @echo "user projects::" diff --git a/test/pcmodel/config/PhysiCell_settings-backup.xml b/test/pcmodel/config/PhysiCell_settings-backup.xml new file mode 100644 index 0000000..e69de29 diff --git a/test/pcmodel/custom_modules/custom.cpp b/test/pcmodel/custom_modules/custom.cpp index df17f9c..59b23bd 100644 --- a/test/pcmodel/custom_modules/custom.cpp +++ b/test/pcmodel/custom_modules/custom.cpp @@ -69,136 +69,136 @@ void create_cell_types( void ) { - // set the random seed - if (parameters.ints.find_index("random_seed") != -1) - { - SeedRandom(parameters.ints("random_seed")); - } - - /* - Put any modifications to default cell definition here if you - want to have "inherited" by other cell types. - - This is a good place to set default functions. - */ - - initialize_default_cell_definition(); - cell_defaults.phenotype.secretion.sync_to_microenvironment( µenvironment ); - - cell_defaults.functions.volume_update_function = standard_volume_update_function; - cell_defaults.functions.update_velocity = standard_update_cell_velocity; - - cell_defaults.functions.update_migration_bias = NULL; - cell_defaults.functions.update_phenotype = NULL; // update_cell_and_death_parameters_O2_based; - cell_defaults.functions.custom_cell_rule = NULL; - cell_defaults.functions.contact_function = NULL; - - cell_defaults.functions.add_cell_basement_membrane_interactions = NULL; - cell_defaults.functions.calculate_distance_to_membrane = NULL; - - /* - This parses the cell definitions in the XML config file. - */ - - initialize_cell_definitions_from_pugixml(); - - /* - This builds the map of cell definitions and summarizes the setup. - */ - - build_cell_definitions_maps(); - - /* - This intializes cell signal and response dictionaries - */ - - setup_signal_behavior_dictionaries(); - - /* - Cell rule definitions - */ - - setup_cell_rules(); - - /* - Put any modifications to individual cell definitions here. - - This is a good place to set custom functions. - */ - - cell_defaults.functions.update_phenotype = phenotype_function; - cell_defaults.functions.custom_cell_rule = custom_function; - cell_defaults.functions.contact_function = contact_function; - - /* - This builds the map of cell definitions and summarizes the setup. - */ - - display_cell_definitions( std::cout ); - - return; + // set the random seed + if (parameters.ints.find_index("random_seed") != -1) + { + SeedRandom(parameters.ints("random_seed")); + } + + /* + Put any modifications to default cell definition here if you + want to have "inherited" by other cell types. + + This is a good place to set default functions. + */ + + initialize_default_cell_definition(); + cell_defaults.phenotype.secretion.sync_to_microenvironment( µenvironment ); + + cell_defaults.functions.volume_update_function = standard_volume_update_function; + cell_defaults.functions.update_velocity = standard_update_cell_velocity; + + cell_defaults.functions.update_migration_bias = NULL; + cell_defaults.functions.update_phenotype = NULL; // update_cell_and_death_parameters_O2_based; + cell_defaults.functions.custom_cell_rule = NULL; + cell_defaults.functions.contact_function = NULL; + + cell_defaults.functions.add_cell_basement_membrane_interactions = NULL; + cell_defaults.functions.calculate_distance_to_membrane = NULL; + + /* + This parses the cell definitions in the XML config file. + */ + + initialize_cell_definitions_from_pugixml(); + + /* + This builds the map of cell definitions and summarizes the setup. + */ + + build_cell_definitions_maps(); + + /* + This intializes cell signal and response dictionaries + */ + + setup_signal_behavior_dictionaries(); + + /* + Cell rule definitions + */ + + setup_cell_rules(); + + /* + Put any modifications to individual cell definitions here. + + This is a good place to set custom functions. + */ + + cell_defaults.functions.update_phenotype = phenotype_function; + cell_defaults.functions.custom_cell_rule = custom_function; + cell_defaults.functions.contact_function = contact_function; + + /* + This builds the map of cell definitions and summarizes the setup. + */ + + display_cell_definitions( std::cout ); + + return; } void setup_microenvironment( void ) { - // set domain parameters - - // put any custom code to set non-homogeneous initial conditions or - // extra Dirichlet nodes here. - - // initialize BioFVM - - initialize_microenvironment(); - - return; + // set domain parameters + + // put any custom code to set non-homogeneous initial conditions or + // extra Dirichlet nodes here. + + // initialize BioFVM + + initialize_microenvironment(); + + return; } void setup_tissue( void ) { - double Xmin = microenvironment.mesh.bounding_box[0]; - double Ymin = microenvironment.mesh.bounding_box[1]; - double Zmin = microenvironment.mesh.bounding_box[2]; - - double Xmax = microenvironment.mesh.bounding_box[3]; - double Ymax = microenvironment.mesh.bounding_box[4]; - double Zmax = microenvironment.mesh.bounding_box[5]; - - if( default_microenvironment_options.simulate_2D == true ) - { - Zmin = 0.0; - Zmax = 0.0; - } - - double Xrange = Xmax - Xmin; - double Yrange = Ymax - Ymin; - double Zrange = Zmax - Zmin; - - // create some of each type of cell - - Cell* pC; - - for( int k=0; k < cell_definitions_by_index.size() ; k++ ) - { - Cell_Definition* pCD = cell_definitions_by_index[k]; - std::cout << "Placing cells of type " << pCD->name << " ... " << std::endl; - for( int n = 0 ; n < parameters.ints("number_of_cells") ; n++ ) - { - std::vector position = {0,0,0}; - position[0] = Xmin + UniformRandom()*Xrange; - position[1] = Ymin + UniformRandom()*Yrange; - position[2] = Zmin + UniformRandom()*Zrange; - - pC = create_cell( *pCD ); - pC->assign_position( position ); - } - } - std::cout << std::endl; - - // load cells from your CSV file (if enabled) - load_cells_from_pugixml(); - set_parameters_from_distributions(); - - return; + double Xmin = microenvironment.mesh.bounding_box[0]; + double Ymin = microenvironment.mesh.bounding_box[1]; + double Zmin = microenvironment.mesh.bounding_box[2]; + + double Xmax = microenvironment.mesh.bounding_box[3]; + double Ymax = microenvironment.mesh.bounding_box[4]; + double Zmax = microenvironment.mesh.bounding_box[5]; + + if( default_microenvironment_options.simulate_2D == true ) + { + Zmin = 0.0; + Zmax = 0.0; + } + + double Xrange = Xmax - Xmin; + double Yrange = Ymax - Ymin; + double Zrange = Zmax - Zmin; + + // create some of each type of cell + + Cell* pC; + + for( int k=0; k < cell_definitions_by_index.size() ; k++ ) + { + Cell_Definition* pCD = cell_definitions_by_index[k]; + std::cout << "Placing cells of type " << pCD->name << " ... " << std::endl; + for( int n = 0 ; n < parameters.ints("number_of_cells") ; n++ ) + { + std::vector position = {0,0,0}; + position[0] = Xmin + UniformRandom()*Xrange; + position[1] = Ymin + UniformRandom()*Yrange; + position[2] = Zmin + UniformRandom()*Zrange; + + pC = create_cell( *pCD ); + pC->assign_position( position ); + } + } + std::cout << std::endl; + + // load cells from your CSV file (if enabled) + load_cells_from_pugixml(); + set_parameters_from_distributions(); + + return; } std::vector my_coloring_function( Cell* pCell ) @@ -208,7 +208,7 @@ void phenotype_function( Cell* pCell, Phenotype& phenotype, double dt ) { return; } void custom_function( Cell* pCell, Phenotype& phenotype , double dt ) -{ return; } +{ return; } void contact_function( Cell* pMe, Phenotype& phenoMe , Cell* pOther, Phenotype& phenoOther , double dt ) -{ return; } +{ return; } \ No newline at end of file diff --git a/test/pcmodel/custom_modules/custom.h b/test/pcmodel/custom_modules/custom.h index a7405d1..0e6df8d 100644 --- a/test/pcmodel/custom_modules/custom.h +++ b/test/pcmodel/custom_modules/custom.h @@ -66,27 +66,27 @@ */ #include "../core/PhysiCell.h" -#include "../modules/PhysiCell_standard_modules.h" +#include "../modules/PhysiCell_standard_modules.h" -using namespace BioFVM; +using namespace BioFVM; using namespace PhysiCell; -// setup functions to help us along +// setup functions to help us along void create_cell_types( void ); -void setup_tissue( void ); +void setup_tissue( void ); -// set up the BioFVM microenvironment -void setup_microenvironment( void ); +// set up the BioFVM microenvironment +void setup_microenvironment( void ); -// custom pathology coloring function +// custom pathology coloring function std::vector my_coloring_function( Cell* ); -// custom functions can go here +// custom functions can go here void phenotype_function( Cell* pCell, Phenotype& phenotype, double dt ); void custom_function( Cell* pCell, Phenotype& phenotype , double dt ); -void contact_function( Cell* pMe, Phenotype& phenoMe , Cell* pOther, Phenotype& phenoOther , double dt ); +void contact_function( Cell* pMe, Phenotype& phenoMe , Cell* pOther, Phenotype& phenoOther , double dt ); diff --git a/test/pcmodel/custom_modules/empty.txt b/test/pcmodel/custom_modules/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/pcmodel/main.cpp b/test/pcmodel/main.cpp index f8285d6..2f7e98c 100644 --- a/test/pcmodel/main.cpp +++ b/test/pcmodel/main.cpp @@ -74,181 +74,181 @@ #include #include "./core/PhysiCell.h" -#include "./modules/PhysiCell_standard_modules.h" +#include "./modules/PhysiCell_standard_modules.h" -// put custom code modules here! - -#include "./custom_modules/custom.h" +// put custom code modules here! +#include "./custom_modules/custom.h" + using namespace BioFVM; using namespace PhysiCell; int main( int argc, char* argv[] ) { - // load and parse settings file(s) - - bool XML_status = false; - char copy_command [1024]; - if( argc > 1 ) - { - XML_status = load_PhysiCell_config_file( argv[1] ); - sprintf( copy_command , "cp %s %s" , argv[1] , PhysiCell_settings.folder.c_str() ); - } - else - { - XML_status = load_PhysiCell_config_file( "./config/PhysiCell_settings.xml" ); - sprintf( copy_command , "cp ./config/PhysiCell_settings.xml %s" , PhysiCell_settings.folder.c_str() ); - } - if( !XML_status ) - { exit(-1); } - - // copy config file to output directry - system( copy_command ); - - // OpenMP setup - omp_set_num_threads(PhysiCell_settings.omp_num_threads); - - // time setup - std::string time_units = "min"; - - /* Microenvironment setup */ - - setup_microenvironment(); // modify this in the custom code - - /* PhysiCell setup */ - - // set mechanics voxel size, and match the data structure to BioFVM - double mechanics_voxel_size = 30; - Cell_Container* cell_container = create_cell_container_for_microenvironment( microenvironment, mechanics_voxel_size ); - - /* Users typically start modifying here. START USERMODS */ - - create_cell_types(); - - setup_tissue(); - - /* Users typically stop modifying here. END USERMODS */ - - // set MultiCellDS save options - - set_save_biofvm_mesh_as_matlab( true ); - set_save_biofvm_data_as_matlab( true ); - set_save_biofvm_cell_data( true ); - set_save_biofvm_cell_data_as_custom_matlab( true ); - - // save a simulation snapshot - - char filename[1024]; - sprintf( filename , "%s/initial" , PhysiCell_settings.folder.c_str() ); - save_PhysiCell_to_MultiCellDS_v2( filename , microenvironment , PhysiCell_globals.current_time ); - - // save a quick SVG cross section through z = 0, after setting its - // length bar to 200 microns - - PhysiCell_SVG_options.length_bar = 200; - - // for simplicity, set a pathology coloring function - - std::vector (*cell_coloring_function)(Cell*) = my_coloring_function; - std::string (*substrate_coloring_function)(double, double, double) = paint_by_density_percentage; - - sprintf( filename , "%s/initial.svg" , PhysiCell_settings.folder.c_str() ); - SVG_plot( filename , microenvironment, 0.0 , PhysiCell_globals.current_time, cell_coloring_function, substrate_coloring_function ); - - sprintf( filename , "%s/legend.svg" , PhysiCell_settings.folder.c_str() ); - create_plot_legend( filename , cell_coloring_function ); - - display_citations(); - - // set the performance timers - - BioFVM::RUNTIME_TIC(); - BioFVM::TIC(); - - std::ofstream report_file; - if( PhysiCell_settings.enable_legacy_saves == true ) - { - sprintf( filename , "%s/simulation_report.txt" , PhysiCell_settings.folder.c_str() ); - - report_file.open(filename); // create the data log file - report_file<<"simulated time\tnum cells\tnum division\tnum death\twall time"<update_all_cells( PhysiCell_globals.current_time ); - - /* - Custom add-ons could potentially go here. - */ - - PhysiCell_globals.current_time += diffusion_dt; - } - - if( PhysiCell_settings.enable_legacy_saves == true ) - { - log_output(PhysiCell_globals.current_time, PhysiCell_globals.full_output_index, microenvironment, report_file); - report_file.close(); - } - } - catch( const std::exception& e ) - { // reference to the base of a polymorphic object - std::cout << e.what(); // information from length_error printed - } - - // save a final simulation snapshot - - sprintf( filename , "%s/final" , PhysiCell_settings.folder.c_str() ); - save_PhysiCell_to_MultiCellDS_v2( filename , microenvironment , PhysiCell_globals.current_time ); - - sprintf( filename , "%s/final.svg" , PhysiCell_settings.folder.c_str() ); - SVG_plot(filename, microenvironment, 0.0, PhysiCell_globals.current_time, cell_coloring_function, substrate_coloring_function); - - // timer - - std::cout << std::endl << "Total simulation runtime: " << std::endl; - BioFVM::display_stopwatch_value( std::cout , BioFVM::runtime_stopwatch_value() ); - - return 0; + // load and parse settings file(s) + + bool XML_status = false; + char copy_command [1024]; + if( argc > 1 ) + { + XML_status = load_PhysiCell_config_file( argv[1] ); + sprintf( copy_command , "cp %s %s" , argv[1] , PhysiCell_settings.folder.c_str() ); + } + else + { + XML_status = load_PhysiCell_config_file( "./config/PhysiCell_settings.xml" ); + sprintf( copy_command , "cp ./config/PhysiCell_settings.xml %s" , PhysiCell_settings.folder.c_str() ); + } + if( !XML_status ) + { exit(-1); } + + // copy config file to output directry + system( copy_command ); + + // OpenMP setup + omp_set_num_threads(PhysiCell_settings.omp_num_threads); + + // time setup + std::string time_units = "min"; + + /* Microenvironment setup */ + + setup_microenvironment(); // modify this in the custom code + + /* PhysiCell setup */ + + // set mechanics voxel size, and match the data structure to BioFVM + double mechanics_voxel_size = 30; + Cell_Container* cell_container = create_cell_container_for_microenvironment( microenvironment, mechanics_voxel_size ); + + /* Users typically start modifying here. START USERMODS */ + + create_cell_types(); + + setup_tissue(); + + /* Users typically stop modifying here. END USERMODS */ + + // set MultiCellDS save options + + set_save_biofvm_mesh_as_matlab( true ); + set_save_biofvm_data_as_matlab( true ); + set_save_biofvm_cell_data( true ); + set_save_biofvm_cell_data_as_custom_matlab( true ); + + // save a simulation snapshot + + char filename[1024]; + sprintf( filename , "%s/initial" , PhysiCell_settings.folder.c_str() ); + save_PhysiCell_to_MultiCellDS_v2( filename , microenvironment , PhysiCell_globals.current_time ); + + // save a quick SVG cross section through z = 0, after setting its + // length bar to 200 microns + + PhysiCell_SVG_options.length_bar = 200; + + // for simplicity, set a pathology coloring function + + std::vector (*cell_coloring_function)(Cell*) = my_coloring_function; + std::string (*substrate_coloring_function)(double, double, double) = paint_by_density_percentage; + + sprintf( filename , "%s/initial.svg" , PhysiCell_settings.folder.c_str() ); + SVG_plot( filename , microenvironment, 0.0 , PhysiCell_globals.current_time, cell_coloring_function, substrate_coloring_function ); + + sprintf( filename , "%s/legend.svg" , PhysiCell_settings.folder.c_str() ); + create_plot_legend( filename , cell_coloring_function ); + + display_citations(); + + // set the performance timers + + BioFVM::RUNTIME_TIC(); + BioFVM::TIC(); + + std::ofstream report_file; + if( PhysiCell_settings.enable_legacy_saves == true ) + { + sprintf( filename , "%s/simulation_report.txt" , PhysiCell_settings.folder.c_str() ); + + report_file.open(filename); // create the data log file + report_file<<"simulated time\tnum cells\tnum division\tnum death\twall time"<update_all_cells( PhysiCell_globals.current_time ); + + /* + Custom add-ons could potentially go here. + */ + + PhysiCell_globals.current_time += diffusion_dt; + } + + if( PhysiCell_settings.enable_legacy_saves == true ) + { + log_output(PhysiCell_globals.current_time, PhysiCell_globals.full_output_index, microenvironment, report_file); + report_file.close(); + } + } + catch( const std::exception& e ) + { // reference to the base of a polymorphic object + std::cout << e.what(); // information from length_error printed + } + + // save a final simulation snapshot + + sprintf( filename , "%s/final" , PhysiCell_settings.folder.c_str() ); + save_PhysiCell_to_MultiCellDS_v2( filename , microenvironment , PhysiCell_globals.current_time ); + + sprintf( filename , "%s/final.svg" , PhysiCell_settings.folder.c_str() ); + SVG_plot(filename, microenvironment, 0.0, PhysiCell_globals.current_time, cell_coloring_function, substrate_coloring_function); + + // timer + + std::cout << std::endl << "Total simulation runtime: " << std::endl; + BioFVM::display_stopwatch_value( std::cout , BioFVM::runtime_stopwatch_value() ); + + return 0; } diff --git a/test/test_anndata_2d.py b/test/test_anndata_2d.py new file mode 100644 index 0000000..bbbcb44 --- /dev/null +++ b/test/test_anndata_2d.py @@ -0,0 +1,181 @@ +#### +# title: test_anndata_2d.py +# +# language: python3 +# author: Elmar Bucher +# date: 2023-06-24 +# license: BSD 3-Clause +# +# description: +# pytest unit test library for the pcdl library TimeStep and TimeSeries class. +# + https://docs.pytest.org/ +# +# note: +# assert actual == expected, message +# == value equality +# is reference equality +# pytest.approx for real values +##### + + +# load library +import numpy as np +import os +import pandas as pd +import pathlib +import pcdl + + +# const +s_path_2d = str(pathlib.Path(pcdl.__file__).parent.resolve()/'output_2d') +s_file_2d = 'output00000024.xml' +s_pathfile_2d = f'{s_path_2d}/{s_file_2d}' + + +# test data +if not os.path.exists(s_path_2d): + pcdl.install_data() + + +## helper function ## +class TestPyAnndataScaler(object): + ''' test for pcdl.scaler function ''' + a_x = np.array([[ 1.,-1., 2., 0.],[ 2., 0., 0.,0.],[ 0., 1.,-1.,0.]]) + df_x = pd.DataFrame(a_x, columns=['a','b','c','d']) + + def test_scaler_none(self, df_x=df_x): + df_scaled = pcdl.pyAnnData.scaler(df_x=df_x, scale=None) + assert(str(type(df_scaled)) == "") and \ + (all(df_scaled == df_x)) + + def test_scaler_minabs(self, df_x=df_x): + df_scaled = pcdl.pyAnnData.scaler(df_x=df_x, scale='maxabs') + assert(str(type(df_scaled)) == "") and \ + (df_scaled.values.sum().round(3) == 2.0) and \ + (df_scaled.values.min().round(3) == -1.0) and \ + (df_scaled.values.max().round(3) == 1.0) + + def test_scaler_minmax(self, df_x=df_x): + df_scaled = pcdl.pyAnnData.scaler(df_x=df_x, scale='minmax') + assert(str(type(df_scaled)) == "") and \ + (df_scaled.values.sum().round(3) == 4.333) and \ + (df_scaled.values.min().round(3) == 0.0) and \ + (df_scaled.values.max().round(3) == 1.0) + + def test_scaler_std(self, df_x=df_x): + df_scaled = pcdl.pyAnnData.scaler(df_x=df_x, scale='std') + assert(str(type(df_scaled)) == "") and \ + (df_scaled.values.sum().round(3) == 0.0) and \ + (df_scaled.values.min().round(3) == -1.0) and \ + (df_scaled.values.max().round(3) == 1.091) + + +## load physicell data time step ## +class TestPyAnndataTimeStep(object): + ''' test for pcdl.TimeStep class. ''' + + ## get_anndata command ## + def test_mcds_get_anndata(self): + mcds = pcdl.TimeStep(s_pathfile_2d, verbose=False) + ann = mcds.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs') + assert(str(type(mcds)) == "") and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 105) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 7) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 2) and \ + (len(ann.obsp) == 2) and \ + (ann.var.shape == (105, 0)) and \ + (len(ann.uns) == 1) + + +## load physicell data time series ## +class TestPyAnndataTimeSeries(object): + ''' test for pcdl.TestSeries class. ''' + + # get_anndata + # get_annmcds_list {integrated} + # value {1, _2_} + # collaps {True, _False_} + # keep_mcds {True, _False_} + + ## get_anndata command ## + def test_mcdsts_get_anndata(self): + mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) + ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 25) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (mcdsts.l_annmcds is None) and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 105) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 8) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 2) and \ + (len(ann.obsp) == 0) and \ + (ann.var.shape == (105, 0)) and \ + (len(ann.uns) == 0) + + def test_mcdsts_get_anndata_value(self): + mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) + ann = mcdsts.get_anndata(values=2, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 25) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (mcdsts.l_annmcds is None) and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 50) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 7) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 2) and \ + (len(ann.obsp) == 0) and \ + (ann.var.shape == (50, 0)) and \ + (len(ann.uns) == 0) + + def test_mcdsts_get_anndata_collapsefalse(self): + mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) + ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=False, keep_mcds=True) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 25) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (str(type(mcdsts.l_annmcds)) == "") and \ + (len(mcdsts.l_annmcds) == 25) and \ + (all([str(type(ann)) == "" for ann in mcdsts.l_annmcds])) and \ + (mcdsts.l_annmcds[24].X.shape[0] > 9) and \ + (mcdsts.l_annmcds[24].X.shape[1] == 105) and \ + (mcdsts.l_annmcds[24].obs.shape[0] > 9) and \ + (mcdsts.l_annmcds[24].obs.shape[1] == 7) and \ + (mcdsts.l_annmcds[24].obsm['spatial'].shape[0] > 9) and \ + (mcdsts.l_annmcds[24].obsm['spatial'].shape[1] == 2) and \ + (len(mcdsts.l_annmcds[24].obsp) == 4) and \ + (mcdsts.l_annmcds[24].var.shape == (105, 0)) and \ + (len(mcdsts.l_annmcds[24].uns) == 2) + + def test_mcdsts_get_anndata_keepmcdsfalse(self): + mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) + ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=False) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 0) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (mcdsts.l_annmcds is None) and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 105) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 8) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 2) and \ + (len(ann.obsp) == 0) and \ + (ann.var.shape == (105, 0)) and \ + (len(ann.uns) == 0) + diff --git a/test/test_anndata_3d.py b/test/test_anndata_3d.py new file mode 100644 index 0000000..27743e2 --- /dev/null +++ b/test/test_anndata_3d.py @@ -0,0 +1,157 @@ +#### +# title: test_anndata_3d.py +# +# language: python3 +# author: Elmar Bucher +# date: 2023-06-24 +# license: BSD 3-Clause +# +# description: +# pytest unit test library for the pcdl library TimeStep and TimeSeries class. +# + https://docs.pytest.org/ +# +# note: +# assert actual == expected, message +# == value equality +# is reference equality +# pytest.approx for real values +##### + + +# load library +import numpy as np +import os +import pandas as pd +import pathlib +import pcdl + + +# const +s_path_3d = str(pathlib.Path(pcdl.__file__).parent.resolve()/'output_3d') +s_file_3d = 'output00000024.xml' +s_pathfile_3d = f'{s_path_3d}/{s_file_3d}' + + +# test data +if not os.path.exists(s_path_3d): + pcdl.install_data() + + +########### +# 3D only # +########### + +## load physicell data time step ## +class TestPyAnndata3DTimeStep(object): + ''' test for pcdl.TimeStep class. ''' + + ## get_anndata command ## + def test_mcds_get_anndata(self): + mcds = pcdl.TimeStep(s_pathfile_3d, verbose=False) + ann = mcds.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs') + assert(str(type(mcds)) == "") and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 105) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 7) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 3) and \ + (len(ann.obsp) == 2) and \ + (ann.var.shape == (105, 0)) and \ + (len(ann.uns) == 1) + + +################## +# test for speed # +################## +# BUE: test functions are mirrored test_anndata_2d.py + +## load physicell data time series ## +class TestPyAnndata3DTimeSeries(object): + ''' test for pcdl.TestSeries class. ''' + + # get_anndata + # get_annmcds_list {integrated} + # value {1, _2_} + # collaps {True, _False_} + # keep_mcds {True, _False_} + + ## get_anndata command ## + def test_mcdsts_get_anndata(self): + mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) + ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 25) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (mcdsts.l_annmcds is None) and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 105) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 8) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 3) and \ + (len(ann.obsp) == 0) and \ + (ann.var.shape == (105, 0)) and \ + (len(ann.uns) == 0) + + def test_mcdsts_get_anndata_value(self): + mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) + ann = mcdsts.get_anndata(values=2, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 25) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (mcdsts.l_annmcds is None) and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 56) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 7) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 3) and \ + (len(ann.obsp) == 0) and \ + (ann.var.shape == (56, 0)) and \ + (len(ann.uns) == 0) + + def test_mcdsts_get_anndata_collapsefalse(self): + mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) + ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=False, keep_mcds=True) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 25) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (str(type(mcdsts.l_annmcds)) == "") and \ + (len(mcdsts.l_annmcds) == 25) and \ + (all([str(type(ann)) == "" for ann in mcdsts.l_annmcds])) and \ + (mcdsts.l_annmcds[24].X.shape[0] > 9) and \ + (mcdsts.l_annmcds[24].X.shape[1] == 105) and \ + (mcdsts.l_annmcds[24].obs.shape[0] > 9) and \ + (mcdsts.l_annmcds[24].obs.shape[1] == 7) and \ + (mcdsts.l_annmcds[24].obsm['spatial'].shape[0] > 9) and \ + (mcdsts.l_annmcds[24].obsm['spatial'].shape[1] == 3) and \ + (len(mcdsts.l_annmcds[24].obsp) == 4) and \ + (mcdsts.l_annmcds[24].var.shape == (105, 0)) and \ + (len(mcdsts.l_annmcds[24].uns) == 2) + + def test_mcdsts_get_anndata_keepmcdsfalse(self): + mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) + ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=False) + l_annmcds = mcdsts.get_annmcds_list() + assert(str(type(mcdsts)) == "") and \ + (len(mcdsts.l_mcds) == 0) and \ + (l_annmcds == mcdsts.l_annmcds) and \ + (mcdsts.l_annmcds is None) and \ + (str(type(ann)) == "") and \ + (ann.X.shape[0] > 9) and \ + (ann.X.shape[1] == 105) and \ + (ann.obs.shape[0] > 9) and \ + (ann.obs.shape[1] == 8) and \ + (ann.obsm['spatial'].shape[0] > 9) and \ + (ann.obsm['spatial'].shape[1] == 3) and \ + (len(ann.obsp) == 0) and \ + (ann.var.shape == (105, 0)) and \ + (len(ann.uns) == 0) + diff --git a/test/test_commandline_2d.py b/test/test_cli_2d.py similarity index 85% rename from test/test_commandline_2d.py rename to test/test_cli_2d.py index c0a26b1..e8df23a 100644 --- a/test/test_commandline_2d.py +++ b/test/test_cli_2d.py @@ -1,4 +1,4 @@ -##### +### # title: test_cli_2d.py # # language: python3 @@ -39,15 +39,15 @@ if not os.path.exists(s_path_2d): pcdl.install_data() -print(f"process: pcdl command line interface functions from the command line...") +print(f"process: pcdl pyCLI functions from the command line...") ############################## # metadata realted test code # ############################## -class TestCommandLineInterfaceVersion(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliVersion(object): + ''' tests for one pcdl.pyCli function. ''' # timestep: # + path (pathfile, path) ok @@ -70,8 +70,8 @@ def test_pcdl_get_version_timestep(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceUnitDict(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliUnitDict(object): + ''' tests for one pcdl.pyCli function. ''' # timestep: # + path (pathfile, path) ok @@ -129,8 +129,8 @@ def test_pcdl_get_unit_dict_timestep_settingxmlnone(self): # substarte relatd test code # ############################## -class TestCommandLineInterfaceSubstrateList(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliSubstrateList(object): + ''' tests for one pcdl.pyCli function. ''' # timestep: # + path (pathfile, path) ok @@ -153,8 +153,8 @@ def test_pcdl_get_substrate_list_timestep(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceConcDfAttribute(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliConcDfAttribute(object): + ''' tests for one pcdl.pyCli function. ''' # timeseries collapsed: # + path (str) nop @@ -210,8 +210,8 @@ def test_pcdl_get_conc_attribute_timeseries_allvalues(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceConcDf(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliConcDf(object): + ''' tests for one pcdl.pyCli function. ''' # timeseries # + collapse (true false) ok @@ -305,8 +305,8 @@ def test_pcdl_get_conc_df_timestep_keep(self): assert o_result.returncode == 0 -class TestCommandLineInterfacePlotContour(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliPlotContour(object): + ''' tests for one pcdl.pyCli function. ''' # time series and time steps. # + path nop @@ -362,8 +362,8 @@ def test_pcdl_plot_contour_set(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceConcVtk(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliConcVtk(object): + ''' tests for one pcdl.pyCli function. ''' # timestep and timeseries: # + path nop @@ -393,8 +393,8 @@ def test_pcdl_make_conc_vtk_timestep_default(self): # cell agent realted test code # ################################ -class TestCommandLineInterfaceCelltypeList(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliCelltypeList(object): + ''' tests for one pcdl.pyCli function. ''' # timestep: # + path (pathfile, path) ok @@ -417,59 +417,8 @@ def test_pcdl_get_celltype_list_timestep(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceCellAttributeList(object): - ''' tests for one pcdl command line interface function. ''' - - # timeseries collapsed: - # + path (str) nop - # + microenv (true, _false_) ok - # + physiboss (true, _false_) - # + settingxml (string, _none_, _false_) ok - # + verbose (true, _false_) nop - - def test_pcdl_get_cell_attribute_list_timeseries(self): - o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - assert o_result.returncode == 0 - - def test_pcdl_get_cell_attribute_list_timeseries_microenv(self): - o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--microenv', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - assert o_result.returncode == 0 - - def test_pcdl_get_cell_attribute_list_timeseries_physiboss(self): - o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--physiboss', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - assert o_result.returncode == 0 - - def test_pcdl_get_cell_attribute_list_timeseries_settingxmlfalse(self): - o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--settingxml', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - assert o_result.returncode == 0 - - def test_pcdl_get_cell_attribute_list_timeseries_settingxmlnone(self): - o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--settingxml', 'none'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - assert o_result.returncode == 0 - - -class TestCommandLineInterfaceCellDfAttribute(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliCellDfAttribute(object): + ''' tests for one pcdl.pyCli function. ''' # timeseries collapsed: # + path (str) nop @@ -574,9 +523,60 @@ def test_pcdl_get_cell_attribute_timeseries_allvalues(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceCellDf(object): +class TestPyCliCellAttributeList(object): ''' tests for one pcdl command line interface function. ''' + # timeseries collapsed: + # + path (str) nop + # + microenv (true, _false_) ok + # + physiboss (true, _false_) + # + settingxml (string, _none_, _false_) ok + # + verbose (true, _false_) nop + + def test_pcdl_get_cell_attribute_list_timeseries(self): + o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d], check=False, capture_output=True) + print(f'o_result: {o_result}\n') + print(f'o_result.returncode: {o_result.returncode}\n') + print(f'o_result.stdout: {o_result.stdout}\n') + print(f'o_result.stderr: {o_result.stderr}\n') + assert o_result.returncode == 0 + + def test_pcdl_get_cell_attribute_list_timeseries_microenv(self): + o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--microenv', 'false'], check=False, capture_output=True) + print(f'o_result: {o_result}\n') + print(f'o_result.returncode: {o_result.returncode}\n') + print(f'o_result.stdout: {o_result.stdout}\n') + print(f'o_result.stderr: {o_result.stderr}\n') + assert o_result.returncode == 0 + + def test_pcdl_get_cell_attribute_list_timeseries_physiboss(self): + o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--physiboss', 'false'], check=False, capture_output=True) + print(f'o_result: {o_result}\n') + print(f'o_result.returncode: {o_result.returncode}\n') + print(f'o_result.stdout: {o_result.stdout}\n') + print(f'o_result.stderr: {o_result.stderr}\n') + assert o_result.returncode == 0 + + def test_pcdl_get_cell_attribute_list_timeseries_settingxmlfalse(self): + o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--settingxml', 'false'], check=False, capture_output=True) + print(f'o_result: {o_result}\n') + print(f'o_result.returncode: {o_result.returncode}\n') + print(f'o_result.stdout: {o_result.stdout}\n') + print(f'o_result.stderr: {o_result.stderr}\n') + assert o_result.returncode == 0 + + def test_pcdl_get_cell_attribute_list_timeseries_settingxmlnone(self): + o_result = subprocess.run(['pcdl_get_cell_attribute_list', s_path_2d, '--settingxml', 'none'], check=False, capture_output=True) + print(f'o_result: {o_result}\n') + print(f'o_result.returncode: {o_result.returncode}\n') + print(f'o_result.stdout: {o_result.stdout}\n') + print(f'o_result.stderr: {o_result.stderr}\n') + assert o_result.returncode == 0 + + +class TestPyCliCellDf(object): + ''' tests for one pcdl.pyCli function. ''' + # timeseries # + collapse (true false) ok @@ -746,8 +746,8 @@ def test_pcdl_get_cell_df_timestep_keep(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceAnndata(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliAnndata(object): + ''' tests for one pcdl.pyCli function. ''' # timeseries # + collapse (true false) ok @@ -921,7 +921,6 @@ def test_pcdl_get_anndata_timestep_settingxmlfalse(self): os.remove(f'{s_path_2d}/output00000024_cell_maxabs.h5ad') assert o_result.returncode == 0 - def test_pcdl_get_anndata_timestep_settingxmlnone(self): o_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--settingxml', 'none'], check=False, capture_output=True) print(f'o_result: {o_result}\n') @@ -968,8 +967,8 @@ def test_pcdl_get_anndata_timestep_scale(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceGraphGml(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliGraphGml(object): + ''' tests for one pcdl.pyCli function. ''' # timestep and timeseries: # + path nop @@ -992,6 +991,7 @@ def test_pcdl_make_graph_gml_timeseries_default(self): os.remove(f'{s_path_2d}/output000000{str(i_step).zfill(2)}_neighbor.gml') assert o_result.returncode == 0 + def test_pcdl_make_graph_gml_timeseries_customtype_nodeattribute_one(self): o_result = subprocess.run(['pcdl_make_graph_gml', s_path_2d, 'neighbor', '--custom_data_type', 'sample:bool', '--node_attribute', 'sample'], check=False, capture_output=True) print(f'o_result: {o_result}\n') @@ -1173,8 +1173,8 @@ def test_pcdl_make_graph_gml_timestep_node_attribute_many(self): assert o_result.returncode == 0 -class TestCommandLineInterfacePlotScatter(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliPlotScatter(object): + ''' tests for one pcdl.pyCli function. ''' # time series and time steps. # + path nop @@ -1240,8 +1240,8 @@ def test_pcdl_plot_scatter_set(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceCellVtk(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliCellVtk(object): + ''' tests for one pcdl.pyCli function. ''' # timestep and timeseries: # + path nop @@ -1390,8 +1390,8 @@ def test_pcdl_make_cell_vtk_timestep_attribute_many(self): # substrate and cell agenat test code # ####################################### -class TestCommandLineInterfacePlotTimeSeries(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliPlotTimeSeries(object): + ''' tests for one pcdl.pyCli function. ''' # time series. # + path nop @@ -1469,198 +1469,12 @@ def test_pcdl_plot_timeseries_set(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceOmeTiff(object): - ''' tests for one pcdl command line interface function. ''' - - # timestep and timeseries: - # + path nop - # + customtype nop (because bool int float might in the end be treated the same and str is recogniced) - # + microenv (true, false) ok - # + physiboss (true, _false_) ok - # + settingxml (string, _none_, _false_) ok - # + verbose (true, _false_) nop - # + cell_attribute (ID, _dead_, _cell_count_voxel_, _pressure_) ok - # + collapse (true, _false_) ok - - def test_pcdl_make_ome_tiff_timeseries_default(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_microenv(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, '--microenv', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_physiboss(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, '--physiboss', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_settingxmlfalse(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, '--settingxml', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_settingxmlnone(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, '--settingxml', 'none'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_cellattribute_dead(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, 'dead'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen_water_default_blood_cells_dead.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_cellattribute_cellcountvoxel(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, 'cell_count_voxel'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen_water_default_blood_cells_cell_count_voxel.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_cellattribute_pressure(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, 'pressure'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen_water_default_blood_cells_pressure.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_conccutoff_oxygenminusone(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, '--conc_cutoff', 'oxygen:-1'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen-1_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_focus_(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, '--focus', 'oxygen'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/timeseries_oxygen.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timeseries_collapse_false(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_path_2d, '--collapse', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - for i_step in range(25): - os.remove(f'{s_path_2d}/output000000{str(i_step).zfill(2)}_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_default(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_microenv(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d, '--microenv', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_physiboss(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d, '--physiboss', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_settingxmlfalse(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d, '--settingxml', 'false'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_settingxmlnone(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d, '--settingxml', 'none'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_oxygen_water_default_blood_cells_ID.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_cellattribute_dead(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d, 'dead'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_oxygen_water_default_blood_cells_dead.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_cellattribute_cellcountvoxel(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d, 'cell_count_voxel'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_oxygen_water_default_blood_cells_cell_count_voxel.ome.tiff') - assert o_result.returncode == 0 - - def test_pcdl_make_ome_tiff_timestep_cellattribute_pressure(self): - o_result = subprocess.run(['pcdl_make_ome_tiff', s_pathfile_2d, 'pressure'], check=False, capture_output=True) - print(f'o_result: {o_result}\n') - print(f'o_result.returncode: {o_result.returncode}\n') - print(f'o_result.stdout: {o_result.stdout}\n') - print(f'o_result.stderr: {o_result.stderr}\n') - os.remove(f'{s_path_2d}/output00000024_oxygen_water_default_blood_cells_pressure.ome.tiff') - assert o_result.returncode == 0 - - ########################### # making movies test code # ########################### -class TestCommandLineInterfaceMakeGif(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliMakeGif(object): + ''' tests for one pcdl.pyCli function. ''' # time series # + path nop @@ -1668,6 +1482,7 @@ class TestCommandLineInterfaceMakeGif(object): def test_pcdl_make_gif_timeseries_default(self): o_path = subprocess.run(['pcdl_plot_scatter', s_path_2d], check=False, capture_output=True) + print(f'o_path: {o_path}\n') s_path = f'{s_path_2d}/cell_cell_type_z0.0/' o_result = subprocess.run(['pcdl_make_gif', s_path], check=False, capture_output=True) print(f'o_result: {o_result}\n') @@ -1679,6 +1494,7 @@ def test_pcdl_make_gif_timeseries_default(self): def test_pcdl_make_gif_timeseries_interface(self): o_path = subprocess.run(['pcdl_plot_contour', s_path_2d, 'oxygen', '--ext', 'tiff'], check=False, capture_output=True) + print(f'o_path: {o_path}\n') s_path = f'{s_path_2d}/conc_oxygen_z0.0/' o_result = subprocess.run(['pcdl_make_gif', s_path, 'tiff'], check=False, capture_output=True) print(f'o_result: {o_result}\n') @@ -1689,8 +1505,8 @@ def test_pcdl_make_gif_timeseries_interface(self): assert o_result.returncode == 0 -class TestCommandLineInterfaceMakeMove(object): - ''' tests for one pcdl command line interface function. ''' +class TestPyCliMakeMovie(object): + ''' tests for one pcdl.pyCli function. ''' # time series # + path nop @@ -1699,6 +1515,7 @@ class TestCommandLineInterfaceMakeMove(object): def test_pcdl_make_movie_timeseries_default(self): o_path = subprocess.run(['pcdl_plot_scatter', s_path_2d], check=False, capture_output=True) + print(f'o_path: {o_path}\n') s_path = f'{s_path_2d}/cell_cell_type_z0.0/' o_result = subprocess.run(['pcdl_make_movie', s_path], check=False, capture_output=True) print(f'o_result: {o_result}\n') @@ -1710,6 +1527,7 @@ def test_pcdl_make_movie_timeseries_default(self): def test_pcdl_make_movie_timeseries_interface(self): o_path = subprocess.run(['pcdl_plot_contour', s_path_2d, 'oxygen', '--ext', 'tiff'], check=False, capture_output=True) + print(f'o_path: {o_path}\n') s_path = f'{s_path_2d}/conc_oxygen_z0.0/' o_result = subprocess.run(['pcdl_make_movie', s_path, 'tiff'], check=False, capture_output=True) print(f'o_result: {o_result}\n') @@ -1721,6 +1539,7 @@ def test_pcdl_make_movie_timeseries_interface(self): def test_pcdl_make_movie_timeseries_farme(self): o_path = subprocess.run(['pcdl_plot_contour', s_path_2d, 'oxygen', '--ext', 'jpeg'], check=False, capture_output=True) + print(f'o_path: {o_path}\n') s_path = f'{s_path_2d}/conc_oxygen_z0.0/' o_result = subprocess.run(['pcdl_make_movie', s_path, '--framerate', '9'], check=False, capture_output=True) print(f'o_result: {o_result}\n') diff --git a/test/test_timeseries_2d.py b/test/test_timeseries_2d.py index 7120c81..1d355f4 100644 --- a/test/test_timeseries_2d.py +++ b/test/test_timeseries_2d.py @@ -7,7 +7,7 @@ # license: BSD 3-Clause # # description: -# pytest unit test library for the pcdl library TimeSeries class. +# pytest unit test library for the pcdl library pyMCDSts class. # + https://docs.pytest.org/ # # note: @@ -39,9 +39,9 @@ ## making movies related functions ## -class TestTimeSeriesMovies(object): - ''' tests for loading a pcdl.TimeSeries data set. ''' - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) +class TestPyMcdsTsMovies(object): + ''' tests for loading a pcdl.pyMCDS data set. ''' + mcdsts = pcdl.pyMCDSts(s_path_2d, verbose=True) ## make_gif and magick ommand ## def test_mcdsts_make_gif_jpeg(self, mcdsts=mcdsts): @@ -51,7 +51,7 @@ def test_mcdsts_make_gif_jpeg(self, mcdsts=mcdsts): path = s_opath, #interface = 'jpeg', ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (os.path.exists(s_opathfile)) and \ (s_opathfile.endswith('pcdl/output_2d/cell_cell_type_z0.0/cell_cell_type_z0.0_jpeg.gif')) #os.remove(s_opathfile) @@ -64,7 +64,7 @@ def test_mcdsts_make_gif_tiff(self, mcdsts=mcdsts): path = s_opath, interface = 'tiff', ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (os.path.exists(s_opathfile)) and \ (s_opathfile.endswith('pcdl/output_2d/cell_cell_type_z0.0/cell_cell_type_z0.0_tiff.gif')) #os.remove(s_opathfile) @@ -79,7 +79,7 @@ def test_mcdsts_make_movie_jpeg12(self, mcdsts=mcdsts): #interface = 'jpeg', #framerate = 12, ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (os.path.exists(s_opathfile)) and \ (s_opathfile.endswith('pcdl/output_2d/cell_cell_type_z0.0/cell_cell_type_z0.0_jpeg12.mp4')) #os.remove(s_opathfile) @@ -93,7 +93,7 @@ def test_mcdsts_make_movie_tiff12(self, mcdsts=mcdsts): interface = 'tiff', #framerate = 12, ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (os.path.exists(s_opathfile)) and \ (s_opathfile.endswith('pcdl/output_2d/cell_cell_type_z0.0/cell_cell_type_z0.0_tiff12.mp4')) #os.remove(s_opathfile) @@ -107,7 +107,7 @@ def test_mcdsts_make_movie_jpeg6(self, mcdsts=mcdsts): #interface = 'jpeg', framerate = 6, ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (os.path.exists(s_opathfile)) and \ (s_opathfile.endswith('pcdl/output_2d/cell_cell_type_z0.0/cell_cell_type_z0.0_jpeg6.mp4')) #os.remove(s_opathfile) @@ -116,61 +116,61 @@ def test_mcdsts_make_movie_jpeg6(self, mcdsts=mcdsts): ## data loading related functions ## -class TestTimeSeriesInit(object): - ''' tests for loading a pcdl.TimeSeries data set. ''' +class TestPyMcdsTsInit(object): + ''' tests for loading a pcdl.pyMCDSts data set. ''' def test_mcdsts_set_verbose_true(self): - mcdsts = pcdl.TimeSeries(s_path_2d, load=False, verbose=False) + mcdsts = pcdl.pyMCDSts(s_path_2d, load=False, verbose=False) mcdsts.set_verbose_true() - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (mcdsts.verbose) def test_mcdsts_set_verbose_false(self): - mcdsts = pcdl.TimeSeries(s_path_2d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_2d, load=False, verbose=True) mcdsts.set_verbose_false() - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (not mcdsts.verbose) ## get_xmlfile and read_mcds command and get_mcds_list ## def test_mcdsts_get_xmlfile_list(self): - mcdsts = pcdl.TimeSeries(s_path_2d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_2d, load=False, verbose=True) ls_xmlfile = mcdsts.get_xmlfile_list() - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_xmlfile[0] == 'output00000000.xml') and \ (ls_xmlfile[-1] == 'output00000024.xml') and \ (len(ls_xmlfile) == 25) def test_mcdsts_get_mcds_list(self): - mcdsts = pcdl.TimeSeries(s_path_2d, load=True, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_2d, load=True, verbose=True) l_mcds = mcdsts.get_mcds_list() - assert(str(type(mcdsts)) == "") and \ - (str(type(mcdsts.l_mcds[0])) == "") and \ - (str(type(mcdsts.l_mcds[-1])) == "") and \ + assert(str(type(mcdsts)) == "") and \ + (str(type(mcdsts.l_mcds[0])) == "") and \ + (str(type(mcdsts.l_mcds[-1])) == "") and \ (mcdsts.l_mcds[0].get_time() == 0) and \ (mcdsts.l_mcds[-1].get_time() == 1440) and \ (len(mcdsts.l_mcds) == 25) and \ (mcdsts.l_mcds == l_mcds) def test_mcdsts_read_mcds(self): - mcdsts = pcdl.TimeSeries(s_path_2d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_2d, load=False, verbose=True) l_mcds_loadfalse = mcdsts.get_mcds_list() mcdsts.read_mcds() - assert(str(type(mcdsts)) == "") and \ - (str(type(mcdsts.l_mcds[0])) == "") and \ - (str(type(mcdsts.l_mcds[-1])) == "") and \ + assert(str(type(mcdsts)) == "") and \ + (str(type(mcdsts.l_mcds[0])) == "") and \ + (str(type(mcdsts.l_mcds[-1])) == "") and \ (mcdsts.l_mcds[0].get_time() == 0) and \ (mcdsts.l_mcds[-1].get_time() == 1440) and \ (len(mcdsts.l_mcds) == 25) and \ (l_mcds_loadfalse is None) def test_mcdsts_read_mcds_xmlfilelist(self): - mcdsts = pcdl.TimeSeries(s_path_2d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_2d, load=False, verbose=True) ls_xmlfile = mcdsts.get_xmlfile_list() ls_xmlfile = ls_xmlfile[-3:] l_mcds = mcdsts.read_mcds(ls_xmlfile) - assert(str(type(mcdsts)) == "") and \ - (str(type(mcdsts.l_mcds[0])) == "") and \ - (str(type(mcdsts.l_mcds[-1])) == "") and \ + assert(str(type(mcdsts)) == "") and \ + (str(type(mcdsts.l_mcds[0])) == "") and \ + (str(type(mcdsts.l_mcds[-1])) == "") and \ (mcdsts.l_mcds[0].get_time() == 1320) and \ (mcdsts.l_mcds[-1].get_time() == 1440) and \ (len(ls_xmlfile) == 3) and \ @@ -180,13 +180,13 @@ def test_mcdsts_read_mcds_xmlfilelist(self): ## micro environment related functions ## -class TestTimeSeriesMicroenv(object): - ''' tests for pcdl.TimeSeriesmicro environment related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) +class TestPyMcdsTsMicroenv(object): + ''' tests for pcdl.pyMCDS micro environment related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_2d, verbose=True) def test_mcdsts_get_conc_df(self, mcdsts=mcdsts): ldf_conc = mcdsts.get_conc_df(values=2, drop=set(), keep=set(), collapse=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(ldf_conc)) == "") and \ (str(type(ldf_conc[0])) == "") and \ (ldf_conc[0].shape == (121, 9)) and \ @@ -195,13 +195,13 @@ def test_mcdsts_get_conc_df(self, mcdsts=mcdsts): def test_mcdsts_get_conc_df_collapse(self, mcdsts=mcdsts): df_conc = mcdsts.get_conc_df(values=2, drop=set(), keep=set(), collapse=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (3025, 11)) def test_mcdsts_get_conc_attribute(self, mcdsts=mcdsts): dl_conc = mcdsts.get_conc_attribute(values=1, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_conc)) == "") and \ (str(type(dl_conc['oxygen'])) == "") and \ (str(type(dl_conc['oxygen'][0])) == "") and \ @@ -210,7 +210,7 @@ def test_mcdsts_get_conc_attribute(self, mcdsts=mcdsts): def test_mcdsts_get_conc_attribute_values(self, mcdsts=mcdsts): dl_conc = mcdsts.get_conc_attribute(values=2, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_conc)) == "") and \ (str(type(dl_conc['oxygen'])) == "") and \ (str(type(dl_conc['oxygen'][0])) == "") and \ @@ -219,7 +219,7 @@ def test_mcdsts_get_conc_attribute_values(self, mcdsts=mcdsts): def test_mcdsts_get_conc_attribute_allvalues(self, mcdsts=mcdsts): dl_conc = mcdsts.get_conc_attribute(values=1, drop=set(), keep=set(), allvalues=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_conc)) == "") and \ (str(type(dl_conc['oxygen'])) == "") and \ (str(type(dl_conc['oxygen'][0])) == "") and \ @@ -244,7 +244,7 @@ def test_mcdsts_plot_contour_if(self, mcdsts=mcdsts): ext = 'jpeg', # test file case figbgcolor = None, # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].replace('\\','/').endswith('/pcdl/output_2d/conc_oxygen_z0.0/output00000000_oxygen.jpeg')) and \ (os.path.exists(ls_pathfile[0])) and \ (os.path.getsize(ls_pathfile[0]) > 2**10) and \ @@ -261,27 +261,27 @@ def test_mcdsts_plot_contour_else(self, mcdsts=mcdsts): focus = 'oxygen', z_slice = 0.0, # jump over if extrema = [0, 38], # jump over if - #alpha = 1, # TimeStep - #fill = True, # TimeStep - #cmap = 'viridis', # TimeStep + #alpha = 1, # pyMCDS + #fill = True, # pyMCDS + #cmap = 'viridis', # pyMCDS title = 'abc', # test non default - #grid = True, # TimeStep + #grid = True, # pyMCDS xlim = [-31, 301], # jump over if ylim = [-21, 201], # jump over if - #xyequal = True, # TimeStep + #xyequal = True, # pyMCDS figsizepx = [641, 481], # test non even pixel ext = None, # test fig case figbgcolor = 'yellow', # not a file ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(l_fig[0])) == "") and \ (str(type(l_fig[-1])) == "") and \ (len(l_fig) == 25) plt.close() def test_mcdsts_make_conc_vtk(self, mcdsts=mcdsts): - ls_pathfile = mcdsts.make_conc_vtk() - assert(str(type(mcdsts)) == "") and \ + ls_pathfile = mcdsts.make_conc_vtk(visualize=False) + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_2d/output00000000_conc.vtr')) and \ (ls_pathfile[-1].endswith('/pcdl/output_2d/output00000024_conc.vtr')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -295,13 +295,13 @@ def test_mcdsts_make_conc_vtk(self, mcdsts=mcdsts): ## cell related functions ## -class TestTimeSeriesCell(object): - ''' tests for pcdl.TimeSeries cell related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=False) +class TestPyMcdsCell(object): + ''' tests for pcdl.pyMCDS cell related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_2d, verbose=False) def test_mcdsts_get_cell_df(self, mcdsts=mcdsts): ldf_cell = mcdsts.get_cell_df(values=2, drop=set(), keep=set(), collapse=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(ldf_cell)) == "") and \ (str(type(ldf_cell[0])) == "") and \ (ldf_cell[0].shape[0] > 9) and \ @@ -312,14 +312,14 @@ def test_mcdsts_get_cell_df(self, mcdsts=mcdsts): def test_mcdsts_get_cell_df_collapse(self, mcdsts=mcdsts): df_cell = mcdsts.get_cell_df(values=2, drop=set(), keep=set(), collapse=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 67) def test_mcdsts_get_cell_attribute(self, mcdsts=mcdsts): dl_cell = mcdsts.get_cell_attribute(values=1, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_cell)) == "") and \ (str(type(dl_cell['dead'])) == "") and \ (str(type(dl_cell['dead'][0])) == "") and \ @@ -337,13 +337,13 @@ def test_mcdsts_get_cell_attribute(self, mcdsts=mcdsts): def test_mcdsts_get_cell_attribute_values(self, mcdsts=mcdsts): dl_cell = mcdsts.get_cell_attribute(values=2, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_cell)) == "") and \ (len(dl_cell.keys()) == 54) def test_mcdsts_get_cell_attribute_allvalues(self, mcdsts=mcdsts): dl_cell = mcdsts.get_cell_attribute(values=1, drop=set(), keep=set(), allvalues=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_cell)) == "") and \ (str(type(dl_cell['dead'])) == "") and \ (str(type(dl_cell['dead'][0])) == "") and \ @@ -372,13 +372,13 @@ def test_mcdsts_plot_scatter_num(self, mcdsts=mcdsts): #legend_loc='lower left', # matplotlib xlim = None, # test if ylim = None, # test if - #xyequal = True, # TimeStep + #xyequal = True, # pyMCDS s = None, # test if figsizepx = None, # case extract from initial.svg ext = 'jpeg', # generate file case figbgcolor = None, # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].replace('\\','/').endswith('/pcdl/output_2d/cell_pressure_z0.0/output00000000_pressure.jpeg')) and \ (os.path.exists(ls_pathfile[0])) and \ (os.path.getsize(ls_pathfile[0]) > 2**10) and \ @@ -394,28 +394,28 @@ def test_mcdsts_plot_scatter_cat(self, mcdsts=mcdsts): focus='cell_type', # case categorical z_slice = 0.0, # jump over if z_axis = None, # test iff categorical - #alpha = 1, # TimeStep - #cmap = 'viridis', # TimeStep + #alpha = 1, # pyMCDS + #cmap = 'viridis', # pyMCDS title = 'abc', # test non default - #grid = True, # TimeStep - #legend_loc='lower left', # TimeStep + #grid = True, # pyMCDS + #legend_loc='lower left', # pyMCDS xlim = None, # test if ylim = None, # test if - #xyequal = True, # TimeStep + #xyequal = True, # pyMCDS s = None, # test if figsizepx = [641, 481], # test case non even pixel number ext = None, # test fig case figbgcolor = None, # not a file ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(l_fig[0])) == "") and \ (str(type(l_fig[-1])) == "") and \ (len(l_fig) == 25) plt.close() def test_mcdsts_make_cell_vtk(self, mcdsts=mcdsts): - ls_pathfile = mcdsts.make_cell_vtk() - assert(str(type(mcdsts)) == "") and \ + ls_pathfile = mcdsts.make_cell_vtk(visualize=False) + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_2d/output00000000_cell.vtp')) and \ (ls_pathfile[-1].endswith('/pcdl/output_2d/output00000024_cell.vtp')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -428,15 +428,14 @@ def test_mcdsts_make_cell_vtk(self, mcdsts=mcdsts): ## graph related functions ## - -class TestTimeSeriesGraph(object): - ''' tests for pcdl.TimeSeries graph related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=False) +class TestPyMcdsGraph(object): + ''' tests for pcdl.pyMCDS graph related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_2d, verbose=False) ## graph related functions ## def test_mcdsts_get_graph_gml_attached_defaultattr(self, mcdsts=mcdsts): ls_pathfile = mcdsts.make_graph_gml(graph_type='attached', edge_attribute=True, node_attribute=[]) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_2d/output00000000_attached.gml')) and \ (ls_pathfile[-1].endswith('/pcdl/output_2d/output00000024_attached.gml')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -447,7 +446,7 @@ def test_mcdsts_get_graph_gml_attached_defaultattr(self, mcdsts=mcdsts): def test_mcdsts_get_graph_gml_neighbor_noneattr(self, mcdsts=mcdsts): ls_pathfile = mcdsts.make_graph_gml(graph_type='neighbor', edge_attribute=False, node_attribute=[]) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_2d/output00000000_neighbor.gml')) and \ (ls_pathfile[-1].endswith('/pcdl/output_2d/output00000024_neighbor.gml')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -458,7 +457,7 @@ def test_mcdsts_get_graph_gml_neighbor_noneattr(self, mcdsts=mcdsts): def test_mcdsts_get_graph_gml_neighbor_allattr(self, mcdsts=mcdsts): ls_pathfile = mcdsts.make_graph_gml(graph_type='neighbor', edge_attribute=True, node_attribute=['dead','cell_count_voxel','cell_density_micron3','cell_type']) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_2d/output00000000_neighbor.gml')) and \ (ls_pathfile[-1].endswith('/pcdl/output_2d/output00000024_neighbor.gml')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -468,98 +467,11 @@ def test_mcdsts_get_graph_gml_neighbor_allattr(self, mcdsts=mcdsts): os.remove(s_pathfile) -## ome tiff related functions ## - -class TestTimeSeriesOmeTiff(object): - ''' tests for pcdl.TimeSeries ome tiff related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=False) - - ## ome tiff related functions ## - def test_mcdsts_make_ome_tiff_defaultattr_00(self, mcdsts=mcdsts): - la_ometiff = mcdsts.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=False, collapse=False) - assert(str(type(mcdsts)) == "") and \ - (type(la_ometiff) is list) and \ - (type(la_ometiff[0]) is np.ndarray) and \ - (type(la_ometiff[-1]) is np.ndarray) and \ - (la_ometiff[0].dtype == np.float32) and \ - (la_ometiff[-1].dtype == np.float32) and \ - (la_ometiff[0].shape == (4, 1, 200, 300)) and \ - (la_ometiff[-1].shape == (4, 1, 200, 300)) and \ - (len(la_ometiff) == 25) - - def test_mcdsts_make_ome_tiff_defaultattr_01(self, mcdsts=mcdsts): - a_ometiff = mcdsts.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=False, collapse=True) - assert(str(type(mcdsts)) == "") and \ - (type(a_ometiff) is np.ndarray) and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (25, 4, 1, 200, 300)) - - def test_mcdsts_make_ome_tiff_defaultattr_10(self, mcdsts=mcdsts): - ls_pathfile = mcdsts.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=True, collapse=False) - assert(str(type(mcdsts)) == "") and \ - (ls_pathfile[0].endswith('pcdl/output_2d/output00000000_oxygen_water_default_blood_cells_ID.ome.tiff')) and \ - (ls_pathfile[-1].endswith('pcdl/output_2d/output00000024_oxygen_water_default_blood_cells_ID.ome.tiff')) and \ - (os.path.exists(ls_pathfile[0])) and \ - (os.path.exists(ls_pathfile[-1])) and \ - (os.path.getsize(ls_pathfile[0]) > 2**10) and\ - (os.path.getsize(ls_pathfile[-1]) > 2**10) and\ - (len(ls_pathfile) == 25) - for s_pathfile in ls_pathfile: - os.remove(s_pathfile) - - def test_mcdsts_make_ome_tiff_defaultattr_11(self, mcdsts=mcdsts): - s_pathfile = mcdsts.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=True, collapse=True) - assert(str(type(mcdsts)) == "") and \ - (s_pathfile.endswith('pcdl/output_2d/timeseries_oxygen_water_default_blood_cells_ID.ome.tiff')) and \ - (os.path.exists(s_pathfile)) and \ - (os.path.getsize(s_pathfile) > 2**10 ) - os.remove(s_pathfile) - +## timeseries related functions ## -class TestTimeSeriesNeuroglancer(object): - ''' tests for loading a pcdl.TimeSeries data set. ''' - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) - - ## make_gif and magick ommand ## - def test_mcdsts_render_neuroglancer_default(self, mcdsts=mcdsts): - s_tiffpathfile = mcdsts.make_ome_tiff() - o_viewer = mcdsts.render_neuroglancer( - tiffpathfile = s_tiffpathfile, - #timestep = 0, - #intensity_cmap='gray', - ) - assert(str(type(o_viewer)) == "") and \ - (str(o_viewer).startswith('http://127.0.0.1:')) - os.remove(s_tiffpathfile) - - def test_mcdsts_render_neuroglancer_timestep(self, mcdsts=mcdsts): - s_tiffpathfile = mcdsts.make_ome_tiff() - o_viewer = mcdsts.render_neuroglancer( - tiffpathfile = s_tiffpathfile, - timestep = 12, - intensity_cmap='gray', - ) - assert(str(type(o_viewer)) == "") and \ - (str(o_viewer).startswith('http://127.0.0.1:')) - os.remove(s_tiffpathfile) - - def test_mcdsts_render_neuroglancer_cmap(self, mcdsts=mcdsts): - s_tiffpathfile = mcdsts.make_ome_tiff() - o_viewer = mcdsts.render_neuroglancer( - tiffpathfile = s_tiffpathfile, - timestep = 0, - intensity_cmap='magma', - ) - assert(str(type(o_viewer)) == "") and \ - (str(o_viewer).startswith('http://127.0.0.1:')) - os.remove(s_tiffpathfile) - - -## time series related functions ## - -class TestTimeSeriesTimeseries(object): - ''' tests for pcdl.TimeSeries graph related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=False) +class TestPyMcdsTimeseries(object): + ''' tests for pcdl.pyMCDS graph related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_2d, verbose=False) ## plot_timeseries command ## def test_mcdsts_plot_timeseries_none_none_none_cell_ax_jpeg(self, mcdsts=mcdsts): @@ -589,7 +501,7 @@ def test_mcdsts_plot_timeseries_none_none_none_cell_ax_jpeg(self, mcdsts=mcdsts) ext = 'jpeg', # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (s_pathfile.endswith('/pcdl/output_2d/timeseries_cell_total_count.jpeg')) and \ (os.path.exists(s_pathfile)) os.remove(s_pathfile) @@ -620,7 +532,7 @@ def test_mcdsts_plot_timeseries_cat_none_yunit_cell(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -650,7 +562,7 @@ def test_mcdsts_plot_timeseries_none_num_yunit_cell(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -680,7 +592,7 @@ def test_mcdsts_plot_timeseries_cat_num_none_cell(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -711,7 +623,7 @@ def test_mcdsts_plot_timeseries_none_none_none_conc_ax_jpeg(self, mcdsts=mcdsts) ext = 'jpeg', # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (s_pathfile.endswith('/pcdl/output_2d/timeseries_conc_total_count.jpeg')) and \ (os.path.exists(s_pathfile)) os.remove(s_pathfile) @@ -743,7 +655,7 @@ def test_mcdsts_plot_timeseries_cat_none_yunit_conc(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -773,7 +685,7 @@ def test_mcdsts_plot_timeseries_none_num_yunit_conc(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -803,97 +715,7 @@ def test_mcdsts_plot_timeseries_cat_num_none_conc(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() - -## anndata time series related functions ## - -class TestTimeSeriesAnnData(object): - ''' test for pcdl.TestSeries class. ''' - - # get_anndata - # get_annmcds_list {integrated} - # value {1, _2_} - # collaps {True, _False_} - # keep_mcds {True, _False_} - - ## get_anndata command ## - def test_mcdsts_get_anndata(self): - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) - ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 25) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (mcdsts.l_annmcds is None) and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 105) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 8) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 2) and \ - (len(ann.obsp) == 0) and \ - (ann.var.shape == (105, 0)) and \ - (len(ann.uns) == 0) - - def test_mcdsts_get_anndata_value(self): - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) - ann = mcdsts.get_anndata(values=2, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 25) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (mcdsts.l_annmcds is None) and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 50) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 7) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 2) and \ - (len(ann.obsp) == 0) and \ - (ann.var.shape == (50, 0)) and \ - (len(ann.uns) == 0) - - def test_mcdsts_get_anndata_collapsefalse(self): - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) - ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=False, keep_mcds=True) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 25) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (str(type(mcdsts.l_annmcds)) == "") and \ - (len(mcdsts.l_annmcds) == 25) and \ - (all([str(type(ann)) == "" for ann in mcdsts.l_annmcds])) and \ - (mcdsts.l_annmcds[24].X.shape[0] > 9) and \ - (mcdsts.l_annmcds[24].X.shape[1] == 105) and \ - (mcdsts.l_annmcds[24].obs.shape[0] > 9) and \ - (mcdsts.l_annmcds[24].obs.shape[1] == 7) and \ - (mcdsts.l_annmcds[24].obsm['spatial'].shape[0] > 9) and \ - (mcdsts.l_annmcds[24].obsm['spatial'].shape[1] == 2) and \ - (len(mcdsts.l_annmcds[24].obsp) == 4) and \ - (mcdsts.l_annmcds[24].var.shape == (105, 0)) and \ - (len(mcdsts.l_annmcds[24].uns) == 2) - - def test_mcdsts_get_anndata_keepmcdsfalse(self): - mcdsts = pcdl.TimeSeries(s_path_2d, verbose=True) - ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=False) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 0) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (mcdsts.l_annmcds is None) and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 105) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 8) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 2) and \ - (len(ann.obsp) == 0) and \ - (ann.var.shape == (105, 0)) and \ - (len(ann.uns) == 0) - diff --git a/test/test_timeseries_3d.py b/test/test_timeseries_3d.py index bbf9a8e..a718dc5 100644 --- a/test/test_timeseries_3d.py +++ b/test/test_timeseries_3d.py @@ -7,7 +7,7 @@ # license: BSD 3-Clause # # description: -# pytest unit test library for the pcdl library TimeSeries class. +# pytest unit test library for the pcdl library pyMCDSts class. # + https://docs.pytest.org/ # # note: @@ -43,61 +43,61 @@ ## data loading related functions ## -class TestTimeSeries3dInit(object): - ''' tests for loading a pcdl.TimeSeries data set. ''' +class TestPyMcdsTs3DInit(object): + ''' tests for loading a pcdl.pyMCDSts data set. ''' def test_mcdsts_set_verbose_true(self): - mcdsts = pcdl.TimeSeries(s_path_3d, load=False, verbose=False) + mcdsts = pcdl.pyMCDSts(s_path_3d, load=False, verbose=False) mcdsts.set_verbose_true() - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (mcdsts.verbose) def test_mcdsts_set_verbose_false(self): - mcdsts = pcdl.TimeSeries(s_path_3d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_3d, load=False, verbose=True) mcdsts.set_verbose_false() - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (not mcdsts.verbose) ## get_xmlfile and read_mcds command and get_mcds_list ## def test_mcdsts_get_xmlfile_list(self): - mcdsts = pcdl.TimeSeries(s_path_3d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_3d, load=False, verbose=True) ls_xmlfile = mcdsts.get_xmlfile_list() - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_xmlfile[0] == 'output00000000.xml') and \ (ls_xmlfile[-1] == 'output00000024.xml') and \ (len(ls_xmlfile) == 25) def test_mcdsts_get_mcds_list(self): - mcdsts = pcdl.TimeSeries(s_path_3d, load=True, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_3d, load=True, verbose=True) l_mcds = mcdsts.get_mcds_list() - assert(str(type(mcdsts)) == "") and \ - (str(type(mcdsts.l_mcds[0])) == "") and \ - (str(type(mcdsts.l_mcds[-1])) == "") and \ + assert(str(type(mcdsts)) == "") and \ + (str(type(mcdsts.l_mcds[0])) == "") and \ + (str(type(mcdsts.l_mcds[-1])) == "") and \ (mcdsts.l_mcds[0].get_time() == 0) and \ (mcdsts.l_mcds[-1].get_time() == 1440) and \ (len(mcdsts.l_mcds) == 25) and \ (mcdsts.l_mcds == l_mcds) def test_mcdsts_read_mcds(self): - mcdsts = pcdl.TimeSeries(s_path_3d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_3d, load=False, verbose=True) l_mcds_loadfalse = mcdsts.get_mcds_list() mcdsts.read_mcds() - assert(str(type(mcdsts)) == "") and \ - (str(type(mcdsts.l_mcds[0])) == "") and \ - (str(type(mcdsts.l_mcds[-1])) == "") and \ + assert(str(type(mcdsts)) == "") and \ + (str(type(mcdsts.l_mcds[0])) == "") and \ + (str(type(mcdsts.l_mcds[-1])) == "") and \ (mcdsts.l_mcds[0].get_time() == 0) and \ (mcdsts.l_mcds[-1].get_time() == 1440) and \ (len(mcdsts.l_mcds) == 25) and \ (l_mcds_loadfalse is None) def test_mcdsts_read_mcds_xmlfilelist(self): - mcdsts = pcdl.TimeSeries(s_path_3d, load=False, verbose=True) + mcdsts = pcdl.pyMCDSts(s_path_3d, load=False, verbose=True) ls_xmlfile = mcdsts.get_xmlfile_list() ls_xmlfile = ls_xmlfile[-3:] l_mcds = mcdsts.read_mcds(ls_xmlfile) - assert(str(type(mcdsts)) == "") and \ - (str(type(mcdsts.l_mcds[0])) == "") and \ - (str(type(mcdsts.l_mcds[-1])) == "") and \ + assert(str(type(mcdsts)) == "") and \ + (str(type(mcdsts.l_mcds[0])) == "") and \ + (str(type(mcdsts.l_mcds[-1])) == "") and \ (mcdsts.l_mcds[0].get_time() == 1320) and \ (mcdsts.l_mcds[-1].get_time() == 1440) and \ (len(ls_xmlfile) == 3) and \ @@ -107,13 +107,13 @@ def test_mcdsts_read_mcds_xmlfilelist(self): ## micro environment related functions ## -class TestTimeSeries3dMicroenv(object): - ''' tests for pcdl.TimeStep micro environment related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) +class TestPyMcdsTs3DMicroenv(object): + ''' tests for pcdl.pyMCDS micro environment related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_3d, verbose=True) def test_mcdsts_get_conc_df(self, mcdsts=mcdsts): ldf_conc = mcdsts.get_conc_df(values=2, drop=set(), keep=set(), collapse=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(ldf_conc)) == "") and \ (str(type(ldf_conc[0])) == "") and \ (ldf_conc[0].shape == (1331, 9)) and \ @@ -122,13 +122,13 @@ def test_mcdsts_get_conc_df(self, mcdsts=mcdsts): def test_mcdsts_get_conc_df_collapse(self, mcdsts=mcdsts): df_conc = mcdsts.get_conc_df(values=2, drop=set(), keep=set(), collapse=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (33275, 11)) def test_mcdsts_get_conc_attribute(self, mcdsts=mcdsts): dl_conc = mcdsts.get_conc_attribute(values=1, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_conc)) == "") and \ (str(type(dl_conc['oxygen'])) == "") and \ (str(type(dl_conc['oxygen'][0])) == "") and \ @@ -137,7 +137,7 @@ def test_mcdsts_get_conc_attribute(self, mcdsts=mcdsts): def test_mcdsts_get_conc_attribute_values(self, mcdsts=mcdsts): dl_conc = mcdsts.get_conc_attribute(values=2, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_conc)) == "") and \ (str(type(dl_conc['oxygen'])) == "") and \ (str(type(dl_conc['oxygen'][0])) == "") and \ @@ -146,7 +146,7 @@ def test_mcdsts_get_conc_attribute_values(self, mcdsts=mcdsts): def test_mcdsts_get_conc_attribute_allvalues(self, mcdsts=mcdsts): dl_conc = mcdsts.get_conc_attribute(values=1, drop=set(), keep=set(), allvalues=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_conc)) == "") and \ (str(type(dl_conc['oxygen'])) == "") and \ (str(type(dl_conc['oxygen'][0])) == "") and \ @@ -159,19 +159,19 @@ def test_mcdsts_plot_contour_if(self, mcdsts=mcdsts): focus = 'oxygen', z_slice = -3.333, # test if extrema = None, # test if and for loop - #alpha = 1, # TimeStep - #fill = True, # TimeStep - #cmap = 'viridis', # TimeStep + #alpha = 1, # pyMCD + #fill = True, # pyMCD + #cmap = 'viridis', # pyMCD #title = '', # test default - #grid = True, # TimeStep + #grid = True, # pyMCD xlim = None, # test if ylim = None, # test if - #xyequal = True, # TimeStep + #xyequal = True, # pyMCD figsizepx = None, # test if ext = 'jpeg', figbgcolor = None, # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].replace('\\','/').endswith('/pcdl/output_3d/conc_oxygen_z-5.0/output00000000_oxygen.jpeg')) and \ (os.path.exists(ls_pathfile[0])) and \ (os.path.getsize(ls_pathfile[0]) > 2**10) and \ @@ -187,19 +187,19 @@ def test_mcdsts_plot_contour_else(self, mcdsts=mcdsts): focus = 'oxygen', z_slice = 0.0, # jump over if extrema = [0, 38], # jump over if - #alpha = 1, # TimeStep - #fill = True, # TimeStep - #cmap = 'viridis', # TimeStep + #alpha = 1, # pyMCDS + #fill = True, # pyMCDS + #cmap = 'viridis', # pyMCDS title = 'abc', # test non default - #grid = True, # TimeStep + #grid = True, # pyMCDS xlim = [-31, 301], # jump over if ylim = [-21, 201], # jump over if - #xyequal = True, # TimeStep + #xyequal = True, # pyMCDS figsizepx = [641, 481], # test non even pixel ext = None, figbgcolor = 'yellow', # jump over if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(l_fig[0])) == "") and \ (str(type(l_fig[-1])) == "") and \ (len(l_fig) == 25) @@ -207,8 +207,8 @@ def test_mcdsts_plot_contour_else(self, mcdsts=mcdsts): def test_mcdsts_make_conc_vtk(self, mcdsts=mcdsts): - ls_pathfile = mcdsts.make_conc_vtk() - assert(str(type(mcdsts)) == "") and \ + ls_pathfile = mcdsts.make_conc_vtk(visualize=False) + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_3d/output00000000_conc.vtr')) and \ (ls_pathfile[-1].endswith('/pcdl/output_3d/output00000024_conc.vtr')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -222,13 +222,13 @@ def test_mcdsts_make_conc_vtk(self, mcdsts=mcdsts): ## cell related functions ## -class TestTimeSeries3dCell(object): - ''' tests for pcdl.TimeStep cell related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=False) +class TestPyMcds3DCell(object): + ''' tests for pcdl.pyMCDS cell related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_3d, verbose=False) def test_mcdsts_get_cell_df(self, mcdsts=mcdsts): ldf_cell = mcdsts.get_cell_df(values=2, drop=set(), keep=set(), collapse=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(ldf_cell)) == "") and \ (str(type(ldf_cell[0])) == "") and \ (ldf_cell[0].shape[0] > 9) and \ @@ -239,14 +239,14 @@ def test_mcdsts_get_cell_df(self, mcdsts=mcdsts): def test_mcdsts_get_cell_df_collapse(self, mcdsts=mcdsts): df_cell = mcdsts.get_cell_df(values=2, drop=set(), keep=set(), collapse=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 73) def test_mcdsts_get_cell_attribute(self, mcdsts=mcdsts): dl_cell = mcdsts.get_cell_attribute(values=1, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_cell)) == "") and \ (str(type(dl_cell['dead'])) == "") and \ (str(type(dl_cell['dead'][0])) == "") and \ @@ -264,13 +264,13 @@ def test_mcdsts_get_cell_attribute(self, mcdsts=mcdsts): def test_mcdsts_get_cell_attribute_values(self, mcdsts=mcdsts): dl_cell = mcdsts.get_cell_attribute(values=2, drop=set(), keep=set(), allvalues=False) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_cell)) == "") and \ (len(dl_cell.keys()) == 60) def test_mcdsts_get_cell_attribute_allvalues(self, mcdsts=mcdsts): dl_cell = mcdsts.get_cell_attribute(values=1, drop=set(), keep=set(), allvalues=True) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(dl_cell)) == "") and \ (str(type(dl_cell['dead'])) == "") and \ (str(type(dl_cell['dead'][0])) == "") and \ @@ -299,13 +299,13 @@ def test_mcdsts_plot_scatter_num(self, mcdsts=mcdsts): #legend_loc='lower left', # matplotlib xlim = None, # test if ylim = None, # test if - #xyequal = True, # TimeStep + #xyequal = True, # pyMCDS s = None, # test if figsizepx = None, # case extract from initial.svg ext = 'jpeg', figbgcolor = None, # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].replace('\\','/').endswith('/pcdl/output_3d/cell_pressure_z-5.0/output00000000_pressure.jpeg')) and \ (os.path.exists(ls_pathfile[0])) and \ (os.path.getsize(ls_pathfile[0]) > 2**10) and \ @@ -321,28 +321,28 @@ def test_mcdsts_plot_scatter_cat(self, mcdsts=mcdsts): focus='cell_type', # case categorical z_slice = 0.0, # jump over if z_axis = None, # test iff categorical - #alpha = 1, # TimeStep - #cmap = 'viridis', # TimeStep + #alpha = 1, # pyMCDS + #cmap = 'viridis', # pyMCDS title = 'abc', # test non default - #grid = True, # TimeStep - #legend_loc='lower left', # TimeStep + #grid = True, # pyMCDS + #legend_loc='lower left', # pyMCDS xlim = None, # test if ylim = None, # test if - #xyequal = True, # TimeStep + #xyequal = True, # pyMCDS s = None, # test if figsizepx = [641, 481], # test case non even pixel number ext = None, figbgcolor = 'cyan', # jump over if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(l_fig[0])) == "") and \ (str(type(l_fig[-1])) == "") and \ (len(l_fig) == 25) plt.close() def test_mcdsts_make_cell_vtk(self, mcdsts=mcdsts): - ls_pathfile = mcdsts.make_cell_vtk() - assert(str(type(mcdsts)) == "") and \ + ls_pathfile = mcdsts.make_cell_vtk(visualize=False) + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_3d/output00000000_cell.vtp')) and \ (ls_pathfile[-1].endswith('/pcdl/output_3d/output00000024_cell.vtp')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -355,14 +355,14 @@ def test_mcdsts_make_cell_vtk(self, mcdsts=mcdsts): ## graph related functions ## -class TestTimeSeries3dGraph(object): - ''' tests for pcdl.TimeStep graph related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=False) +class TestPyMcds3DGraph(object): + ''' tests for pcdl.pyMCDS graph related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_3d, verbose=False) ## graph related functions ## def test_mcdsts_get_graph_gml_attached_defaultattr(self, mcdsts=mcdsts): ls_pathfile = mcdsts.make_graph_gml(graph_type='attached', edge_attribute=True, node_attribute=[]) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_3d/output00000000_attached.gml')) and \ (ls_pathfile[-1].endswith('/pcdl/output_3d/output00000024_attached.gml')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -373,7 +373,7 @@ def test_mcdsts_get_graph_gml_attached_defaultattr(self, mcdsts=mcdsts): def test_mcdsts_get_graph_gml_neighbor_noneattr(self, mcdsts=mcdsts): ls_pathfile = mcdsts.make_graph_gml(graph_type='neighbor', edge_attribute=False, node_attribute=[]) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_3d/output00000000_neighbor.gml')) and \ (ls_pathfile[-1].endswith('/pcdl/output_3d/output00000024_neighbor.gml')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -384,7 +384,7 @@ def test_mcdsts_get_graph_gml_neighbor_noneattr(self, mcdsts=mcdsts): def test_mcdsts_get_graph_gml_neighbor_allattr(self, mcdsts=mcdsts): ls_pathfile = mcdsts.make_graph_gml(graph_type='neighbor', edge_attribute=True, node_attribute=['dead','cell_count_voxel','cell_density_micron3','cell_type']) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (ls_pathfile[0].endswith('/pcdl/output_3d/output00000000_neighbor.gml')) and \ (ls_pathfile[-1].endswith('/pcdl/output_3d/output00000024_neighbor.gml')) and \ (os.path.exists(ls_pathfile[0])) and \ @@ -393,37 +393,12 @@ def test_mcdsts_get_graph_gml_neighbor_allattr(self, mcdsts=mcdsts): for s_pathfile in ls_pathfile: os.remove(s_pathfile) -## ome.tiff related functions ## -class TestTimeSeries3dOmeTiff(object): - ''' tests for pcdl.TimeStep graph related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=False) - ## graph related functions ## - def test_mcdsts_make_ome_tiff_defaultattr_00(self, mcdsts=mcdsts): - la_ometiff = mcdsts.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=False, collapse=False) - assert(str(type(mcdsts)) == "") and \ - (type(la_ometiff) is list) and \ - (type(la_ometiff[0]) is np.ndarray) and \ - (type(la_ometiff[-1]) is np.ndarray) and \ - (la_ometiff[0].dtype == np.float32) and \ - (la_ometiff[-1].dtype == np.float32) and \ - (la_ometiff[0].shape == (4, 11, 200, 300)) and \ - (la_ometiff[-1].shape == (4, 11, 200, 300)) and \ - (len(la_ometiff) == 25) - - def test_mcdsts_make_ome_tiff_defaultattr_01(self, mcdsts=mcdsts): - a_ometiff = mcdsts.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=False, collapse=True) - assert(str(type(mcdsts)) == "") and \ - (type(a_ometiff) is np.ndarray) and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (25, 4, 11, 200, 300)) - - -## time series related functions ## - -class TestTimeSeries3dTimeseries(object): - ''' tests for pcdl.TimeStep graph related functions. ''' - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=False) +## timeseries related functions ## + +class TestPyMcds3DTimeseries(object): + ''' tests for pcdl.pyMCDS graph related functions. ''' + mcdsts = pcdl.pyMCDSts(s_path_3d, verbose=False) ## plot_timeseries command ## def test_mcdsts_plot_timeseries_none_none_none_cell_ax_jpeg(self, mcdsts=mcdsts): @@ -453,7 +428,7 @@ def test_mcdsts_plot_timeseries_none_none_none_cell_ax_jpeg(self, mcdsts=mcdsts) ext = 'jpeg', # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (s_pathfile.endswith('/pcdl/output_3d/timeseries_cell_total_count.jpeg')) and \ (os.path.exists(s_pathfile)) os.remove(s_pathfile) @@ -484,7 +459,7 @@ def test_mcdsts_plot_timeseries_cat_none_yunit_cell(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -514,7 +489,7 @@ def test_mcdsts_plot_timeseries_none_num_yunit_cell(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -544,7 +519,7 @@ def test_mcdsts_plot_timeseries_cat_num_none_cell(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -575,7 +550,7 @@ def test_mcdsts_plot_timeseries_none_none_none_conc_ax_jpeg(self, mcdsts=mcdsts) ext = 'jpeg', # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (s_pathfile.endswith('/pcdl/output_3d/timeseries_conc_total_count.jpeg')) and \ (os.path.exists(s_pathfile)) os.remove(s_pathfile) @@ -607,7 +582,7 @@ def test_mcdsts_plot_timeseries_cat_none_yunit_conc(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -637,7 +612,7 @@ def test_mcdsts_plot_timeseries_none_num_yunit_conc(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() @@ -667,96 +642,7 @@ def test_mcdsts_plot_timeseries_cat_num_none_conc(self, mcdsts=mcdsts): ext = None, # test if else {'jpeg', None} figbgcolor = None # test if ) - assert(str(type(mcdsts)) == "") and \ + assert(str(type(mcdsts)) == "") and \ (str(type(fig)) == "") plt.close() - -## anndata time series related functions ## -class TestTimeSeries3dAnnData(object): - ''' test for pcdl.TestSeries class. ''' - - # get_anndata - # get_annmcds_list {integrated} - # value {1, _2_} - # collaps {True, _False_} - # keep_mcds {True, _False_} - - ## get_anndata command ## - def test_mcdsts_get_anndata(self): - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) - ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 25) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (mcdsts.l_annmcds is None) and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 105) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 8) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 3) and \ - (len(ann.obsp) == 0) and \ - (ann.var.shape == (105, 0)) and \ - (len(ann.uns) == 0) - - def test_mcdsts_get_anndata_value(self): - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) - ann = mcdsts.get_anndata(values=2, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=True) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 25) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (mcdsts.l_annmcds is None) and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 56) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 7) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 3) and \ - (len(ann.obsp) == 0) and \ - (ann.var.shape == (56, 0)) and \ - (len(ann.uns) == 0) - - def test_mcdsts_get_anndata_collapsefalse(self): - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) - ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=False, keep_mcds=True) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 25) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (str(type(mcdsts.l_annmcds)) == "") and \ - (len(mcdsts.l_annmcds) == 25) and \ - (all([str(type(ann)) == "" for ann in mcdsts.l_annmcds])) and \ - (mcdsts.l_annmcds[24].X.shape[0] > 9) and \ - (mcdsts.l_annmcds[24].X.shape[1] == 105) and \ - (mcdsts.l_annmcds[24].obs.shape[0] > 9) and \ - (mcdsts.l_annmcds[24].obs.shape[1] == 7) and \ - (mcdsts.l_annmcds[24].obsm['spatial'].shape[0] > 9) and \ - (mcdsts.l_annmcds[24].obsm['spatial'].shape[1] == 3) and \ - (len(mcdsts.l_annmcds[24].obsp) == 4) and \ - (mcdsts.l_annmcds[24].var.shape == (105, 0)) and \ - (len(mcdsts.l_annmcds[24].uns) == 2) - - def test_mcdsts_get_anndata_keepmcdsfalse(self): - mcdsts = pcdl.TimeSeries(s_path_3d, verbose=True) - ann = mcdsts.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs', collapse=True, keep_mcds=False) - l_annmcds = mcdsts.get_annmcds_list() - assert(str(type(mcdsts)) == "") and \ - (len(mcdsts.l_mcds) == 0) and \ - (l_annmcds == mcdsts.l_annmcds) and \ - (mcdsts.l_annmcds is None) and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 105) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 8) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 3) and \ - (len(ann.obsp) == 0) and \ - (ann.var.shape == (105, 0)) and \ - (len(ann.uns) == 0) - diff --git a/test/test_timestep_2d.py b/test/test_timestep_2d.py index f09ca37..c114c58 100644 --- a/test/test_timestep_2d.py +++ b/test/test_timestep_2d.py @@ -7,7 +7,7 @@ # license: BSD 3-Clause # # description: -# pytest unit test library for the pcdl library TimeStep class. +# pytest unit test library for the pcdl library pyMCDS class. # + https://docs.pytest.org/ # # note: @@ -22,7 +22,6 @@ import matplotlib.pyplot as plt import numpy as np import os -import pandas as pd import pathlib import pcdl @@ -38,267 +37,256 @@ pcdl.install_data() -# const -s_path_2d = str(pathlib.Path(pcdl.__file__).parent.resolve()/'output_2d') -s_file_2d = 'output00000024.xml' -s_pathfile_2d = f'{s_path_2d}/{s_file_2d}' - - -# test data -if not os.path.exists(s_path_2d): - pcdl.install_data() - - ## data loading related functions ## -class TestTimeStepInit(object): - ''' tests for loading a pcdl.TimeStep data set. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInit(object): + ''' tests for loading a pcdl.pyMCDS data set. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -class TestTimeStepInitMicroenvFalse(object): - ''' tests for loading a pcdl.TimeStep data set with microenv false. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=False, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitMicroenvFalse(object): + ''' tests for loading a pcdl.pyMCDS data set with microenv false. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=False, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 116) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 116) -class TestTimeStepInitGraphFalse(object): - ''' tests for loading a pcdl.TimeStep data set with graph false. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=False, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitGraphFalse(object): + ''' tests for loading a pcdl.pyMCDS data set with graph false. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=False, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) == 0) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) == 0) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) == 0) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) == 0) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -class TestTimeStepInitPhysibossFalse(object): - ''' tests for loading a pcdl.TimeStep data set with physiboss false. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=False, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitPhysibossFalse(object): + ''' tests for loading a pcdl.pyMCDS data set with physiboss false. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=False, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -class TestTimeStepInitSettingxmlFalse(object): - ''' tests for loading a pcdl.TimeStep data set with settingxml false. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml=False, verbose=True) +class TestPyMcdsInitSettingxmlFalse(object): + ''' tests for loading a pcdl.pyMCDS data set with settingxml false. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml=False, verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -class TestTimeStepInitSettingxmlNone(object): - ''' tests for loading a pcdl.TimeStep data set with settingxml none. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml=None, verbose=True) +class TestPyMcdsInitSettingxmlNone(object): + ''' tests for loading a pcdl.pyMCDS data set with settingxml none. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml=None, verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -class TestTimeStepInitVerboseTrue(object): - ''' tests for loading a pcdl.TimeStep data set and set_verbose_false function. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitVerboseTrue(object): + ''' tests for loading a pcdl.pyMCDS data set and set_verbose_false function. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) def test_mcds_verbose_true(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (mcds.verbose) def test_mcds_set_verbose_false(self, mcds=mcds): mcds.set_verbose_false() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (not mcds.verbose) -class TestTimeStepInitVerboseFalse(object): - ''' tests for loading a pcdl.TimeStep data set and set_verbose_true function. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=False) +class TestPyMcdsInitVerboseFalse(object): + ''' tests for loading a pcdl.pyMCDS data set and set_verbose_true function. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=False) def test_mcds_verbose_false(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (not mcds.verbose) def test_mcds_set_verbose_true(self, mcds=mcds): mcds.set_verbose_true() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (mcds.verbose) ## metadata related functions ## -class TestTimeStepMetadata(object): - ''' tests for pcdl.TimeStep metadata related functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsMetadata(object): + ''' tests for pcdl.pyMCDS metadata related functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) def test_mcds_get_multicellds_version(self, mcds=mcds): s_mcdsversion = mcds.get_multicellds_version() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(s_mcdsversion)) == "") and \ (s_mcdsversion == 'MultiCellDS_2') def test_mcds_get_physicell_version(self, mcds=mcds): s_pcversion = mcds.get_physicell_version() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(s_pcversion)) == "") and \ (s_pcversion == 'PhysiCell_1.14.1') def test_mcds_get_timestamp(self, mcds=mcds): s_timestamp = mcds.get_timestamp() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(s_timestamp)) == "") and \ (s_timestamp == '2025-01-05T08:08:22Z') def test_mcds_get_time(self, mcds=mcds): r_time = mcds.get_time() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(r_time)) == "") and \ (r_time == 1440.0) def test_mcds_get_runtime(self, mcds=mcds): r_runtime = mcds.get_runtime() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(r_runtime)) == "") and \ (r_runtime == 1.952156) ## setting related functions ## -class TestTimeStepSetting(object): - ''' tests for pcdl.TimeStep setting related functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsSetting(object): + ''' tests for pcdl.pyMCDS setting related functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) def test_mcds_get_unit_dict(self, mcds=mcds): ds_unit = mcds.get_unit_dict() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ds_unit)) == "") and \ (len(ds_unit) == 108) and \ (ds_unit['oxygen'] == 'dimensionless') @@ -306,13 +294,13 @@ def test_mcds_get_unit_dict(self, mcds=mcds): ## mesh related functions ## -class TestTimeStepMesh(object): - ''' tests for pcdl.TimeStep mesh related functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsMesh(object): + ''' tests for pcdl.pyMCDS mesh related functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) def test_mcds_get_voxel_ijk_range(self, mcds=mcds): lti_range = mcds.get_voxel_ijk_range() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(lti_range)) == "") and \ (str(type(lti_range[0])) == "") and \ (str(type(lti_range[0][0])) == "") and \ @@ -320,7 +308,7 @@ def test_mcds_get_voxel_ijk_range(self, mcds=mcds): def test_mcds_get_mesh_mnp_range(self, mcds=mcds): ltr_range = mcds.get_mesh_mnp_range() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ltr_range)) == "") and \ (str(type(ltr_range[0])) == "") and \ (str(type(ltr_range[0][0])) == "") and \ @@ -328,7 +316,7 @@ def test_mcds_get_mesh_mnp_range(self, mcds=mcds): def test_mcds_get_xyz_range(self, mcds=mcds): ltr_range = mcds.get_xyz_range() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ltr_range)) == "") and \ (str(type(ltr_range[0])) == "") and \ (str(type(ltr_range[0][0])) == "") and \ @@ -336,7 +324,7 @@ def test_mcds_get_xyz_range(self, mcds=mcds): def test_mcds_get_voxel_ijk_axis(self, mcds=mcds): lai_axis = mcds.get_voxel_ijk_axis() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(lai_axis)) == "") and \ (str(type(lai_axis[0])) == "") and \ (str(type(lai_axis[0][0])).startswith("") and \ + assert(str(type(mcds)) == "") and \ (str(type(lar_axis)) == "") and \ (str(type(lar_axis[0])) == "") and \ (str(type(lar_axis[0][0])) == "") and \ @@ -358,14 +346,14 @@ def test_mcds_get_mesh_mnp_axis(self, mcds=mcds): def test_mcds_get_mesh_flat_false(self, mcds=mcds): aar_mesh = mcds.get_mesh(flat=False) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(aar_mesh)) == "") and \ (aar_mesh.dtype == np.float64) and \ (aar_mesh.shape == (3, 11, 11, 1)) def test_mcds_get_mesh_flat_true(self, mcds=mcds): aar_mesh = mcds.get_mesh(flat=True) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(aar_mesh)) == "") and \ (aar_mesh.dtype == np.float64) and \ (aar_mesh.shape == (2, 11, 11)) @@ -373,7 +361,7 @@ def test_mcds_get_mesh_flat_true(self, mcds=mcds): def test_mcds_get_mesh_2d(self, mcds=mcds): aar_mesh_flat = mcds.get_mesh(flat=True) aar_mesh_2d = mcds.get_mesh_2D() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(aar_mesh_2d)) == "") and \ (aar_mesh_2d.dtype == np.float64) and \ (aar_mesh_2d.shape == (2, 11, 11)) @@ -386,7 +374,7 @@ def test_mcds_get_mesh_coordinate(self, mcds=mcds): er_p_cube = set(ar_p_cube.flatten()) # linear coordinates aar_voxel = mcds.get_mesh_coordinate() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(aar_voxel)) == "") and \ (aar_voxel.dtype == np.float64) and \ (aar_voxel.shape == (3, 121)) and \ @@ -396,30 +384,30 @@ def test_mcds_get_mesh_coordinate(self, mcds=mcds): def test_mcds_get_voxel_volume(self, mcds=mcds): r_volume = mcds.get_voxel_volume() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(r_volume)) == "") and \ (r_volume == 6000.0) # bue: check else in 3D def test_mcds_get_mesh_spacing(self, mcds=mcds): lr_spacing = mcds.get_mesh_spacing() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(lr_spacing)) == "") and \ (str(type(lr_spacing[0])) == "") and \ (str(type(lr_spacing[1])) == "") and \ (str(type(lr_spacing[-1])) == "") and \ - (lr_spacing == [30.0, 20.0, 10.0]) + (lr_spacing == [30.0, 20.0, 1.0]) def test_mcds_get_voxel_spacing(self, mcds=mcds): lr_spacing = mcds.get_voxel_spacing() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(lr_spacing)) == "") and \ (str(type(lr_spacing[0])) == "") and \ (str(type(lr_spacing[-1])) == "") and \ (lr_spacing == [30.0, 20.0, 10.0]) def test_mcds_is_in_mesh(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (mcds.is_in_mesh(x=0, y=0, z=0, halt=False)) and \ (not mcds.is_in_mesh(x=301, y=0, z=0, halt=False)) and \ (not mcds.is_in_mesh(x=0, y=201, z=0, halt=False)) and \ @@ -430,7 +418,7 @@ def test_mcds_get_mesh_mnp(self, mcds=mcds): li_mesh_1 = mcds.get_mesh_mnp(x=15, y=10, z=0, is_in_mesh=True) # if b_calc li_mesh_2 = mcds.get_mesh_mnp(x=30, y=20, z=0, is_in_mesh=True) # if b_calc li_mesh_none = mcds.get_mesh_mnp(x=-31, y=-21, z=-6, is_in_mesh=True) # else b_calc - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(li_mesh_0)) == "") and \ (str(type(li_mesh_0[0])) == "") and \ (li_mesh_0 == [-15.0, -10.0, 0.0]) and \ @@ -443,7 +431,7 @@ def test_mcds_get_voxel_ijk(self, mcds=mcds): li_voxel_1 = mcds.get_voxel_ijk(x=15, y=10, z=0, is_in_mesh=True) # if b_calc li_voxel_2 = mcds.get_voxel_ijk(x=30, y=20, z=0, is_in_mesh=True) # if b_calc li_voxel_none = mcds.get_voxel_ijk(x=-31, y=-21, z=-6, is_in_mesh=True) # else b_calc - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(li_voxel_0)) == "") and \ (str(type(li_voxel_0[0])) == "") and \ (li_voxel_0 == [0, 0, 0]) and \ @@ -454,63 +442,96 @@ def test_mcds_get_voxel_ijk(self, mcds=mcds): ## micro environment related functions ## -class TestTimeStepMicroenv(object): - ''' tests for pcdl.TimeStep micro environment related functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsMicroenv(object): + ''' tests for pcdl.pyMCDS micro environment related functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) def test_mcds_get_substrate_name(self, mcds=mcds): ls_substrate = mcds.get_substrate_list() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ls_substrate)) == "") and \ (str(type(ls_substrate[0])) == "") and \ (ls_substrate == ['oxygen','water']) def test_mcds_get_substrate_dict(self, mcds=mcds): ds_substrate = mcds.get_substrate_dict() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ds_substrate)) == "") and \ (str(type(ds_substrate['0'])) == "") and \ (len(ds_substrate) == 2) def test_mcds_get_substrate_df(self, mcds=mcds): df_substrate = mcds.get_substrate_df() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_substrate)) == "") and \ (df_substrate.shape == (2, 2)) + def test_mcds_get_concentration_zslice_none(self, mcds=mcds): + ar_conc = mcds.get_concentration(substrate='oxygen', z_slice=None) + assert(str(type(mcds)) == "") and \ + (str(type(ar_conc)) == "") and \ + (ar_conc.dtype == np.float64) and \ + (ar_conc.shape == (11, 11, 1)) + + def test_mcds_get_concentration_zslice_meshcenter(self, mcds=mcds): + ar_conc = mcds.get_concentration(substrate='oxygen', z_slice=0, halt=False) + assert(str(type(mcds)) == "") and \ + (str(type(ar_conc)) == "") and \ + (ar_conc.dtype == np.float64) and \ + (ar_conc.shape == (11, 11)) + + def test_mcds_get_concentration_zslice_notmeshcenter(self, mcds=mcds): + ar_conc = mcds.get_concentration(substrate='oxygen', z_slice=-3.333, halt=False) + assert(str(type(mcds)) == "") and \ + (str(type(ar_conc)) == "") and \ + (ar_conc.dtype == np.float64) and \ + (ar_conc.shape == (11, 11)) + + def test_mcds_get_concentration_at_inmeash(self, mcds=mcds): + ar_conc = mcds.get_concentration_at(x=0, y=0, z=0) + assert(str(type(mcds)) == "") and \ + (str(type(ar_conc)) == "") and \ + (ar_conc.dtype == np.float64) and \ + (ar_conc.shape == (2,)) + + def test_mcds_get_concentration_at_notinmeash(self, mcds=mcds): + ar_conc = mcds.get_concentration_at(x=-31, y=-21, z=-6) + assert(str(type(mcds)) == "") and \ + (ar_conc is None) + def test_mcds_get_conc_df(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 11)) def test_mcds_get_conc_df_zslice_center(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=0, halt=False, values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 11)) def test_mcds_get_conc_df_zslice_outofcenter(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=-6, halt=False, values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 11)) def test_mcds_get_conc_df_values(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=2, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 11)) def test_mcds_get_conc_df_drop(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=1, drop={'oxygen'}, keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 10)) def test_mcds_get_conc_df_keep(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=1, drop=set(), keep={'oxygen'}) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 10)) @@ -533,7 +554,7 @@ def test_mcds_plot_contour(self, mcds=mcds): ext = None, # test fig case figbgcolor = None, # not at file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -557,15 +578,15 @@ def test_mcds_plot_contourf(self, mcds=mcds): ext = 'tiff', # test file case figbgcolor = 'yellow', # jump over if ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_2d/conc_oxygen_z0/output00000024_oxygen.tiff')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) os.remove(s_pathfile) def test_mcds_make_conc_vtk(self, mcds=mcds): - s_pathfile = mcds.make_conc_vtk() - assert(str(type(mcds)) == "") and \ + s_pathfile = mcds.make_conc_vtk(visualize=False) + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_2d/output00000024_conc.vtr')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) @@ -573,59 +594,71 @@ def test_mcds_make_conc_vtk(self, mcds=mcds): ## cell related functions ## -class TestTimeStepCell(object): - ''' tests for pcdl.TimeStep cell related functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsCell(object): + ''' tests for pcdl.pyMCDS cell related functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) def test_mcds_get_cell_attribute_list(self, mcds=mcds): ls_cellattr = mcds.get_cell_attribute_list() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ls_cellattr)) == "") and \ (str(type(ls_cellattr[0])) == "") and \ (len(ls_cellattr) == 110) def test_mcds_get_celltype_list(self, mcds=mcds): ls_celltype = mcds.get_celltype_list() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ls_celltype)) == "") and \ (str(type(ls_celltype[0])) == "") and \ (len(ls_celltype) == 2) def test_mcds_get_celltype_dict(self, mcds=mcds): ds_celltype = mcds.get_celltype_dict() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ds_celltype)) == "") and \ (str(type(ds_celltype['0'])) == "") and \ (len(ds_celltype) == 2) def test_mcds_get_cell_df(self, mcds=mcds): df_cell = mcds.get_cell_df(values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_get_cell_df_values(self, mcds=mcds): df_cell = mcds.get_cell_df(values=2, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 66) def test_mcds_get_cell_df_drop(self, mcds=mcds): df_cell = mcds.get_cell_df(values=1, drop={'oxygen'}, keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 121) def test_mcds_get_cell_df_keep(self, mcds=mcds): df_cell = mcds.get_cell_df(values=1, drop=set(), keep={'oxygen'}) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 13) + def test_mcds_get_cell_df_at_inmeash(self, mcds=mcds): + df_cell = mcds.get_cell_df_at(x=0, y=0, z=0, values=1, drop=set(), keep=set()) + assert(str(type(mcds)) == "") and \ + (str(type(df_cell)) == "") and \ + (df_cell.shape[0] > 0) and \ + (df_cell.shape[1] == 122) + + def test_mcds_get_cell_df_at_notinmeash(self, mcds=mcds): + df_cell = mcds.get_cell_df_at(x=-31, y=-21, z=-6, values=1, drop=set(), keep=set()) + assert(str(type(mcds)) == "") and \ + (df_cell is None) + # scatter categorical def test_mcds_plot_scatter_cat_if(self, mcds=mcds): fig = mcds.plot_scatter( @@ -646,7 +679,7 @@ def test_mcds_plot_scatter_cat_if(self, mcds=mcds): ext = None, # test fig case figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -669,7 +702,7 @@ def test_mcds_plot_scatter_cat_else1(self, mcds=mcds): ext = 'tiff', # test file case figbgcolor = 'cyan', # jump over if ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_2d/cell_cell_type_z0/output00000024_cell_type.tiff')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) @@ -695,7 +728,7 @@ def test_mcds_plot_scatter_cat_else2(self, mcds=mcds): #ext = None, # test fig case #figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -719,7 +752,7 @@ def test_mcds_plot_scatter_num_if(self, mcds=mcds): #ext = None, # test fig case #figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -742,23 +775,24 @@ def test_mcds_plot_scatter_num_else(self, mcds=mcds): #ext = None, # test fig case #figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() def test_mcds_make_cell_vtk_attribute_default(self, mcds=mcds): s_pathfile = mcds.make_cell_vtk( #attribute=['cell_type'], + visualize=False, ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_2d/output00000024_cell.vtp')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) os.remove(s_pathfile) def test_mcds_make_cell_vtk_attribute_zero(self, mcds=mcds): - s_pathfile = mcds.make_cell_vtk(attribute=[]) - assert(str(type(mcds)) == "") and \ + s_pathfile = mcds.make_cell_vtk(attribute=[], visualize=False) + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_2d/output00000024_cell.vtp')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) @@ -767,8 +801,9 @@ def test_mcds_make_cell_vtk_attribute_zero(self, mcds=mcds): def test_mcds_make_cell_vtk_attribute_many(self, mcds=mcds): s_pathfile = mcds.make_cell_vtk( attribute=['dead', 'cell_count_voxel', 'pressure', 'cell_type'], + visualize=False, ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_2d/output00000024_cell.vtp')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) @@ -777,24 +812,24 @@ def test_mcds_make_cell_vtk_attribute_many(self, mcds=mcds): ## graph related functions ## -class TestTimeStepGraph(object): - ''' tests for pcdl.TimeStep graph related functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsGraph(object): + ''' tests for pcdl.pyMCDS graph related functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) # graph dictionatry def test_mcds_get_attached_graph_dict(self, mcds=mcds): - dei_graph = mcds.data['cell']['dei_graph']['attached_cells'] + dei_graph = mcds.data['discrete_cells']['graph']['attached_cells'] #print('graph attached:', sorted(dei_graph.items())) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(dei_graph)) == "") and \ (str(type(dei_graph[10])) == "") and \ (len(dei_graph[10]) == 0) and \ (len(dei_graph) > 9) def test_mcds_get_neighbor_graph_dict(self, mcds=mcds): - dei_graph = mcds.data['cell']['dei_graph']['neighbor_cells'] + dei_graph = mcds.data['discrete_cells']['graph']['neighbor_cells'] #print('graph neighbor:', sorted(dei_graph.items())) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(dei_graph)) == "") and \ (str(type(dei_graph[10])) == "") and \ (len(dei_graph[10]) == 6) and \ @@ -807,7 +842,7 @@ def test_mcds_make_graph_gml_attached_defaultattr(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_2d/output00000024_attached.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -822,7 +857,7 @@ def test_mcds_make_graph_gml_attached_edgeattrfalse(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_2d/output00000024_attached.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -837,7 +872,7 @@ def test_mcds_make_graph_gml_neighbor_nodeattrtrue(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_2d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -857,7 +892,7 @@ def test_mcds_make_graph_gml_neighbor_defaultattr(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_2d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -872,7 +907,7 @@ def test_mcds_make_graph_gml_neighbor_edgeattrfalse(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_2d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -887,7 +922,7 @@ def test_mcds_make_graph_gml_neighbor_nodeattrtrue(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_2d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -900,174 +935,3 @@ def test_mcds_make_graph_gml_neighbor_nodeattrtrue(self, mcds=mcds): (s_file.find('edge [\n source') > -1) and \ (s_file.find('distance_microns') > -1) os.remove(s_pathfile) - - -## ome tiff related functions ## - -class TestTimeStepOmeTiff(object): - ''' tests for pcdl.TimeStep graph related functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) - - ## ome tiff related functions ## - def test_mcds_make_ome_tiff_default(self, mcds=mcds): - s_pathfile = mcds.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=True) - assert(str(type(mcds)) == "") and \ - (s_pathfile.replace('\\','/').endswith('pcdl/output_2d/output00000024_oxygen_water_default_blood_cells_ID.ome.tiff')) and \ - (os.path.exists(s_pathfile)) and \ - (os.path.getsize(s_pathfile) > 2**10) - os.remove(s_pathfile) - - def test_mcds_make_ome_tiff_bool(self, mcds=mcds): - a_ometiff = mcds.make_ome_tiff(cell_attribute='dead', conc_cutoff={}, focus=None, file=False) - assert(str(type(mcds)) == "") and \ - (str(type(a_ometiff)) == "") and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (4, 1, 200, 300)) and \ - (a_ometiff[2].min() == 0.0) and \ - (a_ometiff[3].min() == 0.0) and \ - (a_ometiff[0].max() >= 1.0) and \ - (a_ometiff[1].max() >= 1.0) and \ - (a_ometiff[2].max() >= 1.0) and \ - (a_ometiff[3].max() >= 1.0) - - def test_mcds_make_ome_tiff_int(self, mcds=mcds): - a_ometiff = mcds.make_ome_tiff(cell_attribute='cell_count_voxel', conc_cutoff={}, focus=None, file=False) - assert(str(type(mcds)) == "") and \ - (str(type(a_ometiff)) == "") and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (4, 1, 200, 300)) and \ - (a_ometiff[2].min() == 0.0) and \ - (a_ometiff[3].min() == 0.0) and \ - (a_ometiff[0].max() >= 1.0) and \ - (a_ometiff[1].max() >= 1.0) and \ - (a_ometiff[2].max() >= 1.0) and \ - (a_ometiff[3].max() >= 1.0) - - def test_mcds_make_ome_tiff_float(self, mcds=mcds): - a_ometiff = mcds.make_ome_tiff(cell_attribute='pressure', conc_cutoff={}, focus=None, file=False) - assert(str(type(mcds)) == "") and \ - (str(type(a_ometiff)) == "") and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (4, 1, 200, 300)) and\ - (a_ometiff[2].min() == 0.0) and \ - (a_ometiff[3].min() == 0.0) and \ - (a_ometiff[0].max() >= 1.0) and \ - (a_ometiff[1].max() >= 1.0) and \ - (a_ometiff[2].max() >= 1.0) and \ - (a_ometiff[3].max() >= 1.0) - - def test_mcds_make_ome_tiff_conccutoff(self, mcds=mcds): - a_ometiff = mcds.make_ome_tiff(cell_attribute='ID', conc_cutoff={'oxygen': -1}, focus=None, file=False) - assert(str(type(mcds)) == "") and \ - (str(type(a_ometiff)) == "") and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (4, 1, 200, 300)) and \ - (a_ometiff[2].min() == 0.0) and \ - (a_ometiff[3].min() == 0.0) and \ - (a_ometiff[0].max() >= 1.0) and \ - (a_ometiff[1].max() >= 1.0) and \ - (a_ometiff[2].max() >= 1.0) and \ - (a_ometiff[3].max() >= 1.0) - - def test_mcds_make_ome_tiff_focus(self, mcds=mcds): - a_ometiff = mcds.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus={'default'}, file=False) - assert(str(type(mcds)) == "") and \ - (str(type(a_ometiff)) == "") and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (1, 1, 200, 300)) and \ - (a_ometiff[0].min() == 0.0) and \ - (a_ometiff[0].max() >= 1.0) - - -class TestTimeStepNeuroglancer(object): - ''' tests for loading a pcdl.TimeStep data set. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_2d, output_path=s_path_2d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) - - ## make_gif and magick ommand ## - def test_mcds_render_neuroglancer_default(self, mcds=mcds): - s_tiffpathfile = mcds.make_ome_tiff() - o_viewer = mcds.render_neuroglancer( - tiffpathfile = s_tiffpathfile, - #timestep = 0, - #intensity_cmap='gray', - ) - assert(str(type(o_viewer)) == "") and \ - (str(o_viewer).startswith('http://127.0.0.1:')) - os.remove(s_tiffpathfile) - - def test_mcds_render_neuroglancer_timestep(self, mcds=mcds): - s_tiffpathfile = mcds.make_ome_tiff() - o_viewer = mcds.render_neuroglancer( - tiffpathfile = s_tiffpathfile, - timestep = -1, - intensity_cmap='gray', - ) - assert(str(type(o_viewer)) == "") and \ - (str(o_viewer).startswith('http://127.0.0.1:')) - os.remove(s_tiffpathfile) - - def test_mcds_render_neuroglancer_cmap(self, mcds=mcds): - s_tiffpathfile = mcds.make_ome_tiff() - o_viewer = mcds.render_neuroglancer( - tiffpathfile = s_tiffpathfile, - timestep = 0, - intensity_cmap='magma', - ) - assert(str(type(o_viewer)) == "") and \ - (str(o_viewer).startswith('http://127.0.0.1:')) - os.remove(s_tiffpathfile) - - -## anndata helper function ## -class TestTimeStepScaler(object): - ''' test for pcdl.scaler function ''' - a_x = np.array([[ 1.,-1., 2., 0.],[ 2., 0., 0.,0.],[ 0., 1.,-1.,0.]]) - df_x = pd.DataFrame(a_x, columns=['a','b','c','d']) - - def test_scaler_none(self, df_x=df_x): - df_scaled = pcdl.timestep.scaler(df_x=df_x, scale=None) - assert(str(type(df_scaled)) == "") and \ - (all(df_scaled == df_x)) - - def test_scaler_minabs(self, df_x=df_x): - df_scaled = pcdl.timestep.scaler(df_x=df_x, scale='maxabs') - assert(str(type(df_scaled)) == "") and \ - (df_scaled.values.sum().round(3) == 2.0) and \ - (df_scaled.values.min().round(3) == -1.0) and \ - (df_scaled.values.max().round(3) == 1.0) - - def test_scaler_minmax(self, df_x=df_x): - df_scaled = pcdl.timestep.scaler(df_x=df_x, scale='minmax') - assert(str(type(df_scaled)) == "") and \ - (df_scaled.values.sum().round(3) == 4.333) and \ - (df_scaled.values.min().round(3) == 0.0) and \ - (df_scaled.values.max().round(3) == 1.0) - - def test_scaler_std(self, df_x=df_x): - df_scaled = pcdl.timestep.scaler(df_x=df_x, scale='std') - assert(str(type(df_scaled)) == "") and \ - (df_scaled.values.sum().round(3) == 0.0) and \ - (df_scaled.values.min().round(3) == -1.0) and \ - (df_scaled.values.max().round(3) == 1.091) - - -## anndata time step related functions ## -class TestTimeStepAnnData(object): - ''' test for pcdl.TimeStep class. ''' - - ## get_anndata command ## - def test_mcds_get_anndata(self): - mcds = pcdl.TimeStep(s_pathfile_2d, verbose=False) - ann = mcds.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs') - assert(str(type(mcds)) == "") and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 105) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 7) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 2) and \ - (len(ann.obsp) == 2) and \ - (ann.var.shape == (105, 0)) and \ - (len(ann.uns) == 1) - diff --git a/test/test_timestep_3d.py b/test/test_timestep_3d.py index 8b370dd..4af50c5 100644 --- a/test/test_timestep_3d.py +++ b/test/test_timestep_3d.py @@ -7,7 +7,7 @@ # license: BSD 3-Clause # # description: -# pytest unit test library for the pcdl library TimeStep class. +# pytest unit test library for the pcdl library pyMCDS class. # focus is only in 3d and speed. # + https://docs.pytest.org/ # @@ -22,7 +22,6 @@ # load library import numpy as np import os -import pandas as pd import pathlib import pcdl import matplotlib.pyplot as plt @@ -43,15 +42,15 @@ # 3D only # ########### -class TestTimeStep3dOnly(object): - ''' test for 3D only conditions in pcdl.TimeStep functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True +class TestPyMcds3dOnly(object): + ''' test for 3D only conditions in pcdl.pyMCDS functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True ## mesh related functions ## # bue: check if in 2D def test_mcds_get_mesh_spacing(self, mcds=mcds): lr_spacing = mcds.get_mesh_spacing() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(lr_spacing)) == "") and \ (str(type(lr_spacing[0])) == "") and \ (str(type(lr_spacing[1])) == "") and \ @@ -63,180 +62,180 @@ def test_mcds_get_mesh_spacing(self, mcds=mcds): # test workhorse for speed # ############################ -class TestTimeStepInit(object): - ''' tests for loading a pcdl.TimeStep data set. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInit(object): + ''' tests for loading a pcdl.pyMCDS data set. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -class TestTimeStepInitMicroenvFalse(object): - ''' tests for loading a pcdl.TimeStep data set with microenv false. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=False, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitMicroenvFalse(object): + ''' tests for loading a pcdl.pyMCDS data set with microenv false. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=False, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 116) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 116) -class TestTimeStepInitGraphFalse(object): - ''' tests for loading a pcdl.TimeStep data set with graph false. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=False, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitGraphFalse(object): + ''' tests for loading a pcdl.pyMCDS data set with graph false. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=False, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) == 0) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) == 0) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) == 0) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) == 0) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -class TestTimeStepInitPhysibossFalse(object): - ''' tests for loading a pcdl.TimeStep data set with physiboss false. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=False, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitPhysibossFalse(object): + ''' tests for loading a pcdl.pyMCDS data set with physiboss false. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=False, settingxml='PhysiCell_settings.xml', verbose=True) df_cell = mcds.get_cell_df() def test_mcds_init_microenv(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_init_graph(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['attached_cells'])) == "") and \ - (str(type(mcds.data['cell']['dei_graph']['neighbor_cells'])) == "") and \ - (len(mcds.data['cell']['dei_graph']['attached_cells']) > 9) and \ - (len(mcds.data['cell']['dei_graph']['neighbor_cells']) > 9) + assert(str(type(mcds)) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['attached_cells'])) == "") and \ + (str(type(mcds.data['discrete_cells']['graph']['neighbor_cells'])) == "") and \ + (len(mcds.data['discrete_cells']['graph']['attached_cells']) > 9) and \ + (len(mcds.data['discrete_cells']['graph']['neighbor_cells']) > 9) - #def test_mcds_init_physiboss(self, mcds=mcds): - # assert(str(type(mcds)) == "") and \ - # (mcds.data['cell']['physiboss'] == None) + def test_mcds_init_physiboss(self, mcds=mcds): + assert(str(type(mcds)) == "") and \ + (mcds.data['discrete_cells']['physiboss'] == None) def test_mcds_init_settingxml(self, mcds=mcds, df_cell=df_cell): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (set(df_cell.columns).issuperset({'default_fusion_rates'})) and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) -#class TestTimeStepInitSettingxmlFalse(object): -# ''' tests for loading a pcdl.TimeStep data set with settingxml false. ''' +#class TestPyMcdsInitSettingxmlFalse(object): +# ''' tests for loading a pcdl.pyMCDS data set with settingxml false. ''' # NOP PhysiCell >= v1.14.0 -#class TestTimeStepInitSettingxmlNone(object): -# ''' tests for loading a pcdl.TimeStep data set with settingxml none. ''' +#class TestPyMcdsInitSettingxmlNone(object): +# ''' tests for loading a pcdl.pyMCDS data set with settingxml none. ''' # NOP PhysiCell >= v1.14.0 -class TestTimeStepInitVerboseTrue(object): - ''' tests for loading a pcdl.TimeStep data set and set_verbose_false function. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) +class TestPyMcdsInitVerboseTrue(object): + ''' tests for loading a pcdl.pyMCDS data set and set_verbose_false function. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True) def test_mcds_verbose_true(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (mcds.verbose) def test_mcds_set_verbose_false(self, mcds=mcds): mcds.set_verbose_false() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (not mcds.verbose) -class TestTimeStepInitVerboseFalse(object): - ''' tests for loading a pcdl.TimeStep data set and set_verbose_true function. ''' - mcds = pcdl.TimeStep(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=False) +class TestPyMcdsInitVerboseFalse(object): + ''' tests for loading a pcdl.pyMCDS data set and set_verbose_true function. ''' + mcds = pcdl.pyMCDS(xmlfile=s_file_3d, output_path=s_path_3d, custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=False) def test_mcds_verbose_false(self, mcds=mcds): - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (not mcds.verbose) def test_mcds_set_verbose_true(self, mcds=mcds): mcds.set_verbose_true() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (mcds.verbose) -class TestTimeStep3dSettingWorkhorse(object): - ''' tests on 3D data set, for speed, for pcdl.TimeStep unit related workhorse functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True +class TestPyMcds3dSettingWorkhorse(object): + ''' tests on 3D data set, for speed, for pcdl.pyMCDS unit related workhorse functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True def test_mcds_get_unit_dict(self, mcds=mcds): ds_unit = mcds.get_unit_dict() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(ds_unit)) == "") and \ (len(ds_unit) > 9) and \ (ds_unit['oxygen'] == 'dimensionless') -class TestTimeStep3dMicroenvWorkhorse(object): - ''' tests on 3D data set, for speed, for pcdl.TimeStep microenvironment related workhorse functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True +class TestPyMcds3dMicroenvWorkhorse(object): + ''' tests on 3D data set, for speed, for pcdl.pyMCDS microenvironment related workhorse functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True ## micro environment related functions ## def test_mcds_get_conc_df(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (1331, 11)) #(df_conc.shape[0] > 9) and \ @@ -244,31 +243,31 @@ def test_mcds_get_conc_df(self, mcds=mcds): def test_mcds_get_conc_df_zslice_center(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=0, halt=False, values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 11)) def test_mcds_get_conc_df_zslice_outofcenter(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=-6, halt=False, values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (121, 11)) def test_mcds_get_conc_df_values(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=2, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (1331, 11)) def test_mcds_get_conc_df_drop(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=1, drop={'oxygen'}, keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (1331, 10)) def test_mcds_get_conc_df_keep(self, mcds=mcds): df_conc = mcds.get_conc_df(z_slice=None, halt=False, values=1, drop=set(), keep={'oxygen'}) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_conc)) == "") and \ (df_conc.shape == (1331, 10)) @@ -292,7 +291,7 @@ def test_mcds_plot_contour(self, mcds=mcds): ext = None, # test fig case figbgcolor = None, # not at file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -315,50 +314,50 @@ def test_mcds_plot_contourf(self, mcds=mcds): ext = 'tiff', # test file case figbgcolor = 'orange', # jump over if ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_3d/conc_oxygen_z-5.0/output00000024_oxygen.tiff')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) os.remove(s_pathfile) def test_mcds_make_conc_vtk(self, mcds=mcds): - s_pathfile = mcds.make_conc_vtk() - assert(str(type(mcds)) == "") and \ + s_pathfile = mcds.make_conc_vtk(visualize=False) + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_3d/output00000024_conc.vtr')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) os.remove(s_pathfile) -class TestTimeStep3dCellWorkhorse(object): - ''' tests on 3D data set, for speed, for pcdl.TimeStep cell related workhorse functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True +class TestPyMcds3dCellWorkhorse(object): + ''' tests on 3D data set, for speed, for pcdl.pyMCDS cell related workhorse functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True ## cell related functions ## def test_mcds_get_cell_df(self, mcds=mcds): df_cell = mcds.get_cell_df(values=1, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 122) def test_mcds_get_cell_df_values(self, mcds=mcds): df_cell = mcds.get_cell_df(values=2, drop=set(), keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 72) def test_mcds_get_cell_df_drop(self, mcds=mcds): df_cell = mcds.get_cell_df(values=1, drop={'oxygen'}, keep=set()) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 121) def test_mcds_get_cell_df_keep(self, mcds=mcds): df_cell = mcds.get_cell_df(values=1, drop=set(), keep={'oxygen'}) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(df_cell)) == "") and \ (df_cell.shape[0] > 9) and \ (df_cell.shape[1] == 13) @@ -384,7 +383,7 @@ def test_mcds_plot_scatter_cat_if(self, mcds=mcds): ext = None, # test fig case figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -407,7 +406,7 @@ def test_mcds_plot_scatter_cat_else1(self, mcds=mcds): ext = 'tiff', # test file case figbgcolor = 'lime', # jump over if ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_3d/cell_cell_type_z-5.0/output00000024_cell_type.tiff')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) @@ -433,7 +432,7 @@ def test_mcds_plot_scatter_cat_else2(self, mcds=mcds): ext = None, # test fig case figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -457,7 +456,7 @@ def test_mcds_plot_scatter_num_if(self, mcds=mcds): ext = None, # test fig case figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() @@ -480,23 +479,24 @@ def test_mcds_plot_scatter_num_else(self, mcds=mcds): ext = None, # test fig case figbgcolor = None, # not a file ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (str(type(fig)) == "") plt.close() def test_mcds_make_cell_vtk_attribute_default(self, mcds=mcds): s_pathfile = mcds.make_cell_vtk( #attribute=['cell_type'], + visualize=False, ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_3d/output00000024_cell.vtp')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) os.remove(s_pathfile) def test_mcds_make_cell_vtk_attribute_zero(self, mcds=mcds): - s_pathfile = mcds.make_cell_vtk(attribute=[]) - assert(str(type(mcds)) == "") and \ + s_pathfile = mcds.make_cell_vtk(attribute=[], visualize=False) + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_3d/output00000024_cell.vtp')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) @@ -505,17 +505,18 @@ def test_mcds_make_cell_vtk_attribute_zero(self, mcds=mcds): def test_mcds_make_cell_vtk_attribute_many(self, mcds=mcds): s_pathfile = mcds.make_cell_vtk( attribute=['dead', 'cell_count_voxel', 'pressure', 'cell_type'], + visualize=False, ) - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('/pcdl/output_3d/output00000024_cell.vtp')) and \ (os.path.exists(s_pathfile)) and \ (os.path.getsize(s_pathfile) > 2**10) os.remove(s_pathfile) -class TestTimeStep3dGraphWorkhorse(object): - ''' tests on 3D data set, for speed, for pcdl.TimeStep graph related workhorse functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True +class TestPyMcds3dGraphWorkhorse(object): + ''' tests on 3D data set, for speed, for pcdl.pyMCDS graph related workhorse functions. ''' + mcds = pcdl.pyMCDS(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True ## graph related functions ## # attached graph gml files @@ -524,7 +525,7 @@ def test_mcds_make_graph_gml_attached_defaultattr(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_3d/output00000024_attached.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -539,7 +540,7 @@ def test_mcds_make_graph_gml_attached_edgeattrfalse(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_3d/output00000024_attached.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -554,7 +555,7 @@ def test_mcds_make_graph_gml_neighbor_nodeattrtrue(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_3d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -574,7 +575,7 @@ def test_mcds_make_graph_gml_neighbor_defaultattr(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_3d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -589,7 +590,7 @@ def test_mcds_make_graph_gml_neighbor_edgeattrfalse(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_3d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -604,7 +605,7 @@ def test_mcds_make_graph_gml_neighbor_nodeattrtrue(self, mcds=mcds): f = open(s_pathfile) s_file = f.read() f.close() - assert(str(type(mcds)) == "") and \ + assert(str(type(mcds)) == "") and \ (s_pathfile.replace('\\','/').endswith('pcdl/output_3d/output00000024_neighbor.gml')) and \ (os.path.exists(s_pathfile)) and \ (s_file.find('Creator "pcdl_v') > -1) and \ @@ -617,46 +618,3 @@ def test_mcds_make_graph_gml_neighbor_nodeattrtrue(self, mcds=mcds): (s_file.find('edge [\n source') > -1) and \ (s_file.find('distance_microns') > -1) os.remove(s_pathfile) - - -class TestTimeStep3dOmeTiffWorkhorse(object): - ''' tests on 3D data set, for speed, for pcdl.TimeStep ome tiff related workhorse functions. ''' - mcds = pcdl.TimeStep(xmlfile=s_pathfile_3d) # custom_data_type={}, microenv=True, graph=True, physiboss=True, settingxml='PhysiCell_settings.xml', verbose=True - - ## ome tiff related functions ## - def test_mcds_make_ome_tiff_default(self, mcds=mcds): - s_pathfile = mcds.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=True) - assert(str(type(mcds)) == "") and \ - (s_pathfile.replace('\\','/').endswith('pcdl/output_3d/output00000024_oxygen_water_default_blood_cells_ID.ome.tiff')) and \ - (os.path.exists(s_pathfile)) and \ - (os.path.getsize(s_pathfile) > 2**10) - os.remove(s_pathfile) - - def test_mcds_make_ome_tiff_nofile(self, mcds=mcds): - a_ometiff = mcds.make_ome_tiff(cell_attribute='ID', conc_cutoff={}, focus=None, file=False) - assert(str(type(mcds)) == "") and \ - (str(type(a_ometiff)) == "") and \ - (a_ometiff.dtype == np.float32) and \ - (a_ometiff.shape == (4, 11, 200, 300)) - - -## anndata time step related functions ## -class TestTimeStep3dAnnData(object): - ''' test for pcdl.TimeStep class. ''' - - ## get_anndata command ## - def test_mcds_get_anndata(self): - mcds = pcdl.TimeStep(s_pathfile_3d, verbose=False) - ann = mcds.get_anndata(values=1, drop=set(), keep=set(), scale='maxabs') - assert(str(type(mcds)) == "") and \ - (str(type(ann)) == "") and \ - (ann.X.shape[0] > 9) and \ - (ann.X.shape[1] == 105) and \ - (ann.obs.shape[0] > 9) and \ - (ann.obs.shape[1] == 7) and \ - (ann.obsm['spatial'].shape[0] > 9) and \ - (ann.obsm['spatial'].shape[1] == 3) and \ - (len(ann.obsp) == 2) and \ - (ann.var.shape == (105, 0)) and \ - (len(ann.uns) == 1) -