LSStats: Difference between revisions
Appearance
| Line 337: | Line 337: | ||
GLOBAL VARIABLE: AW_MPG[MaxPTypes+1], meanAW_MPG[MaxPTypes+1], nAW_MPG[MaxPTypes+1] | GLOBAL VARIABLE: AW_MPG[MaxPTypes+1], meanAW_MPG[MaxPTypes+1], nAW_MPG[MaxPTypes+1] | ||
/* NOTE: This variable must be resized to NP (which is unkown before running stats.lse) */ | /* NOTE: This variable must be resized to NP (which is unkown before running stats.lse) */ | ||
// GLOBAL VARIABLE: effectivePatchId[2], PosList[2] | |||
GLOBAL VARIABLE: effectivePatchId[2], PosList[200] | GLOBAL VARIABLE: effectivePatchId[2], PosList[200] | ||
// NN graph | |||
GLOBAL CONSTANT: NumNNStats, rEdgeType, rPatchType, rDist, rWeight, rStartLoc, rEndLoc, rNode1Id, rNode2Id | GLOBAL CONSTANT: NumNNStats, rEdgeType, rPatchType, rDist, rWeight, rStartLoc, rEndLoc, rNode1Id, rNode2Id | ||
GLOBAL GRAPH{NumPatchStats, NumNNStats} VARIABLE: nnGraph[MaxPTypes+1] | GLOBAL GRAPH{NumPatchStats, NumNNStats} VARIABLE: nnGraph[MaxPTypes+1] | ||
| Line 347: | Line 347: | ||
CELL VARIABLE: currEffectivePid, newEffectivePid | CELL VARIABLE: currEffectivePid, newEffectivePid | ||
CELL VARIABLE: startLocation, numSteps | CELL VARIABLE: startLocation, numSteps | ||
// MIN OUTPUT VARIABLE: LSStatsFile = "LSStatsNN.txt" | |||
MIN OUTPUT VARIABLE: ClassStatsFile = "ClassStatsNN.txt" | MIN OUTPUT VARIABLE: ClassStatsFile = "ClassStatsNN.txt" | ||
// MIN OUTPUT VARIABLE: PatchStatsFile = "PatchStatsNN.txt" | |||
// MIN OUTPUT VARIABLE: EdgeStatsFile = "EdgeStatsNN.txt" | |||
ENDDEF | ENDDEF | ||
INITIALSTATE | INITIALSTATE | ||
| Line 359: | Line 359: | ||
ENDIS | ENDIS | ||
RETURNTIME | RETURNTIME | ||
// RETURNTIME = IF Time EQ 0 THEN (EventId + 5 * 365.25) ELSE 0 | |||
RETURNTIME = IF Time EQ 0 THEN (EventId + 6 * 365.25) ELSE 0 | RETURNTIME = IF Time EQ 0 THEN (EventId + 6 * 365.25) ELSE 0 | ||
/* Finalize and output stats */ | /* Finalize and output stats */ | ||
| Line 393: | Line 393: | ||
patchVar [=] GET(nnGraph[pType], pos) | patchVar [=] GET(nnGraph[pType], pos) | ||
pos = NEXT(nnGraph[pType], pos) | pos = NEXT(nnGraph[pType], pos) | ||
// pType = patchVar[rType] | |||
MeanNN[pType] = MeanNN[pType] + patchVar[rNNDist] | MeanNN[pType] = MeanNN[pType] + patchVar[rNNDist] | ||
| Line 400: | Line 400: | ||
MaxNN[pType] = MAX(patchVar[rNNDist], MaxNN[pType]) | MaxNN[pType] = MAX(patchVar[rNNDist], MaxNN[pType]) | ||
ENDFN | ENDFN | ||
/* | |||
OUTPUT RECORD(PatchStatsFile) | OUTPUT RECORD(PatchStatsFile) | ||
LandscapeId: LandscapeId | LandscapeId: LandscapeId | ||
| Line 408: | Line 408: | ||
nnDist: patchVar[rNNDist] * CellWidth | nnDist: patchVar[rNNDist] * CellWidth | ||
ENDFN | ENDFN | ||
*/ | |||
ENDFN | ENDFN | ||
// Iterate over list of edges to compute stats | // Iterate over list of edges to compute stats | ||
pos = FIRST LINK(nnGraph[pType]) | pos = FIRST LINK(nnGraph[pType]) | ||
| Line 416: | Line 415: | ||
edgeVar [=] GET LINK(nnGraph[pType], pos) | edgeVar [=] GET LINK(nnGraph[pType], pos) | ||
pos = NEXT LINK(nnGraph[pType], pos) | pos = NEXT LINK(nnGraph[pType], pos) | ||
// pType = edgeVar[rPatchType] | |||
IF (edgeVar[rEdgeType] EQ rNN) | IF (edgeVar[rEdgeType] EQ rNN) | ||
MeanNN2[pType] = MeanNN2[pType] + edgeVar[rDist] | MeanNN2[pType] = MeanNN2[pType] + edgeVar[rDist] | ||
| Line 430: | Line 429: | ||
AW_MPG[pType] = AW_MPG[pType] + edgeWeight | AW_MPG[pType] = AW_MPG[pType] + edgeWeight | ||
nAW_MPG[pType] = nAW_MPG[pType] + edgeWeight | nAW_MPG[pType] = nAW_MPG[pType] + edgeWeight | ||
/* | |||
OUTPUT RECORD(EdgeStatsFile) | OUTPUT RECORD(EdgeStatsFile) | ||
LandscapeId: LandscapeId | LandscapeId: LandscapeId | ||
| Line 440: | Line 439: | ||
dist: edgeVar[rDist] * CellWidth | dist: edgeVar[rDist] * CellWidth | ||
edgeWeight: edgeWeight | edgeWeight: edgeWeight | ||
node1Row: ROW(edgeVar[rStartLoc]) | node1Row: ROW(edgeVar[rStartLoc]) | ||
node1Col: COL(edgeVar[rStartLoc]) | node1Col: COL(edgeVar[rStartLoc]) | ||
| Line 446: | Line 444: | ||
node3Col: COL(edgeVar[rEndLoc]) | node3Col: COL(edgeVar[rEndLoc]) | ||
ENDFN | ENDFN | ||
*/ | |||
ENDFN | ENDFN | ||
tMeanNN = tMeanNN + MeanNN[pType] | tMeanNN = tMeanNN + MeanNN[pType] | ||
| Line 484: | Line 482: | ||
OVER INDEX SEQUENCE(0, NumPTypes-1) | OVER INDEX SEQUENCE(0, NumPTypes-1) | ||
pType = Index + MinPType | pType = Index + MinPType | ||
/* | |||
pos = FIRST LINK(nnGraph[pType]) | pos = FIRST LINK(nnGraph[pType]) | ||
WHILE (pos) | WHILE (pos) | ||
| Line 493: | Line 491: | ||
ENDFN | ENDFN | ||
ENDFN | ENDFN | ||
*/ | |||
pos = FIRST(nnGraph[pType]) | pos = FIRST(nnGraph[pType]) | ||
WHILE (pos) | WHILE (pos) | ||
| Line 716: | Line 714: | ||
ENDFN | ENDFN | ||
REGION CENTRED(1, maxD, EUCLIDEAN) | REGION CENTRED(1, maxD, EUCLIDEAN) | ||
// DECISION (PatchId NEQ currPatchId) AND (!onEdge OR (PatchLayer EQ currType) OR (DISTANCE(Location, SOURCE | // DECISION (PatchId NEQ currPatchId) AND (!onEdge OR (PatchLayer EQ currType) OR (DISTANCE(Location, SOURCE Location) EQ 1)) | ||
DECISION IF (PatchId EQ currPatchId) THEN (ComponentId EQ 0) AND (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0) | DECISION IF (PatchId EQ currPatchId) THEN (ComponentId EQ 0) AND (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0) | ||
ELSE IF (NNType NEQ rMPG) THEN (effectivePatchId[ComponentId] NEQ effectivePatchId[currPatchId]) AND (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0) | ELSE IF (NNType NEQ rMPG) THEN (effectivePatchId[ComponentId] NEQ effectivePatchId[currPatchId]) AND (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0) | ||
Revision as of 22:34, 8 November 2006
Summary
Screenshot
Download Model
Download the .scn, .sel and .lse files by clicking on the following link: File:LSStats.zip
Model Code Exploration
In the following sections we will examine all of the model files for this model. Note that instead of downloading the zip file above, you could just copy the text in the boxes below into a text editor and save it with the appropriate name (Section title). Opening the resulting .scn file in the SELES simulator would run this model.
BatchStats.scn
SELES Scenario $baseGisDir$ = ..\..\CaseStudy\gisData\cell $gisDir$ = ..\..\CaseStudy\v6_roads\output\cell $first$ = TRUE // load first layers StudyArea = $baseGisDir$\StudyArea cwd oStats // Loop over all files in the directory that start with "ACPatchType1." for($x$ = "..\$gisDir$\OldForest_1_*") // Open a patch layer (ensure previous one, if any, is closed) PatchLayer = ..\$gisDir$\OldForest_1_$x$ // Load model Model Dimensions: PatchLayer ..\stats.sel // Set up parameters CellWidth = 100 HaPerCell = 1 NNType = 2 // For MPG LandscapeId = $x$ // This sets the variable LandscapeId to the integer represented by $x$ NumPTypes = 1 Reset Output $first$ // Tell SELES to not reset output files (i.e. to append to them) except on first iteration $first$ = FALSE // Turn off centroid stats (time consuming if there are lots of patches (np^2)) // centroid.lse OFF // nn.lse OFF // Run simulation SimStart 100*365.25 1 Close PatchLayer end
centroid.lse
LSEVENT: CENTROID_LSE
DEFINITIONS
GLOBAL CONSTANT: MaxPTypes
GLOBAL VARIABLE: NumPTypes, MinPType, HaPerCell, CellWidth, LandscapeId, Replicate
/* Assume these stats are defined */
GLOBAL VARIABLE: A, Area[], NP, NumPatches[]
/* Mean Connectivity between patches */
GLOBAL VARIABLE: MeanCCE[MaxPTypes+1], MaxCCE[MaxPTypes+1], MeanCD[MaxPTypes+1]
GLOBAL VARIABLE: tCCE, tMaxCCE, tCD
// Patch list: type and area previously defined
GLOBAL CONSTANT: NumPatchStats, rId, rType, rArea, rCentroid, rMaxCCE
GLOBAL LIST{NumPatchStats} VARIABLE: patchList[]
GLOBAL VARIABLE: patchVar[NumPatchStats]
EVENT VARIABLE: id
CLUSTER VARIABLE: rowTotal, colTotal, numActiveCells
// MIN OUTPUT VARIABLE: LSStatsFile = "LSStatsC.txt"
MIN OUTPUT VARIABLE: ClassStatsFile = "ClassStatsC.txt"
// MIN OUTPUT VARIABLE: EdgeStatsFile = "EdgeStatsC.txt"
ENDDEF
INITIALSTATE
tCCE = 0
tMaxCCE = 0
tCD = 0
OVER INDEX SEQUENCE(0, NumPTypes-1)
pType = Index + MinPType
MeanCCE[pType] = 0
MaxCCE[pType] = 0
MeanCD[pType] = 0
ENDFN
/* All the stats area available in the patch list, we only need to */
/* summarize and output results */
INITIALSTATE = 1
ENDIS
RETURNTIME
// RETURNTIME = IF Time EQ 0 THEN (4 * 365.25) ELSE 0
RETURNTIME = IF Time EQ 0 THEN (4 * 365.25) ELSE 0
/* Finalize stats */
// Iterate over each pair of patches of the same type
OVER INDEX SEQUENCE(0, NumPTypes-1)
i = Index + MinPType
pos1 = HEAD(patchList[i])
WHILE (pos1)
patchVar [=] GET(patchList[i], pos1)
currType = patchVar[rType]
id1 = patchVar[rId]
patchArea1 = patchVar[rArea]
centroid1 = patchVar[rCentroid]
maxCCE1 = patchVar[rMaxCCE]
nextPos = NEXT(patchList[i], pos1)
// pos2 = HEAD(patchList[i])
pos2 = nextPos
WHILE (pos2)
IF (currType EQ GET(patchList[i], pos2, rType))
patchVar [=] GET(patchList[i], pos2)
patchArea2 = patchVar[rArea]
centroid2 = patchVar[rCentroid]
maxCCE2 = patchVar[rMaxCCE]
/* Determine the max patch CCE */
d = DISTANCE(centroid1, centroid2)
CCE = (patchArea1 * patchArea2) / d^2
MeanCCE[currType] = MeanCCE[currType] + CCE
maxCCE1 = MAX(CCE, maxCCE1)
maxCCE2 = MAX(CCE, maxCCE2)
MeanCD[currType] = MeanCD[currType] + d
// Update maxCCE in patches
SET(patchList[i], pos2, rMaxCCE, maxCCE2)
/*
OUTPUT RECORD(EdgeStatsFile)
LandscapeId: LandscapeId
Replicate: Replicate
type: currType
patchId1: id1
patchId2: patchVar[rId]
d: d
ENDFN
*/
ENDFN
pos2 = NEXT(patchList[i], pos2)
ENDFN
/* At this point, the patch CCE for p1 will be known */
SET(patchList[i], pos1, rMaxCCE, maxCCE1)
MaxCCE[currType] = MaxCCE[currType] + maxCCE1
pos1 = nextPos
ENDFN
ENDFN
OVER INDEX SEQUENCE(0, NumPTypes-1)
currType = Index + MinPType
tCCE = tCCE + MeanCCE[currType]
tMaxCCE = tMaxCCE + MaxCCE[currType]
MeanCCE[currType] = MeanCCE[currType] / (NumPatches[currType] * (NumPatches[currType] - 1) * 0.5)
MaxCCE[currType] = MaxCCE[currType] / (NumPatches[currType] - 1)
tCD = tCD + MeanCD[currType]
MeanCD[currType] = MeanCD[currType] / (NumPatches[currType] * (NumPatches[currType] - 1) * 0.5)
OUTPUT RECORD(ClassStatsFile)
DECISION Area[currType] > 0
LandscapeId: LandscapeId
Replicate: Replicate
pType: currType
CCE: MeanCCE[currType]
MaxCCE: MaxCCE[currType]
CD: MeanCD[currType]
ENDFN
ENDFN
tCCE = IF (NP <= 1) THEN 0 ELSE tCCE / (NP * (NP - 1) * 0.5)
tMaxCCE = tMaxCCE / NP
tCD = IF (NP <= 1) THEN 0 ELSE tCD / (NP * (NP - 1) * 0.5)
/*
OUTPUT RECORD(LSStatsFile)
LandscapeId: LandscapeId
Replicate: Replicate
tCCE: tCCE
tMaxCCE: tMaxCCE
tCD: tCD
ENDFN
*/
ENDRT
EVENTOPENINGS = 0
identifyPatches.lse
/* Combination of stats from fragstats and apack */
/* Note: A particular problem should decide on stats required */
/* and adapt a custom stats event, which may include some of the */
/* stats herein, and may include new or modified stats. */
/* Apack stats are denoted using lower case identifiers. */
LSEVENT: STATS_LSE
DEFINITIONS
LAYER: PatchLayer, StudyArea, PatchId, CoreAreaId
LAYER: EdgeInterior
FEATURE 1: Edge
FEATURE 2: Interior
GLOBAL CONSTANT: MaxPTypes
CONSTANT: pi = 3.141593
GLOBAL VARIABLE: NumPTypes, MinPType, HaPerCell, CellWidth, LandscapeId
// Area indices
GLOBAL VARIABLE: Area[]
// Patch indices
GLOBAL VARIABLE: NumPatches[]
// Edge Indices
GLOBAL VARIABLE: TotalEdge[], TotalEdgeArea[]
// Core Area Indices
GLOBAL VARIABLE: TCA[], NumCA[]
// Adjacency matrix
GLOBAL VARIABLE: Eik[]
// pContagion
GLOBAL VARIABLE: NumNeighbs[], NumSameNeighbs[]
// Patch list
GLOBAL CONSTANT: NumPatchStats, rId, rType, rArea, rCoreArea, rPerim, rCentroid, rMaxCCE, rNNDist, MaxDist
GLOBAL LIST{NumPatchStats} VARIABLE: patchList[], coreAreaList[]
// GLOBAL LIST{NumPatchStats} VARIABLE: patchList, coreAreaList
GLOBAL VARIABLE: patchVar[NumPatchStats]
GLOBAL VARIABLE: pId, coreId
EVENT VARIABLE: isPatchEvent, currType
CLUSTER VARIABLE: numActiveCells, currPatchSize, currPatchCoreSize, currPatchPerim
CLUSTER VARIABLE: rowTotal, colTotal
ENDDEF
INITIALSTATE
EdgeInterior = 0
pId = 0
coreId = 0
/* Three types of instances: the first NumPType instances identify and computes first order stats for patches */
/* (a separate instance is used per type so that the list is ordered by type) */
/* The second identifies and computes first order stats for core areas */
/* The third computes second order stats and then summarizes and outputs */
INITIALSTATE = 1 + NumPTypes
isPatchEvent = EventId <= NumPTypes
currType = EventId + MinPType - 1 // This is ok for patches, but don't use for core areas
ENDIS
RETURNTIME = IF Time EQ 0 THEN EventId + 365.25 ELSE 0
PROBINIT
PROBINIT = IF (isPatchEvent) THEN (PatchLayer EQ currType) AND (StudyArea > 0)
ELSE (EdgeInterior EQ Interior)
numActiveCells = 0
currPatchSize = 0
currPatchCoreSize = 0
currPatchPerim = 0
rowTotal = 0
colTotal = 0
// For the patches ...
IF (isPatchEvent)
Area[PatchLayer] = Area[PatchLayer] + 1
IF (PatchId EQ 0) // not visited...
pId = pId + 1
NumPatches[PatchLayer] = NumPatches[PatchLayer] + 1
ENDFN
// For the core areas ...
ELSE
coreId = coreId + (CoreAreaId EQ 0)
NumCA[PatchLayer] = NumCA[PatchLayer] + (CoreAreaId EQ 0)
ENDFN
ENDPI
TRANSITIONS
TRANSITIONS = IF isPatchEvent THEN (PatchId EQ 0) ELSE (CoreAreaId EQ 0)
// For the patches ...
IF isPatchEvent
numSimilarNeighbours = 0
numDifferentNeighbours = 0
OVER REGION CENTRED(1, 1)
DECISION (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0)
similarNeighb = (PatchLayer EQ currType)
numSimilarNeighbours = numSimilarNeighbours + similarNeighb
numDifferentNeighbours = numDifferentNeighbours + (similarNeighb EQ FALSE)
NumNeighbs[PatchLayer] = NumNeighbs[PatchLayer] + 1
NumSameNeighbs[PatchLayer] = NumSameNeighbs[PatchLayer] + similarNeighb
Eik[currType, PatchLayer] = Eik[currType, PatchLayer] + (similarNeighb EQ FALSE)
ENDFN
currPatchPerim = currPatchPerim + 4 - numSimilarNeighbours
TotalEdge[PatchLayer] = TotalEdge[PatchLayer] + numDifferentNeighbours
// Add in the diagonal neighbours
OVER REGION CENTRED(1.1, 1.5)
DECISION (0 <= PatchLayer < (MinPType + NumPTypes))
similarNeighb = (PatchLayer EQ currType)
numSimilarNeighbours = numSimilarNeighbours + similarNeighb
numDifferentNeighbours = numDifferentNeighbours + (similarNeighb EQ FALSE)
ENDFN
EdgeInterior = IF (numDifferentNeighbours > 0) THEN Edge ELSE Interior
TCA[PatchLayer] = TCA[PatchLayer] + (EdgeInterior EQ Interior)
TotalEdgeArea[PatchLayer] = TotalEdgeArea[PatchLayer] + (EdgeInterior EQ Edge)
PatchId = pId
ELSE
CoreAreaId = coreId
ENDFN
currPatchSize = currPatchSize + 1
currPatchCoreSize = currPatchCoreSize + (EdgeInterior EQ Interior)
numActiveCells = numActiveCells + 1
rowTotal = rowTotal + ROW(Location)
colTotal = colTotal + COL(Location)
ENDTR
SPREADLOCATION
REGION CENTRED(1, 1.5)
DECISION IF isPatchEvent THEN (PatchId EQ 0) AND (PatchLayer EQ currType) AND (StudyArea > 0)
ELSE (CoreAreaId EQ 0) AND (EdgeInterior EQ Interior) //AND (PatchLayer EQ currType)
ENDSL
SPREADTIMESTEP
SPREADTIMESTEP = -1
// If numActiveCells becomes 0, then this patch is done
numActiveCells = numActiveCells - 1
IF (numActiveCells EQ 0)
// Add the patch to the list
patchVar[rArea] = currPatchSize
patchVar[rCoreArea] = currPatchCoreSize
patchVar[rPerim] = currPatchPerim
patchVar[rCentroid] = LOCATION(FLOOR(rowTotal/currPatchSize), FLOOR(colTotal/currPatchSize))
patchVar[rMaxCCE] = 0
patchVar[rNNDist] = -1
IF isPatchEvent
patchVar[rType] = currType
patchVar[rId] = pId
INSERT TAIL(patchList[currType], patchVar)
ELSE
patchVar[rType] = PatchLayer
patchVar[rId] = coreId
INSERT TAIL(coreAreaList[PatchLayer], patchVar)
ENDFN
ENDFN
ENDST
nn.lse
LSEVENT: NearestNeighbour_LSE
DEFINITIONS
// Nearest neighbour algorithm types
GLOBAL CONSTANT: rNN, rMST, rMPG
GLOBAL VARIABLE: NNType
/* Assume these layers are all defined */
LAYER: PatchLayer, PatchId, StudyArea
LAYER: EdgeInterior
FEATURE 1: Edge
FEATURE 2: Interior
// Scratch id layer for connectve components
LAYER: ComponentId, PatchLinks
GLOBAL CONSTANT: MaxPTypes, MaxDist
GLOBAL VARIABLE: NumPTypes, MinPType, HaPerCell, CellWidth, LandscapeId, Replicate
LAYER: NNLoc
GLOBAL CONSTANT: MaxLoc
/* Assume these stats are defined */
GLOBAL VARIABLE: A, Area[], NP, NumPatches[]
// Patch list: previously defined (except rNNDist, which is at MaxDist)
GLOBAL CONSTANT: NumPatchStats, rId, rType, rArea, rNNDist, rEffectiveId
GLOBAL LIST{NumPatchStats} VARIABLE: patchList[]
GLOBAL VARIABLE: patchVar[NumPatchStats]
/* Nearest Neighbour */
GLOBAL VARIABLE: MeanNN[MaxPTypes+1], MinNN[MaxPTypes+1], MaxNN[MaxPTypes+1], NNSD[MaxPTypes+1]
GLOBAL VARIABLE: MeanNN2[MaxPTypes+1]
GLOBAL VARIABLE: tMeanNN, tMinNN, tMaxNN
/* MST */
GLOBAL VARIABLE: MST[MaxPTypes+1], meanMST[MaxPTypes+1]
GLOBAL VARIABLE: startId[MaxPTypes+1]
/* Straigh line least cost */
GLOBAL VARIABLE: MPG[MaxPTypes+1], meanMPG[MaxPTypes+1], nMPG[MaxPTypes+1]
GLOBAL VARIABLE: AW_MPG[MaxPTypes+1], meanAW_MPG[MaxPTypes+1], nAW_MPG[MaxPTypes+1]
/* NOTE: This variable must be resized to NP (which is unkown before running stats.lse) */
// GLOBAL VARIABLE: effectivePatchId[2], PosList[2]
GLOBAL VARIABLE: effectivePatchId[2], PosList[200]
// NN graph
GLOBAL CONSTANT: NumNNStats, rEdgeType, rPatchType, rDist, rWeight, rStartLoc, rEndLoc, rNode1Id, rNode2Id
GLOBAL GRAPH{NumPatchStats, NumNNStats} VARIABLE: nnGraph[MaxPTypes+1]
GLOBAL VARIABLE: edgeVar[NumNNStats], edgeVar2[NumNNStats]
EVENT VARIABLE: currType, PatchesToConnect, ComponentsToConnect
CLUSTER VARIABLE: currPatchId, patchPos
CELL VARIABLE: currEffectivePid, newEffectivePid
CELL VARIABLE: startLocation, numSteps
// MIN OUTPUT VARIABLE: LSStatsFile = "LSStatsNN.txt"
MIN OUTPUT VARIABLE: ClassStatsFile = "ClassStatsNN.txt"
// MIN OUTPUT VARIABLE: PatchStatsFile = "PatchStatsNN.txt"
// MIN OUTPUT VARIABLE: EdgeStatsFile = "EdgeStatsNN.txt"
ENDDEF
INITIALSTATE
/* The first NumPType instances compute stats for each type */
/* The last summarizes and outputs */
INITIALSTATE = NumPTypes + 1
currType = EventId + MinPType - 1
ENDIS
RETURNTIME
// RETURNTIME = IF Time EQ 0 THEN (EventId + 5 * 365.25) ELSE 0
RETURNTIME = IF Time EQ 0 THEN (EventId + 6 * 365.25) ELSE 0
/* Finalize and output stats */
IF currType >= NumPTypes + MinPType
tMeanNN = 0
tMinNN = MaxDist
tMaxNN = 0
N_MST = 0
tMeanMST = 0
tMST = 0
tAWMST = 0
tMPG = 0
tMeanMPG = 0
// Iterate over list of patches to compute stats
OVER INDEX SEQUENCE(0, NumPTypes-1)
pType = Index + MinPType
MeanNN[pType] = 0
MinNN[pType] = MaxDist
MaxNN[pType] = 0
NNSD[pType] = 0
MeanNN2[pType] = 0
nNN = 0
MST[pType] = 0
meanMST[pType] = 0
MPG[pType] = 0
meanMPG[pType] = 0
nMPG[pType] = 0
AW_MPG[pType] = 0
meanAW_MPG[pType] = 0
nAW_MPG[pType] = 0
pos = FIRST(nnGraph[pType])
WHILE (pos)
patchVar [=] GET(nnGraph[pType], pos)
pos = NEXT(nnGraph[pType], pos)
// pType = patchVar[rType]
MeanNN[pType] = MeanNN[pType] + patchVar[rNNDist]
IF (patchVar[rNNDist] > 0)
MinNN[pType] = MIN(patchVar[rNNDist], MinNN[pType])
MaxNN[pType] = MAX(patchVar[rNNDist], MaxNN[pType])
ENDFN
/*
OUTPUT RECORD(PatchStatsFile)
LandscapeId: LandscapeId
Replicate: Replicate
id: patchVar[rId]
patchType: pType
nnDist: patchVar[rNNDist] * CellWidth
ENDFN
*/
ENDFN
// Iterate over list of edges to compute stats
pos = FIRST LINK(nnGraph[pType])
WHILE (pos)
edgeVar [=] GET LINK(nnGraph[pType], pos)
pos = NEXT LINK(nnGraph[pType], pos)
// pType = edgeVar[rPatchType]
IF (edgeVar[rEdgeType] EQ rNN)
MeanNN2[pType] = MeanNN2[pType] + edgeVar[rDist]
nNN = nNN + 1
ENDFN
IF (edgeVar[rEdgeType] EQ rNN) OR (edgeVar[rEdgeType] EQ rMST)
MST[pType] = MST[pType] + edgeVar[rDist]
ENDFN
MPG[pType] = MPG[pType] + edgeVar[rDist]
nMPG[pType] = nMPG[pType] + 1
edgeWeight = (CellWidth * edgeVar[rDist]) / (HaPerCell * edgeVar[rWeight]) // m/ha
AW_MPG[pType] = AW_MPG[pType] + edgeWeight
nAW_MPG[pType] = nAW_MPG[pType] + edgeWeight
/*
OUTPUT RECORD(EdgeStatsFile)
LandscapeId: LandscapeId
Replicate: Replicate
patchType: pType
edgeType: edgeVar[rEdgeType]
id1: edgeVar[rNode1Id]
id2: edgeVar[rNode2Id]
dist: edgeVar[rDist] * CellWidth
edgeWeight: edgeWeight
node1Row: ROW(edgeVar[rStartLoc])
node1Col: COL(edgeVar[rStartLoc])
node2Row: ROW(edgeVar[rEndLoc])
node3Col: COL(edgeVar[rEndLoc])
ENDFN
*/
ENDFN
tMeanNN = tMeanNN + MeanNN[pType]
IF MinNN[pType] > 0
tMinNN = MIN(tMinNN, MinNN[pType])
ENDFN
tMaxNN = MAX(tMaxNN, MaxNN[pType])
IF NumPatches[pType] > 0
MeanNN[pType] = MeanNN[pType] / NumPatches[pType]
ENDFN
IF nNN > 0
MeanNN2[pType] = MeanNN2[pType] / nNN
ENDFN
IF (NumPatches[pType] > 1)
meanMST[pType] = MST[pType] / (NumPatches[pType] - 1)
tMeanMST = tMeanMST + meanMST[pType]
tMST = tMST + MST[pType]
tAWMST = tAWMST + MST[pType] * Area[pType]
N_MST = N_MST + 1
ENDFN
IF (nMPG[pType] > 1)
meanMPG[pType] = MPG[pType] / nMPG[pType]
tMPG = tMPG + MPG[pType]
ENDFN
IF (nAW_MPG[pType] > 1)
meanAW_MPG[pType] = AW_MPG[pType] / nAW_MPG[pType]
tAW_MPG = tAW_MPG + AW_MPG[pType]
ENDFN
ENDFN
tMST = IF (N_MST > 0) THEN tMST / N_MST ELSE 0
tAWMST = IF (A > 0) THEN tAWMST / A ELSE 0
tMPG = IF (N_MST > 0) THEN tMPG / N_MST ELSE 0
/* Compute stddev and second order stats */
tNNSD = 0
tMSTSD = 0
tMPGSD = 0
OVER INDEX SEQUENCE(0, NumPTypes-1)
pType = Index + MinPType
/*
pos = FIRST LINK(nnGraph[pType])
WHILE (pos)
edgeVar [=] GET LINK(nnGraph[pType], pos)
pos = NEXT LINK(nnGraph[pType], pos)
IF (edgeVar[rEdgeType] EQ rNN)
NNSD[pType] = NNSD[pType] + (edgeVar[rDist] - MeanNN[pType])^2
ENDFN
ENDFN
*/
pos = FIRST(nnGraph[pType])
WHILE (pos)
patchVar [=] GET(nnGraph[pType], pos)
pos = NEXT(nnGraph[pType], pos)
// pType = patchVar[rType]
NNSD[pType] = NNSD[pType] + (patchVar[rNNDist] - MeanNN[pType])^2
ENDFN
tNNSD = tNNSD + NNSD[pType]
NNSD[pType] = IF (NumPatches[pType] EQ 0) THEN 0 ELSE (NNSD[pType] / NumPatches[pType])^(1/2)
tMSTSD = tMSTSD + (MST[pType] - tMST)^2
tMPGSD = tMPGSD + (MPG[pType] - tMPG)^2
OUTPUT RECORD(ClassStatsFile)
DECISION Area[pType] > 0
LandscapeId: LandscapeId
Replicate: Replicate
pType: pType
MNN: CellWidth * MeanNN[pType]
MNN2: CellWidth * MeanNN2[pType]
MinNN: CellWidth * MinNN[pType]
MaxNN: CellWidth * MaxNN[pType]
NNSD: CellWidth * NNSD[pType]
NNCV: 100 * NNSD[pType] / MeanNN[pType]
Dispersion: 2 * (NumPatches[pType] / Area[pType])^(1/2) * MeanNN[pType]
meanMST: IF (NNType EQ rNN) THEN 0 ELSE CellWidth * meanMST[pType]
tMST: IF (NNType EQ rNN) THEN 0 ELSE CellWidth * MST[pType]
meanMPG: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE CellWidth * meanMPG[pType]
tMPG: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE CellWidth * MPG[pType]
nMPG: nMPG[pType]
meanAW_MPG: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE CellWidth * meanAW_MPG[pType]
tAW_MPG: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE CellWidth * AW_MPG[pType]
nAW_MPG: nMPG[pType]
ENDFN
ENDFN
tMeanNN = tMeanNN / NP
tNNSD = (tNNSD / NP)^(1/2)
tMSTSD = (tMSTSD / N_MST)^(1/2)
tMPGSD = (tMPGSD / N_MST)^(1/2)
/*
OUTPUT RECORD(LSStatsFile)
LandscapeId: LandscapeId
Replicate: Replicate
MNN: CellWidth * tMeanNN
MinNN: CellWidth * tMinNN
MaxNN: CellWidth * tMaxNN
NNSD: CellWidth * tNNSD
NNCV: 100 * tNNSD / tMeanNN
MST: IF (NNType EQ rNN) THEN 0 ELSE CellWidth * tMST
// tMSTSD: IF (NNType EQ rNN) THEN 0 ELSE CellWidth * tMSTSD
// MSTCV: IF (NNType EQ rNN) THEN 0 ELSE 100 * tMSTSD / tMST
AWMST: IF (NNType EQ rNN) THEN 0 ELSE CellWidth * tAWMST
MPG: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE CellWidth * tMPG
AWMPG: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE CellWidth * tAW_MPG
// tMPGSD: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE CellWidth * tMPGSD
// MPGCV: IF (NNType EQ rNN) OR (NNType EQ rMST) THEN 0 ELSE 100 * tMPGSD / tMPG
ENDFN
*/
ENDFN
IF ((currType < (NumPTypes + MinPType)) AND (NumPatches[currType] > 1))
/// PatchId = 0
ComponentId = 0
NNLoc = MaxLoc
PatchesToConnect = NumPatches[currType]
ComponentsToConnect = NumPatches[currType] - 1
ENDFN
/* NOTE: BE very careful to be sure that variables shared with */
/* other events are valid (especially after initialization in INITIALSTATE!) */
IF (currType EQ MinPType)
/* Resize the effective patch id vector */
RESIZE(effectivePatchId, NP+1)
RESIZE(PosList, NP+1)
//RESIZE(effectivePatchId, 200)
//RESIZE(PosList, 200)
tmpPid = 1
OVER INDEX SEQUENCE(0, NumPTypes-1)
pType = Index + MinPType
OVER INDEX SEQUENCE(0, NumPatches[pType]-1)
effectivePatchId[tmpPid] = tmpPid
tmpPid = tmpPid + 1
ENDFN
// NOTE: the patches must be ordered by type for this to work
startId[pType] = IF (pType EQ 0) THEN 0 ELSE startId[pType-1] + NumPatches[pType-1]
/*
// Copy nodes to graph. Alternative: define patchList as a graph
pos = FIRST(patchList[pType])
WHILE (pos)
patchVar [=] GET(patchList[pType], pos)
pos = NEXT(patchList[pType], pos)
patchVar[rNNDist] = MaxDist
INSERT(nnGraph[pType], patchVar)
i = patchVar[rId]
PosList[i] = FIRST(nnGraph[pType])
ENDFN
*/
ENDFN
ENDFN
ENDRT
/* Don't continue if this is the output instance */
NUMCLUSTERS
// IF (currType EQ NumPTypes) OR (NumPatches[currType] <= 1) THEN 0 ELSE -1
NUMCLUSTERS = IF (currType >= (NumPTypes+MinPType)) OR (NumPatches[currType] <= 1) THEN 0 ELSE -1
ENDNC
PROBINIT
// PROBINIT = (PatchLayer EQ currType) AND (EdgeInterior EQ Edge)
PROBINIT = (PatchLayer EQ currType) AND (StudyArea > 0)
currPatchId = PatchId
startLocation = Location
numSteps = 0
pos = FIND(patchList[currType], patchVar, patchVar[rId] EQ currPatchId)
pos2 = FIND(nnGraph[currType], patchVar, patchVar[rId] EQ currPatchId)
patchVar [=] GET(patchList[currType], pos)
i = patchVar[rId]
IF (!pos2)
patchVar[rNNDist] = MaxDist
INSERT(nnGraph[currType], patchVar)
PosList[i] = FIRST(nnGraph[currType])
ENDFN
patchPos = PosList[i]
/// patchPos = FIND(nnGraph[currType], patchVar, patchVar[rId] EQ currPatchId)
// patchPos = PosList[currPatchId]
ENDPI
TRANSITIONS
/* Check if we meet another spreading component */
IF ((ComponentId NEQ 0) AND (effectivePatchId[ComponentId] NEQ effectivePatchId[currPatchId]))
// Find the patch nodes
// nPos2 = FIND(nnGraph[currType], patchVar, patchVar[rId] EQ ComponentId)
nPos2 = PosList[ComponentId]
// d = DISTANCE(NNLoc, startLocation)
rowDiff = MAX(0, |ROW(NNLoc) - ROW(startLocation)| - 1)
colDiff = MAX(0, |COL(NNLoc) - COL(startLocation)| - 1)
d = (rowDiff^2 + colDiff^2)^0.5
// Add the patch to the list or update the distance if it is already there
// edgePos = FIND LINK(nnGraph[currType], edgeVar, ((edgeVar[rNode1Id] EQ currPatchId) AND (edgeVar[rNode2Id] EQ ComponentId)) OR ((edgeVar[rNode1Id] EQ ComponentId) AND (edgeVar[rNode2Id] EQ currPatchId)))
// Set NN at patch level
currD1 = GET(nnGraph[currType], patchPos, rNNDist)
IF d < currD1
SET(nnGraph[currType], patchPos, rNNDist, d)
ENDFN
currD2 = GET(nnGraph[currType], nPos2, rNNDist)
IF d < currD2
SET(nnGraph[currType], nPos2, rNNDist, d)
ENDFN
PatchesToConnect = PatchesToConnect - (currD1 EQ MaxDist) - (currD2 EQ MaxDist)
// Create a link
edgeVar[rEdgeType] = IF (currD1 EQ MaxDist) OR (currD2 EQ MaxDist) THEN rNN ELSE rMST
edgeVar[rPatchType] = PatchLayer
edgeVar[rNode1Id] = currPatchId
edgeVar[rNode2Id] = ComponentId
edgeVar[rDist] = d
Area1 = GET(nnGraph[currType], patchPos, rArea)
Area2 = GET(nnGraph[currType], nPos2, rArea)
edgeVar[rWeight] = (Area1 * Area2)^0.5
edgeVar[rStartLoc] = startLocation
edgeVar[rEndLoc] = NNLoc
INSERT LINK(nnGraph[currType], patchPos, nPos2, edgeVar)
IF (currType EQ MinPType)
OVER REGION VECTOR(startLocation,NNLoc)
PatchLinks = edgeVar[rEdgeType]+1
ENDFN
ENDFN
ComponentsToConnect = ComponentsToConnect - 1
/* Need to update all effective id's */
currEffectivePid = effectivePatchId[currPatchId]
newEffectivePid = effectivePatchId[ComponentId]
OVER INDEX SEQUENCE(1, NP)
// NOTE: the patches must be ordered by type for this to work
// OVER INDEX SEQUENCE(startId[currType], startId[currType] + NumPatches[currType] - 1)
effectivePatchId[Index] = IF (effectivePatchId[Index] EQ currEffectivePid)
THEN newEffectivePid ELSE effectivePatchId[Index]
ENDFN
ENDFN
/* Check if we meet another patch in the same component */
IF (NNType EQ rMPG) AND (ComponentId NEQ 0) AND (ComponentId NEQ currPatchId)
// Find the patch nodes
nPos2 = PosList[ComponentId]
// If these are not directly connected
IF (!LINKED(nnGraph[currType], patchPos, nPos2, 0))
// d = DISTANCE(NNLoc, startLocation)
rowDiff = MAX(0, |ROW(NNLoc) - ROW(startLocation)| - 1)
colDiff = MAX(0, |COL(NNLoc) - COL(startLocation)| - 1)
d = (rowDiff^2 + colDiff^2)^0.5
// Create a link
edgeVar[rEdgeType] = rMPG
edgeVar[rPatchType] = PatchLayer
edgeVar[rNode1Id] = currPatchId
edgeVar[rNode2Id] = ComponentId
edgeVar[rDist] = d
Area1 = GET(nnGraph[currType], patchPos, rArea)
Area2 = GET(nnGraph[currType], nPos2, rArea)
edgeVar[rWeight] = (Area1 * Area2)^0.5
edgeVar[rStartLoc] = startLocation
edgeVar[rEndLoc] = NNLoc
INSERT LINK(nnGraph[currType], patchPos, nPos2, edgeVar)
IF (currType EQ 1)
OVER REGION VECTOR(startLocation,NNLoc)
PatchLinks = edgeVar[rEdgeType]+1
ENDFN
ENDFN
ENDFN
ENDFN
TRANSITIONS = CLASSIFY(NNType)
rNN: (ComponentId EQ 0) AND (PatchesToConnect > 0)
rMST: (ComponentId EQ 0) AND (ComponentsToConnect > 0) AND (currPatchId <= NP)
rMPG: (ComponentId EQ 0) // Continue until the entire layer has been visited
ENDFN
ComponentId = currPatchId
NNLoc = startLocation
ENDTR
SPREADTIMESTEP
IF (startLocation NEQ Location) THEN 0.0000001*(1/MaxDist) ELSE -2
ENDST
/* Patches include diagonal neighbours, but if we spread */
/* using diagonal, then we have problems */
SPREADLOCATION
IF (PatchId EQ currPatchId)
maxD = 1.5
ELSE
rowDiff = | FLOOR(Location / NUMCOLS) - FLOOR(startLocation / NUMCOLS) | + 1
colDiff = | (Location % NUMCOLS) - (startLocation % NUMCOLS) | + 1
maxD = IF ((numSteps + 2 - CEIL((rowDiff^2 + colDiff^2)^0.5)) > 1) THEN 1.5 ELSE 1
ENDFN
REGION CENTRED(1, maxD, EUCLIDEAN)
// DECISION (PatchId NEQ currPatchId) AND (!onEdge OR (PatchLayer EQ currType) OR (DISTANCE(Location, SOURCE Location) EQ 1))
DECISION IF (PatchId EQ currPatchId) THEN (ComponentId EQ 0) AND (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0)
ELSE IF (NNType NEQ rMPG) THEN (effectivePatchId[ComponentId] NEQ effectivePatchId[currPatchId]) AND (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0)
ELSE (ComponentId NEQ currPatchId) AND (0 <= PatchLayer < (MinPType + NumPTypes)) AND (StudyArea > 0)
ENDSL
SPREADPROB
SPREADPROB = 1
// onEdge = (EdgeInterior EQ Edge) AND (PatchLayer EQ currType)
IF (PatchId EQ currPatchId)
startLocation = Location
numSteps = 0
ELSE
startLocation = SOURCE startLocation
numSteps = SOURCE numSteps + 1
ENDFN
ENDSP
stats.lse
stats.scn
stats.sel
Seles Model Time Units: Step kiloStep 1000 100000 Landscape Events: ConvexHull.lse Spatial Constants: StudyArea Spatial Variables: ConvexHullPoints[1] <= 0 ConvexHull[1] <= 0 Global Variables: TRLoc = 0 Output Frequency: 1
ConvexHull.lse
// Sweep-line approach to identifying convex hull using a worker agent
LSAGENT: ConvexHull
DEFINITIONS
LAYER: StudyArea, ConvexHull, ConvexHullPoints
GLOBAL VARIABLE: TRLoc
LOCAL VARIABLE: EndingPivotLoc
AGENT VARIABLE: PivotLocation, EdgeLocation, EdgeRow, EdgeCol
ENDDEF
INITIALSTATE
INITIALSTATE = 1
// Find the top-right-most point
TRLoc = 0
OVER REGION WHOLE MAP
DECISION StudyArea > 0
TRLoc = MAX(TRLoc, Location)
ENDFN
ConvexHull = 1 // Initialize convex hull layer to all ones (will erase areas outside hull)
ENDIS
NUMAGENTS = 1 // Only need a single worker
// Start on the first pivot vertex on convex hull
AGENTLOCATION
REGION LOCATION(TRLoc)
ENDAL
PROBINIT
PROBINIT = 1
// Set up first pivot vertex, and first raster boundary location
PivotLocation = Location
EdgeRow = NUMROWS-1
EdgeCol = COL(Location)
EdgeLocation = LOCATION(EdgeRow, EdgeCol)
EndingPivotLoc = -1
ENDPI
TRANSITIONS
// Continue until we reach the ending pivot location (which is actually second time we hit the second pivot vertex,
// since the first pivot vertex gets only partially processed on first pass)
TRANSITIONS = (PivotLocation NEQ EndingPivotLoc)
ConvexHullPoints = 1
// Walk line from boundary to vertex and erase until a new pivot vertex is hit (then move there)
oldPivot = PivotLocation
hitPivot = FALSE
OVER REGION VECTOR(EdgeLocation, PivotLocation)
DECISION !hitPivot
IF (StudyArea > 0) // hit new vertex
hitPivot = TRUE
IF (EndingPivotLoc EQ -1) AND (Location NEQ PivotLocation)
EndingPivotLoc = PivotLocation
ENDFN
PivotLocation = Location
ELSE
ConvexHull = 0 // erase
ENDFN
ENDFN
// Move boundary location clockwise around outside edge of raster
IF (EdgeRow EQ NUMROWS-1) AND (EdgeCol < NUMCOLS-1)
EdgeCol = EdgeCol + 1
ELSE IF (EdgeCol EQ NUMCOLS-1) AND (EdgeRow > 0)
EdgeRow = EdgeRow - 1
ELSE IF (EdgeRow EQ 0) AND (EdgeCol > 0)
EdgeCol = EdgeCol - 1
ELSE
EdgeRow = EdgeRow + 1
ENDFN
EdgeLocation = LOCATION(EdgeRow, EdgeCol)
ENDTR
POPULATIONTIME = 1
// Move to current/new pivot location
MOVELOCATION
REGION LOCATION(PivotLocation)
ENDML
Suggested Experiments
To explore this cellular automata model further, try the following:
