Skip to content

Commit fb8d58d

Browse files
Start revamp
1 parent 1fca6cb commit fb8d58d

File tree

1 file changed

+227
-0
lines changed

1 file changed

+227
-0
lines changed

analyses/patter_02_analyses.R

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
###########################
2+
###########################
3+
#### patter-analyses
4+
5+
#### Aims
6+
# 1) Run patter analyses
7+
8+
#### Prerequisites
9+
# 1) patter-setup.R
10+
11+
12+
###########################
13+
###########################
14+
#### Setup
15+
16+
#### Wipe workspace
17+
rm(list = ls())
18+
# try(pacman::p_unload("all"), silent = TRUE)
19+
dv::clear()
20+
21+
#### Essential packages
22+
library(data.table)
23+
library(dtplyr)
24+
library(dplyr, warn.conflicts = FALSE)
25+
library(JuliaCall)
26+
library(lubridate)
27+
library(patter)
28+
library(tictoc)
29+
30+
#### Load data
31+
champlain <- terra::vect("data/ChamplainRegions.shp")
32+
moorings <- readRDS("data/moorings.rds")
33+
detections <- readRDS("data/simulated_detections.rds")
34+
metadata <- readRDS("data/simulations_metadata.rds")
35+
dv::src()
36+
37+
#### Julia setup
38+
julia_connect()
39+
set_seed()
40+
set_map()
41+
42+
###########################
43+
###########################
44+
#### Prepare algorithms
45+
46+
47+
###########################
48+
#### Individual datasets
49+
50+
#### Define individual & associated datasets
51+
id <- 1
52+
sid <- paste0("sim_", id)
53+
path <- paths[sim_id == id, ]
54+
dets <- detections[sim_id == sid, ]
55+
meta <- metadata[sim_id == sid, ]
56+
stopifnot(nrow(meta) == 1L)
57+
58+
59+
###########################
60+
#### Define study timeline
61+
62+
# The timeline is individual specific
63+
# Start time: "2022-01-01 00:00:00"
64+
# Simulated tracks comprised 5000 steps
65+
# Each step comprised 500 m
66+
# But velocity set to different values in transmit_along_path()
67+
# I.e., For each individual, the duration of a step length is different
68+
# Transmissions were generated along the paths every 120 + 7 s
69+
70+
# Calculate the step duration
71+
trms_time <- 120 # + 7 # transmission time (every 127 s)
72+
mvt_speed <- meta$velocity # speed @ each _movement_ time step (m/s)
73+
mvt_distance <- 500 # distance moved at each _movement_ time step (m)
74+
mvt_time <- mvt_distance / mvt_speed # duration of each _movement_ time step (s)
75+
76+
# Define simulation start and end times
77+
end <- max(seq(start, length.out = 5000, by = paste(mvt_time, "secs")))
78+
79+
# Define timeline
80+
# * The timeline is based on a transmission interval of ~2 mins
81+
# * (The step length (m) in ~2 mins given {mvt_speed} is {mvt_speed} * {trms_time})
82+
# * (The angle is simulated every {mvt_time} s)
83+
timeline <- seq.POSIXt(start, end, by = paste(trms_time, "secs"))
84+
85+
# Validate that the timeline spans the detection time series for the ID
86+
stopifnot(interval(min(dets$timestamp), max(dets$timestamp)) %within% interval(min(timeline), max(timeline)))
87+
88+
89+
###########################
90+
#### Movement model
91+
92+
#### Movement & timeline validation
93+
# Check simulated step lengths and turning angles at 127 s resolution
94+
# * The path was simulated with 5000 steps, each of {mvt_time} secs
95+
# * complete_simulated_transmissions_regions.rds contains the 'full' path @ resolution of 127 s
96+
# * Step lengths should match {mvt_speed} (m/s) * {trms_time} (s)
97+
mvt_len <- mvt_speed * trms_time
98+
if (FALSE) {
99+
movements <-
100+
path |>
101+
lazy_dt() |>
102+
select(sim_id, date = timestamp, x, y) |>
103+
as.data.frame() |>
104+
bayesmove::prep_data(coord.names = c("x", "y"), id = "sim_id")
105+
unique(diff(movements$date)) # time stamps evenly spaced
106+
unique(movements$step) # step lengths not exactly equal
107+
hist(movements$step) # but most step lengths are correct
108+
unique(movements$angle) # angles (radians)
109+
}
110+
# Check distance of individual to receiver @ moment of detection
111+
# TO DO
112+
113+
#### Define movement model
114+
# Paths were simulated via glatos::crw_in_polygon() which calls glatos::crw()
115+
# * https://github.com/ocean-tracking-network/glatos/blob/main/R/sim-crw_in_polygon.r
116+
# * https://github.com/ocean-tracking-network/glatos/blob/main/R/simutil-crw.r
117+
# Step length in {trms_time} given {mvt_speed} is {mvt_speed} (m/s) * {trms_time} (s)
118+
# heading_{t = 1} = Uniform(0, 360)
119+
# heading_{t > 1} = Normal(0, meta$theta) + cumsum(heading{t = 1, ..., t})
120+
# Angles updated every {mvt_time} (s) / {trms_time} (s) time steps
121+
# (Internally glatos enlarges the SD if the simulation steps outside the polygon!)
122+
state <- "StateXYD"
123+
model_move <- move_xyd(length = mvt_len, theta = meta$theta)
124+
update_angle_every <- round(mvt_time / trms_time)
125+
julia_assign("update_angle_every", update_angle_every)
126+
julia_command('include("Julia/src/model-movement.jl");')
127+
128+
129+
###########################
130+
#### Define observation model
131+
132+
model_obs <- c("ModelObsAcousticLogisTrunc", "ModelObsAcousticContainer")
133+
acoustics <- assemble_acoustics(.timeline = timeline, .acoustics = dets, .moorings = moorings)
134+
containers <- assemble_acoustics_containers(.acoustics = acoustics,
135+
.direction = "forward",
136+
.mobility = mvt_len,
137+
.threshold = ydist)
138+
yobs <- list(ModelObsAcousticLogisTrunc = acoustics,
139+
ModelObsAcousticContainer = containers)
140+
141+
142+
###########################
143+
###########################
144+
#### Algorithm runs
145+
146+
#### Define filter arguments
147+
args <- list(.map = map,
148+
.timeline = timeline,
149+
.state = state,
150+
.xinit = NULL,
151+
.xinit_pars = list(mobility = mvt_len),
152+
.yobs = yobs,
153+
.model_obs = model_obs,
154+
.model_move = model_move,
155+
.n_particle = 10000L,
156+
.direction = "forward"
157+
)
158+
159+
160+
#### Run forward filter
161+
# * NB: Setting observations is slow (1 min)
162+
# > Weights from filter (1 -> 208292) are zero at time 62367
163+
fwd <- do.call(pf_filter, args, quote = TRUE)
164+
165+
#### Run backward filter
166+
# * Update containers & yobs for .direction = "backward", then run filter
167+
containers <- assemble_acoustics_containers(.acoustics = acoustics,
168+
.direction = "backward",
169+
.mobility = distance,
170+
.threshold = ydist)
171+
yobs <- list(ModelObsAcousticLogisTrunc = acoustics,
172+
ModelObsAcousticContainer = containers)
173+
args$.yobs <- yobs
174+
args$.direction <- "backward"
175+
do.call(pf_filter, args, quote = TRUE)
176+
177+
#### Run smoother
178+
out_smo <- pf_smoother_two_filter()
179+
180+
181+
###########################
182+
###########################
183+
#### Quick residency analysis
184+
185+
#### True residency in each region
186+
path_res <-
187+
path |>
188+
group_by(region) |>
189+
summarise(n = n()) |>
190+
mutate(residency = (n / sum(n)) * 100,
191+
estimate = "path",
192+
sim_id = id) |>
193+
as.data.table()
194+
195+
#### Particle residency estimates
196+
part_res <-
197+
out_smo |>
198+
mutate(region = terra::extract(map, cbind(x, y))) |>
199+
group_by(region) |>
200+
mutate(residency = (n / sum(n)) * 100,
201+
estimate = "smoother",
202+
sim_id = id) |>
203+
as.data.table()
204+
205+
#### Collect data
206+
res <- rbind(path_res, part_res)
207+
qs::qsave(res, "data/patter/output/residency/qresidency/", paste0(id, ".qs"))
208+
209+
210+
###########################
211+
###########################
212+
#### Synthesis
213+
214+
# Read residency data for each individual
215+
residency <-
216+
lapply(unique(paths$sim_id), function(id) {
217+
qs::qread("data/patter/qresidency.qs")
218+
}) |> rbindlist()
219+
220+
# Visualise residency ~ individual, coloured by truth/algorithm
221+
#
222+
# > TO DO
223+
224+
225+
#### End of code.
226+
###########################
227+
###########################

0 commit comments

Comments
 (0)