I have been learning how to use MESA Agent Based Modelling library in python to help my PhD student supervisor. I am simulating the activity and bone formation phase of the bone remodelling process. I specifically focus on osteoblasts cells and how they work together to deposit bone matrix.
I have finally understood how to actually use the agents and model commands from MESA, I will share my code so that it an be checked. My code basically shows osteoblast cells moving around bone matrix until reaching a white bone matrix (cell with density = 0). Once they locate themselves in a cell with density == 0 they stop and start secreting bone, making the density increase by 0.5 at each step. My problem is that I have tried to add a dynamic line graph so that I can see at each step the bone formation rate that would be measured by the increase in the density. Every time I import ChartModule and DataCollector and run my code everything in the server visualization goes blank. I would like a suggestion, not any solution of how to tackle this.
This first code is my working version without any graph added. I would like to implement to this code a data collector module with a ChartModule module from MESA to see a dynamic chart.
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 31 15:46:01 2023
@author: alvar
"""
import mesa
from mesa.time import RandomActivation
from mesa.space import MultiGrid
from mesa.visualization.modules import CanvasGrid
from mesa.visualization.ModularVisualization import ModularServer
class Barrier(mesa.Agent): # Barrier agent is just the line that separates the soft tissue or outside bone are from bone matrix.
def __init__(self, unique_id, model):
super().__init__(unique_id, model)
class BoneMatrix(mesa.Agent):
def __init__(self, unique_id, model):
super().__init__(unique_id, model)
self.density = 5
def step(self):
print(f"bone_matrix({self.unique_id}) is taking a big steppy")
class Osteocyte(mesa.Agent):
def __init__(self, unique_id, model):
super().__init__(unique_id, model)
def step(self):
print(f"osteocyte{self.unique_id} is taking a big steppy")
class Osteoblast(mesa.Agent):
def __init__(self, unique_id, model, white_cells,white_cell_colors):
super().__init__(unique_id,model)
self.white_cells = white_cells
self.white_cell_colors = white_cell_colors
self.mobile = True
def move(self):
while True:
#look for move
possible_steps = self.model.grid.get_neighborhood(
self.pos, moore = True, include_center = False
)
new_position = self.random.choice(possible_steps)
cell_contents = self.model.grid.get_cell_list_contents(new_position)
no_osteocyte_present = not any(isinstance(agent, Osteocyte) for agent in cell_contents)
if no_osteocyte_present:
break
self.model.grid.move_agent(self,new_position)
white_bone_matrix_present = any(
isinstance(agent, BoneMatrix) and agent.density == 0 for agent in cell_contents
)
osteo_neighbour_present = False
for nx, ny in self.model.grid.get_neighborhood(new_position, moore=True, include_center = False):
neighbour_contents = self.model.grid.get_cell_list_contents((nx,ny))
cell_density_max = False
embedded_osteoblast = False
if (any(isinstance(agent,Osteoblast) for agent in neighbour_contents)):
for n in neighbour_contents:
if isinstance(n, BoneMatrix):
if (n.density == 5):
cell_density_max = True
if isinstance(n, Osteoblast):
if (not n.mobile):
embedded_osteoblast = True
condition1 = any(isinstance(agent,Osteocyte) for agent in neighbour_contents)
condition2 = cell_density_max and embedded_osteoblast
if (condition1 or condition2):
osteo_neighbour_present = True
break
if (white_bone_matrix_present and osteo_neighbour_present):
self.mobile = False
def differentiation(self):
return 0
def apoptosis(self):
return 0
def step(self):
if (self.mobile):
self.move()
class BoneModel(mesa.Model):
def __init__(self,width, height):
self.grid = MultiGrid(width, height, False)
self.schedule = RandomActivation(self)
# Loop to create Barrier agents
for r in range(width):
for s in range(height-3, height-2):
barrier = Barrier(f'barrier_r{r}_{s}', self)
self.grid.place_agent(barrier, (r,s))
self.schedule.add(barrier)
white_cells = set()
while len(white_cells) < 12:
x, y = self.random.randrange(3,7), self.random.randrange(height - 6, height - 3)
white_cells.add((x, y))
white_cell_colors = {cell: 0 for cell in white_cells}
# Loop to create BoneMatrix agents
for x in range(width):
for y in range(height - 3):
bm = BoneMatrix(f' bone_matrix_{x}_{y}', self)
if (x, y) in white_cells:
bm.density = 0
self.grid.place_agent(bm, (x, y))
self.schedule.add(bm)
# Loop to create Osteocyte agents and place them in the bottom 4 rows
for p in range(width):
for q in range(height-6):
osc = Osteocyte(f' osteocyte_{p}_{q}', self)
self.grid.place_agent(osc, (p, q))
self.schedule.add(osc)
# Loop to create Osteoblast agents and place them in the top 2 rows in different gridcells
unique_positions = set() # To keep track of unique positions
for _ in range(12):
while True:
x, y = self.random.randrange(width), self.random.randrange(height-2, height)
if (x, y) not in unique_positions: # Check if the position is unique
osb = Osteoblast(f' osteoblast_{_}', self, white_cells, white_cell_colors)
self.grid.place_agent(osb, (x, y))
self.schedule.add(osb)
unique_positions.add((x, y))
break
def step(self):
# Iterate through all grid cells
self.schedule.step()
for x in range(width):
for y in range(height):
cell_contents = self.grid.get_cell_list_contents((x, y))
osteoblast_present = any(isinstance(agent, Osteoblast) for agent in cell_contents)
neighbours = self.grid.get_neighborhood((x,y), moore=True, include_center=False)
osteo_neighbour_present = False
for nx, ny in neighbours:
neighbour_contents = self.grid.get_cell_list_contents((nx,ny))
cell_density_max = False
embedded_osteoblast = False
if (any(isinstance(agent, Osteoblast) for agent in neighbour_contents)):
for n in neighbour_contents:
if isinstance(n, BoneMatrix):
if (n.density == 5):
cell_density_max=True
if isinstance(n, Osteoblast):
if (not n.mobile):
embedded_osteoblast = True
condition1 = any(isinstance(agent,Osteocyte) for agent in neighbour_contents) # osteocyte neighbyr
condition2 = cell_density_max and embedded_osteoblast# osteoblast fully formed neighbor
if (condition1 or condition2):
osteo_neighbour_present = True
break
for agent in cell_contents:
if isinstance(agent, BoneMatrix):
if osteoblast_present and osteo_neighbour_present:
if agent.density < 5:
agent.density += 0.25
osteo_neighbour_present = False
def agent_portrayal(agent):
portrayal = {
"Shape": "rect",
"w": 1,
"h": 1,
}
if isinstance(agent, BoneMatrix):
# Map density to shades of blue between 0 and 10
max_density = 10 # Set the maximum density
min_density = 0 # Set the minimum density
density = agent.density
normalized_density = (density - min_density) / (max_density - min_density)
color = f"rgba(0, 0, 255, {normalized_density})"
portrayal["Color"] = color
portrayal["Filled"] = "true"
portrayal["Layer"] = 0
portrayal["text"] = "BM"
portrayal["text_color"] = "white"
elif isinstance(agent, Osteocyte):
portrayal["Shape"] = "circle"
portrayal["Color"] = "salmon"
portrayal["Filled"] = "true"
portrayal["Layer"] = 1
portrayal["text"] = "Osc"
portrayal["r"] = 0.45
elif isinstance(agent, Barrier):
portrayal["Color"] = "rgba(245, 200, 245, 1)"
portrayal["Filled"] = "true"
portrayal["Layer"] = 2
elif isinstance(agent, Osteoblast):
portrayal["Shape"] = "rect"
portrayal["Color"] = "green"
portrayal["Filled"] = "true"
portrayal["Layer"] = 3
portrayal["text"] = "Osb"
portrayal["w"] = 0.5
portrayal["h"] = 0.3
portrayal["x"] = -0.1
portrayal["y"] = 0
portrayal["r"] = 3
if agent.pos in agent.white_cells:
portrayal["Color"] = "yellow"
return portrayal
width = 10 # Grid width
height = 10 # Grid height
canvas_grid = CanvasGrid(agent_portrayal, width, height, 500, 500)
# Create a DataCollector object to collect data
server = ModularServer(
BoneModel,
[canvas_grid],
"Bone Model",
{"width": width, "height": height}
)
server.port = 8521 # Set the port for the web interface
server.launch()
This second code was my attempt of adding one. I still do not understand very well how to implement it and where to put the data collector function. I want to collect at the agents level which is just the increase of density and in the model i want to calculate the bone_formation_rate which will be measured by the increase of density at each step.
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 31 15:46:01 2023
@author: alvar
"""
import mesa
from mesa.time import RandomActivation
from mesa.space import MultiGrid
from mesa.visualization.modules import CanvasGrid
from mesa.visualization.ModularVisualization import ModularServer
from mesa.datacollection import DataCollector
from mesa.visualization.modules import ChartModule
def bone_formation_rate(self):
white_bone_matrix_agents = [agent for agent in self.schedule.agents if isinstance(agent, BoneMatrix) and agent.density == 0]
total_density_increase = sum(1 for agent in white_bone_matrix_agents if agent.density > 0)
return total_density_increase
class Barrier(mesa.Agent): # Barrier agent is just the line that separates the soft tissue or outside bone are from bone matrix.
def __init__(self, unique_id, model):
super().__init__(unique_id, model)
class BoneMatrix(mesa.Agent):
def __init__(self, unique_id, model):
super().__init__(unique_id, model)
self.density = 5
def step(self):
print(f"bone_matrix({self.unique_id}) is taking a big steppy")
class Osteocyte(mesa.Agent):
def __init__(self, unique_id, model):
super().__init__(unique_id, model)
def step(self):
print(f"osteocyte{self.unique_id} is taking a big steppy")
class Osteoblast(mesa.Agent):
def __init__(self, unique_id, model, white_cells,white_cell_colors):
super().__init__(unique_id,model)
self.white_cells = white_cells
self.white_cell_colors = white_cell_colors
self.mobile = True
def move(self):
while True:
#look for move
possible_steps = self.model.grid.get_neighborhood(
self.pos, moore = True, include_center = False
)
new_position = self.random.choice(possible_steps)
cell_contents = self.model.grid.get_cell_list_contents(new_position)
no_osteocyte_present = not any(isinstance(agent, Osteocyte) for agent in cell_contents)
if no_osteocyte_present:
break
self.model.grid.move_agent(self,new_position)
white_bone_matrix_present = any(
isinstance(agent, BoneMatrix) and agent.density == 0 for agent in cell_contents
)
osteo_neighbour_present = False
for nx, ny in self.model.grid.get_neighborhood(new_position, moore=True, include_center = False):
neighbour_contents = self.model.grid.get_cell_list_contents((nx,ny))
cell_density_max = False
embedded_osteoblast = False
if (any(isinstance(agent,Osteoblast) for agent in neighbour_contents)):
for n in neighbour_contents:
if isinstance(n, BoneMatrix):
if (n.density == 5):
cell_density_max = True
if isinstance(n, Osteoblast):
if (not n.mobile):
embedded_osteoblast = True
condition1 = any(isinstance(agent,Osteocyte) for agent in neighbour_contents)
condition2 = cell_density_max and embedded_osteoblast
if (condition1 or condition2):
osteo_neighbour_present = True
break
if (white_bone_matrix_present and osteo_neighbour_present):
self.mobile = False
def differentiation(self):
return 0
def apoptosis(self):
return 0
def step(self):
if (self.mobile):
self.move()
class BoneModel(mesa.Model):
def __init__(self,width, height):
self.grid = MultiGrid(width, height, False)
self.schedule = RandomActivation(self)
self.datacollector = DataCollector(
model_reporters={"Density": "density"},
agent_reporters={"WhiteBoneMatrixDensity": lambda a: a.density if isinstance(a, BoneMatrix) and a.density == 0 else None},
)
# Loop to create Barrier agents
for r in range(width):
for s in range(height-3, height-2):
barrier = Barrier(f'barrier_r{r}_{s}', self)
self.grid.place_agent(barrier, (r,s))
self.schedule.add(barrier)
white_cells = set()
while len(white_cells) < 12:
x, y = self.random.randrange(3,7), self.random.randrange(height - 6, height - 3)
white_cells.add((x, y))
white_cell_colors = {cell: 0 for cell in white_cells}
# Loop to create BoneMatrix agents
for x in range(width):
for y in range(height - 3):
bm = BoneMatrix(f' bone_matrix_{x}_{y}', self)
if (x, y) in white_cells:
bm.density = 0
self.grid.place_agent(bm, (x, y))
self.schedule.add(bm)
# Loop to create Osteocyte agents and place them in the bottom 4 rows
for p in range(width):
for q in range(height-6):
osc = Osteocyte(f' osteocyte_{p}_{q}', self)
self.grid.place_agent(osc, (p, q))
self.schedule.add(osc)
# Loop to create Osteoblast agents and place them in the top 2 rows in different gridcells
unique_positions = set() # To keep track of unique positions
for _ in range(12):
while True:
x, y = self.random.randrange(width), self.random.randrange(height-2, height)
if (x, y) not in unique_positions: # Check if the position is unique
osb = Osteoblast(f' osteoblast_{_}', self, white_cells, white_cell_colors)
self.grid.place_agent(osb, (x, y))
self.schedule.add(osb)
unique_positions.add((x, y))
break
def step(self):
# Iterate through all grid cells
self.datacollector.collect(self)
self.schedule.step()
for x in range(width):
for y in range(height):
cell_contents = self.grid.get_cell_list_contents((x, y))
osteoblast_present = any(isinstance(agent, Osteoblast) for agent in cell_contents)
neighbours = self.grid.get_neighborhood((x,y), moore=True, include_center=False)
osteo_neighbour_present = False
for nx, ny in neighbours:
neighbour_contents = self.grid.get_cell_list_contents((nx,ny))
cell_density_max = False
embedded_osteoblast = False
if (any(isinstance(agent, Osteoblast) for agent in neighbour_contents)):
for n in neighbour_contents:
if isinstance(n, BoneMatrix):
if (n.density == 5):
cell_density_max=True
if isinstance(n, Osteoblast):
if (not n.mobile):
embedded_osteoblast = True
condition1 = any(isinstance(agent,Osteocyte) for agent in neighbour_contents) # osteocyte neighbyr
condition2 = cell_density_max and embedded_osteoblast# osteoblast fully formed neighbor
if (condition1 or condition2):
osteo_neighbour_present = True
break
for agent in cell_contents:
if isinstance(agent, BoneMatrix):
if osteoblast_present and osteo_neighbour_present:
if agent.density < 5:
agent.density += 0.25
osteo_neighbour_present = False
def agent_portrayal(agent):
portrayal = {
"Shape": "rect",
"w": 1,
"h": 1,
}
if isinstance(agent, BoneMatrix):
# Map density to shades of blue between 0 and 10
max_density = 10 # Set the maximum density
min_density = 0 # Set the minimum density
density = agent.density
normalized_density = (density - min_density) / (max_density - min_density)
color = f"rgba(0, 0, 255, {normalized_density})"
portrayal["Color"] = color
portrayal["Filled"] = "true"
portrayal["Layer"] = 0
portrayal["text"] = "BM"
portrayal["text_color"] = "white"
elif isinstance(agent, Osteocyte):
portrayal["Shape"] = "circle"
portrayal["Color"] = "salmon"
portrayal["Filled"] = "true"
portrayal["Layer"] = 1
portrayal["text"] = "Osc"
portrayal["r"] = 0.45
elif isinstance(agent, Barrier):
portrayal["Color"] = "rgba(245, 200, 245, 1)"
portrayal["Filled"] = "true"
portrayal["Layer"] = 2
elif isinstance(agent, Osteoblast):
portrayal["Shape"] = "rect"
portrayal["Color"] = "green"
portrayal["Filled"] = "true"
portrayal["Layer"] = 3
portrayal["text"] = "Osb"
portrayal["w"] = 0.5
portrayal["h"] = 0.3
portrayal["x"] = -0.1
portrayal["y"] = 0
portrayal["r"] = 3
if agent.pos in agent.white_cells:
portrayal["Color"] = "yellow"
return portrayal
width = 10 # Grid width
height = 10 # Grid height
canvas_grid = CanvasGrid(agent_portrayal, width, height, 500, 500)
chart = ChartModule([{"Label": "Density", "Color": "blue"}, {"Label": "WhiteBoneMatrixDensity", "Color": "red"}])
server = ModularServer(
BoneModel,
[canvas_grid, chart], # Include the chart module
"Bone Model",
{"width": width, "height": height}
)
server.port = 8521 # Set the port for the web interface
server.launch()