-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathGuide2bLSmodelR.r
More file actions
443 lines (397 loc) · 18.5 KB
/
Guide2bLSmodelR.r
File metadata and controls
443 lines (397 loc) · 18.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
#################################
# Guide to bLSmodelR Version 4+
#################################
# I am very sorry for the underdocumented help pages in bLSmodelR.
# I hope this guide will bring some better insight into the bLSmodelR...
# The current version of bLSmodelR to use with this guide is version 4.3-0 (2020-06-12)
library(bLSmodelR)
####################################
#~~~~~~~I. Setting up a Run:~~~~~~~#
####################################
#******
# I. What input is needed?
#******
#******
# I.M. What input is mandatory?
#******
# I.M.0: Have a look at the main model function *runbLS*:
# ?runbLS
# to run bLSmodelR a list of data.frames defining the model input is needed
# ?genInputList
# The argument 'ModelInput' in *runbLS* contains 2 mandatory entries:
# $Interval -> I.M.1
# $Sensors -> I.M.2
#
# and 3 optional entry:
# $Sources -> I.O.1
# $Model -> I.O.2
# $Tolerances -> I.O.3
#******
#******
# I.M.1: $Interval:
# ?genInterval
print(Interval <- genInterval())
str(Interval)
# contains all information about the individual intervals that will be calculated
# i.e. necessary turbulence and windprofile parameters incl. wind direction
# Sensors and Sources that will be included in the calculation of the interval can be defined as well
# if arguments 'SensorNames' and/or 'SourceNames' are not provided, all available Sensors/Sources will be included
# when gathering the model input with the function *genInputList* (c.f. example below IntervalDemo).
# example 1: 'Sensor1' will be calculated in first interval, 'Sensor2' in second
print(Interval1 <- genInterval(SensorNames = c("Sensor1","Sensor2")))
# example 2: 'Sensor1' and 'Sensor2' will be calculated in the same interval
print(Interval2 <- genInterval(SensorNames = "Sensor1,Sensor2"))
### 'MaxFetch' argument
# argument 'MaxFetch' can be provided as negative number. If done so, the maximum distance between the specific
# Sources and the Sensors will be calculated based on the wind direction during the model run
# and the absolute value of the 'MaxFetch' argument will be added on top of this distance in order to define the MaxFetch
# values (i.e. the maximum distance upwind of the Sensor) for each interval (see example in ?genInterval)
print(Interval3 <- genInterval(MaxFetch = -50))
####
# an option to construct the Interval data.frame by hand is given as well (using argument 'Data').
# This comes most of the time handier and additionally allows for more information changing on an interval basis (e.g. start/end times etc.),
# which is passed to the output. Columns that should be recognized as Interval arguments should be named according to the
# corresponding arguments, i.e names(as.list(args(genInterval)))[2:14]
IntervalData <- cbind(Interval3,Numeric=c(1.2,3.1),Characters=letters[1:2],Factors=factor(letters[1:2]),POSXct=as.POSIXct(1:2,origin=Sys.time()),stringsAsFactors=FALSE)
print(IntervalDemo <- genInterval(Data=IntervalData))
str(IntervalDemo)
#******
#******
# I.M.2: $Sensors:
# ?genSensors
# genSensors accepts either lists or data.frames as argument input
# these lists/data.frames need specific named entries (mandatory are
# entries x, y, and z; optional is entry name - the name of the sensor):
# Point Sensors need 3 entries (x,y,z)
# PathSensors need one of these entries (x,y,z) with length >1 and can
# have two additional arguments:
# - entry 'd' refines the way to interpolate the path sensor by multiple point sensors:
# -> d = Distance between point sensors in m
# - entry 'id' defines 'sub-groups' of a sensor, such that multiple sensors at different
# locations can be treated as one sensor
# providing no argument to genSensors results in an error
# genSensors()
print(Sensors <- genSensors(PointSensor=list(x=0,y=0,z=1.2)))
str(Sensors)
# other example:
print(Sensors1 <- genSensors(data.frame(name=paste0("Sensor",1:3),x=10,y=0,z=c(0.5,1.2,2.05))))
# defining Path Sensors:
print(Sensors2 <- genSensors(
LineSensor1=list(x=c(0,20),y=0,z=1.2),
LineSensor2=list(x=c(-10,20),y=10,z=c(2,2.2),d=0.5),
PathSensor1=list(x=c(0, 10, 10),y=c(15,25,30),z=1.2),
PathSensor2=list(x=c(2, 2, 10, 10, 20),y=c(0, 15,5,10, 5),z=1.2,id=c(1,1,2,2,2))
))
plot(Sensors2[Sensors2$"Sensor Name" == "PathSensor2",])
# joining Sensors:
head(join(Sensors1,Sensors2))
# Now define the two Sensors included in IntervalDemo for further use:
print(SensorsDemo <- genSensors(Sensor1=list(x=10,y=0,z=1.2),Sensor2=list(x=20,y=0,z=2.05)))
#******
#******
# Gathering ModelInput:
# ?genInputList
# Given these 2 list entries, bLSmodelR could be run to pre-calculate TD catalogs - but only, if TDonly==TRUE:
# *genInputList* will set TDonly==TRUE with a warning
# Default values for Model Parameters and Tolerances (see I.O.2 and I.O.3 below) will be used
InputWithoutSource <- genInputList(IntervalDemo,SensorsDemo)
print(InputWithoutSource)
# Since no sensors were defined explicitly in the Interval data.frame, all existing sensors were added to each interval.
#******
#******
# I.O. Optional model input:
#******
# I.O.1: $Sources:
# Sources are treated as homogeneously emitting source ares, defined by polygons.
# ?genSources
print(Sources <- genSources(list(x=rnorm(10),y=rnorm(10))))
str(Sources)
# generating sources is done in a somewhat inconvenient way...
# source areas are either defined by:
# 1) a list of named lists, where the names of the list entries correspond to the source area names.
# 2) arguments providing lists or data.frames, where the argument names correspond to the source are names.
# There are two optional polygon "Classes" to help building source areas:
# circle 'Class'="c":
print(Circle <- genSources(SourceCirc=list(Class="c",M=c(10,50),R=10)))
# rectangle 'Class'="r":
print(Rect <- genSources(SourceRect=list(Class="r",x1=0,x2=10,y1=0,y2=20)))
# Otherwise source polygons are defined by a list with vectors x and y:
print(Poly <- genSources(SourcePoly=list(
x=c(seq(-10,10,1),seq(9.5,-9.5,-0.5))-20,
y=c(rep(0,21),seq(h <- sqrt(20^2-10^2)/20,h*20,h),seq(19*h,h,-h))+30
)))
# or equivalently by supplying a data.frame:
print(Poly <- genSources(SourcePoly=data.frame(
x=c(seq(-10,10,1),seq(9.5,-9.5,-0.5))-20,
y=c(rep(0,21),seq(h <- sqrt(20^2-10^2)/20,h*20,h),seq(19*h,h,-h))+30
)))
# Multiple entries with equal names are treated as one source.
# One can provide them as one list argument with named list entries
print(MultiPoly <- genSources(list(
SourceRects=list(Class="r",x1=10,x2=20,y1=5,y2=25),
SourceRects=list(Class="r",x1=-10,x2=-20,y1=5,y2=25),
SourceRects=list(Class="r",x1=-10,x2=10,y1=-10,y2=0),
OtherSource=list(Class="r",x1=-5,x2=5,y1=20,y2=30)
)))
# or, equivalently, as multiple (possibly named) arguments:
print(MultiPoly <- genSources(
SourceRects=list(Class="r",x1=10,x2=20,y1=5,y2=25),
SourceRects=list(Class="r",x1=-10,x2=-20,y1=5,y2=25),
SourceRects=list(Class="r",x1=-10,x2=10,y1=-10,y2=0),
OtherSource=list(Class="r",x1=-5,x2=5,y1=20,y2=30)
))
# join all previous in one for demo:
print(SourcesDemo <- join(
Circle,
Rect,
Poly,
MultiPoly
))
head(SourcesDemo)
#******
#******
# I.O.2: $Model:
# ?genModel
print(Model <- genModel())
str(Model)
# kv: von Karman constant
# A: Scaling Factor (see Flesch et al., 2004)
# alpha: fraction of "tau_L" to define timestep (see Flesch et al., 2004)
# wTDcutoff: lower limit of touchdown velocities (TD velocities below the cutoff will be set to the cutoff value)
# TDwrite: Writing (i.e. permanently saving) touchdown catalogs?
# overwriteTD: Overwrite existing (identical) touchdown catalogs?
# TDread: Reading previously saved touchdown catalogs?
# TDonly: Calculate touchdown catalogs only?
# ncores: Number of cores to use. 'ncores' <= 0 == "use all detected cores" as given by the
# default call of *parallel::detectCores* (although this is not recommended
# -> see: ?parallel::detectCores)
#
# For demo: do not save TD catalogs:
print(ModelDemo <- genModel(TDwrite=FALSE))
#******
#******
# I.O.3: $Tolerances:
# ?genTolerances
print(Tol <- genTolerances())
str(Tol)
# only used for choosing appropriate touchdown catalogs, if TDread is "TRUE".
# Tol.Zero prohibits using tolerances when choosing TD cataolgs,
# i.e. only exactly matching catalogs will be used:
print(genTolerances(Tol.Zero=TRUE))
#******
#******
# Gathering ModelInput:
# Generate ModelInput for demo:
print(demoInput <- genInputList(IntervalDemo,SensorsDemo,SourcesDemo,ModelDemo,Tol))
# If you use default values (as here Tolerances), you don't need to supply them:
identicalDemoInput <- genInputList(IntervalDemo,SensorsDemo,SourcesDemo,ModelDemo)
identical(demoInput,identicalDemoInput)
#******
#******
# II. Have a look at your set up
#******
#******
siteMap(demoInput,polygon.args = list(col = rainbow(5,s=0.3)),
sources.text.args = list(col = rainbow(5,v=0.5)))
# or equivalently:
plot(demoInput,polygon.args = list(col = rainbow(5,s=0.3)),
sources.text.args = list(col = rainbow(5,v=0.5)))
args(siteMap)
# additional possible calls (arguments order doesn't matter):
par(mfrow=c(2,2))
plot(SourcesDemo,SensorsDemo,main="plot(SourcesDemo,SensorsDemo)")
plot(SensorsDemo,SourcesDemo,main="plot(SensorsDemo,SourcesDemo)")
plot(SensorsDemo,main="plot(SensorsDemo)")
plot(SourcesDemo,main="plot(SourcesDemo)")
# make a somewhat nicer looking plot :-)
nicerMap <- function(...){}
body(nicerMap) <- expression({plot(demoInput
,polygon.args=list(col=rainbow(5,s=0.3))
,points.args=list(pch=4,cex=1,col=c("darkgreen","darkred"),lwd=2)
,sensors.text.args=list(pos=4)
,sources.text.args=list(col = rainbow(5,v=0.5))
,...)})
nicerMap()
# add a scale bar:
bnicerMap <- body(nicerMap)
bnicerMap[3] <- expression(addScaleBar())
body(nicerMap) <- bnicerMap
nicerMap()
bnicerMap[3] <- expression(addScaleBar(pos=4))
body(nicerMap) <- bnicerMap
nicerMap()
# wind direction from NW would be best:
bnicerMap[4] <- expression(addWindrose(WD=315,pos=1))
body(nicerMap) <- bnicerMap
nicerMap()
#******
#******
# III. Run the model
#******
#******
# ?runbLS
# define a temporary catalog path. Even if you don't save the TD catalogs at the end,
# temporary TD catalogs will be stored there and later removed.
# Make sure to have enough space available there. The following example will only require
# around 2 MB for the temporary TD catalogs.
Cat.Path <- getwd()
# set Interval WD to 315 deg N for demo:
demoInput$Interval[,"WD [deg N]"] <- 315
# run the model
args(runbLS)
# results as data.table:
DemoOutput_DataTable <- runbLS(demoInput,Cat.Path=Cat.Path)
# temporary TD catalogs will be used as long as they're not removed (i.e. actively removed
# by a call to cleanTemporary(), or by exiting the R session (where cleanTemporary() will be called before exiting))
# results as data.frame
DemoOutput <- runbLS(demoInput,Cat.Path=Cat.Path,asDT=FALSE)
#
DemoOutput
head(DemoOutput,1)
names(attributes(DemoOutput))
# initially attached columns:
DemoOutput[,39:42]
str(DemoOutput[,39:42])
# change from data.frame to data.table:
setDT(DemoOutput)
DemoOutput
# and back to data.frame:
setDF(DemoOutput)
DemoOutput
# switch column names to simple:
DemoOutput <- switchNames(DemoOutput)
DemoOutput
# change back to data.table with more 'informative' column names:
switchNames(setDT(DemoOutput),FALSE)
DemoOutput
# switch names back:
switchNames(DemoOutput)
# switch the heights of sensors (in a somewhat unconventional way). One catalog needs to be calculated
# due to the new, longer Fetch required. This could have been omitted by setting a large enough MaxFetch value.
demoInput2 <- demoInput
demoInput2$Sensors <- genSensors(data.frame(name=c("S1.High","S2.Low"),x=(1:2)*10,y=0,z=c(2.05,1.2)))
demoInput2$Interval[,"Sensor Names (sep = \",\")"] <- c("S1.High","S2.Low")
DemoOutput2 <- runbLS(demoInput2,Cat.Path=Cat.Path)
DemoOutput2[,.(Sensor,Source,SensorHeight,CE)]
DemoOutput[,.(Sensor,Source,SensorHeight,CE)]
# extract Sensor1 and sort C/E in decreasing order (have a look at data.table::setorder for increasing/decreasing ordering -> i.e. -/+ for decreasing/increasing column ordering):
S1Output <- extractResult(DemoOutput,sortArgs = list("Sensor"="Sensor1",-"CE"),dropAttr=FALSE)
S1Output[]
names(attributes(S1Output))
# otherwise, if dropAttr=TRUE (default), model specific attributes are dropped:
S1Results <- extractResult(DemoOutput,sortArgs = list("Sensor"="Sensor1",-"CE"))
S1Results[]
names(attributes(S1Results))
#
# extract Sources c("SourceRect","SourceRects") and drop all columns but c("Sensor","Source","C/E","C/E SE"):
OutShort <- extractResult(DemoOutput,sortArgs = list("Source"=c("SourceRect","SourceRects")),keep=c("Sensor","Source","CE","CE_se"))
OutShort
#
# get catalog: (somewhat inconvenient... will be improved someday)
# row 1:
CatInfo <- getCatalogs(DemoOutput,1)
Catalog <- readCatalog(CatInfo[,Catalog])
Catalog
par(mfrow=c(1,1))
plot(Catalog, col = "#00000011", pch = 20)
# Visualize footprint (quite a number of arguments, sorry for that!):
args(plotFootprint)
par(mfrow=c(2,2))
# Plot C-Footprint of Sensor 1:
nicerMap(main="C-FP as modelled")
plotFootprint(DemoOutput,"Sensor1",1,add=TRUE,addSource=FALSE,showSensor=FALSE,lpos="topright",showPerc=TRUE,alpha=0.5)
# Plot 'despiked' C-Footprint of Sensor 1:
nicerMap(main="C-FP (use.avg = TRUE)")
plotFootprint(DemoOutput,"Sensor1",1,use.avg=TRUE,add=TRUE,addSource=FALSE,showSensor=FALSE,lpos="topright",showPerc=TRUE,alpha=0.5)
# Plot 'despiked' C-Footprint of Sensor 1 using symmetry property due to horizontal homogeneity of turbulence:
nicerMap(main="C-FP (use.avg = TRUE, use.sym = TRUE)")
plotFootprint(DemoOutput,"Sensor1",1,use.avg=TRUE,use.sym=TRUE,add=TRUE,addSource=FALSE,showSensor=FALSE,lpos="topright",showPerc=TRUE,alpha=0.5)
# Plot w'C'-Footprint:
x11(width=10,height=5)
par(mfrow=c(1,2))
nicerMap(main="w'C'-FP")
plotFootprint(DemoOutput,"Sensor1",1,type="wCE",use.avg=TRUE,add=TRUE,addSource=FALSE,showSensor=FALSE,lpos="topright",showPerc=TRUE)
# Plot C-Footprint of Sensor 1 and Sensor 2:
nicerMap(main="C-FP of both Sensors (units: CE s/m)")
plotFootprint(DemoOutput,"Sensor1",1,add=TRUE,use.avg=TRUE,use.sym=TRUE,addSource=FALSE,showSensor=FALSE,lpos="topright",alpha=0.5,breaks = function(x) exp(seq(log(1E-4),log(0.027),length.out=4)),sigNums=2)
plotFootprint(DemoOutput,"Sensor2",2,add=TRUE,use.avg=TRUE,use.sym=TRUE,addSource=FALSE,showSensor=FALSE,lpos=NA,alpha=0.3,breaks = function(x) exp(seq(log(1E-4),log(0.027),length.out=4)))
#******
#******
# IV. Deposition post-processing
#******
# Run the deposition post-processing function: deposition
args(deposition)
# vDep must be given as m/s. It is defined as the 'surface' deposition velocity ath height d + z0 (the "bLS model surface").
# post-process results with a 'rather high' deposition velocity of 3 cm/s:
RunDep <- deposition(DemoOutput,0.03,Sensor="Sensor1",Source="SourceRects")
RunDep
RunDep[,.(CE,CE_Dep,"Reduction of CE by deposition (in %)"=(CE - CE_Dep)/CE*100)]
# spatially inhomogeneous deposition velocity
# this is still experimental and input format may change in future updates...
vDepAreas <- join(Circle,Rect,MultiPoly)
vDepList <- list(
SourceRects = 0
,SourceRect = 0.03
,OtherSource = 0.01
)
plot(join(Poly,vDepAreas),SensorsDemo,polygon.args = list(col = rainbow(5,s=0.3)),sources.text.args = list(
labels = paste0("vDep = ",c("0 (emitting area)","0.002 (same as default,\n because it is not defined in \"vDepList\")","0.03","0","0.01"))
,col = rainbow(5,v=0.5)))
text(0,35,"vDep = 0.002 (all non-defined areas)",cex=0.5,font=2)
RunDep2 <- deposition(DemoOutput,0.002,Sensor="Sensor1",Source="SourcePoly",vDepSpatial = list(vDepList,vDepAreas))
RunDep2[,.(CE,CE_Dep,"Reduction of CE by deposition (in %)"=(CE - CE_Dep)/CE*100)]
#### example on new vDepSpatial treatment
## define key
#snames <- unique(Sources[, 1])
## source names key
#vdn_key <- setNames(snames, snames[c(2, 1, 4, 3)])
##
## 1. data.frame-like (is.data.frame/inherits, one column defining patches/polygons/"Sources" per row/interval, columns with patches names defining vDep per row/interval
#vds <- nodep[, .(rn, Sensor, Source)]
#vds[, Spatial := nodep[, vdn_key[Source]]]
## spatial, zones, sources, select, regions
#vds[, id := .I]
#vdadd <- dcast(vds, id + rn + Sensor + Source + Spatial ~ Spatial, value.var = 'Spatial', fun.aggregate = function(x) 0.0)
## check:
## - empty row entry
#vdadd[sample.int(.N, 10), Spatial := '']
## define vDep via columns & set vDep to either vDep or 0
#vDepSpatialList <- list(
# # list with vDep == 0
# vdadd,
# # Sources object
# Sources
#)
## run deposition
#dep <- deposition(nodep, vDep = 'vDep', vDepSpatial = vDepSpatialList, ncores = 1)
# # 2. vector of columns including NA
# xx <- copy(nodep)
# # provide character vector with column names
# # vdadd <- setNames(paste0('test_', snames), snames)
# vdadd <- snames
# # add columns in Run
# # xx[, paste0('test_', snames) := lapply(snames, function(x) {
# xx[, (snames) := lapply(snames, function(x) {
# out <- rep(NA_real_, .N)
# out[which(vdn_key[Source] == x)] <- 0.0
# out
# })]
# # define vDep via columns & set vDep to either vDep or 0
# vDepSpatialList <- list(
# # list with vDep == 0
# vdadd,
# # Sources object
# Sources
# )
# # run deposition
# dep <- deposition(xx, vDep = 'vDep', vDepSpatial = vDepSpatialList, ncores = 1)
#******
# V. Parallelism
#******
# A very! simple attempt to parallelize parts of the model calculation has been implemented using the package snow (and snowfall)
# It is possible to either:
# a) provide the number of cores (argument: ncores) to build a SOCKET type structure or
DemoOutput_Parallel <- runbLS(demoInput,Cat.Path=Cat.Path,ncores=2)
# b) to have a network already running before calling runbLS (if so, keep in mind, that the TD catalog must be available to all nodes!!!)
sfInit(TRUE,2)
DemoOutput_Parallel2 <- runbLS(demoInput,Cat.Path=Cat.Path);sfStop()