-
-
Notifications
You must be signed in to change notification settings - Fork 5
🐛 Resolved the issue where configurations could only be recorded as strings. #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a934497
ac71950
1745558
e0b5d5f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,8 +20,12 @@ | |
| from mesa.visualization.utils import update_counter | ||
| from xarray import DataArray | ||
|
|
||
| from abses.main import MainModel | ||
| from abses.patch import PatchModule | ||
| try: | ||
| from mesa.visualization.components import AgentPortrayalStyle | ||
| except ImportError: | ||
| AgentPortrayalStyle = None | ||
|
|
||
| from abses import MainModel, PatchModule | ||
|
|
||
|
|
||
| def draw_property_layers( | ||
|
|
@@ -106,12 +110,14 @@ def collect_agent_data( | |
| Args: | ||
| space: The space containing the Agents. | ||
| agent_portrayal: A callable that is called with the agent and returns a dict | ||
| or AgentPortrayalStyle object (Mesa 3.3+) | ||
| color: default color | ||
| size: default size | ||
| marker: default marker | ||
| zorder: default zorder | ||
|
|
||
| agent_portrayal should return a dict, limited to size (size of marker), color (color of marker), zorder (z-order), | ||
| agent_portrayal should return a dict or AgentPortrayalStyle object, limited to | ||
| size (size of marker), color (color of marker), zorder (z-order), | ||
| marker (marker style), alpha, linewidths, and edgecolors | ||
|
|
||
| """ | ||
|
|
@@ -126,7 +132,43 @@ def collect_agent_data( | |
| } | ||
|
|
||
| for agent in space.agents: | ||
| portray = agent_portrayal(agent) | ||
| portray_result = agent_portrayal(agent) | ||
|
|
||
| # Convert AgentPortrayalStyle to dict if needed (Mesa 3.3+) | ||
| if AgentPortrayalStyle is not None and isinstance( | ||
| portray_result, AgentPortrayalStyle | ||
| ): | ||
| # AgentPortrayalStyle objects can be converted using vars() or direct attribute access | ||
| try: | ||
| # Try vars() first (works for most Python objects with __dict__) | ||
| portray = vars(portray_result).copy() | ||
| except (TypeError, AttributeError): | ||
| # Fallback: build dict from direct attribute access | ||
| portray = {} | ||
| for attr in [ | ||
| "size", | ||
| "color", | ||
| "marker", | ||
| "zorder", | ||
| "alpha", | ||
| "edgecolors", | ||
| "linewidths", | ||
| ]: | ||
| if hasattr(portray_result, attr): | ||
| value = getattr(portray_result, attr) | ||
| if value is not None: | ||
| portray[attr] = value | ||
| elif isinstance(portray_result, dict): | ||
| portray = portray_result.copy() | ||
| else: | ||
| # Fallback: try to convert to dict | ||
| try: | ||
| portray = ( | ||
| vars(portray_result) if hasattr(portray_result, "__dict__") else {} | ||
| ) | ||
| except (TypeError, AttributeError): | ||
| portray = {} | ||
|
|
||
| arguments["s"].append(portray.pop("size", size)) | ||
| arguments["c"].append(portray.pop("color", color)) | ||
| arguments["marker"].append(portray.pop("marker", marker)) | ||
|
|
@@ -145,7 +187,21 @@ def collect_agent_data( | |
| ) | ||
| # ensure loc is always a shape of (n, 2) array, even if n=0 | ||
| result = {k: np.asarray(v) for k, v in arguments.items()} | ||
| result["loc"] = space.agents.array("indices") | ||
| indices_array = space.agents.array("indices") | ||
| # Convert to (n, 2) shape | ||
| if len(indices_array) == 0: | ||
| result["loc"] = np.empty((0, 2), dtype=int) | ||
| else: | ||
| # Convert list of tuples/arrays to 2D array | ||
| result["loc"] = np.array( | ||
| [ | ||
| list(idx) if isinstance(idx, (tuple, list)) else idx | ||
| for idx in indices_array | ||
| ] | ||
| ) | ||
| # Ensure it's 2D | ||
| if result["loc"].ndim == 1: | ||
| result["loc"] = result["loc"].reshape(-1, 2) | ||
|
Comment on lines
+190
to
+204
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find where 'indices' is defined or assigned
echo "=== Search for indices property/attribute definition ==="
rg -n "indices\s*=" --type=py | head -20
echo -e "\n=== Search for array method in agents ==="
rg -n "def array\(" --type=py -A 5
echo -e "\n=== Search for indices in agents module ==="
rg -n "indices" --type=py -g '**/agents/**' -C 2 | head -40Repository: SongshGeoLab/ABSESpy Length of output: 3884 🏁 Script executed: #!/bin/bash
# Find the ActorsList or agents class definition
echo "=== Finding ActorsList class ==="
find . -type f -name "*.py" | xargs grep -l "class ActorsList\|class Agents" | head -10
# Check what array() method returns
echo -e "\n=== Searching for array method ==="
rg -n "def array" --type=py -A 10 | grep -A 10 "def array"Repository: SongshGeoLab/ABSESpy Length of output: 1982 🏁 Script executed: #!/bin/bash
# Get more context about the solara.py file and indices usage
echo "=== Full context around lines 190-204 in solara.py ==="
cat -n abses/viz/solara.py | sed -n '180,210p'
echo -e "\n=== Search for 'indices' in solara.py ==="
rg -n "indices" abses/viz/solara.py -B 3 -A 3Repository: SongshGeoLab/ABSESpy Length of output: 2061 🏁 Script executed: #!/bin/bash
# Check if there are any filters/guards before calling array("indices")
echo "=== Context in solara.py before indices_array usage ==="
cat -n abses/viz/solara.py | sed -n '160,210p'
echo -e "\n=== Check if actors are filtered for spatial position ==="
rg -n "on_earth|at is not None" abses/viz/solara.py -B 5 -A 5Repository: SongshGeoLab/ABSESpy Length of output: 2429 🏁 Script executed: #!/bin/bash
# Check what happens with None values in the actual array conversion
echo "=== Check for None handling in indices ==="
rg -n "indices.*None\|None.*indices" --type=py -B 2 -A 2
# See if there are tests for the solara visualization
echo -e "\n=== Check solara.py tests ==="
fd "test.*solara\|solara.*test" --type=pyRepository: SongshGeoLab/ABSESpy Length of output: 324 🏁 Script executed: #!/bin/bash
# Check what function this code is in and how it's called
echo "=== Function containing this code ==="
cat -n abses/viz/solara.py | sed -n '120,195p'
echo -e "\n=== Check for filtering of spatial actors ==="
rg -n "on_earth\|\.at\s*is\s*not\s*None" abses/viz/solara.py -B 3 -A 3Repository: SongshGeoLab/ABSESpy Length of output: 3598 🏁 Script executed: #!/bin/bash
# Check the broader context and how agents are selected
echo "=== Check entire function ==="
sed -n '100,210p' abses/viz/solara.py | cat -n
# Check what happens when None is in the array
echo -e "\n=== Test what np.array does with None and tuples ==="
python3 << 'EOF'
import numpy as np
# Simulate what space.agents.array("indices") returns with mixed None/tuples
mixed_indices = [None, (0, 0), (1, 1)]
arr = np.array(mixed_indices)
print(f"Array from mixed None/tuples: {arr}")
print(f"Array dtype: {arr.dtype}")
print(f"Array shape: {arr.shape}")
print(f"Array ndim: {arr.ndim}")
# What happens with list comprehension in the code
result = np.array([
list(idx) if isinstance(idx, (tuple, list)) else idx
for idx in arr
])
print(f"\nAfter list comprehension: {result}")
print(f"Result dtype: {result.dtype}")
print(f"Result shape: {result.shape}")
print(f"Result ndim: {result.ndim}")
# Can it be reshaped?
try:
reshaped = result.reshape(-1, 2)
print(f"Reshaped successfully: {reshaped.shape}")
except Exception as e:
print(f"Reshape failed: {type(e).__name__}: {e}")
EOFRepository: SongshGeoLab/ABSESpy Length of output: 5007 🏁 Script executed: #!/bin/bash
# Test with pip installed numpy
pip install -q numpy 2>/dev/null
python3 << 'EOF'
import numpy as np
print("=== Simulating what space.agents.array('indices') returns ===")
print("Case 1: All agents have spatial positions")
indices_all_spatial = [(0, 0), (1, 1), (2, 2)]
arr1 = np.array(indices_all_spatial)
print(f"Indices: {indices_all_spatial}")
print(f"Array: {arr1}, shape: {arr1.shape}, dtype: {arr1.dtype}")
print("\nCase 2: Some agents lack spatial positions (None values)")
indices_mixed = [None, (0, 0), (1, 1), None, (2, 2)]
arr2 = np.array(indices_mixed)
print(f"Indices: {indices_mixed}")
print(f"Array: {arr2}, shape: {arr2.shape}, dtype: {arr2.dtype}, ndim: {arr2.ndim}")
print("\nCase 3: Processing mixed array with list comprehension (as in code)")
result = np.array([
list(idx) if isinstance(idx, (tuple, list)) else idx
for idx in arr2
])
print(f"After list comp: {result}, shape: {result.shape}, dtype: {result.dtype}, ndim: {result.ndim}")
print("\nCase 4: Attempting reshape(-1, 2) on mixed array")
try:
reshaped = result.reshape(-1, 2)
print(f"Reshaped successfully: {reshaped}, shape: {reshaped.shape}")
except Exception as e:
print(f"Reshape FAILED: {type(e).__name__}: {e}")
print("\nCase 5: All tuples (no None)")
indices_tuples = [(0, 0), (1, 1), (2, 2)]
result_tuples = np.array([
list(idx) if isinstance(idx, (tuple, list)) else idx
for idx in indices_tuples
])
print(f"Result: {result_tuples}, shape: {result_tuples.shape}, ndim: {result_tuples.ndim}")
EOFRepository: SongshGeoLab/ABSESpy Length of output: 579 🏁 Script executed: #!/bin/bash
# Check if there's filtering logic elsewhere or if this is truly an issue
echo "=== Check if actors without spatial positions can exist ==="
rg -n "on_earth\|\.at\s*=\s*None\|indices.*None" --type python -B 2 -A 2 | head -40
echo -e "\n=== Check if space.agents includes non-spatial actors ==="
rg -n "space.agents\|agent in.*agents" abses/viz/solara.py -B 1 -A 1Repository: SongshGeoLab/ABSESpy Length of output: 230 🏁 Script executed: #!/bin/bash
# Test numpy with correct exception handling
pip install -q numpy 2>/dev/null
python3 << 'EOF'
import numpy as np
print("=== Understanding the actual behavior ===")
print("\nCase 1: All spatial positions (tuples)")
indices_all = [(0, 0), (1, 1), (2, 2)]
arr1 = np.array(indices_all)
print(f"Input: {indices_all}")
print(f"np.array result: shape {arr1.shape}, dtype {arr1.dtype}")
print(f"ndim: {arr1.ndim}")
print("\nCase 2: Mixed None and tuples - NumPy cannot create uniform array")
indices_mixed = [None, (0, 0), (1, 1)]
try:
arr2 = np.array(indices_mixed)
except ValueError as e:
print(f"np.array() fails: {e}")
# Create object array instead
arr2 = np.array(indices_mixed, dtype=object)
print(f"np.array(dtype=object) result: shape {arr2.shape}, dtype {arr2.dtype}")
print(f"Content: {arr2}")
print("\n After list comprehension:")
result = np.array([
list(idx) if isinstance(idx, (tuple, list)) else idx
for idx in arr2
], dtype=object)
print(f" Result: shape {result.shape}, ndim {result.ndim}, dtype {result.dtype}")
print(f" Content: {result}")
print("\n Attempting reshape(-1, 2):")
try:
reshaped = result.reshape(-1, 2)
print(f" Success: {reshaped}")
except Exception as e2:
print(f" Failed: {type(e2).__name__}: {e2}")
print("\nCase 3: How space.agents.array() works")
print("It calls np.array(self.get(attr)) on a list of all agent indices")
print("If some agents have None (not positioned), this would create dtype=object array")
EOFRepository: SongshGeoLab/ABSESpy Length of output: 957 🏁 Script executed: #!/bin/bash
# Check filtering and how the function is used
echo "=== Check how agents are filtered before collecting data ==="
rg -n "collect_agent_data" abses/viz/solara.py -B 5 -A 5
echo -e "\n=== Check if there's filtering by on_earth or spatial position ==="
rg -n "on_earth" abses/viz/ -B 2 -A 2Repository: SongshGeoLab/ABSESpy Length of output: 842 🏁 Script executed: #!/bin/bash
# Check what agents are included in space.agents
echo "=== Check if space.agents is all agents or only spatial ones ==="
rg -n "def agents" abses/space/ --type py -A 10 | head -50
echo -e "\n=== Check space module structure ==="
ls -la abses/space/Repository: SongshGeoLab/ABSESpy Length of output: 1841 🏁 Script executed: #!/bin/bash
# Check if there are any agents that are in model.agents but not in space.agents
echo "=== Check model.agents vs space.agents ==="
rg -n "model.agents\|space.agents" abses/viz/solara.py -B 2 -A 2
echo -e "\n=== Check if there are tests showing how space.agents.array works ==="
fd "test" abses/viz/ --type f -name "*.py"
echo -e "\n=== Verify that all agents in the loop have valid indices ==="
cat -n abses/viz/solara.py | sed -n '134,140p'Repository: SongshGeoLab/ABSESpy Length of output: 803 🏁 Script executed: #!/bin/bash
# Final check: can indices be None in space.agents?
echo "=== Can an agent in space.agents have None indices? ==="
rg -n "at\s*=\s*None" abses/agents/ -B 2 -A 2
echo -e "\n=== How is an agent added to a cell/space? ==="
rg -n "def move\|def place" abses/agents/actor.py -A 10 | head -40Repository: SongshGeoLab/ABSESpy Length of output: 167 🏁 Script executed: #!/bin/bash
# Final verification: space.agents property
echo "=== Full context of space.agents property ==="
sed -n '400,410p' abses/space/patch.py | cat -n
echo -e "\n=== What is cells_lst? ==="
rg -n "cells_lst" abses/space/patch.py -B 2 -A 2 | head -30
echo -e "\n=== Confirm: are there any agents outside of cells? ==="
rg -n "model.agents\|AgentList" abses/space/patch.py -B 2 -A 2 | head -40Repository: SongshGeoLab/ABSESpy Length of output: 1785 🏁 Script executed: #!/bin/bash
# Check if there's any way agents can be in space but not spatial
echo "=== Actor indices property ==="
sed -n '309,315p' abses/agents/actor.py | cat -n
echo -e "\n=== Where are agents without spatial position kept? ==="
rg -n "model.agents" abses/core/ --type py -B 2 -A 2 | head -30Repository: SongshGeoLab/ABSESpy Length of output: 1584 🏁 Script executed: #!/bin/bash
# Verify the guarantee: space.agents only returns agents in cells
echo "=== Confirming space.agents only includes spatial agents ==="
cat -n abses/space/patch.py | sed -n '400,410p'
echo -e "\n=== What happens when agent is added to cell? ==="
rg -n "self.at\s*=" abses/agents/ -B 2 -A 2 | head -40
echo -e "\n=== Conclusion: what does space.agents.array('indices') return? ==="
python3 << 'EOF'
# Simulating: all agents in space.agents are in cells
# All agents in cells have valid tuple indices
indices_data = [(0, 0), (1, 1), (2, 2), (0, 1)]
import sys
sys.path.insert(0, '/tmp')
# This is what np.array() does with all tuples
try:
import numpy as np
result = np.array(indices_data)
print(f"space.agents.array('indices') returns:")
print(f" Shape: {result.shape}")
print(f" Dtype: {result.dtype}")
print(f" ndim: {result.ndim}")
print(f" Content: {result}")
print(f"\nConclusion: Already a (n, 2) array - no conversion needed!")
except ImportError:
print("NumPy not available, but the logic is clear")
EOFRepository: SongshGeoLab/ABSESpy Length of output: 883 Simplify indices conversion—it's already guaranteed to be consistent tuples. The The code can be reduced to: indices_array = space.agents.array("indices")
result["loc"] = np.empty((0, 2), dtype=int) if len(indices_array) == 0 else np.array(indices_array)🤖 Prompt for AI Agents |
||
| return result | ||
|
|
||
|
|
||
|
|
@@ -168,8 +224,9 @@ def draw_orthogonal_grid( | |
| Returns: | ||
| Returns the Axes object with the plot drawn onto it. | ||
|
|
||
| ``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color", | ||
| "size", "marker", and "zorder". Other field are ignored and will result in a user warning. | ||
| ``agent_portrayal`` is called with an agent and should return a dict or AgentPortrayalStyle | ||
| object (Mesa 3.3+). Valid fields are "color", "size", "marker", "zorder", "alpha", | ||
| "linewidths", and "edgecolors". Other fields are ignored and will result in a user warning. | ||
|
|
||
| """ | ||
| if ax is None: | ||
|
|
@@ -214,6 +271,11 @@ def SpaceMatplotlib( | |
| fig = Figure() | ||
| ax = fig.add_subplot() | ||
|
|
||
| # Draw property layers first (background) | ||
| if propertylayer_portrayal: | ||
| draw_property_layers(space, propertylayer_portrayal, ax=ax) | ||
|
|
||
| # Draw agents on top | ||
| draw_orthogonal_grid( | ||
| space, | ||
| agent_portrayal, | ||
|
|
@@ -228,9 +290,6 @@ def SpaceMatplotlib( | |
| fig, format="png", bbox_inches="tight", dependencies=dependencies | ||
| ) | ||
|
|
||
| if propertylayer_portrayal: | ||
| draw_property_layers(space, propertylayer_portrayal, ax=ax) | ||
|
|
||
|
|
||
| def make_mpl_space_component( | ||
| agent_portrayal: Callable | None = None, | ||
|
|
@@ -247,8 +306,9 @@ def make_mpl_space_component( | |
| space_drawing_kwargs : additional keyword arguments to be passed on to the underlying space drawer function. See | ||
| the functions for drawing the various spaces for further details. | ||
|
|
||
| ``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color", | ||
| "size", "marker", "zorder", alpha, linewidths, and edgecolors. Other field are ignored and will result in a user warning. | ||
| ``agent_portrayal`` is called with an agent and should return a dict or AgentPortrayalStyle | ||
| object (Mesa 3.3+). Valid fields are "color", "size", "marker", "zorder", "alpha", | ||
| "linewidths", and "edgecolors". Other fields are ignored and will result in a user warning. | ||
|
|
||
| Returns: | ||
| function: A function that creates a SpaceMatplotlib component | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,3 +10,4 @@ date: 2024-12-20 | |
|
|
||
|
|
||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.