Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/plot_field.py
4034 views
1
"""
2
Plotting fields
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2006 Alex Clemesha <[email protected]>,
6
# William Stein <[email protected]>,
7
# 2008 Mike Hansen <[email protected]>,
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
#
11
# This code is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
# General Public License for more details.
15
#
16
# The full text of the GPL is available at:
17
#
18
# http://www.gnu.org/licenses/
19
#*****************************************************************************
20
from sage.plot.primitive import GraphicPrimitive
21
from sage.misc.decorators import options
22
from sage.misc.misc import xsrange
23
24
# Below is the base class that is used to make 'field plots'.
25
# Its implementation is motivated by 'PlotField'.
26
# Currently it is used to make the functions 'plot_vector_field'
27
# and 'plot_slope_field'.
28
# TODO: use this to make these functions:
29
# 'plot_gradient_field' and 'plot_hamiltonian_field'
30
class PlotField(GraphicPrimitive):
31
"""
32
Primitive class that initializes the
33
PlotField graphics type
34
"""
35
def __init__(self, xpos_array, ypos_array, xvec_array, yvec_array, options):
36
"""
37
Create the graphics primitive PlotField. This sets options
38
and the array to be plotted as attributes.
39
40
EXAMPLES::
41
42
sage: x,y = var('x,y')
43
sage: R=plot_slope_field(x+y,(x,0,1),(y,0,1),plot_points=2)
44
sage: r=R[0]
45
sage: r.options()['headaxislength']
46
0
47
sage: r.xpos_array
48
[0.0, 0.0, 1.0, 1.0]
49
sage: r.yvec_array
50
masked_array(data = [0.0 0.707106781187 0.707106781187 0.894427191],
51
mask = [False False False False],
52
fill_value = 1e+20)
53
54
TESTS:
55
56
We test dumping and loading a plot::
57
58
sage: x,y = var('x,y')
59
sage: P = plot_vector_field((sin(x), cos(y)), (x,-3,3), (y,-3,3))
60
sage: Q = loads(dumps(P))
61
"""
62
self.xpos_array = xpos_array
63
self.ypos_array = ypos_array
64
self.xvec_array = xvec_array
65
self.yvec_array = yvec_array
66
GraphicPrimitive.__init__(self, options)
67
68
def get_minmax_data(self):
69
"""
70
Returns a dictionary with the bounding box data.
71
72
EXAMPLES::
73
74
sage: x,y = var('x,y')
75
sage: d = plot_vector_field((.01*x,x+y), (x,10,20), (y,10,20))[0].get_minmax_data()
76
sage: d['xmin']
77
10.0
78
sage: d['ymin']
79
10.0
80
"""
81
from sage.plot.plot import minmax_data
82
return minmax_data(self.xpos_array, self.ypos_array, dict=True)
83
84
def _allowed_options(self):
85
"""
86
Returns a dictionary with allowed options for PlotField.
87
88
EXAMPLES::
89
90
sage: x,y = var('x,y')
91
sage: P=plot_vector_field((sin(x), cos(y)), (x,-3,3), (y,-3,3))
92
sage: d=P[0]._allowed_options()
93
sage: d['pivot']
94
'Where the arrow should be placed in relation to the point (tail, middle, tip)'
95
"""
96
return {'plot_points':'How many points to use for plotting precision',
97
'pivot': 'Where the arrow should be placed in relation to the point (tail, middle, tip)',
98
'headwidth': 'Head width as multiple of shaft width, default is 3',
99
'headlength': 'head length as multiple of shaft width, default is 5',
100
'headaxislength': 'head length at shaft intersection, default is 4.5',
101
'zorder':'The layer level in which to draw',
102
'color':'The color of the arrows'}
103
104
def _repr_(self):
105
"""
106
String representation of PlotField graphics primitive.
107
108
EXAMPLES::
109
110
sage: x,y = var('x,y')
111
sage: P=plot_vector_field((sin(x), cos(y)), (x,-3,3), (y,-3,3))
112
sage: P[0]
113
PlotField defined by a 20 x 20 vector grid
114
"""
115
return "PlotField defined by a %s x %s vector grid"%(self.options()['plot_points'], self.options()['plot_points'])
116
117
def _render_on_subplot(self, subplot):
118
"""
119
TESTS::
120
121
sage: x,y = var('x,y')
122
sage: P=plot_vector_field((sin(x), cos(y)), (x,-3,3), (y,-3,3))
123
"""
124
options = self.options()
125
quiver_options = options.copy()
126
quiver_options.pop('plot_points')
127
subplot.quiver(self.xpos_array, self.ypos_array, self.xvec_array, self.yvec_array, angles='xy', **quiver_options)
128
129
@options(plot_points=20,frame=True)
130
def plot_vector_field((f, g), xrange, yrange, **options):
131
r"""
132
``plot_vector_field`` takes two functions of two variables xvar and yvar
133
(for instance, if the variables are `x` and `y`, take `(f(x,y), g(x,y))`)
134
and plots vector arrows of the function over the specified ranges, with
135
xrange being of xvar between xmin and xmax, and yrange similarly (see below).
136
137
``plot_vector_field((f, g), (xvar, xmin, xmax), (yvar, ymin, ymax))``
138
139
EXAMPLES:
140
141
Plot some vector fields involving sin and cos::
142
143
sage: x,y = var('x y')
144
sage: plot_vector_field((sin(x), cos(y)), (x,-3,3), (y,-3,3))
145
146
::
147
148
sage: plot_vector_field(( y, (cos(x)-2)*sin(x)), (x,-pi,pi), (y,-pi,pi))
149
150
Plot a gradient field::
151
152
sage: u,v = var('u v')
153
sage: f = exp(-(u^2+v^2))
154
sage: plot_vector_field(f.gradient(), (u,-2,2), (v,-2,2), color='blue')
155
156
Plot two orthogonal vector fields::
157
158
sage: x,y = var('x,y')
159
sage: a=plot_vector_field((x,y), (x,-3,3),(y,-3,3),color='blue')
160
sage: b=plot_vector_field((y,-x),(x,-3,3),(y,-3,3),color='red')
161
sage: show(a+b)
162
163
We ignore function values that are infinite or NaN::
164
165
sage: x,y = var('x,y')
166
sage: plot_vector_field( (-x/sqrt(x^2+y^2), -y/sqrt(x^2+y^2)), (x, -10, 10), (y, -10, 10))
167
168
::
169
170
sage: x,y = var('x,y')
171
sage: plot_vector_field( (-x/sqrt(x+y), -y/sqrt(x+y)), (x, -10, 10), (y, -10, 10))
172
173
Extra options will get passed on to show(), as long as they are valid::
174
175
sage: plot_vector_field((x, y), (x, -2, 2), (y, -2, 2), xmax=10)
176
sage: plot_vector_field((x, y), (x, -2, 2), (y, -2, 2)).show(xmax=10) # These are equivalent
177
"""
178
from sage.plot.all import Graphics
179
from sage.plot.misc import setup_for_eval_on_grid
180
z, ranges = setup_for_eval_on_grid([f,g], [xrange, yrange], options['plot_points'])
181
f,g = z
182
183
xpos_array, ypos_array, xvec_array, yvec_array = [],[],[],[]
184
for x in xsrange(*ranges[0], include_endpoint=True):
185
for y in xsrange(*ranges[1], include_endpoint=True):
186
xpos_array.append(x)
187
ypos_array.append(y)
188
xvec_array.append(f(x,y))
189
yvec_array.append(g(x,y))
190
191
import numpy
192
xvec_array = numpy.ma.masked_invalid(numpy.array(xvec_array, dtype=float))
193
yvec_array = numpy.ma.masked_invalid(numpy.array(yvec_array, dtype=float))
194
g = Graphics()
195
g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
196
g.add_primitive(PlotField(xpos_array, ypos_array, xvec_array, yvec_array, options))
197
return g
198
199
def plot_slope_field(f, xrange, yrange, **kwds):
200
r"""
201
``plot_slope_field`` takes a function of two variables xvar and yvar
202
(for instance, if the variables are `x` and `y`, take `f(x,y)`), and at
203
representative points `(x_i,y_i)` between xmin, xmax, and ymin, ymax
204
respectively, plots a line with slope `f(x_i,y_i)` (see below).
205
206
``plot_slope_field(f, (xvar, xmin, xmax), (yvar, ymin, ymax))``
207
208
EXAMPLES:
209
210
A logistic function modeling population growth::
211
212
sage: x,y = var('x y')
213
sage: capacity = 3 # thousand
214
sage: growth_rate = 0.7 # population increases by 70% per unit of time
215
sage: plot_slope_field(growth_rate*(1-y/capacity)*y, (x,0,5), (y,0,capacity*2))
216
217
Plot a slope field involving sin and cos::
218
219
sage: x,y = var('x y')
220
sage: plot_slope_field(sin(x+y)+cos(x+y), (x,-3,3), (y,-3,3))
221
222
Plot a slope field using a lambda function::
223
224
sage: plot_slope_field(lambda x,y: x+y, (-2,2), (-2,2))
225
226
TESTS:
227
228
Verify that we're not getting warnings due to use of headless quivers
229
(trac #11208)::
230
231
sage: x,y = var('x y')
232
sage: import numpy # bump warnings up to errors for testing purposes
233
sage: old_err = numpy.seterr('raise')
234
sage: plot_slope_field(sin(x+y)+cos(x+y), (x,-3,3), (y,-3,3))
235
sage: dummy_err = numpy.seterr(**old_err)
236
"""
237
slope_options = {'headaxislength': 0, 'headlength': 1e-9, 'pivot': 'middle'}
238
slope_options.update(kwds)
239
240
from sage.functions.all import sqrt
241
from inspect import isfunction
242
if isfunction(f):
243
norm_inverse=lambda x,y: 1/sqrt(f(x,y)**2+1)
244
f_normalized=lambda x,y: f(x,y)*norm_inverse(x,y)
245
else:
246
norm_inverse = 1/sqrt((f**2+1))
247
f_normalized=f*norm_inverse
248
return plot_vector_field((norm_inverse, f_normalized), xrange, yrange, **slope_options)
249
250
251