parflow.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. """This module provides ParaView equivalents to several ParFlow
  2. pftools algorithms; these are implemented using VTKPythonAlgorithmBase
  3. subclasses and demonstrate how to access both surface and subsurface
  4. computational grids within a single filter."""
  5. from paraview.util.vtkAlgorithm import *
  6. import numpy as np
  7. class pftools:
  8. """This class defines some utilities to perform computations similar
  9. to those implemented by ParFlow's pftools suite."""
  10. @staticmethod
  11. def dataPointExtent(dataset):
  12. """Return the parflow extents in (k,j,i) order of the *point*
  13. extents of this dataset, which is assumed to be structured.
  14. Note that VTK normally assumes data is (i,j,k) ordered."""
  15. ext = tuple(np.flip(dataset.GetDimensions()))
  16. return ext
  17. @staticmethod
  18. def dataCellExtent(dataset):
  19. """Return the parflow extents in (k,j,i) order of the *cell*
  20. extents of this dataset, which is assumed to be structured.
  21. Note that (1) VTK normally uses point counts along axes rather
  22. than cell counts and (2) assumes data is (i,j,k) ordered."""
  23. # Subtracting 1 from GetDimensions accounts for point-counts
  24. # vs cell-counts, while np.flip(...) switches the axis order:
  25. ext = tuple(np.flip(dataset.GetDimensions()) - 1)
  26. return ext
  27. @staticmethod
  28. def computeTopSurface(ext, mask):
  29. # I. Reshape the mask and create zk, a column of k-axis indices:
  30. mr = np.reshape(mask > 0, ext)
  31. zk = np.reshape(np.arange(ext[0]), (ext[0],1,1))
  32. # II. The top surface at each (i,j) is the location
  33. # of the highest zk-value that is not masked:
  34. top = np.argmax(mr * zk, axis=0)
  35. # III. Mark completely-masked columns of cells with an
  36. # invalid "top" index of -1:
  37. # Columns in mask that had no valid value for mr * zk result
  38. # in entries of "top" that are 0. But 0 is also a valid k index
  39. # so we mark those which should be marked out with -1:
  40. mr = (mr == False) # Invert the mask to catch bad cells
  41. top = top - np.all(mr, axis=0) # Subtract 1 from masked column entries.
  42. return top
  43. @staticmethod
  44. def computeTopSurfaceIndices(top):
  45. """Convert the "top" surface matrix into an array of indices that
  46. only include valid top-surface cells. Any negative entry of "top"
  47. does not have its index included.
  48. The result is a matrix; each row corresponds to a top-surface cell
  49. and each column holds a cell's k-, j-, and i-indices, respectively.
  50. """
  51. itop = np.array([(top[i,j], j, i) \
  52. for i in range(top.shape[0]) \
  53. for j in range(top.shape[1]) \
  54. if top[i,j] >= 0])
  55. return itop
  56. @staticmethod
  57. def computeSurfaceStorage(ext, itop, xx, pressure):
  58. # I. Compute the dx * dy area term using point coordinates from the mesh:
  59. xt = xx[itop[:,0], itop[:,1], itop[:,2]]
  60. dx = xx[itop[:,0], itop[:,1], itop[:,2] + 1] - xt
  61. dy = xx[itop[:,0], itop[:,1] + 1, itop[:,2]] - xt
  62. # Now dx and dy are matrices whose rows are vectors.
  63. # Compute the norms of the vectors.
  64. # dx = np.sqrt(np.sum(dx * dx,axis=1))
  65. # dy = np.sqrt(np.sum(dy * dy,axis=1))
  66. # To more closely match parflow:
  67. dx = dx[:,0]
  68. dy = dy[:,1]
  69. pp = np.reshape(pressure, ext)
  70. pt = pp[itop[:,0], itop[:,1], itop[:,2]]
  71. sus = pt * (pt > 0.0) * dx * dy
  72. return sus
  73. @staticmethod
  74. def computeSubsurfaceStorage(saturation, pressure, volume, porosity, specific_storage):
  75. """Compute the subsurface storage for the entire domain."""
  76. import numpy as np
  77. sbs = saturation * volume * (porosity + pressure * specific_storage)
  78. return sbs
  79. @staticmethod
  80. def computeGroundwaterStorage(saturation, pressure, volume, porosity, specific_storage):
  81. """Compute the groundwater storage.
  82. This is the same calculation as subsurface storage, but only
  83. sums values over cells with a saturation of 1.0.
  84. """
  85. import numpy as np
  86. sbs = (saturation == 1.0) * volume * (porosity + pressure * specific_storage)
  87. return sbs
  88. @staticmethod
  89. def computeSurfaceRunoff(top, xx, pressure, slope, mannings):
  90. """Compute surface runoff (water leaving the domain boundary
  91. or flowing into a masked area)"""
  92. def addToRunoff(runoff, sro, top, slope, pressure, mannings, delta):
  93. """Given a truthy 2-d array (idx) of places where runoff occurs,
  94. compute the runoff and add it to the total (sro).
  95. """
  96. # We would like to do this:
  97. # sro += \
  98. # runoff * np.sqrt(np.abs(slope)) / mannings * \
  99. # np.power(ptop, 5.0/3.0) * delta
  100. # ... but we can't since it might result in NaN values where
  101. # runoff is False. Instead, only compute the runoff exactly
  102. # at locations where runoff is true:
  103. ww = np.where(runoff)
  104. sro[ww] += \
  105. np.sqrt(np.abs(slope[ww])) / mannings[ww] * \
  106. np.power(ptop[ww], 5.0/3.0) * delta[ww]
  107. # Initialize runoff:
  108. sro = np.zeros(top.shape)
  109. # Prepare indices and tempororary variables:
  110. sz = np.prod(top.shape)
  111. ext = (int(np.prod(pressure.shape)/sz),) + top.shape
  112. tk = np.reshape(top, sz)
  113. tj = np.floor(np.arange(sz)/top.shape[1]).astype(int)
  114. ti = np.arange(sz) % top.shape[1]
  115. tt = np.zeros(top.shape) # temporary array
  116. # Subset of pressure at the top surface:
  117. ptop = np.reshape(np.reshape(pressure, ext)[tk, tj, ti], top.shape)
  118. # Determine size of top-surface cells along north-south direction:
  119. delta = np.reshape((xx[tk, tj + 1, ti] - xx[tk, tj, ti])[:,1], top.shape)
  120. # Fill the temporary array with data shifted south by 1:
  121. np.concatenate((top[1:,:], -np.ones((1,top.shape[1]))), axis=0, out=tt)
  122. # Compute conditions for flow exiting to the north:
  123. cnorth = (top >= 0) & (tt < 0) & (slope[:,:,1] < 0) & (ptop > 0)
  124. # Now add values to per-cell runoff sro:
  125. addToRunoff(cnorth, sro, top, slope[:,:,1], pressure, mannings, delta)
  126. # Fill the temporary array with data shifted north by 1:
  127. np.concatenate((-np.ones((1,top.shape[1])), top[0:-1,:]), axis=0, out=tt)
  128. # Compute conditions for flow exiting to the south:
  129. csouth = (top >= 0) & (tt < 0) & (slope[:,:,1] > 0) & (ptop > 0)
  130. # Now add values to per-cell runoff sro:
  131. addToRunoff(csouth, sro, top, slope[:,:,1], pressure, mannings, delta)
  132. # Determine size of top-surface cells along east-west direction:
  133. delta = np.reshape((xx[tk, tj, ti + 1] - xx[tk, tj, ti])[:,0], top.shape)
  134. # Fill the temporary array with data shifted east by 1:
  135. np.concatenate((top[:,1:], -np.ones((top.shape[0], 1))), axis=1, out=tt)
  136. # Compute conditions for flow exiting to the west:
  137. cwest = (top >= 0) & (tt < 0) & (slope[:,:,0] < 0) & (ptop > 0)
  138. # Now add values to per-cell runoff sro:
  139. addToRunoff(cwest, sro, top, slope[:,:,0], pressure, mannings, delta)
  140. # Fill the temporary array with data shifted north by 1:
  141. np.concatenate((-np.ones((top.shape[0], 1)), top[:,0:-1]), axis=1, out=tt)
  142. # Compute conditions for flow exiting to the south:
  143. ceast = (top >= 0) & (tt < 0) & (slope[:,:,0] > 0) & (ptop > 0)
  144. # Now add values to per-cell runoff sro:
  145. addToRunoff(ceast, sro, top, slope[:,:,0], pressure, mannings, delta)
  146. return sro
  147. @staticmethod
  148. def computeWaterTableDepth(top, saturation, xx):
  149. """Compute the depth to the water table.
  150. The returned array is the distance along the z axis between
  151. the water surface and ground level. It should always be
  152. negative (i.e., it is a displacement on the z axis starting
  153. at the surface).
  154. """
  155. sz = np.prod(top.shape)
  156. ext = (int(np.prod(saturation.shape)/sz),) + top.shape
  157. sr = np.reshape(saturation >= 1.0, ext)
  158. zk = np.reshape(np.arange(ext[0]), (ext[0],1,1))
  159. # The top surface at each (i,j) is the location
  160. # of the highest zk-value that is not masked:
  161. depthIdx = np.argmax(sr * zk, axis=0)
  162. tk = np.reshape(top, sz)
  163. dk = np.reshape(depthIdx, sz)
  164. dj = np.floor(np.arange(sz)/top.shape[1]).astype(int)
  165. di = np.arange(sz) % top.shape[1]
  166. depth = xx[dk, dj, di, 2] - xx[tk, dj, di, 2]
  167. # Mark locations where the domain is masked or
  168. # no water table appears as NaN (not-a-number):
  169. sr = (sr == False) # Invert the saturation mask
  170. ww = np.where(np.reshape(np.all(sr, axis=0), sz))
  171. depth[ww] = np.nan
  172. return depth
  173. class ParFlowAlgorithm(VTKPythonAlgorithmBase):
  174. """A base class for algorithms that operate on
  175. ParFlow simulation data.
  176. This class provides method implementations that
  177. simplify working with data from the vtkParFlowMetaReader.
  178. Specifically,
  179. 1. RequestUpdateExtent will ensure valid extents
  180. are passed to each of the filter's inputs based on
  181. their available whole extents. Without this, passing
  182. both the surface and subsurface meshes to a python
  183. filter would cause warnings as the default
  184. implementation simply mirrors the downstream request
  185. to each of its upstream filters.
  186. 2. RequestDataObject will create the same type of
  187. output as the input data objects so that either
  188. image data or structured grids (both of which may
  189. be output by the reader) can be passed through the
  190. filter.
  191. """
  192. def FillInputPortInformation(self, port, info):
  193. info.Set(self.INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet")
  194. return 1
  195. def RequestDataObject(self, request, inInfoVec, outInfoVec):
  196. """Create a data object of the same type as the input."""
  197. from vtkmodules.vtkCommonDataModel import vtkTable, vtkDataSet, vtkPolyData
  198. from vtkmodules import vtkCommonDataModel as dm
  199. input0 = vtkDataSet.GetData(inInfoVec[0], 0)
  200. opt = dm.vtkDataObject.GetData(outInfoVec)
  201. if opt and opt.IsA(input0.GetClassName()):
  202. return 1
  203. opt = input0.NewInstance()
  204. outInfoVec.GetInformationObject(0).Set(dm.vtkDataObject.DATA_OBJECT(), opt)
  205. return 1
  206. def RequestUpdateExtent(self, request, inInfoVec, outInfoVec):
  207. """Override the default algorithm for updating extents to handle the
  208. surface and subsurface models, which have different dimensionality.
  209. Take the requested (downstream) extent and intersect it with the
  210. available (upstream) extents; use that as the request rather than
  211. blindly copying the request upstream.
  212. """
  213. from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline as sddp
  214. # Determine the port requesting an update from us:
  215. reqPort = request.Get(sddp.FROM_OUTPUT_PORT())
  216. # Get that port's information and the extents it is requesting:
  217. outInfo = self.GetOutputInformation(reqPort)
  218. esrc = outInfo.Get(sddp.UPDATE_EXTENT())
  219. # Slice the extents into tuples holding the lo and hi indices:
  220. losrc = esrc[0:6:2]
  221. hisrc = esrc[1:6:2]
  222. np = self.GetNumberOfInputPorts()
  223. for port in range(np):
  224. # Loop over all the input connections and set their update extents:
  225. nc = self.GetNumberOfInputConnections(port)
  226. for connection in range(nc):
  227. inInfo = self.GetInputInformation(port, connection)
  228. # Set UPDATE_EXTENT to be the intersection of the input
  229. # port's WHOLE_EXTENT with the output port's UPDATE_EXTENT.
  230. #
  231. # NB/FIXME: Really this is incorrect. If reqPort is 1, then
  232. # someone is asking for an update of the 2-d surface grid
  233. # and they could conceivably need the entire 3-d subsurface
  234. # grid that overlaps the surface grid in x and y...
  235. etgt = inInfo.Get(sddp.WHOLE_EXTENT())
  236. lotgt = etgt[0:6:2]
  237. hitgt = etgt[1:6:2]
  238. lodst = [int(max(x)) for x in zip(losrc, lotgt)]
  239. hidst = [int(min(x)) for x in zip(hisrc, hitgt)]
  240. edst = [val for tup in zip(lodst, hidst) for val in tup]
  241. inInfo.Set(sddp.UPDATE_EXTENT(), tuple(edst), 6)
  242. return 1
  243. @smproxy.filter(label='Subsurface Storage')
  244. @smhint.xml("""
  245. <ShowInMenu category="ParFlow"/>
  246. <RepresentationType port="0" view="RenderView" type="Surface" />
  247. """)
  248. @smproperty.input(name="Subsurface", port_index=0)
  249. @smdomain.datatype(dataTypes=["vtkDataSet"], composite_data_supported=False)
  250. class SubsurfaceStorage(ParFlowAlgorithm):
  251. """This algorithm computes the subsurface storage across the entire domain.
  252. The result is added as field data and marked as time-varying so it
  253. can be plotted as a timeseries.
  254. """
  255. def __init__(self):
  256. VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, nOutputPorts=1, outputType="vtkDataSet")
  257. def RequestData(self, request, inInfoVec, outInfoVec):
  258. from vtkmodules.vtkCommonDataModel import vtkTable, vtkDataSet, vtkPolyData
  259. from vtkmodules.vtkIOExodus import vtkExodusIIReader as e2r
  260. from vtkmodules.vtkFiltersVerdict import vtkMeshQuality as mq
  261. from vtkmodules.util.numpy_support import vtk_to_numpy as vton
  262. from vtkmodules.util.numpy_support import numpy_to_vtk as ntov
  263. import numpy as np
  264. ## Fetch the filter inputs and outputs:
  265. input0 = vtkDataSet.GetData(inInfoVec[0], 0)
  266. output = vtkDataSet.GetData(outInfoVec, 0)
  267. output.ShallowCopy(input0)
  268. ## Fetch input arrays
  269. cd = output.GetCellData()
  270. vmask = cd.GetArray('mask')
  271. vsaturation = cd.GetArray('saturation')
  272. vporosity = cd.GetArray('porosity')
  273. vpressure = cd.GetArray('pressure')
  274. vspecstor = cd.GetArray('specific storage')
  275. if vmask == None or vsaturation == None or \
  276. vporosity == None or vpressure == None or \
  277. vspecstor == None:
  278. print('Error: A required array was not present.')
  279. return 0
  280. ## Compute the volume of each cell:
  281. mqf = mq()
  282. mqf.SetHexQualityMeasureToVolume()
  283. mqf.SetInputDataObject(0, output)
  284. mqf.Update()
  285. volume = vton(mqf.GetOutputDataObject(0).GetCellData().GetArray('Quality'))
  286. ## Get NumPy versions of each array so we can do arithmetic:
  287. mask = vton(vmask)
  288. saturation = vton(vsaturation)
  289. porosity = vton(vporosity)
  290. pressure = vton(vpressure)
  291. specstor = vton(vspecstor)
  292. ## Compute the subsurface storage as sbs
  293. ## and store it as field data.
  294. sbs = np.sum(pftools.computeSubsurfaceStorage( \
  295. saturation, pressure, volume, porosity, specstor))
  296. arr = ntov(sbs)
  297. arr.SetName('subsurface storage')
  298. arr.GetInformation().Set(e2r.GLOBAL_TEMPORAL_VARIABLE(), 1)
  299. output.GetFieldData().AddArray(arr)
  300. return 1
  301. @smproxy.filter(label='Water Table Depth')
  302. @smhint.xml("""
  303. <ShowInMenu category="ParFlow"/>
  304. <RepresentationType port="0" view="RenderView" type="Surface" />
  305. """)
  306. @smproperty.input(name="Surface", port_index=1)
  307. @smdomain.datatype(dataTypes=["vtkDataSet"], composite_data_supported=False)
  308. @smproperty.input(name="Subsurface", port_index=0)
  309. @smdomain.datatype(dataTypes=["vtkDataSet"], composite_data_supported=False)
  310. class WaterTableDepth(ParFlowAlgorithm):
  311. """This algorithm computes the depth to the water table the domain surface.
  312. """
  313. def __init__(self):
  314. VTKPythonAlgorithmBase.__init__(self, nInputPorts=2, nOutputPorts=1, outputType="vtkDataSet")
  315. def RequestInformation(self, request, inInfoVec, outInfoVec):
  316. """Tell ParaView our output extents match the surface, not the subsurface."""
  317. from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline as sddp
  318. iin = inInfoVec[1].GetInformationObject(0)
  319. outInfoVec.GetInformationObject(0).Set(sddp.WHOLE_EXTENT(), iin.Get(sddp.WHOLE_EXTENT()), 6);
  320. return 1
  321. def RequestData(self, request, inInfoVec, outInfoVec):
  322. from vtkmodules.vtkCommonDataModel import vtkTable, vtkDataSet, vtkPolyData
  323. from vtkmodules.vtkIOExodus import vtkExodusIIReader as e2r
  324. from vtkmodules.vtkFiltersVerdict import vtkMeshQuality as mq
  325. from vtkmodules.util.numpy_support import vtk_to_numpy as vton
  326. from vtkmodules.util.numpy_support import numpy_to_vtk as ntov
  327. import numpy as np
  328. ## Fetch the filter inputs and outputs:
  329. input0 = vtkDataSet.GetData(inInfoVec[0], 0)
  330. input1 = vtkDataSet.GetData(inInfoVec[1], 0)
  331. output = vtkDataSet.GetData(outInfoVec, 0)
  332. output.ShallowCopy(input1)
  333. ## Fetch input arrays
  334. cd = output.GetCellData()
  335. scd = input0.GetCellData()
  336. vmask = scd.GetArray('mask')
  337. vsaturation = scd.GetArray('saturation')
  338. if vmask == None or vsaturation == None:
  339. print('Error: A required array was not present.')
  340. return 0
  341. ## Get NumPy versions of each array so we can do arithmetic:
  342. mask = vton(vmask)
  343. saturation = vton(vsaturation)
  344. ## Find the top surface
  345. ext = pftools.dataCellExtent(input0)
  346. top = pftools.computeTopSurface(ext, mask)
  347. ## Compute the water table depth storage as wtd
  348. ## and store it as cell data.
  349. ps = pftools.dataPointExtent(input0) + (3,)
  350. xx = np.reshape(vton(input0.GetPoints().GetData()), ps)
  351. wtd = pftools.computeWaterTableDepth(top, saturation, xx)
  352. arr = ntov(wtd)
  353. arr.SetName('water table depth')
  354. output.GetCellData().AddArray(arr)
  355. return 1
  356. @smproxy.filter(label='Water Balance')
  357. @smhint.xml("""
  358. <ShowInMenu category="ParFlow"/>
  359. <RepresentationType port="0" view="RenderView" type="Surface" />
  360. """)
  361. @smproperty.input(name="Surface", port_index=1)
  362. @smdomain.datatype(dataTypes=["vtkDataSet"], composite_data_supported=False)
  363. @smproperty.input(name="Subsurface", port_index=0)
  364. @smdomain.datatype(dataTypes=["vtkDataSet"], composite_data_supported=False)
  365. class WaterBalance(ParFlowAlgorithm):
  366. """This algorithm computes the subsurface storage across the entire domain.
  367. The result is added as field data and marked as time-varying so it
  368. can be plotted as a timeseries.
  369. """
  370. def __init__(self):
  371. VTKPythonAlgorithmBase.__init__(self, nInputPorts=2, nOutputPorts=1, outputType="vtkDataSet")
  372. def RequestData(self, request, inInfoVec, outInfoVec):
  373. from vtkmodules.vtkCommonDataModel import vtkTable, vtkDataSet, vtkPolyData
  374. from vtkmodules.vtkIOExodus import vtkExodusIIReader as e2r
  375. from vtkmodules.vtkFiltersVerdict import vtkMeshQuality as mq
  376. from vtkmodules.util.numpy_support import vtk_to_numpy as vton
  377. from vtkmodules.util.numpy_support import numpy_to_vtk as ntov
  378. import numpy as np
  379. ## Fetch the filter inputs and outputs:
  380. input0 = vtkDataSet.GetData(inInfoVec[0], 0)
  381. input1 = vtkDataSet.GetData(inInfoVec[1], 0)
  382. output = vtkDataSet.GetData(outInfoVec, 0)
  383. output.ShallowCopy(input0)
  384. ## Fetch input arrays
  385. cd = output.GetCellData()
  386. scd = input1.GetCellData()
  387. vmask = cd.GetArray('mask')
  388. vsaturation = cd.GetArray('saturation')
  389. vporosity = cd.GetArray('porosity')
  390. vpressure = cd.GetArray('pressure')
  391. vspecstor = cd.GetArray('specific storage')
  392. vslope = scd.GetArray('slope')
  393. vmannings = scd.GetArray('mannings')
  394. if vmask == None or vsaturation == None or \
  395. vporosity == None or vpressure == None or \
  396. vspecstor == None or vslope == None or \
  397. vmannings == None:
  398. print('Error: A required array was not present.')
  399. return 0
  400. ## Compute the volume of each cell:
  401. mqf = mq()
  402. mqf.SetHexQualityMeasureToVolume()
  403. mqf.SetInputDataObject(0, output)
  404. mqf.Update()
  405. volume = vton(mqf.GetOutputDataObject(0).GetCellData().GetArray('Quality'))
  406. ## Get NumPy versions of each array so we can do arithmetic:
  407. mask = vton(vmask)
  408. saturation = vton(vsaturation)
  409. porosity = vton(vporosity)
  410. pressure = vton(vpressure)
  411. specstor = vton(vspecstor)
  412. slope = vton(vslope)
  413. mannings = vton(vmannings)
  414. ## Compute the subsurface storage as sbs
  415. ## and store it as field data.
  416. sbs = np.sum(pftools.computeSubsurfaceStorage( \
  417. saturation, pressure, volume, porosity, specstor))
  418. arr = ntov(sbs)
  419. arr.SetName('subsurface storage')
  420. arr.GetInformation().Set(e2r.GLOBAL_TEMPORAL_VARIABLE(), 1)
  421. output.GetFieldData().AddArray(arr)
  422. ## Find the top surface
  423. ext = pftools.dataCellExtent(input0)
  424. top = pftools.computeTopSurface(ext, mask)
  425. itop = pftools.computeTopSurfaceIndices(top)
  426. ## Compute the surface storage
  427. ps = pftools.dataPointExtent(input0) + (3,)
  428. xx = np.reshape(vton(output.GetPoints().GetData()), ps)
  429. sus = np.sum(pftools.computeSurfaceStorage(ext, itop, xx, pressure))
  430. arr = ntov(sus)
  431. arr.SetName('surface storage')
  432. arr.GetInformation().Set(e2r.GLOBAL_TEMPORAL_VARIABLE(), 1)
  433. output.GetFieldData().AddArray(arr)
  434. ## Compute the surface runoff
  435. slope = np.reshape(slope, top.shape + (2,))
  436. mannings = np.reshape(mannings, top.shape)
  437. sro = np.sum(pftools.computeSurfaceRunoff(top, xx, pressure, slope, mannings))
  438. arr = ntov(sro)
  439. arr.SetName('surface runoff')
  440. arr.GetInformation().Set(e2r.GLOBAL_TEMPORAL_VARIABLE(), 1)
  441. output.GetFieldData().AddArray(arr)
  442. return 1
  443. if __name__ == "__main__":
  444. from paraview.detail.pythonalgorithm import get_plugin_xmls
  445. from xml.dom.minidom import parseString
  446. for xml in get_plugin_xmls(globals()):
  447. dom = parseString(xml)
  448. print(dom.toprettyxml(" ","\n"))