# Configure Jupyter so figures appear in the notebook
%matplotlib inline

# Configure Jupyter to display the assigned value after an assignment
%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'

# import functions from the modsim.py module
from modsim import *

"""
Explanation: Modeling and Simulation in Python
Chapter 20
Copyright 2017 Allen Downey
License: Creative Commons Attribution 4.0 International
End of explanation
"""


m = UNITS.meter
s = UNITS.second

"""
Explanation: Dropping pennies
I'll start by getting the units we need from Pint.
End of explanation
"""


init = State(y=381 * m, 
             v=0 * m/s)

"""
Explanation: And defining the initial state.
End of explanation
"""


g = 9.8 * m/s**2

"""
Explanation: Acceleration due to gravity is about 9.8 m / s$^2$.
End of explanation
"""


t_end = 10 * s

dt = 0.1 * s

"""
Explanation: I'll start with a duration of 10 seconds and step size 0.1 second.
End of explanation
"""


system = System(init=init, g=g, t_end=t_end, dt=dt)

"""
Explanation: Now we make a System object.
End of explanation
"""


def slope_func(state, t, system):
    """Compute derivatives of the state.
    
    state: position, velocity
    t: time
    system: System object containing `g`
    
    returns: derivatives of y and v
    """
    y, v = state
    g = system.g    

    dydt = v
    dvdt = -g
    
    return dydt, dvdt

"""
Explanation: And define the slope function.
End of explanation
"""


dydt, dvdt = slope_func(system.init, 0, system)
print(dydt)
print(dvdt)

"""
Explanation: It's always a good idea to test the slope function with the initial conditions.
End of explanation
"""


results, details = run_ode_solver(system, slope_func)
details

"""
Explanation: Now we're ready to call run_ode_solver
End of explanation
"""


results.head()

results.tail()

"""
Explanation: Here are the results:
End of explanation
"""


def plot_position(results):
    plot(results.y, label='y')
    decorate(xlabel='Time (s)',
             ylabel='Position (m)')

plot_position(results)
savefig('figs/chap20-fig01.pdf')

"""
Explanation: And here's position as a function of time:
End of explanation
"""


t_crossings = crossings(results.y, 0)

"""
Explanation: Onto the sidewalk
To figure out when the penny hit the sidewalk, we can use crossings, which finds the times where a Series passes through a given value.
End of explanation
"""


t_sidewalk = t_crossings[0] * s

"""
Explanation: For this example there should be just one crossing, the time when the penny hits the sidewalk.
End of explanation
"""


sqrt(2 * init.y / g)

"""
Explanation: We can compare that to the exact result.  Without air resistance, we have
$v = -g t$
and
$y = 381 - g t^2 / 2$
Setting $y=0$ and solving for $t$ yields
$t = \sqrt{\frac{2 y_{init}}{g}}$
End of explanation
"""


def event_func(state, t, system):
    """Return the height of the penny above the sidewalk.
    """
    y, v = state
    return y

"""
Explanation: The estimate is accurate to about 9 decimal places.
Events
Instead of running the simulation until the penny goes through the sidewalk, it would be better to detect the point where the penny hits the sidewalk and stop.  run_ode_solver provides exactly the tool we need, event functions.
Here's an event function that returns the height of the penny above the sidewalk:
End of explanation
"""


results, details = run_ode_solver(system, slope_func, events=event_func)
details

"""
Explanation: And here's how we pass it to run_ode_solver.  The solver should run until the event function returns 0, and then terminate.
End of explanation
"""


results.tail()

"""
Explanation: The message from the solver indicates the solver stopped because the event we wanted to detect happened.
Here are the results:
End of explanation
"""


t_sidewalk = get_last_label(results) * s

"""
Explanation: With the events option, the solver returns the actual time steps it computed, which are not necessarily equally spaced. 
The last time step is when the event occurred:
End of explanation
"""


v_sidewalk = get_last_value(results.v)

"""
Explanation: The result is accurate to about 4 decimal places.
We can also check the velocity of the penny when it hits the sidewalk:
End of explanation
"""


km = UNITS.kilometer
h = UNITS.hour
v_sidewalk.to(km / h)

"""
Explanation: And convert to kilometers per hour.
End of explanation
"""


source_code(crossings)

"""
Explanation: If there were no air resistance, the penny would hit the sidewalk (or someone's head) at more than 300 km/h.
So it's a good thing there is air resistance.
Under the hood
Here is the source code for crossings so you can see what's happening under the hood:
End of explanation
"""


# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here

"""
Explanation: The documentation of InterpolatedUnivariateSpline is here.
Exercises
Exercise: Here's a question from the web site Ask an Astronomer:
"If the Earth suddenly stopped orbiting the Sun, I know eventually it would be pulled in by the Sun's gravity and hit it. How long would it take the Earth to hit the Sun? I imagine it would go slowly at first and then pick up speed."
Use run_ode_solver to answer this question.
Here are some suggestions about how to proceed:


Look up the Law of Universal Gravitation and any constants you need.  I suggest you work entirely in SI units: meters, kilograms, and Newtons.


When the distance between the Earth and the Sun gets small, this system behaves badly, so you should use an event function to stop when the surface of Earth reaches the surface of the Sun.


Express your answer in days, and plot the results as millions of kilometers versus days.


If you read the reply by Dave Rothstein, you will see other ways to solve the problem, and a good discussion of the modeling decisions behind them.
You might also be interested to know that it's actually not that easy to get to the Sun.
End of explanation
"""


