Skip to content

Commit 39a4074

Browse files
- updated marine diffusion approach for global shallow seas
1 parent 45ca3ff commit 39a4074

File tree

6 files changed

+202
-21
lines changed

6 files changed

+202
-21
lines changed

docs/api_ref/sea_ref.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Class SEAMesh
2323

2424
.. autosummary::
2525

26+
~SEAMesh._depMarineSystem
2627
~SEAMesh._diffuseOcean
2728
~SEAMesh._distanceCoasts
2829
~SEAMesh._distOcean
@@ -40,6 +41,7 @@ Public functions
4041
Private functions
4142
---------------------
4243

44+
.. automethod:: sed.seaplex.SEAMesh._depMarineSystem
4345
.. automethod:: sed.seaplex.SEAMesh._diffuseOcean
4446
.. automethod:: sed.seaplex.SEAMesh._distanceCoasts
4547
.. automethod:: sed.seaplex.SEAMesh._distOcean

docs/tech_guide/ero.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,13 @@ The upstream incoming sediment flux is obtained from the total sediment flux :ma
208208

209209
.. math::
210210
211-
\mathrm{Q_{t_i}^{t+\Delta t} - \sum_{ups} w_{i,j} Q_{t_u}^{t+\Delta t}}= \mathrm{(\eta_i^{t} - \eta_i^{t+\Delta t}) \frac{\Delta t}{\Omega_i}}
211+
\mathrm{Q_{t_i}^{t+\Delta t} - \sum_{ups} w_{i,j} Q_{t_u}^{t+\Delta t}}= \mathrm{(\eta_i^{t} - \eta_i^{t+\Delta t}) \frac{\Omega_i}{\Delta t}}
212212
213213
which gives:
214214

215215
.. math::
216216
217-
\mathrm{Q_{s_i}} = \mathrm{Q_{t_i}} - \mathrm{(\eta_i^{t} - \eta_i^{t+\Delta t}) \frac{\Delta t}{\Omega_i}}
217+
\mathrm{Q_{s_i}} = \mathrm{Q_{t_i}} - \mathrm{(\eta_i^{t} - \eta_i^{t+\Delta t}) \frac{\Omega_i}{\Delta t}}
218218
219219
This system of coupled equations is solved implicitly using PETSc by assembling the matrix and vectors using the nested submatrix and subvectors and by using the ``fieldsplit`` preconditioner combining two separate preconditioners for the collections of variables.
220220

fortran/functions.F90

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,8 +1589,13 @@ end subroutine quicksort
15891589
dist(k,p) = dst(p)
15901590
val = val + slp(p)
15911591
enddo
1592+
! For marine deposition we don't scale the flow direction distribution
1593+
! based on slope, rather eberything downstream will get an equal proportion
15921594
do p = 1, ngbs
1593-
wgt(k,p) = slp(p) / val
1595+
! wgt(k,p) = slp(p) / val
1596+
if(slp(p) > 0.)then
1597+
wgt(k,p) = 1. / kk
1598+
endif
15941599
enddo
15951600
else
15961601
rcv(k,1:ngbs) = k-1
@@ -1605,8 +1610,13 @@ end subroutine quicksort
16051610
dist(k,n) = dst(p)
16061611
val = val + slp(p)
16071612
enddo
1613+
! For marine deposition we don't scale the flow direction distribution
1614+
! based on slope, rather eberything downstream will get an equal proportion
16081615
do p = 1, ngbs
1609-
wgt(k,p) = slope(p)/val
1616+
! wgt(k,p) = slope(p)/val
1617+
if(slp(p) > 0.)then
1618+
wgt(k,p) = 1. / kk
1619+
endif
16101620
enddo
16111621
endif
16121622
enddo

gospl/sed/seaplex.py

Lines changed: 162 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from vtk.util import numpy_support # type: ignore
1313

1414
if "READTHEDOCS" not in os.environ:
15+
from gospl._fortran import mfdreceivers
1516
from gospl._fortran import donorslist
1617
from gospl._fortran import donorsmax
1718
from gospl._fortran import mfdrcvrs
@@ -150,26 +151,37 @@ def _matOcean(self):
150151

151152
# Define multiple flow directions for filled + eps elevations
152153
hl = self.hLocal.getArray().copy()
154+
if not self.flatModel:
155+
# Only consider filleps in the first kms offshore
156+
hsmth = self._hillSlope(smooth=2)
157+
hsmth[self.coastDist > self.offshore] = -1.e6
158+
else:
159+
hsmth = hl.copy()
160+
153161
fillz = np.zeros(self.mpoints, dtype=np.float64) - 1.0e8
154-
fillz[self.locIDs] = hl
162+
fillz[self.locIDs] = hsmth
155163
MPI.COMM_WORLD.Allreduce(MPI.IN_PLACE, fillz, op=MPI.MAX)
156164
if MPIrank == 0:
157165
minh = np.min(fillz) + 0.1
158166
if not self.flatModel:
159-
minh = min(minh, -3.e3)
167+
minh = min(minh, self.oFill)
160168
fillz = epsfill(minh, fillz)
161-
162169
# Send elevation + eps globally
163170
fillEPS = MPI.COMM_WORLD.bcast(fillz, root=0)
164-
rcv, _, wght = mfdrcvrs(12, self.flowExp, fillEPS[self.locIDs], -1.0e6)
171+
fillz = fillEPS[self.locIDs]
172+
if not self.flatModel:
173+
fillz[self.coastDist > self.offshore] = hl[self.coastDist > self.offshore]
174+
rcv, _, wght = mfdrcvrs(12, self.flowExp, fillz, -1.0e6)
165175

166176
# Set borders nodes
167177
if self.flatModel:
168178
rcv[self.idBorders, :] = np.tile(self.idBorders, (12, 1)).T
169179
wght[self.idBorders, :] = 0.0
170180

171181
# Define downstream matrix based on filled + dir elevations
172-
self.dMat = self.zMat.copy()
182+
self.dMat1 = self.zMat.copy()
183+
if not self.flatModel and self.Gmar > 0.:
184+
self.dMat2 = self.iMat.copy()
173185
indptr = np.arange(0, self.lpoints + 1, dtype=petsc4py.PETSc.IntType)
174186
nodes = indptr[:-1]
175187

@@ -186,7 +198,9 @@ def _matOcean(self):
186198
)
187199
tmpMat.assemblyEnd()
188200
# Add the weights from each direction
189-
self.dMat.axpy(1.0, tmpMat)
201+
self.dMat1.axpy(1.0, tmpMat)
202+
if not self.flatModel and self.Gmar > 0.:
203+
self.dMat2.axpy(-1.0, tmpMat)
190204
tmpMat.destroy()
191205

192206
if self.memclear:
@@ -195,7 +209,9 @@ def _matOcean(self):
195209
gc.collect()
196210

197211
# Store flow direction matrix
198-
self.dMat.transpose()
212+
self.dMat1.transpose()
213+
if not self.flatModel and self.Gmar > 0.:
214+
self.dMat2.transpose()
199215

200216
return
201217

@@ -333,7 +349,6 @@ def _diffuseOcean(self, dh):
333349
ts.setMaxTime(self.dt)
334350
ts.setMaxSteps(self.tsStep)
335351
ts.setExactFinalTime(petsc4py.PETSc.TS.ExactFinalTime.MATCHSTEP)
336-
# ts.setExactFinalTime(petsc4py.PETSc.TS.ExactFinalTime.INTERPOLATE)
337352

338353
# Allow an unlimited number of failures
339354
ts.setMaxSNESFailures(-1) # (step will be rejected and retried)
@@ -393,14 +408,145 @@ def _diffuseOcean(self, dh):
393408

394409
return
395410

411+
def _depMarineSystem(self, sedflux):
412+
r"""
413+
Setup matrix for the marine sediment deposition.
414+
415+
The upstream incoming sediment flux is obtained from the total river sediment flux :math:`\mathrm{Q_{t_i}}` where:
416+
417+
.. math::
418+
419+
\mathrm{Q_{t_i}^{t+\Delta t} - \sum_{ups} w_{i,j} Q_{t_u}^{t+\Delta t}}= \mathrm{(\eta_i^{t} - \eta_i^{t+\Delta t}) \frac{\Delta t}{\Omega_i}}
420+
421+
which gives:
422+
423+
.. math::
424+
425+
\mathrm{Q_{s_i}} = \mathrm{Q_{t_i}} - \mathrm{(\eta_i^{t} - \eta_i^{t+\Delta t}) \frac{\Delta t}{\Omega_i}}
426+
427+
And the evolution of marine elevation is based on incoming sediment flux resulting.
428+
429+
.. math::
430+
431+
\mathrm{\frac{\eta_i^{t+\Delta t}-\eta_i^t}{\Delta t}} = \mathrm{G{_m} Q_{s_i} / \Omega_i}
432+
433+
This system of coupled equations is solved implicitly using PETSc by assembling the matrix and vectors using the nested submatrix and subvectors and by using the ``fieldsplit`` preconditioner combining two separate preconditioners for the collections of variables.
434+
435+
:arg sedflux: incoming marine sediment volumes
436+
437+
:return: volDep (the deposited volume of the distributed sediments)
438+
"""
439+
440+
hl = self.hLocal.getArray()
441+
fDepm = np.full(self.lpoints, self.Gmar)
442+
fDepm[fDepm > 0.99] = 0.99
443+
fDepm[hl > self.sealevel] = 0.
444+
445+
# Define submatrices
446+
A00 = self._matrix_build_diag(-fDepm)
447+
A00.axpy(1.0, self.iMat)
448+
A01 = self._matrix_build_diag(-fDepm * self.dt / self.larea)
449+
A10 = self._matrix_build_diag(self.larea / self.dt)
450+
451+
# Assemble the matrix for the coupled system
452+
mats = [[A00, A01], [A10, self.dMat2]]
453+
sysMat = petsc4py.PETSc.Mat().createNest(mats=mats, comm=MPIcomm)
454+
sysMat.assemblyBegin()
455+
sysMat.assemblyEnd()
456+
457+
# Clean up
458+
A00.destroy()
459+
A01.destroy()
460+
A10.destroy()
461+
self.dMat2.destroy()
462+
mats[0][0].destroy()
463+
mats[0][1].destroy()
464+
mats[1][0].destroy()
465+
mats[1][1].destroy()
466+
467+
# Create nested vectors
468+
self.tmpL.setArray(1. - fDepm)
469+
self.dm.localToGlobal(self.tmpL, self.tmp)
470+
self.tmp.pointwiseMult(self.tmp, self.hGlobal)
471+
472+
self.tmpL.setArray(sedflux / self.dt)
473+
self.dm.localToGlobal(self.tmpL, self.tmp1)
474+
self.h.pointwiseMult(self.hGlobal, self.areaGlobal)
475+
self.h.scale(1. / self.dt)
476+
self.tmp1.axpy(1., self.h)
477+
478+
rhs_vec = petsc4py.PETSc.Vec().createNest([self.tmp, self.tmp1], comm=MPIcomm)
479+
rhs_vec.setUp()
480+
hq_vec = rhs_vec.duplicate()
481+
482+
# Define solver and precondition conditions
483+
ksp = petsc4py.PETSc.KSP().create(petsc4py.PETSc.COMM_WORLD)
484+
ksp.setType(petsc4py.PETSc.KSP.Type.TFQMR)
485+
ksp.setOperators(sysMat)
486+
ksp.setTolerances(rtol=self.rtol)
487+
488+
pc = ksp.getPC()
489+
pc.setType("fieldsplit")
490+
nested_IS = sysMat.getNestISs()
491+
pc.setFieldSplitIS(('h', nested_IS[0][0]), ('q', nested_IS[0][1]))
492+
493+
subksps = pc.getFieldSplitSubKSP()
494+
subksps[0].setType("preonly")
495+
subksps[0].getPC().setType("asm")
496+
subksps[1].setType("preonly")
497+
subksps[1].getPC().setType("bjacobi")
498+
499+
ksp.solve(rhs_vec, hq_vec)
500+
r = ksp.getConvergedReason()
501+
if r < 0:
502+
KSPReasons = self._make_reasons(petsc4py.PETSc.KSP.ConvergedReason())
503+
if MPIrank == 0:
504+
print(
505+
"Linear solver for marine deposition failed to converge after iterations",
506+
ksp.getIterationNumber(),
507+
flush=True,
508+
)
509+
print("with reason: ", KSPReasons[r], flush=True)
510+
else:
511+
if MPIrank == 0 and self.verbose:
512+
print(
513+
"Linear solver for marine deposition converge after %d iterations"
514+
% ksp.getIterationNumber(),
515+
flush=True,
516+
)
517+
518+
# Update the solution
519+
self.newH = hq_vec.getSubVector(nested_IS[0][0])
520+
521+
# Clean up
522+
subksps[0].destroy()
523+
subksps[1].destroy()
524+
nested_IS[0][0].destroy()
525+
nested_IS[1][0].destroy()
526+
nested_IS[0][1].destroy()
527+
nested_IS[1][1].destroy()
528+
pc.destroy()
529+
ksp.destroy()
530+
sysMat.destroy()
531+
hq_vec.destroy()
532+
rhs_vec.destroy()
533+
534+
# Get the marine deposition volume
535+
self.tmp.waxpy(-1.0, self.hGlobal, self.newH)
536+
self.dm.globalToLocal(self.tmp, self.tmpL)
537+
volDep = self.tmpL.getArray().copy() * self.larea
538+
volDep[volDep < 0] = 0.
539+
540+
return volDep
541+
396542
def _distOcean(self, sedflux):
397543
"""
398544
Based on the incoming marine volumes of sediment and maximum clinoforms slope we distribute
399545
locally sediments downslope.
400546
401547
:arg sedflux: incoming marine sediment volumes
402548
403-
:return: seddep (the deposied volume of the distributed sediments)
549+
:return: vdep (the deposited volume of the distributed sediments)
404550
"""
405551

406552
marVol = self.maxDepQs.copy()
@@ -413,7 +559,7 @@ def _distOcean(self, sedflux):
413559
while self.tmp.sum() > 1.0:
414560

415561
# Move to downstream nodes
416-
self.dMat.mult(self.tmp, self.tmp1)
562+
self.dMat1.mult(self.tmp, self.tmp1)
417563
self.dm.globalToLocal(self.tmp1, self.tmpL)
418564

419565
# In case there is too much sediment coming in
@@ -445,7 +591,6 @@ def _distOcean(self, sedflux):
445591
)
446592

447593
step += 1
448-
self.dMat.destroy()
449594

450595
if self.memclear:
451596
del marVol, sinkVol
@@ -491,6 +636,12 @@ def seaChange(self):
491636
# Downstream direction matrix for ocean distribution
492637
self._matOcean()
493638
marDep = self._distOcean(sedFlux)
639+
if not self.flatModel and self.Gmar > 0.:
640+
vdep = self._depMarineSystem(marDep)
641+
marDep = self._distOcean(vdep)
642+
self.dMat1.destroy()
643+
644+
# Diffuse downstream
494645
dh = np.divide(marDep, self.larea, out=np.zeros_like(self.larea), where=self.larea != 0)
495646
dh[dh < 1.e-3] = 0.
496647
self.tmpL.setArray(dh)

gospl/sed/sedplex.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ def getHillslope(self):
276276
# Compute Hillslope Diffusion Law
277277
h = self.hLocal.getArray().copy()
278278
self.seaID = np.where(h <= self.sealevel)[0]
279-
self._hillSlope()
279+
self._hillSlope(smooth=0)
280280

281281
# Update layer elevation
282282
if self.stratNb > 0:
@@ -309,19 +309,23 @@ def _hillSlope(self, smooth=0):
309309
.. note::
310310
The hillslope processes in `gospl` are considered to be happening at the same rate for coarse and fine sediment sizes.
311311
312-
:arg smooth: integer specifying if the diffusion equation is used for marine deposits (1) and ice flow (2).
312+
:arg smooth: integer specifying if the diffusion equation is used for ice flow (1) and marine deposits (2).
313313
"""
314314

315-
if not smooth:
315+
if smooth == 0:
316316
if self.Cda == 0.0 and self.Cdm == 0.0:
317317
return
318318

319319
t0 = process_time()
320-
321320
# Diffusion matrix construction
322321
if smooth == 1:
323322
Cd = np.full(self.lpoints, self.gaussIce, dtype=np.float64)
324323
Cd[~self.iceIDs] = 0.0
324+
elif smooth == 2:
325+
# Hard-coded coefficients here, used to generate a smooth surface
326+
# for computing marine flow directions...
327+
Cd = np.full(self.lpoints, 1.e5, dtype=np.float64)
328+
Cd[self.seaID] = 5.e6
325329
else:
326330
Cd = np.full(self.lpoints, self.Cda, dtype=np.float64)
327331
Cd[self.seaID] = self.Cdm
@@ -350,19 +354,23 @@ def _hillSlope(self, smooth=0):
350354
tmpMat.destroy()
351355

352356
# Get elevation values for considered time step
353-
if smooth > 0:
357+
if smooth == 1:
354358
if self.tmp1.max()[1] > 0:
355359
self._solve_KSP(True, diffMat, self.tmp1, self.tmp)
356360
else:
357361
self.tmp1.copy(result=self.tmp)
358362
diffMat.destroy()
359363
self.dm.globalToLocal(self.tmp, self.tmpL)
360364
return self.tmpL.getArray().copy()
365+
elif smooth == 2:
366+
self._solve_KSP(True, diffMat, self.hGlobal, self.tmp)
367+
diffMat.destroy()
368+
self.dm.globalToLocal(self.tmp, self.tmpL)
369+
return self.tmpL.getArray().copy()
361370
else:
362371
self.hGlobal.copy(result=self.hOld)
363372
self._solve_KSP(True, diffMat, self.hOld, self.hGlobal)
364373
diffMat.destroy()
365-
366374
# Update cumulative erosion/deposition and elevation
367375
self.tmp.waxpy(-1.0, self.hOld, self.hGlobal)
368376
self.cumED.axpy(1.0, self.tmp)

0 commit comments

Comments
 (0)