Skip to content

Instantly share code, notes, and snippets.

@pwolfram
Last active July 10, 2019 22:22
Show Gist options
  • Save pwolfram/d963664449fe02f511099dc272c47d1a to your computer and use it in GitHub Desktop.
Save pwolfram/d963664449fe02f511099dc272c47d1a to your computer and use it in GitHub Desktop.
#!/bin/bash
#SBATCH --nodes=1
#SBATCH --time=16:00:00
#SBATCH --job-name=vis
#SBATCH --output=vis.o%j
#SBATCH --error=vis.e%j
#SBATCH --qos=standard
#SBATCH -A w19_mpascoastal
#SBATCH --mail-user=pwolfram@lanl.gov
#SBATCH --mail-type=ALL
eval "$(/lustre/scratch4/turquoise/pwolfram/BGC_wrangling/miniconda3/bin/conda shell.bash hook)"
source activate compass_py3.7
paraview_vtk_field_extractor.py \
-v bottomDepth,latVertex,lonVertex \
-f /lustre/scratch3/turquoise/maltrud/ACME/input_data/ocn/mpas-o/oRRS30to10v3/mpaso.rst.0026-01-01_00000.GMPAS-IAF_oRRS30to10v3_theta04.Apr07_2018+BGC_IC.Nov30_2017.FESEDx5.nc \
-x none \
-o './data'
paraview_vtk_field_extractor.py \
-v timeMonthly_avg_velocityMeridional,timeMonthly_avg_velocityZonal,timeMonthly_avg_ssh,timeMonthly_avg_layerThickness,timeMonthly_avg_potentialDensity,timeMonthly_avg_dThreshMLD,timeMonthly_avg_vertVelocityTop,timeMonthly_avg_SSHSquared,timeMonthly_avg_ecosys_diag_pH_3D,timeMonthly_avg_activeTracers_temperature,timeMonthly_avg_activeTracers_salinity,timeMonthly_avg_ecosysTracers_PO4,timeMonthly_avg_ecosysTracers_NO3,timeMonthly_avg_ecosysTracers_NH4,timeMonthly_avg_ecosysTracers_DON,timeMonthly_avg_ecosysTracers_spChl,timeMonthly_avg_ecosysTracers_diatChl,timeMonthly_avg_ecosysTracers_diazChl,timeMonthly_avg_ecosysTracers_phaeoChl,timeMonthly_avg_ssh\
-f '/lustre/scratch4/turquoise//maltrud/ACME/cases/GMPAS-OECO-ODMS-IAF_oRRS30to10v3_grizzly03/run/mpaso.hist.am.timeSeriesStatsMonthly.0046-*.nc' \
-m /lustre/scratch3/turquoise/maltrud/ACME/input_data/ocn/mpas-o/oRRS30to10v3/mpaso.rst.0026-01-01_00000.GMPAS-IAF_oRRS30to10v3_theta04.Apr07_2018+BGC_IC.Nov30_2017.FESEDx5.nc \
-x none \
-d nVertLevels=0:79 nVertLevelsP1=0:80 \
-o './data'
# build the 3D files
python to3D.vpy
# individually zip it up
for i in TEST-world*; do
tar czvf $i.tgz $i &
done
# Greg Abram, Phillip J. Wolfram, 07/03/2019
from vtk import *
from glob import glob
import numpy as np
from vtk.numpy_interface import dataset_adapter as dsa
from vtk.numpy_interface import algorithms as algs
import sys
import math
args = sys.argv[1:]
DEPTH_SCALE = 300
do_uvw = False
do_latlon = True
variables = []
while len(args) > 0:
if args[0] == 'd':
DEPTH_SCALE = float(args[1]) # vertical extent to exagerate veritcal dimension
args = args[2:]
elif args[0] == '+uvw':
do_uvw = True # uvw is vector zonal, meridiional, vertical vectors
args = args[1:]
elif args[0] == '-uvw':
do_uvw = False
args = args[1:]
elif args[0] == '+latlon':
do_latlon = True # latlon project
args = args[1:]
elif args[0] == '-latlon':
do_latlon = False
args = args[1:]
else:
variables.append(args[0])
args = args[1:]
# files coming in from Xylar's post-processing tool
prdr = vtkXMLPolyDataReader()
prdr.SetFileName('data/staticFieldsOnVertices.vtp')
c2p = vtkCellDataToPointData()
c2p.SetInputConnection(prdr.GetOutputPort())
c2p.Update()
pgrid = dsa.WrapDataObject(c2p.GetOutput())
crdr = vtkXMLPolyDataReader()
crdr.SetFileName('data/staticFieldsOnCells.vtp')
crdr.Update()
# create python wrapper for vtk object
cgrid = dsa.WrapDataObject(crdr.GetOutput())
# bottom depth is now assigned to the triangular vertex points
pgrid.PointData.append(cgrid.CellData['bottomDepth'], 'bottomDepth')
nPoints = pgrid.Points.shape[0]
nTriangles = pgrid.Polygons.shape[0]/4
lat = pgrid.PointData['latVertex']
lon = pgrid.PointData['lonVertex']
print('got latlon')
# radial vectors through each surface point
rxyz = pgrid.Points / np.sqrt((pgrid.Points*pgrid.Points).sum(axis=1)).reshape(len(pgrid.Points),1)
if do_uvw:
# compute zonal/meridonal frame of reference for each point on the surface
# computes the position vectors
rx = rxyz[:,0]
ry = rxyz[:,1]
rz = rxyz[:,2]
theta = np.arctan2(ry, rx)
d = np.sqrt(rx*rx + ry*ry)
phi = np.arctan2(rz, d)
ijhat = rx[:,np.newaxis]*[1.0, 0.0, 0.0] + ry[:,np.newaxis]*[0.0, 1.0, 0.0]
d = np.linalg.norm(ijhat, axis=1)
ijhat = ijhat / d[:,np.newaxis]
# zonal, meridional, vertical 3D coordinate vector on the sphere
U = np.column_stack((-np.sin(theta), np.cos(theta), [0]*len(theta)))
V = -np.sin(phi)[:,np.newaxis]*ijhat + np.cos(phi)[:,np.newaxis]*[0.0, 0.0, 1.0]
W = rxyz
print('uvw ready')
else:
print('not doing uvw')
# and points at sea level
nPoints = len(pgrid.Points)
timesteps = glob('data/time_series/fieldsOnCells.*.vtp')
#timesteps = glob('data/time_series/timeDependentFieldsOnCells.*.vtp')
#timesteps = [timesteps[0]]
# produces grid to be shared for each timestep and assign values on the mesh
# for each timestep
first = True
for tf in timesteps:
ts = tf.split('.')[1]
print( 'timestep: {} {}'.format(ts, tf))
rdr1 = vtkXMLPolyDataReader()
rdr1.SetFileName(tf)
rdr1.Update()
cgrid = dsa.WrapDataObject(rdr1.GetOutput())
print('loaded')
if first:
first = False
# get number of layers (checks last two digits from Xylar's vtk output)
nlayers = -1
for i in cgrid.CellData.keys():
if i[:30] == 'timeMonthly_avg_layerThickness':
l = int(i[-2:])
if nlayers < l:
nlayers = l
# create list of slab thicknesses
thicknesses = [cgrid.CellData['timeMonthly_avg_layerThickness_%02d' % i] for i in range(nlayers+1)]
print('{} thicknesses'.format(len(thicknesses)))
# BottomDepth is positive - eg. units below the sea level. Slab thicknesses are also
# positive. Therefore, the top level is the *negative* bottom depth plus the sum
# of the layer thicknesses.
# this is equivalent to the ssh (something that could be checked with an assert)
toplevel = -pgrid.PointData['bottomDepth'] + np.sum(thicknesses, axis=0)
# arangement to correspond to interior interpolation of the scalar field
# (ssh,ssh - dz/2) half top layer
# ...
# interior layers
# ...
# (dz/2, bottomDepth) half bottom layer
# To handle the cell-centeredness of the data, we force a layer at the top level, then
# one 1/2 thickness[0] below that, which would be the center of the first slab. Then we add
# a layer at the center of each *subsequent* slab, then a layer at the bottom. This results
# in n+2 layers, so for cell-centered variables (that have the same number of vertical layers
# as the layerThickness variable) we copy the first and last data slices. For the P1 variables,
# these are top-centered, so we assign the first slice, then interpolate values for each slab
# center, then dup the last slab-center slice for the bottom slice.
# depths[i] are the depths of the i'th slice at each point. The 0'th slice is at the top
# level; then there's a slice at the center of each slab, then there's the bottom. The center
# of slice k (k > 1) is the center of the previous slab (k-1) + the rest of slab that slice
# (thickness[k-1]/2) plus the top half of the current slab.
# (ssh,ssh - dz/2) half top layer (builds out whole layer)
depths = [toplevel, toplevel - (thicknesses[0]/2)]
# interior layers (including top of the very bottom layer)
for i in range(1, len(thicknesses)):
depths.append(depths[-1] - ((thicknesses[i-1] + thicknesses[i]) / 2))
# (dz/2, bottomDepth) half bottom layer (close it out)
depths.append(-pgrid.PointData['bottomDepth'])
# diagnostic to check to make sure depths are reasonable
print('depths at random point on the surface')
for i,d in enumerate(depths):
print('{} {}'.format(i, depths[i][10000]))
# projection with vertical exageration to produce vtk points
points = []
for i,depth in enumerate(depths):
x = pgrid.Points + (DEPTH_SCALE*depth)[:,np.newaxis]*rxyz
y = np.array(x).astype('f4')
points.append(y)
# point values being converted in prep for vtk inputs
points = np.ascontiguousarray(np.vstack(points)).astype('f4')
print('points done')
triangles = pgrid.GetPolygons().reshape((-1,4))[:,[1,2,3]]
# wedge ("prism") vtk container topology
prisms = []
for i in range(len(depths)-1):
prisms.append(np.hstack(([[6]]*len(triangles), triangles+[i*nPoints]*3, triangles+[(i+1)*nPoints]*3)))
prisms = np.vstack(prisms).astype('i4')
nPrisms = prisms.shape[0]
print('nPrisms {}'.format(nPrisms))
# build vtk type as wedges
cellTypes = dsa.VTKArray([VTK_WEDGE]*nPrisms).astype('u1')
# range doesn't include the last point (e.g., a wedge is defined by 6 points)
cellLocations = dsa.VTKArray(np.array(range(0, 7*nPrisms, 7)).astype('i4'))
ogrid = dsa.WrapDataObject(vtkUnstructuredGrid())
ogrid.SetCells(cellTypes, cellLocations, prisms.flatten())
ogrid.SetPoints(points)
print('{} prisms on {} points. max ID {} ... {}'.format(prisms, len(points), ogrid.GetCells().max(), len(ogrid.Cells)))
# appends lat/lon data to produced file
if do_latlon:
lat = 90.0 * ((2*lat) / 3.1415926)
lat = np.concatenate([lat]*len(depths)).astype('f4')
ogrid.PointData.append(lat, 'lat')
lon = np.where(lon > 180, lon-360, lon)
lon = np.concatenate([lon]*len(depths)).astype('f4')
ogrid.PointData.append(lon, 'lon')
print('stacked latlon done')
else:
print('not doing uvw')
# vectors to project scalar values into vector
# multiply velocityZonal, velocityMeridional, etc to project in 3D to build VTK/ParaView vector
if do_uvw:
U = np.concatenate([U]*len(depths)).astype('f4')
V = np.concatenate([V]*len(depths)).astype('f4')
W = np.concatenate([W]*len(depths)).astype('f4')
ogrid.PointData.append(U, 'u')
ogrid.PointData.append(V, 'v')
ogrid.PointData.append(W, 'w')
print('uvw done')
else:
print('not doing uvw')
otf = 'TEST-world-%d-%s.vtu' % (DEPTH_SCALE, ts)
# get variable names
if len(variables) == 0:
variables = [i[:-3] for i in cgrid.CellData.keys() if i[-3:] == '_00']
# for v in variables:
for v in variables:
print('starting {}'.format(v))
vnames = ['%s_%02i' % (v, i) for i in range(len(thicknesses))]
slices = [np.array(cgrid.CellData[vname]).astype('f4') for vname in vnames]
if len(slices) == len(thicknesses):
# Replicate first slab value at top, last at bottom
data = [slices[0]] + slices + [slices[-1]]
elif len(slices) == (len(thicknesses)+1):
# then the data is centered with the *top* of each slab, plus the bottom
# of the lowest slab. First slice is correct. Then, for each *slab*, interpolate
# between top and bottom values. Then add in the final slice which,
# again, is correct
data = [slices[0]]
# vertically interpolate a slice at each slab center
for i in range(len(slices)-1):
data.append(np.sum([slices[i], slices[i+1]], axis=0) / 2.0)
# and add the bottom
data.append(slices[-1])
else:
print("unknown data length {} {}".format(v, len(slices)))
continue
data = np.concatenate(data)
data = np.nan_to_num(data).flatten() # nans go to zero (should replace with -1E34)
# needed to do streamlines / particle tracking / particle advection
# correctly (but note that this won't be a physical value in the output --
# probably should rename so that this is clear)
if v == 'timeMonthly_avg_vertVelocityTop':
data = DEPTH_SCALE * data
ogrid.PointData.append(data, v)
print( '{} done {} elements,'.format(v, len(data)))
print('OGRID =========================================')
print(ogrid.VTKObject)
# code that follows is experimental and problem specific (e.g., vis of ice sheets)
# this helps reduce output ("clip") in order to produce a smaller file size
# carve out ice sheets
threshold1 = vtkThreshold()
threshold1.SetInputData(ogrid.VTKObject)
threshold1.SetInputArrayToProcess(1, 0, 0, 0, 'lat')
#threshold1.ThresholdBetween(-1.48876, -1.261)
threshold1.ThresholdBetween(0.0579517, 0.96905)
threshold1.Update()
print('T1 =========================================')
print(threshold1.GetOutput())
threshold2 = vtkThreshold()
threshold2.SetInputConnection(threshold1.GetOutputPort())
threshold2.SetInputArrayToProcess(1, 0, 0, 0, 'lon')
#threshold2.ThresholdBetween(4.84, 5.75)
threshold2.ThresholdBetween(3.81101, 5.98078)
threshold2.Update()
print('T2 =========================================')
print(threshold2.GetOutput())
print('writing {}'.format(otf))
wrtr = vtkXMLUnstructuredGridWriter()
wrtr.SetFileName(otf)
wrtr.SetInputData(threshold2.GetOutput()) # need to use clipped output
wrtr.SetInputData(ogrid.VTKObject)
wrtr.Write()
print('{} {} done'.format(ts, tf))
# end the loop: "for tf in timesteps:"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment