Two contour plots in single viewer- Python FiPy - python

I am trying to solve two independent variables varying geometrically over a given domain. I want to plot their variance in a single viewer display. How can I get two different contour plots one each for the independent variable in single viewer box? I have used the following code for double contour but cannot get different contours for both the variables (phasegamma and phasesigma in my case). Please suggest how it can be corrected or any other possible way to get two contours in one plot.
import pylab
class PhaseViewer(Matplotlib2DGridViewer):
def __init__(self, phasesigma, phasegamma, title = None, limits ={}, **kwlimits):
self.phasesigma = phasesigma
self.contour1 = None
self.phasegamma = phasegamma
self.contour2 = None
Matplotlib2DGridViewer.__init__(self, vars=(1-phasegamma-phasesigma),title=title,cmap=pylab.cm.hot,limits ={}, **kwlimits)
def _plot(self):
Matplotlib2DGridViewer._plot(self)
if self.contour1 is not None or self.contour2 is not None:
for Ccr in self.contour1.collections:
Ccr.remove()
for Cni in self.contour1.collections:
Cni.remove()
mesh = self.phasesigma.getMesh()
mesh2 = self.phasegamma.getMesh()
shape = mesh.getShape()
shape2 = mesh2.getShape()
x, y = mesh.getCellCenters()
z = self.phasesigma.getValue()
x, y, z = [a.reshape(shape, order="FORTRAN") for a in (x, y, z)]
self.contour1 = pylab.contour(x, y, z, (0.5,))
l, m = mesh1.getCellCenters()
w = self.phasegamma.getValue()
l, m, w = [b.reshape(shape, order ="FORTRAN") for b in (l, m, w)]
self.contour2 = pylab.contour(l, m, w, (0.5,))
raw_input("check2")
viewer = PhaseViewer(phasesigma=phasesigma, phasegamma=phasegamma,\
title = r"%s & %s" % (phasegamma.name, phasesigma.name), datamin=0., datamax=1.)
except ImportError:
viewer = MultiViewer(viewers=(Viewer(vars=phasesigma,datamin=0.,datamax=1),Viewer(vars=phasegamma,datamin=0.,datamax=1.)))

I just saw this, so hopefully it's still useful to you. I'm not sure why your version didn't work, although I generally find that pylab works at too high a level and does too many things automatically.
I based the following on Matplotlib2DContourViewer and it seems to do what you want:
class PhaseViewer(Matplotlib2DGridViewer):
def __init__(self, phasesigma, phasegamma, title = None, limits ={}, **kwlimits):
self.phasesigma = phasesigma
self.contour1 = None
self.phasegamma = phasegamma
self.contour2 = None
self.number = 10
self.levels = None
Matplotlib2DGridViewer.__init__(self, vars=(1-phasegamma-phasesigma),title=title,cmap=pylab.cm.hot,limits ={}, **kwlimits)
def _plot(self):
Matplotlib2DGridViewer._plot(self)
if hasattr(self, "_contourSet"):
for countourSet in self._contourSet:
for collection in ccontourSet.collections:
try:
ix = self.axes.collections.index(collection)
except ValueError, e:
ix = None
if ix is not None:
del self.axes.collections[ix]
self._contourSet = []
for var in (self.phasesigma, self.phasegamma):
mesh = var.mesh
x, y = mesh.cellCenters
z = var.value
xmin, ymin = mesh.extents['min']
xmax, ymax = mesh.extents['max']
from matplotlib.mlab import griddata
xi = fp.numerix.linspace(xmin, xmax, 1000)
yi = fp.numerix.linspace(ymin, ymax, 1000)
# grid the data.
zi = griddata(x, y, z, xi, yi, interp='linear')
zmin, zmax = self._autoscale(vars=[var],
datamin=self._getLimit(('datamin', 'zmin')),
datamax=self._getLimit(('datamax', 'zmax')))
self.norm.vmin = zmin
self.norm.vmax = zmax
if self.levels is not None:
levels = self.levels
else:
levels = fp.numerix.arange(self.number + 1) * (zmax - zmin) / self.number + zmin
self._contourSet.append(self.axes.contour(xi, yi, zi, levels=levels, cmap=self.cmap))
self.axes.set_xlim(xmin=self._getLimit('xmin'),
xmax=self._getLimit('xmax'))
self.axes.set_ylim(ymin=self._getLimit('ymin'),
ymax=self._getLimit('ymax'))
if self.colorbar is not None:
self.colorbar.plot()

Related

Python Matplotlib Funcanimation issue: struggle to animate a contour plot (contourf) within functions

I'm struggling with FuncAnimation in matplotlib.animation, and I could not find out any examples or post looking similar to my problem (I mean, yes there is post concerning contourf used in funcAnimation but in those posts they succeed to delete the PathCollection object but in my case something is not working).
Context:
In a school project concerning One-vs-All notion (multiple binary classifiers), I want to implement functions to animate a figure having 3 Axes and containing multiple Line2D objects, an PathCollection object from scatter method and a QuadContourSet from contourf method.
Here a screen of how it looks like (obtained when I plot the data at the end of the training of the One-vs-All):
Representation of the static graph
Legend:
Left: Boundary decision in (Herbology)-(Defense against Dark Arts) plane,
Top right: Loss function of each binary classifiers,
Bottom right: Precision and Recall metrics of each classifiers.
Methods:
I am trying to have a animated version of the plot using FuncAnimation from matplotlib.Animation module. Animated version of the plot is a bonus feature of my project, then the animation part/core is made in functions, you can see a simplification below (a bare bones representation) :
def anim_visu(models, data):
# initialization of the figure and object representing the data
...
def f_anim():
# Function which update the data at each frames
visu = FuncAnimation(fig, f_anim, ...)
return fig
[...]
if __name__ == "__main__":
[...]
if bool_dynamic: # activation of the dynamic visualization
anim_visu(models, data)
And here a minimal workish example:
# =========================================================================== #
# |Importation des lib/packages| #
# =========================================================================== #
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.animation import FuncAnimation
from matplotlib.gridspec import GridSpec
dct_palet = {"C1":"dodgerblue",
"C2":"red",
"C3":"green",
"C4":"goldenrod"}
fps = 15
# =========================================================================== #
# | Definition des fonctions | #
# =========================================================================== #
def one_vs_all_prediction(classifiers:list, X:np.array) -> np.array:
"""
... Docstring ...
"""
preds = np.zeros((X.shape[0],1))
for clf in classifiers:
tmp = clf.predict(X)
mask = preds == 0
preds[mask] = tmp[mask]
return preds
def one_vs_all_class_onehot(class_pred:np.array):
"""
... Docstring ...
"""
house = {"C1":1., "C2":2., "C3":3., "C4":4.}
onehot_pred = np.chararray((class_pred.shape[0],1), itemsize=2)
for key, item in house.items():
mask = class_pred == item
onehot_pred[mask] = key
return onehot_pred
def do_animation(clfs:list, data:np.ndarray):
""" Core function for the animated vizualisation.
The function defines all the x/y_labels, the titles.
"""
global idx, cost_clf1, cost_clf2, cost_clf3, cost_clf4, \
met1_clf1, met1_clf2, met1_clf3, met1_clf4, \
met2_clf1, met2_clf2, met2_clf3, met2_clf4, \
boundary, axes, \
l_cost_clf1, l_cost_clf2, l_cost_clf3, l_cost_clf4, \
l_met1_clf1, l_met1_clf2, l_met1_clf3, l_met1_clf4, \
l_met2_clf1, l_met2_clf2, l_met2_clf3, l_met2_clf4
plt.style.use('seaborn-pastel')
# -- Declaring the figure and the axes -- #
fig = plt.figure(figsize=(15,9.5))
gs = GridSpec(2, 2, figure=fig)
axes = [fig.add_subplot(gs[:, 0]), fig.add_subplot(gs[0, 1]), fig.add_subplot(gs[1, 1])]
# --formatting the different axes -- #
axes[0].set_xlabel("X_1")
axes[0].set_ylabel("X_2")
axes[0].set_title("Decision boundary")
axes[1].set_xlabel("i: iteration")
axes[1].set_xlim(-10, 1000)
axes[1].set_ylim(-10, 350)
axes[1].set_ylabel(r"$\mathcal{L}_{\theta_0,\theta_1}$")
axes[1].grid()
axes[2].set_xlabel("i: iteration")
axes[2].set_ylabel("Scores (metric_1 & metric_2)")
axes[2].set_xlim(-10, 1000)
axes[2].set_ylim(0.0,1.01)
axes[2].grid()
# -- Reading min and max values along X dimensions-- #
X = data[:,0:2]
X = X.astype(np.float64)
Y = data[:,2].reshape(-1,1)
idx = np.array([0])
X_min, X_max = X[:,:2].min(axis=0), X[:,:2].max(axis=0)
# -- Generate a grid of points with distance h between them -- #
h = 0.01
XX_1, XX_2 = np.meshgrid(np.arange(X_min[0], X_max[0], h),
np.arange(X_min[1], X_max[1], h))
zeros_arr = np.zeros((XX_1.shape[0] * XX_1.shape[1], 1))
XX = np.c_[XX_1.ravel(), XX_2.ravel(),
zeros_arr.ravel(), zeros_arr.ravel(), zeros_arr.ravel()]
# -- Predict the function value for the whole grid -- #
preds = one_vs_all_prediction(clfs, XX)
Z = preds.reshape(XX_1.shape)
## Initialisation of the PathCollection for the Axes[0] objects
boundary = axes[0].contourf(XX_1, XX_2, Z, 3,
colors=["red", "green", "goldenrod", "dodgerblue"], alpha=0.5)
lst_colors = np.array([dct_palet[house] for house in data[:,2]])
raw_data = axes[0].scatter(X[:,0], X[:,1], c=lst_colors, edgecolor="k")
## Initialisation of the Line2D object for the Axes[1] objects
cost_clf1 = clfs[0].cost()
cost_clf2 = clfs[1].cost()
cost_clf3 = clfs[2].cost()
cost_clf4 = clfs[3].cost()
l_cost_clf1, = axes[1].plot(idx, cost_clf1,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[0].house])
l_cost_clf2, = axes[1].plot(idx, cost_clf2,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[1].house])
l_cost_clf3, = axes[1].plot(idx, cost_clf3,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[2].house])
l_cost_clf4, = axes[1].plot(idx, cost_clf4,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[3].house])
## Initialisation of the Line2D object for the Axes[2] objects
met1_clf1 = clfs[0].dummy_metric1()
met1_clf2 = clfs[1].dummy_metric1()
met1_clf3 = clfs[2].dummy_metric1()
met1_clf4 = clfs[3].dummy_metric1()
met2_clf1 = clfs[0].dummy_metric2()
met2_clf2 = clfs[1].dummy_metric2()
met2_clf3 = clfs[2].dummy_metric2()
met2_clf4 = clfs[3].dummy_metric2()
l_met1_clf1, = axes[2].plot(idx, met1_clf1,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[0].house])
l_met1_clf2, = axes[2].plot(idx, met1_clf2,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[1].house])
l_met1_clf3, = axes[2].plot(idx, met1_clf3,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[2].house])
l_met1_clf4, = axes[2].plot(idx, met1_clf4,
ls='-', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[3].house])
l_met2_clf1, = axes[2].plot(idx, met2_clf1,
ls='--', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[0].house])
l_met2_clf2, = axes[2].plot(idx, met2_clf2,
ls='--', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[1].house])
l_met2_clf3, = axes[2].plot(idx, met2_clf3,
ls='--', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[2].house])
l_met2_clf4, = axes[2].plot(idx, met2_clf4,
ls='--', marker='o', ms=2, lw=1.2, color=dct_palet[clfs[3].house])
fig.canvas.mpl_connect('close_event', f_close)
anim_fig = FuncAnimation(fig, f_animate, fargs=(XX_1, XX_2, XX,), frames=int(1000/fps), repeat=False, cache_frame_data = False, blit=False)
plt.waitforbuttonpress()
return fig
def f_animate(i, XX_1, XX_2, XX):
"""
... Docstring ...
"""
global clfs, idx, \
cost_clf1, cost_clf2, cost_clf3, cost_clf4, \
met1_clf1, met1_clf2, met1_clf3, met1_clf4, \
met2_clf1, met2_clf2, met2_clf3, met2_clf4, \
boundary, axes, l_cost_clf1, l_cost_clf2, l_cost_clf3, l_cost_clf4, \
l_met1_clf1, l_met1_clf2, l_met1_clf3, l_met1_clf4, \
l_met2_clf1, l_met2_clf2, l_met2_clf3, l_met2_clf4
n_cycle = 100
clfs[0].fit(n_cycle)
clfs[1].fit(n_cycle)
clfs[2].fit(n_cycle)
clfs[3].fit(n_cycle)
idx = np.concatenate((idx, np.array([i * n_cycle])))
preds = one_vs_all_prediction(clfs, XX)
Z = preds.reshape(XX_1.shape)
cost_clf1 = np.concatenate((cost_clf1, clfs[0].cost()))
cost_clf2 = np.concatenate((cost_clf2, clfs[1].cost()))
cost_clf3 = np.concatenate((cost_clf3, clfs[2].cost()))
cost_clf4 = np.concatenate((cost_clf4, clfs[3].cost()))
tmp_met1_clf1 = clfs[0].dummy_metric1()
tmp_met1_clf2 = clfs[1].dummy_metric1()
tmp_met1_clf3 = clfs[2].dummy_metric1()
tmp_met1_clf4 = clfs[3].dummy_metric1()
tmp_met2_clf1 = clfs[0].dummy_metric2()
tmp_met2_clf2 = clfs[1].dummy_metric2()
tmp_met2_clf3 = clfs[2].dummy_metric2()
tmp_met2_clf4 = clfs[3].dummy_metric2()
met1_clf1 = np.concatenate((met1_clf1, tmp_met1_clf1))
met1_clf2 = np.concatenate((met1_clf2, tmp_met1_clf2))
met1_clf3 = np.concatenate((met1_clf3, tmp_met1_clf3))
met1_clf4 = np.concatenate((met1_clf4, tmp_met1_clf4))
met2_clf1 = np.concatenate((met2_clf1, tmp_met2_clf1))
met2_clf2 = np.concatenate((met2_clf2, tmp_met2_clf2))
met2_clf3 = np.concatenate((met2_clf3, tmp_met2_clf3))
met2_clf4 = np.concatenate((met2_clf4, tmp_met2_clf4))
# -- Plot the contour and training examples -- #
# Update the plot objects: remove the previous collections to save memory.
#l = len(boundary.collections)
for coll in boundary.collections:
# Remove the existing contours
boundary.collections.remove(coll)
boundary = axes[0].contourf(XX_1, XX_2, Z, 3, colors=["red", "green", "goldenrod", "dodgerblue"], alpha=0.5)
l_cost_clf1.set_data(idx, cost_clf1)
l_cost_clf2.set_data(idx, cost_clf2)
l_cost_clf3.set_data(idx, cost_clf3)
l_cost_clf4.set_data(idx, cost_clf4)
l_met1_clf1.set_data(idx, met1_clf1)
l_met1_clf2.set_data(idx, met1_clf2)
l_met1_clf3.set_data(idx, met1_clf3)
l_met1_clf4.set_data(idx, met1_clf4)
l_met2_clf1.set_data(idx, met2_clf1)
l_met2_clf2.set_data(idx, met2_clf2)
l_met2_clf3.set_data(idx, met2_clf3)
l_met2_clf4.set_data(idx, met2_clf4)
return boundary.collections, l_cost_clf1, l_cost_clf2, l_cost_clf3, l_cost_clf4, \
l_met1_clf1, l_met1_clf2, l_met1_clf3, l_met1_clf4, \
l_met2_clf1, l_met2_clf2, l_met2_clf3, l_met2_clf4
def f_close(event):
""" Functions called when the graphical window is closed.
It prints the last value of the theta vector and the last value of the
cost function.
"""
plt.close()
class DummyBinary():
def __init__(self, house, theta0, theta1, alpha=1e-3):
self.house = house
self.theta0 = theta0
self.theta1 = theta1
self.alpha = alpha
if self.house == "C1":
self.border_x = 6
self.border_y = 6
if self.house == "C2":
self.border_x = 6
self.border_y = 13
if self.house == "C3":
self.border_x = 13
self.border_y = 6
if self.house == "C4":
self.border_x = 13
self.border_y = 13
def fit(self, n_cycle:int):
for _ in range(n_cycle):
self.theta0 = self.theta0 + self.alpha * (self.border_x - self.theta0)
self.theta1 = self.theta1 + self.alpha * (self.border_y - self.theta1)
def cost(self) -> float:
cost = (self.theta0 - self.border_x)**2 + (self.theta1 - self.border_y)**2
return cost
def predict(self, X:np.array) -> np.array:
if self.house == 'C1':
mask = (X[:,0] < self.theta0) & (X[:,1] < self.theta1)
if self.house == 'C2':
mask = (X[:,0] < self.theta0) & (X[:,1] > self.theta1)
if self.house == 'C3':
mask = (X[:,0] > self.theta0) & (X[:,1] < self.theta1)
if self.house == 'C4':
mask = (X[:,0] > self.theta0) & (X[:,1] > self.theta1)
pred =np.zeros((X.shape[0], 1))
pred[mask] = int(self.house[1])
return pred
def dummy_metric1(self):
return np.array([0.5 * (self.theta0 / self.border_x + self.theta1 / self.border_y)])
def dummy_metric2(self):
return np.array([0.5 * ((self.theta0 / self.border_x)**2 + (self.theta1 / self.border_y)**2)])
# =========================================================================== #
# _________________________________ MAIN __________________________________ #
# =========================================================================== #
if __name__ == "__main__":
# -- Dummy data -- #
x1 = np.random.randn(60,1) * 2.5 + 3.5
x2 = np.random.randn(60,1) * 2.5 + 3.5
x3 = np.random.randn(60,1) * 2.5 + 15.5
x4 = np.random.randn(60,1) * 2.5 + 15.5
stud_house = 60 * ['C1'] + 60 * ['C2'] + 60 * ['C3'] + 60 * ['C4']
c_house = [dct_palet[house] for house in stud_house]
y1 = np.random.randn(60,1) * 2.5 + 3.5
y2 = np.random.randn(60,1) * 2.5 + 15.5
y3 = np.random.randn(60,1) * 2.5 + 3.5
y4 = np.random.randn(60,1) * 2.5 + 15.5
X = np.concatenate((x1, x2, x3, x4)) # shape: (240,1)
Y = np.concatenate((y1, y2, y3, y4)) # shape: (240,1)
data = np.concatenate((X, Y, np.array(stud_house).reshape(-1,1)), axis=1) # shape: (240,3)
clf1 = DummyBinary("C1", np.random.rand(1), np.random.rand(1))
clf2 = DummyBinary("C2", np.random.rand(1), np.random.rand(1))
clf3 = DummyBinary("C3", np.random.rand(1), np.random.rand(1))
clf4 = DummyBinary("C4", np.random.rand(1), np.random.rand(1))
clfs = [clf1, clf2, clf3, clf4]
## Visualize the raw dummy data.
#plt.scatter(X, Y, c=c_house, s=5)
#plt.show()
do_animation(clfs, data)
The Class DummyBinary mimics in a simplified way, what my One-vs-All class can do.
You can see a bunch of global in anim_visu and f_anim, in this way the code "works", but I'm aware there is something very wrong.
Attempts:
No global variables, everything were passed to f_anim via fargs, but when returning from f_anim, all the modification of the variables in f_anim scope were lost (normal behavior obviously),
Moving the definition of the f_anim within the body of anim_visu, to make f_anim an inner_function. I'm not experienced enough, so I did not succeed to make it works this way, I noticed that It may appeared it is not possible to modify variable declared in the anim_visu scope in the inner function.
Declare all the variables I need as global, it work in a way, but as you can see by running the code (in the axes[0]), the PathCollections are not cleared/deleted (despite the loop with boundary.collections.remove(coll)) and the number of PathCollection in the axes[0] seems to increased, leading to a drop of the speed the frames are updated.
Looking forward for your advice (and solution+explanation I hope).
And thank you for your times and neurons.

Leapfrog algorithm to compute a objects trajectory in a Gravitational field around a central body (Python 3.8.2)

I pretty much deleted the last code and started new. I added a new class called Object which is the replacement for the lists called body_1 and body_2. Also all the calculations are now done from within the Object class. Most previous existing issues were resolved through this process but there is still one that presists. I believe its inside the StartVelocity() function which creates the v1/2 needed to start the Leapfrog algorithm. This should give me a geostationary Orbit but as clearly visible the Satelite escapes very quickly after zooming through earth.
Codes are:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from object import Object
import numpy as np
class Simulation:
def __init__(self):
# Index: 0 Name, 1 Position, 2 Velocity, 3 Mass
body_1 = Object("Earth", "g", "r",
np.array([[0.0], [0.0], [0.0]]),
np.array([[0.0], [0.0], [0.0]]),
5.9722 * 10**24)
body_2 = Object("Satelite", "b", "r",
np.array([[42164.0], [0.0], [0.0]]),
np.array([[0.0], [3075.4], [0.0]]),
5000.0)
self.bodies = [body_1, body_2]
def ComputePath(self, time_limit, time_step):
time_range = np.arange(0, time_limit, time_step)
for body in self.bodies:
body.StartVelocity(self.bodies, time_step)
for T in time_range:
for body in self.bodies:
body.Leapfrog(self.bodies, time_step)
def PlotObrit(self):
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for body in self.bodies:
body.ReshapePath()
X, Y, Z = [], [], []
for position in body.path:
X.append(position[0])
Y.append(position[1])
Z.append(position[2])
ax.plot(X, Y, Z, f"{body.linecolor}--")
for body in self.bodies:
last_pos = body.path[-1]
ax.plot(last_pos[0], last_pos[1], last_pos[2], f"{body.bodycolor}o", label=body.name)
ax.set_xlabel("x-Axis")
ax.set_ylabel("y-Axis")
ax.set_zlabel("z-Axis")
ax.legend()
fig.savefig("Leapfrog.png")
if __name__ == "__main__":
sim = Simulation()
sim.ComputePath(0.5, 0.01)
sim.PlotObrit()
import numpy as np
class Object:
def __init__(self, name, bodycolor, linecolor, pos_0, vel_0, mass):
self.name = name
self.bodycolor = bodycolor
self.linecolor = linecolor
self.position = pos_0
self.velocity = vel_0
self.mass = mass
self.path = []
def StartVelocity(self, other_bodies, time_step):
force = self.GetForce(other_bodies)
self.velocity += (force / self.mass) * time_step * 0.5
def Leapfrog(self, other_bodies, time_step):
self.position += self.velocity * time_step
self.velocity += (self.GetForce(other_bodies) / self.mass) * time_step
self.path.append(self.position.copy())
def GetForce(self, other_bodies):
force = 0
for other_body in other_bodies:
if other_body != self:
force += self.Force(other_body)
return force
def Force(self, other_body):
G = 6.673 * 10**-11
dis_vec = other_body.position - self.position
dis_mag = np.linalg.norm(dis_vec)
dir_vec = dis_vec / dis_mag
for_mag = G * (self.mass * other_body.mass) / dis_mag**2
for_vec = for_mag * dir_vec
return for_vec
def ReshapePath(self):
for index, position in enumerate(self.path):
self.path[index] = position.reshape(3).tolist()
Im aware that Body 2's position has to be multiplied by 1000 to get meters but it would just fly in a straight line if i would do that and there would be no signs of gravitational forces what so ever.
The constant G is in kg-m-sec units. The radius of the satellite orbit however only makes sense in km, else the orbit would be inside the Earth core. Then the speed in m/sec gives a near circular orbit with negligible eccentricity. (Code from a math.SE question on Kepler law quantities)
import math as m
G = 6.673e-11*1e-9 # km^3 s^-2 kg^-1
M_E = 5.9722e24 # kg
R_E = 6378.137 # km
R_sat = 42164.0 # km from Earth center
V_sat = 3075.4/1000 # km/s
theta = 0
r0 = R_sat
dotr0 = V_sat*m.sin(theta)
dotphi0 = -V_sat/r0*m.cos(theta)
R = (r0*V_sat*m.cos(theta))**2/(G*M_E)
wx = R/r0-1; wy = -dotr0*(R/(G*M_E))**0.5
E = (wx*wx+wy*wy)**0.5; psi = m.atan2(wy,wx)
T = m.pi/(G*M_E)**0.5*(R/(1-E*E))**1.5
print(f"orbit constants R={R} km, E={E}, psi={psi} rad")
print(f"above ground: min={R/(1+E)-R_E} km, max={R/(1-E)-R_E} km")
print(f"T={2*T} sec, {T/1800} h")
with output
orbit constants R=42192.12133271948 km, E=0.0006669512550867562, psi=-0.0 rad
above ground: min=35785.863 km, max=35842.14320159004 km
T=86258.0162673565 sec, 23.960560074265697 h
for r(phi)=R/(1+E*cos(phi-psi))
Implementing these changes in your code and calling with
sim.ComputePath(86e+3, 600.0)
gives a nice circular orbit

White area on matplotlib plot with pygrib data between 359.5 and 360 degrees

What I try is to plot output of the gfs weather model with matplotlib using pygrib to save the data, which is saved in grib files. Nearly everything works fine, the output looks like this:
It appears that the program isn't closing the gap between 359.5 and 360 degress by using the data of 0 degress. If the data would be in a regular list or something I would use the data of 0° and save it for 360° too by appending the list. I've seen people having the same problem with non-pygrib data.
If you know how to change the pygrib data (regular operations don't work on pygrib data unfortunately) or how to make matplotlib close the gap, you would really help me out of this problem. Maybe the function "addcyclic" could help, but I don't know how.
EDIT: I solved the problem, see my answer.
So here is the code producing the problem:
#!/usr/bin/python3
import os, sys, datetime, string
from abc import ABCMeta, abstractmethod
import numpy as np
import numpy.ma as ma
from scipy.ndimage.filters import minimum_filter, maximum_filter
import pygrib
from netCDF4 import Dataset
from pylab import *
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap, addcyclic, shiftgrid
import laplaceFilter
import mpl_util
class Plot(Basemap):
def __init__(self, basemapParams):
super().__init__(**basemapParams)
self.layers = []
def addLayer(self, layer):
self.layers.append(layer)
def plot(self, data):
for layer in self.layers:
layer.plot(self, data)
plt.title('Plot')
plt.show()
class Layer(metaclass=ABCMeta):
def __init__(self):
pass
#abstractmethod
def plot(self, plot, data):
return NotImplemented
class BackgroundLayer(Layer):
def __init__(self, bgtype, coords):
#possible bgtype values: borders, topo, both
self.bgtype = bgtype
self.lonStart = coords[0]
self.lonEnd = coords[1]
self.latStart = coords[2]
self.latEnd = coords[3]
def plot(self, plot, data):
[...]
def findSubsetIndices(self,min_lat,max_lat,min_lon,max_lon,lats,lons):
[...]
class LegendLayer(Layer):
def __init__(self):
pass
class GribDataLayer(Layer, metaclass=ABCMeta):
def __init__(self, varname, level, clevs, cmap, factor):
self.varname = varname
self.level = level
self.clevs = clevs
self.cmap = cmap
self.factor = factor
def plot(self, plot, data):
#depending on the height we want to use, we have to change the index
indexes = {1000:0, 2000:1, 3000:2, 5000:3, 7000:4, 10000:5, 15000:6, 20000:7, 25000:8, 30000:9,
35000:10, 40000:11, 45000:12, 50000:13, 55000:14, 60000:15, 65000:16, 70000:17,
75000:18, 80000:19, 85000:20, 90000:21, 92500:22, 95000:23, 97500:24, 100000:25, 0:0}
selecteddata = data.select(name = self.varname)[indexes[self.level]]
lats, lons = selecteddata.latlons()
layerdata = selecteddata.values*self.factor
x, y = plot(lons, lats) # compute map proj coordinates.
self.fillLayer(plot, x, y, layerdata, self.clevs, self.cmap)
#abstractmethod
def fillLayer(self, plot, x, y, layerdata, clevs, cmap):
return NotImplemented
class ContourLayer(GribDataLayer):
def __init__(self, varname, level, clevs, cmap, factor, linewidth=1.5, fontsize=15,
fmt="%3.1f", inline=0,labelcolor = 'k'):
self.linewidth = linewidth
self.fontsize = fontsize
self.fmt = fmt
self.inline = inline
self.labelcolor = labelcolor
super().__init__(varname, level, clevs, cmap, factor)
def fillLayer(self, plot, x, y, layerdata, clevs, cmap):
# contour data over the map.
cs = plot.contour(x,y,layerdata,clevs,colors = cmap,linewidths = self.linewidth)
plt.clabel(cs, clevs, fontsize = self.fontsize, fmt = self.fmt,
inline = self.inline, colors = self.labelcolor)
if self.varname == "Pressure reduced to MSL":
self.plotHighsLows(plot,layerdata,x,y)
def plotHighsLows(self,plot,layerdata,x,y):
[...]
class ContourFilledLayer(GribDataLayer):
def __init__(self, varname, level, clevs, cmap, factor, extend="both"):
self.extend = extend
super().__init__(varname, level, clevs, cmap, factor)
def fillLayer(self, plot, x, y, layerdata, clevs, cmap):
# contourfilled data over the map.
cs = plot.contourf(x,y,layerdata,levels=clevs,cmap=cmap,extend=self.extend)
#cbar = plot.colorbar.ColorbarBase(cs)
[...]
ger_coords = [4.,17.,46.,56.]
eu_coords = [-25.,57.,22.,70.]
### Choose Data
data = pygrib.open('gfs.t12z.mastergrb2f03')
### 500hPa Europe
coords = eu_coords
plot1 = Plot({"projection":"lcc","resolution":"h","rsphere":(6378137.00,6356752.3142), "area_thresh": 1000.,
"llcrnrlon":coords[0],"llcrnrlat":coords[2],"urcrnrlon":coords[1],"urcrnrlat":coords[3],
"lon_0":(coords[0]+coords[1])/2.,"lat_0":(coords[2]+coords[3])/2.})
clevs = range(480,600,4)
cmap = plt.cm.nipy_spectral
factor = .1
extend = "both"
level = 50000
layer1 = ContourFilledLayer('Geopotential Height', level, clevs, cmap, factor, extend)
clevs = [480.,552.,600.]
linewidth = 2.
fontsize = 14
fmt = "%d"
inline = 0
labelcolor = 'k'
layer2 = ContourLayer('Geopotential Height', level, clevs, 'k', factor, linewidth, fontsize, fmt, inline, labelcolor)
level = 0
clevs = range(800,1100,5)
factor = .01
linewidth = 1.5
inline = 0
labelcolor = 'k'
layer3 = ContourLayer('Pressure reduced to MSL', level, clevs, 'w', factor, linewidth, fontsize, fmt, inline, labelcolor)
plot1.addLayer(BackgroundLayer('borders', coords))
plot1.addLayer(layer1)
plot1.addLayer(layer2)
plot1.addLayer(layer3)
plot1.plot(data)
I solved it myself 2 months later:
Matplotlib doesn't fill the area if your longitude range is from 0 to 359.75 because it ends there from matplotlibs point of view. I solved it by dividing up the data and then stacking it.
selecteddata_all = data.select(name = "Temperature")[0]
selecteddata1, lats1, lons1 = selecteddata_all.data(lat1=20,lat2=60,lon1=335,lon2=360)
selecteddata2, lats2, lons2 = selecteddata_all.data(lat1=20,lat2=60,lon1=0,lon2=30)
lons = np.hstack((lons1,lons2))
lats = np.hstack((lats1,lats2))
selecteddata = np.hstack((selecteddata1,selecteddata2))
No white area left of 0° anymore.
I don't know whether there is a fix if you wanna plot a whole hemisphere (0 to 359.75 deg).
I've run in to this myself a few times, and the addcyclic function of the basemap module actually works pretty well. The basemap docs lay out the syntax and use pretty well.
In terms of the variables in your code you can add the cyclic point either before or after you multiply by self.factor in your GribDataLayer class:
layerdata, lons = addcyclic(layerdata, lons)
You can also use np.append and write your own function to accomplish this same task. It would look something like this:
layerdata = np.append(layerdata,layerdata[...,0,None],axis=-1)
If your input data are 2D then the syntax above is equivalent to selecting all the data in the first longitude band (i.e. layerdata[:,0])
layerdata = np.append(layerdata,layerdata[:,0,None],axis=-1)
Hope this helps!

How to change tube radius of a line plot in Mayavi?

I am trying to dynamically modify the tube radius of a 3D line plot in Mayavi2. For example
from traits.api import HasTraits, Float, Instance, on_trait_change
from traitsui.api import View, Item, Group
from mayavi.core.api import PipelineBase
from mayavi.core.ui.api import MayaviScene, SceneEditor, MlabSceneModel
import numpy
def curve():
n_mer, n_long = 6, 11
pi = numpy.pi
dphi = pi / 1000.0
phi = numpy.arange(0.0, 2 * pi + 0.5 * dphi, dphi)
mu = phi * n_mer
x = numpy.cos(mu) * (1 + numpy.cos(n_long * mu / n_mer) * 0.5)
y = numpy.sin(mu) * (1 + numpy.cos(n_long * mu / n_mer) * 0.5)
z = numpy.sin(n_long * mu / n_mer) * 0.5
t = numpy.sin(mu)
return x, y, z, t
class MyModel(HasTraits):
radius = Float(0.025)
scene = Instance(MlabSceneModel, ())
plot = Instance(PipelineBase)
#on_trait_change('radius,scene.activated')
def update_plot(self):
x, y, z, t = curve()
if self.plot is None:
self.plot = self.scene.mlab.plot3d(x, y, z, t,
tube_radius=self.radius, colormap='Spectral')
else:
print self.radius
self.plot.mlab_source.set(tube_radius=self.radius)
self.scene.mlab.draw()
view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
height=250, width=300, show_label=False),
Group(
'radius',
),
resizable=True,
)
my_model = MyModel()
my_model.configure_traits()
This gives:
However, when I change the radius nothing happens with the visual line plot.
Don't use trait.set or trait.reset to set mayavi or vtk attributes. Indeed the MLineSource that you are using doesn't have such an attribute, but it probably wouldn't work even if it did.
It's usually useful to find the mayavi attribute manually that controls this feature to see what mayavi object it is assigned to. In this case, going through the mayavi pipeline GUI shows that it is on the tube filter.
mlab.plot3d is a helper function that tries to do everything for you and doesn't maintain a
reference to the filters used. But in general it is good practice to keep references to each step in the mayavi pipeline if you construct the pipeline yourself. That way you can easily access the mayavi object which controls this.
If you don't construct the pipeline yourself you can always get to it in the pipeline by manually navigating the tree of parent and child objects. In this case you can access it like so:
#on_trait_change('radius,scene.activated')
def update_plot(self):
x, y, z, t = curve()
if self.plot is None:
self.plot = self.scene.mlab.plot3d(x, y, z, t,
tube_radius=self.radius, colormap='Spectral')
else:
self.plot.parent.parent.filter.radius = self.radius

locking camera in mayavi

I'm trying to make an animation with a sequence of datafiles in mayavi. Unfortunately i have noticed that camera doesn't lock (it is zooming and zooming out). I think it is happening because the Z componrnt of my mesh is changing and mayavi is trying to recalculate scales.
How can I fix it?
import numpy
from mayavi import mlab
mlab.figure(size = (1024,768),bgcolor = (1,1,1))
mlab.view(azimuth=45, elevation=60, distance=0.01, focalpoint=(0,0,0))
#mlab.move(forward=23, right=32, up=12)
for i in range(8240,8243):
n=numpy.arange(10,400,20)
k=numpy.arange(10,400,20)
[x,y] = numpy.meshgrid(k,n)
z=numpy.zeros((20,20))
z[:] = 5
M = numpy.loadtxt('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\1disk_j9.5xyz\\'+'{0:05}'.format(i)+'.txt')
Mx = M[:,0]; My = M[:,1]; Mz = M[:,2]
Mx = Mx.reshape(20,20); My = My.reshape(20,20); Mz = Mz.reshape(20,20);
s = mlab.quiver3d(x,y,z,Mx, My, -Mz, mode="cone",resolution=40,scale_factor=0.016,color = (0.8,0.8,0.01))
Mz = numpy.loadtxt('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\Mzi\\' + '{0:05}'.format(i) + '.txt')
n=numpy.arange(2.5,400,2)
k=numpy.arange(2.5,400,2)
[x,y] = numpy.meshgrid(k,n)
f = mlab.mesh(x, y, -Mz/1.5,representation = 'wireframe',opacity=0.3,line_width=1)
mlab.savefig('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\figs\\'+'{0:05}'.format(i)+'.png')
mlab.clf()
#mlab.savefig('B:\\Dropbox\\Master.Diploma\\figures\\vortex.png')
print(i)
mlab.show()
for anyone still interested in this, you could try wrapping whatever work you're doing in this context, which will disable rendering and return the disable_render value and camera views to their original states after the context exits.
with constant_camera_view():
do_stuff()
Here's the class:
class constant_camera_view(object):
def __init__(self):
pass
def __enter__(self):
self.orig_no_render = mlab.gcf().scene.disable_render
if not self.orig_no_render:
mlab.gcf().scene.disable_render = True
cc = mlab.gcf().scene.camera
self.orig_pos = cc.position
self.orig_fp = cc.focal_point
self.orig_view_angle = cc.view_angle
self.orig_view_up = cc.view_up
self.orig_clipping_range = cc.clipping_range
def __exit__(self, t, val, trace):
cc = mlab.gcf().scene.camera
cc.position = self.orig_pos
cc.focal_point = self.orig_fp
cc.view_angle = self.orig_view_angle
cc.view_up = self.orig_view_up
cc.clipping_range = self.orig_clipping_range
if not self.orig_no_render:
mlab.gcf().scene.disable_render = False
if t != None:
print t, val, trace
ipdb.post_mortem(trace)
I do not really see the problem in your plot but to reset the view after each plotting instance insert your view point:
mlab.view(azimuth=45, elevation=60, distance=0.01, focalpoint=(0,0,0))
directly above your mlab.savefig callwithin your for loop .
You could just use the vmin and vmax function in your mesh command, if u do so the scale will not change with your data and your camera should stay where it is.
Like this:
f = mlab.mesh(x, y, -Mz/1.5,representation = 'wireframe',vmin='''some value''',vmax='''some value''',opacity=0.3,line_width=1)

Categories

Resources