Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
R
RedModRphree
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Marco De Lucia
RedModRphree
Merge requests
!3
Integrate RcppBTCS with diffusion
Code
Review changes
Check out branch
Download
Patches
Plain diff
Merged
Integrate RcppBTCS with diffusion
1-integrate-rcppbtcs-with-diffusion
into
master
Overview
0
Commits
4
Pipelines
0
Changes
13
Merged
Marco De Lucia
requested to merge
1-integrate-rcppbtcs-with-diffusion
into
master
2 years ago
Overview
0
Commits
4
Pipelines
0
Changes
13
Expand
Closes
#1 (closed)
bump to version 0.3.10
refactor/update RepSol and Distribute to deal with sorption/exchange
add diffusion reactive transport
SOLUTION_MODIFY
Edited
2 years ago
by
Marco De Lucia
0
0
Merge request reports
Compare
master
version 2
b62aeafd
2 years ago
version 1
340314f6
2 years ago
master (base)
and
latest version
latest version
3cdf095e
4 commits,
2 years ago
version 2
b62aeafd
3 commits,
2 years ago
version 1
340314f6
2 commits,
2 years ago
13 files
+
795
−
37
Inline
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
Files
13
Search (e.g. *.vue) (Ctrl+P)
R/Rphree_Diffusion.R
0 → 100644
+
454
−
0
Options
## Functions for RT simulations using through RcppBTCS's diffusion
### Marco De Lucia, delucia@gfz-potsdam.de, 2009-2022
### Time-stamp: "Last modified 2022-07-13 16:17:43 delucia"
##' This function is somehow equivalent to
##' \code{phreeqc::phrGetSelectedOutput} but it won't call
##' \code{make.names} to preserve special characters in the column
##' names and it also gives the opportunity of selecting one single
##' element from the selected output list it is called upon.
##' @title Modified wrapper to phreeqc method "getSelOutLst"
##' @param n integer, defaults to 1. List index of the selected output
##' that we want
##' @return a selected output array of dim 2.
##' @author MDL
##' @export
GetSelectedOutput
<-
function
(
n
=
1L
)
{
sel_outs
<-
.Call
(
"getSelOutLst"
,
PACKAGE
=
"phreeqc"
)
if
(
length
(
sel_outs
)
>
1
)
msg
(
"selout is a list with more than 1 element, returning number "
,
n
)
return
(
sel_outs
[[
n
]])
}
##' Cfr \code{demo}
##' @title 1D diffusive reactive transport simulations with surface
##' exchange and sorption
##' @param setup a list with several input parameters
##' @param init optional, initial state of the simulation
##' @param maxtime depending on \code{step}, the simulation time
##' @param step one of c("time","iter","fix_dt"), defines the time
##' stepping
##' @param db the chemical database
##' @param maxsim number of simulations above which parallelization is
##' triggered
##' @param ebreak logical, defaults to FALSE. If TRUE, an early break
##' is invoked
##' @param scheme character, one of "FTCS" and "BTCS"
##' @param procs number of processors to be used in parallel execution
##' @param writeout logical, defaults to FALSE. Should we write the
##' phreeqc output on disk?
##' @param surrogate logical, defaults to FALSE. Should we use
##' surrogates instead of phreeqc?
##' @param surrogate.FUN The function which calls the surrogate
##' @param model a list containing all surrogate models, one element
##' per variable
##' @param baleq data structure containing the balance equations
##' @param tol absolute tolerance on the mass balance. If this is
##' trespassed, phreeqc is called instead
##' @param call_pqc logical, defaults to TRUE: if TRUE, calls phreeqc
##' on the surrogate simulations which trespass the tolerance
##' @return a list containing lots of stuff. Each element of the list
##' represent an iteration and has two elements: $T
##' (concentrations after the transport) and $C, concentrations
##' after the chemistry
##' @author MDL
##' @export
DiffRT
<-
function
(
setup
,
init
,
maxtime
,
step
=
c
(
"time"
,
"iter"
,
"fix_dt"
),
db
=
"phreeqc.dat"
,
maxsim
=
1
,
ebreak
=
FALSE
,
scheme
=
"FTCS"
)
{
require
(
RcppBTCS
)
attach
(
setup
)
on.exit
({
msg
(
"breaking up before main loop. Traceback:"
)
print
(
traceback
())
msg
(
"detaching setup, returning state_C"
)
detach
(
setup
)
msg
(
" Bye."
)
return
(
state_C
)
})
dx
<-
len
/
n
# m
dt_cfl
<-
dx
^
2
/
alpha
/
2
msg
(
"Pure diffusion 1D simulations"
)
## Find out the number of iterations we're about to calculate
if
(
step
==
"fix_dt"
)
{
maxiter
<-
floor
(
maxtime
/
dt
)
+1
msg
(
"Fixed time step of "
,
dt
,
". "
,
maxiter
,
" iterations required to reach enditme of"
,
maxtime
)
}
if
(
step
==
"time"
)
{
maxiter
<-
floor
(
maxtime
/
dt
)
+1
msg
(
maxiter
,
"iterations required"
)
}
if
(
step
==
"iter"
)
{
maxiter
<-
maxtime
msg
(
"Simulation will end at approx "
,
floor
(
maxiter
*
dt
),
"secs"
)
}
## MDL: add "complete" list. This will be a list of lists. Each
## iteration is a 3-components sublist storing state_T (the input
## to chemistry, after transport step), state_C (the input to
## transport, after chemistry), and the whole PHREEQC output.
saved_complete
<-
vector
(
mode
=
"list"
,
length
=
maxiter
+1
)
pad
<-
floor
(
log10
(
maxiter
+1
))
+1
## to properly format the step names
spr_string
<-
paste0
(
"%0"
,
pad
,
"d"
)
names
(
saved_complete
)
<-
paste0
(
"step_"
,
sprintf
(
spr_string
,
seq
(
0
,
maxiter
)))
msg
(
"Loading phreeqc db... "
)
phreeqc
::
phrLoadDatabaseString
(
db
)
msg
(
"database loaded."
)
if
(
missing
(
init
))
{
msg
(
"missing initial chemical state"
)
if
(
!
is.character
(
initsim
))
{
msg
(
"using base sim to compute initial state"
)
runinitsim
<-
RepSol
(
base
,
n
,
first
=
first
)
runinitsim
<-
runinitsim
[
-
grep
(
"^END"
,
runinitsim
)]
}
else
{
msg
(
"using provided initial script to compute initial state"
)
runinitsim
<-
RepSol
(
initsim
,
n
,
first
=
first
)
runinitsim
<-
runinitsim
[
-
grep
(
"^END"
,
runinitsim
)]
}
## msg("running this PHREEQC script:")
## print(runinitsim)
## append the RUN_CELL and make sure there is no other END statement
phreeqc
::
phrSetOutputStringsOn
(
TRUE
)
phreeqc
::
phrSetDumpStringsOn
(
TRUE
)
tmpinp
<-
c
(
runinitsim
,
"RUN_CELLS"
,
paste0
(
"-cells 1-"
,
n
),
"END"
,
paste0
(
"SAVE solution 1-"
,
n
),
paste0
(
"SAVE exchange 1-"
,
n
),
paste0
(
"SAVE surface 1-"
,
n
),
"END"
,
"DUMP"
,
"-all"
,
"END"
)
phreeqc
::
phrRunString
(
tmpinp
)
tmpfirstrun
<-
GetSelectedOutput
()
## tmpout <- phreeqc::phrGetOutputStrings()
## dump <- phreeqc::phrGetDumpStrings()
if
(
nrow
(
tmpfirstrun
)
>
n
)
{
state_C
<-
data.matrix
(
tail
(
subset
(
tmpfirstrun
,
state
==
"react"
),
n
=
n
))
}
else
{
state_C
<-
data.matrix
(
tmpfirstrun
)
}
rownames
(
state_C
)
<-
state_C
[,
"soln"
]
}
else
{
msg
(
"given initial state; assuming correct colnames"
)
phreeqc
::
phrRunString
(
first
)
state_C
<-
data.matrix
(
init
)
rownames
(
state_C
)
<-
state_C
[,
"soln"
]
## tmpinp <- ""
## tmpout <- ""
}
index_saved
<-
1
saved_complete
[[
index_saved
]]
<-
list
(
C
=
state_C
)
## , input=tmpinp ,out=tmpout, dump=dump)
index_saved
<-
index_saved
+
1
msg
(
"first step, Diffusion by "
,
scheme
,
"..."
)
state_T
<-
DiffusionScheme
(
state_C
,
inflow
=
bound
,
dx
=
dx
,
dt
=
dt
,
alpha
=
alpha
,
scheme
=
scheme
,
transported
=
transported
)
msg
(
"Diffusion done..."
)
## remember that total_h, total_o and cb are automatically assumed
modBlock
<-
c
(
"SOLUTION_MODIFY 1"
,
"-temp 13"
,
"-total_h 111.01243359981"
,
"-total_o 55.506216800086"
,
"-cb -3.657928589893e-10"
,
"-totals"
,
paste
(
transported
,
0.1
,
sep
=
" "
))
msg
(
"iteration 0..."
)
## print(state_T)
newinput
<-
SolutionModify
(
modBlock
,
state_T
,
transported
)
## print(newinput)
tmpinp
<-
c
(
first
,
newinput
,
"RUN_CELLS"
,
paste0
(
"-cells 1-"
,
n
),
paste0
(
"SAVE solution 1-"
,
n
),
paste0
(
"SAVE exchange 1-"
,
n
),
paste0
(
"SAVE surface 1-"
,
n
),
"END"
)
phreeqc
::
phrRunString
(
tmpinp
)
state_C
<-
GetSelectedOutput
()
msg
(
"dim(state_C)="
,
paste
(
dim
(
state_C
),
collapse
=
", "
))
## print(c(newinput, "RUN_CELLS", paste("-cells 1-",n),"END"))
saved_complete
[[
index_saved
]]
<-
list
(
T
=
state_T
,
C
=
state_C
)
index_saved
<-
index_saved
+
1
if
(
ebreak
)
{
msg
(
"Early break invoked, bye."
)
return
(
saved_complete
[[
1
]])
}
simtime
<-
0
iter
<-
1
msg
(
"going through iterations now!"
)
on.exit
({
msg
(
"MAIN LOOP interrupted during iteration"
,
iter
,
", simulation time="
,
round
(
simtime
,
3
),
"!!"
)
msg
(
"returning last calculated chemistry."
)
print
(
traceback
())
msg
(
" Bye."
)
saved_complete
$
currentT
<-
state_T
saved_complete
$
currentC
<-
state_C
saved_complete
$
curinput
<-
c
(
newinput
,
"RUN_CELLS"
,
paste0
(
"-cells 1-"
,
n
),
"END"
)
detach
(
setup
)
return
(
saved_complete
)
})
## start of the iterations
while
(
iter
<
maxiter
)
{
## iterations
## TODO: It seems to me that iteration numberins is off by
## one, so that the first run on Rphree is actually one and
## the next ones end
simtime
<-
simtime
+
dt
iter
<-
iter
+
1
msg
(
"starting iteration"
,
iter
,
"/"
,
maxiter
,
" - to reach simulation time "
,
SimTime
(
simtime
))
## transform the pH/pe back into activities
state_T
<-
DiffusionScheme
(
state_C
,
inflow
=
bound
,
dx
=
dx
,
dt
=
dt
,
alpha
=
alpha
,
scheme
=
scheme
,
transported
=
transported
)
## new input
newinput
<-
SolutionModify
(
modBlock
,
state_T
,
transported
)
tmpinp
<-
c
(
newinput
,
"RUN_CELLS"
,
paste0
(
"-cells 1-"
,
n
),
paste0
(
"SAVE solution 1-"
,
n
),
paste0
(
"SAVE exchange 1-"
,
n
),
paste0
(
"SAVE surface 1-"
,
n
),
"END"
)
phreeqc
::
phrRunString
(
tmpinp
)
## tmpout <- phreeqc::phrGetOutputStrings()
state_C
<-
GetSelectedOutput
()
msg
(
"done iteration"
,
iter
,
"/"
,
maxiter
)
saved_complete
[[
index_saved
]]
<-
list
(
T
=
state_T
,
C
=
state_C
)
index_saved
<-
index_saved
+
1
}
on.exit
()
msg
(
" detaching setup..."
)
detach
(
setup
)
msg
(
" Bye."
)
return
(
saved_complete
)
}
##' @title Add a logical block in a PHREEQC input script
##' @param input the input script to be amended
##' @param block a block template. First word on the first line is
##' interpreted as keyword
##' @param before character, defaults to "END". Keyword before which
##' the block will be inserted
##' @return modified input with the added block
##' @author MDL
##' @export
AddBlock
<-
function
(
input
,
block
,
before
=
"END"
)
{
indsol
<-
grep
(
"^SOLUTION "
,
input
)
indend
<-
grep
(
"^END"
,
input
)
indbefore
<-
grep
(
before
,
input
)
if
(
length
(
indsol
)
!=
length
(
indbefore
))
{
stop
(
"Len before != len solution"
)
}
firstkey
<-
unlist
(
strsplit
(
block
[
1
],
" "
))[
1
]
msg
(
"Adding "
,
length
(
indsol
),
"blocks with keyword "
,
firstkey
)
## dimension of new input
newinp
<-
character
(
length
(
input
)
+
length
(
indsol
)
*
length
(
block
))
## vectorized index magic
offset
<-
c
(
0
,
cumsum
(
rep
(
length
(
block
),
length
(
indbefore
)
-1
)))
copy
<-
mapply
(
seq
,
indbefore
+
offset
,
indbefore
+
offset
+
length
(
block
)
-1
)
dim
(
copy
)
<-
NULL
newinp
[
-
copy
]
<-
input
newinp
[
copy
]
<-
rep
(
block
,
length
(
indsol
))
## correct numbering of new block
indkey
<-
grep
(
firstkey
,
newinp
)
newinp
[
indkey
]
<-
paste
(
firstkey
,
seq
(
1
,
length
(
indsol
)),
sep
=
" "
)
return
(
newinp
)
}
##' @title Form a "SOLUTION_MODIFY" input
##' @param block the template "SOLUTION_MODIFY" block
##' @param state named matrix with current concentrations
##' @param prop character vector. Names of properties to Distribute()
##' within the SOLUTION_MODIFY block
##' @return modified input
##' @author MDL
##' @export
SolutionModify
<-
function
(
block
,
state
,
prop
)
{
n
<-
nrow
(
state
)
## msg("N: ",n)
tmp
<-
rep
(
block
,
n
)
new
<-
Distribute
(
tmp
,
"-total_o"
,
state
[,
"total_o"
])
new
<-
Distribute
(
new
,
"-total_h"
,
state
[,
"total_h"
])
new
<-
Distribute
(
new
,
"-cb"
,
state
[,
"cb"
])
for
(
i
in
prop
)
new
<-
Distribute
(
new
,
i
,
state
[,
i
])
## Fix numbering of SOLUTION_MODIFY
indkey
<-
grep
(
"SOLUTION_MODIFY"
,
new
)
new
[
indkey
]
<-
paste
(
"SOLUTION_MODIFY"
,
rownames
(
state
),
sep
=
" "
)
return
(
new
)
}
##' This function takes the current state of a chemical system in form
##' of a matrix, a vector of concentrations representing boundary
##' conditions (injection), the required time step (dt/s), grid
##' spacing (dx/m) and alpha (diffusion coefficient in m^2/s).
##'
##' @title Diffusion interface to BTCS or FTCS scheme
##' @param conc either a vector or a matrix containing the chemical
##' state to be updated
##' @param inflow vector of fixed concentrations at the inlet
##' @param dx the grid spacing in m
##' @param dt the required time step in s
##' @param alpha diffusion coefficient in m^2/s (constant,
##' homogeneous)
##' @param scheme
##' @param transported
##' @return a matrix containing the state after the advection
##' @author MDL
##' @export
DiffusionScheme
<-
function
(
conc
,
inflow
,
dx
,
dt
,
alpha
,
scheme
,
transported
)
{
totrans
<-
c
(
transported
,
"total_o"
,
"total_h"
,
"cb"
)
if
(
is.matrix
(
conc
)
|
is.data.frame
(
conc
))
{
## cnew is the state, without prepended boundary conditions
cnew
<-
data.matrix
(
conc
)
if
(
scheme
==
"FTCS"
)
{
for
(
sp
in
totrans
)
{
tmp
<-
as.numeric
(
c
(
inflow
[
sp
],
cnew
[,
sp
]))
cnew
[,
sp
]
<-
FTCS
(
tmp
,
dx
,
dt
,
alpha
)
}
}
else
{
for
(
sp
in
totrans
)
{
## msg("Diffusing names(inflow[sp])", names(inflow[sp]))
tmp
<-
as.numeric
(
c
(
inflow
[
sp
],
cnew
[,
sp
]))
cnew
[,
sp
]
<-
BTCS
(
tmp
,
dx
,
dt
,
alpha
)
}
}
if
(
typeof
(
cnew
)
!=
"double"
)
{
##mode(res) <- "double"
msg
(
"cnew is not 'double'"
)
}
return
(
cnew
)
}
else
{
cj
<-
c
(
inflow
,
conc
)
if
(
scheme
==
"FTCS"
)
{
cnew
<-
FTCS
(
cj
,
dx
,
dt
,
alpha
)
}
else
{
cnew
<-
BTCS
(
cj
,
dx
,
dt
,
alpha
)
}
if
(
typeof
(
cnew
)
!=
"double"
)
{
msg
(
"cnew is not double"
)
}
return
(
cnew
)
}
}
##' @title Call \code{RcppBTCS::BTCS1D()}
##' @param field NumericVector of length n+1 with Dirichlet boundary
##' conditions prepended
##' @param dx the grid spacing in m
##' @param dt the required time step in s
##' @param alpha diffusion coefficient in m^2/s (constant,
##' homogeneous)
##' @return updated concentration vector
##' @author MDL
##' @export
BTCS
<-
function
(
field
,
dx
,
dt
,
alpha
)
{
require
(
RcppBTCS
)
n
<-
length
(
field
)
-1
RcppBTCS
::
BTCS1D
(
count_x
=
n
,
grid_size_x
=
dx
*
n
,
field
=
field
[
-1
],
alpha
=
alpha
,
dt
=
dt
,
dt_divide
=
1
,
bc_left
=
field
[
1
],
bc_right
=
-1
)
}
##' @title Call \code{RcppBTCS::RcppFTCS()}
##' @param field NumericVector of length n+1 with Dirichlet boundary
##' conditions prepended
##' @param dx the grid spacing in m
##' @param dt the required time step in s
##' @param alpha diffusion coefficient in m^2/s (constant,
##' homogeneous)
##' @return updated concentration vector
##' @author MDL
##' @export
FTCS
<-
function
(
field
,
dx
,
dt
,
alpha
)
{
require
(
RcppBTCS
)
RcppBTCS
::
RcppFTCS
(
n
=
length
(
field
)
-1
,
length
=
1
,
field
=
field
[
-1
],
alpha
=
alpha
,
bc_left
=
field
[
1
],
timestep
=
dt
)
}
##' @title Run a PHREEQC input and retrieve the DUMP
##' @param input the input script
##' @return the output of \code{phreeqc::phrGetDumpStrings()}
##' @author MDL
##' @export
PQCRunAndDump
<-
function
(
input
)
{
if
(
!
is.list
(
input
))
{
if
(
is.character
(
input
))
{
out
<-
phreeqc
::
phrSetDumpStringsOn
(
TRUE
)
phreeqc
::
phrRunString
(
input
)
out
<-
phreeqc
::
phrGetDumpStrings
()
}
else
{
stopmsg
(
"something wrong with the input, dying!"
)
}
}
return
(
out
)
}
##' @title Nice formatting of time in s
##' @param ts current time in s
##' @return a string with time converted in days or year as
##' appropriated
##' @author MDL
##' @export
SimTime
<-
function
(
ts
)
{
if
(
ts
<=
3600
*
24
)
return
(
paste
(
ts
,
" [s]"
))
else
if
(
ts
>
3600
*
24
&&
ts
<=
3600
*
24
*
365.25
)
return
(
paste
(
round
(
ts
/
3600
/
24
,
2
),
" [d]"
))
else
return
(
paste
(
round
(
ts
/
3600
/
24
/
365.25
,
2
),
" [y]"
))
}
Loading