#StackBounty: #python #vector #plotly Create 3D Streamtube plot in Plotly

Bounty: 100

Aim

I would like to create a 3D Streamtube Plot with Plotly.

Here is a cross-section of the vector field in the middle of the plot to give you an idea of how it looks like:

enter image description here

The final vector field should have rotational symmetry.

My Attempt

  1. Download the data here: https://filebin.net/x6ywfuo6v4851v74
  2. Run the code bellow:

Code:

import plotly.graph_objs as go
import plotly.express as px
import pandas as pd
import numpy as np

import plotly.io as pio
pio.renderers.default='browser'

# Import data to pandas
df = pd.read_csv("data.csv")
# Plot

X = np.linspace(0,1,101)
Y = np.linspace(0,1,10)
Z = np.linspace(0,1,101)

# Points from which the streamtubes should originate
xpos,ypos = np.meshgrid(X[::5],Y, indexing="xy")
xpos = xpos.reshape(1,-1)[0]
ypos = ypos.reshape(1,-1)[0]


starting_points = px.scatter_3d(
    x=xpos,
    y=ypos,
    z=[-500]*len(xpos)
    )
starting_points.show()

# Streamtube Plot

data_plot = [go.Streamtube(
    x = df['x'],
    y = df['y'],
    z = df['z'],
    u = df['u'],
    v = df['v'],
    w = df['w'],
    starts = dict(                           #Determines the streamtubes starting position.
          x=xpos,
          y=ypos,
          z=[-500]*len(xpos)
    ),
    #sizeref = 0.3,
    colorscale = 'jet',
    showscale = True,
    maxdisplayed = 300                      #Determines the maximum segments displayed in a streamtube.
)]

fig = go.Figure(data=data_plot)
fig.show()

The initial points (starting points) of the streamtubes seem to be nicely defined:

enter image description here

…but the resulting 3D streamtube plot is very weird:

enter image description here

Edit:

I tried normalizing the field plot, but the result is still not satisfactory:

import plotly.graph_objs as go
import pandas as pd
import numpy as np

import plotly.io as pio
pio.renderers.default='browser'

# Import data to pandas
df = pd.read_csv("data.csv")

# NORMALIZE VECTOR FIELD -> between [0,1]
df["u"] = (df["u"]-df["u"].min()) / (df["u"].max()-df["u"].min())
df["v"] = (df["v"]-df["v"].min()) / (df["v"].max()-df["v"].min())
df["w"] = (df["w"]-df["w"].min()) / (df["w"].max()-df["w"].min())

# Plot

X = np.linspace(0,1,101)
Y = np.linspace(0,1,10)
Z = np.linspace(0,1,101)

# Points from which the streamtubes should originate
xpos,ypos = np.meshgrid(X[::5],Y, indexing="xy")
xpos = xpos.reshape(1,-1)[0]
ypos = ypos.reshape(1,-1)[0]


# Streamtube Plot

data_plot = [go.Streamtube(
    x = df['x'],
    y = df['y'],
    z = df['z'],
    u = df['u'],
    v = df['v'],
    w = df['w'],
    starts = dict(                           #Determines the streamtubes starting position.
          x=xpos,
          y=ypos,
          z=[0]*len(xpos)
    ),
    #sizeref = 0.3,
    colorscale = 'jet',
    showscale = True,
    maxdisplayed = 300                      #Determines the maximum segments displayed in a streamtube.
)]

fig = go.Figure(data=data_plot)
fig.show()

enter image description here

Data

As for the data itself:

It is created from 10 slices (y-direction). For each slice (y), [u,v,w] on a regular xz mesh (101×101) was computed. The whole was then assembled into the dataframe which you can download, and which has 101x101x10 data points.


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.