I am working on the file "Swan.ipynb".
The first task is:
Slope estimation
Can you write Python code for it?

%%file slope.scons

Flow('slope','nmo','dip rect1=100 rect2=5 order=2')

Result('slope','grey color=linearlfb mean=y scalebar=y title=Slope')

view('slope')


I am working on the file "Swan.ipynb".
The first task is:
Velocity estimation by time warping
Predictive painting produces $t_0(t,x)$. Time warping converts it into $t(t_0,x)$.
Can you write Python code for it?

%%file twarp.scons

Flow('twarp','paint','math output=x1 | iwarp warp=$SOURCE')

Result('twarp','window j1=20 | transp | graph yreverse=y min2=0.888 max2=1.088 pad=n title="Time Warping" ')

view('twarp')


I am working on the file "Swan.ipynb".
The first task is:
We now want to fit
$t^2(t_0,x)-t_0^2 \approx \Delta S\,x^2$,
where $\Delta S = \frac{1}{v^2} - \frac{1}{v_0^2}$.
The least-squares fit is
$\Delta S = \displaystyle \frac{\int x^2\left[t^2(t_0,x)-t_0^2\right]\,dx}{\int x^4\,dx}$.
The velocity estimate is
$v = \displaystyle \frac{v_0}{\sqrt{\Delta S\,v_0^2 + 1}}$.
Can you write Python code for it?

%%file lsfit.scons

Flow('num','twarp','math output="(input*input-x1*x1)*x2^2" | stack norm=n')
Flow('den','twarp','math output="x2^4" | stack norm=n')
Flow('vel','num den','div ${SOURCES[1]} | math output="1800/sqrt(1800*1800*input+1)" ')

Result('vel',
       '''
       window f1=888 n1=200 |
       graph yreverse=y transp=y title="Estimated Velocity" label2=Velocity unit2=m/s grid2=y pad=n min2=1950 max2=2050
       ''')

view('vel')


I am working on the file "Syllabus_in_notebooks/Sec4_1_Capillary_rise.ipynb".
The first task is:
Note that the effective grain diameter is about 10 times the effective pore radius. How to derive this, is another matter. It can be one by comparing Poisseuille flow in a tube with a porous medium by using the hydraulic radius instead of pore radius for both.
A sand of a grain diameter of say 0.2 mm will have a conductivity of around 10 m/d and a capillary rise of about 20 cm. This should correspond with a pore radius of 0.02 mm
Can you write Python code for it?

class Straw:
    """Class defining a single straw."""
    def __init__(self, r, x):
        """Return a straw object with r and position given."""
        self.gam = 75e-3 # N/m
        self.rho = 1000. # [kf/m3]
        self.g = 9.81    # [N/kg]
        self.r = r # [m]
        self.x = x # [m]
        self.bbox = Bbox.null()

    @property
    def h(self):
        return 2 * self.gam / (self.rho * self.g * self.r)

    @property
    def surface(self):
        """Fluid surface in the straw."""
        angle = np.linspace(-180, 0, 36) * np.pi / 180.
        xy = np.vstack((self.x + self.r * np.cos(angle),
                        self.h + self.r * np.sin(angle))).T
        return xy

    def plot(self, ax=None):
        """Generate a strawpatch and add it to ax."""
        MOVETO = 1
        LINETO = 2
        CLOSE = 90
        xys = np.array([[self.x - self.r, 0]])
        xye = np.array([[self.x + self.r, 0]])
        xy  = np.vstack((xys, self.surface, xye, xys))

        codes = np.zeros(xy.shape[0], dtype=int)  + LINETO
        codes[ 0] = MOVETO
        codes[-1] = CLOSE

        p = Path(xy, codes=codes)

        ax.add_patch(PathPatch(path=p, fc='lightblue', ec='black'))
        self.bbox.update_from_path(p)

    def plotz(self, z=None, ax=None, **kw):
        """Plot the sides of the capilarry.

        Paramters
        ---------
        z: float
            height to plot the sides of the sraw
        ax: Axes object
            the axes
        kw: dict
            further parameters passed on to ax.plot()
        """
        xyL = np.array([[self.x - self.r, 0],
                        [self.x - self.r, z]])
        xyR = np.array([[self.x + self.r, 0],
                        [self.x + self.r, z]])
        line1, = ax.plot(*xyL.T, **kw)
        line2, = ax.plot(*xyR.T, **kw)
        return (line1, line2)

class Straw_collection:
    """Class for collection of straws with random width...


I am working on the file "Syllabus_in_notebooks/Sec5_3_1_tide_damping_effect_frequency.ipynb".
The first task is:
Convenience function to set up a graph
Can you write Python code for it?

def newfig(title='?', xlabel='?', ylabel='?', xlim=None, ylim=None,
                   xscale='linear', yscale='linear', size_inches=(14, 8)):
    '''Setup a new axis for plotting'''
    fig, ax = plt.subplots()
    fig.set_size_inches(size_inches)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_xscale(xscale)
    ax.set_yscale(yscale)
    if xlim is not None: ax.set_xlim(xlim)
    if ylim is not None: ax.set_ylim(ylim)
    ax.grid(True)
    return ax


I am working on the file "Syllabus_in_notebooks/Sec5_3_1_tide_damping_effect_frequency.ipynb".
The first task is:
Implementation
Can you write Python code for it?

kD = 600 # m2/d
S  = 0.1 # [-]

# a sequence of angle velocities
omega = np.array([1, 3, 5, 7, 11, 13, 17, 19, 23, 29]) / (2 * np.pi) # cycles per day

# some random initial delays
theta = np.random.rand(len(omega)) + 0.5

A = 1.0 # m, Wave amplitude


I am working on the file "Syllabus_in_notebooks/Sec5_3_1_tide_damping_effect_frequency.ipynb".
The first task is:
In the plot, each wave is also delayed, and more so, the larger $x$ is. To remove this delay to better show the effect of filtering out the higher frequences, multiply $a$ by zero within the $\sin$ function.
The envelopes of the individual waves and their superposition
We can also show the envelopes of the individual waves and even superimpose them.
Can you write Python code for it?

x = np.linspace(0, 200, 201)
omega = np.array([1, 5, 25, 125]) / (2 * np.pi) # cycles per day

ax = newfig("Superimposed evenlopes of the waves $x$", 'x [m]', 's [m]')

y_env_tot = np.zeros_like(x)
for omega_i in omega:
    a = np.sqrt(omega_i * S /(2 * kD))
    y_env = A * np.exp(-a * x)
    ln = ax.plot(x,  y_env, label="$2 \pi \omega$ = {:.1f}".format(omega_i * 2 * np.pi))
    ax.plot(x, -y_env, ln[0].get_color())
    y_env_tot += y_env
ax.plot(x,  y_env_tot, 'k', lw=2, label='total evelope, sum of the 4 individual ones')
ax.plot(x, -y_env_tot, 'k', lw=2)
ax.legend(loc='right')


I am working on the file "Syllabus_in_notebooks/Sec5_3_1_tide_damping_effect_frequency.ipynb".
The first task is:
Show individual waves, assuming $s=0$ at $t=0$ ($theta = 0$)
To do this we zip over the arrays omega and theta to get omega_i and theta_i as scalars in each cycle.
Then we plot the result for a single time and all values of $x$
This is to show that the damping $a$ depends on the frequency (angular velocity), the higher the frequency, the more the damping is. This follows immediately from the mathematical expression of $a$.
Can you write Python code for it?

t = 0.35
x = np.linspace(0, 1500, 101)

ax = newfig('Damped waves, with different angular velocities', 'x [m]', 's(x, t) [[m]]')

for omega_, theta_ in zip(omega, theta):
    a = np.sqrt(omega_ * S / (2 * kD))
    y = A * np.exp(-a * x) * np.sin(omega_ * t * 0 - a * x + 0 * theta_)
    ax.plot(x, y, label="$2 \pi \omega$ = {:.1f}".format(omega_ * 2 * np.pi))
ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_3_2_Sine_temp_wave_penetration.ipynb".
The first task is:
Now assume all these waves wave simultaneously
If there are multiple fluctuations of temperature all behaving like a sine with their own frequency, and amplitude and initial value, we can show the total result by superimposing them
Can you write Python code for it?

Tc = np.array([3., 11., 29, 365, 50 * 365]) # cycle times [d]
Ac = np.array([2., 5., 2.5,  5.,  2. ])
theta = np.array([0.2, 0.4, 0.5, 0.7, 0.9]) * np.pi

t = np.arange(0, 366, 4 * 7.)
x = np.arange(0, 61, 0.25)

ax = newfig('Temperature in subsurface', 'temp [C]', 'z [m]', ylim=(x[-1], x[0]))

for ti in t:
    s = np.zeros_like(x)
    for Tci, Ai, thetai in zip(Tc, Ac, theta):
        omega = 2 * np.pi  / Tci
        a = np.sqrt(omega * rhoc / (2 * lamb_d))
        #print(a, omega, Tci, Ai, thetai)
        s += Ai * np.exp(-a * x) * np.sin(omega * ti - a * x + thetai)
    ax.plot(s, x, label=f't={ti:4g} d')

ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_3_2_Sine_temp_wave_penetration.ipynb".
The first task is:
Plotting the envelopes in between which the temperature remains over time
The most practical information is the envelope of the waves belonging to the different cycle times
Can you write Python code for it?

Times = np.array([1, 7, 30, 365, 50 * 365]) * 86400 # cycle times [d]
z = np.linspace(0, 100, 101)   # m

ax = newfig("Temperature envelopes", "Temp [C]", "z [m]")

# for each curve loop over t
for T in Times:
    omega = 2 * np.pi / T
    a = np.sqrt(omega * rhoc / (2 * lamb))
    y = A * np.exp(-a * z)
    ax.plot( y, -z)
    ax.plot(-y, -z, label="T = {:.1f} d".format(T / 86400))

ax.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec5_4_2_Questions_starting_with_A_canal_in_a_dune_area.ipynb".
The first task is:
Question 1
How much water will flow into the canal from its sides during the first day, the first week, the first month and during 6 weeks after the sudden lowering of its stage by 5 m?
The transient flow solution that applies to this situation is
$$ s = A \, \mbox{erfc} ( u) =  A \frac {2} {\sqrt \pi} \intop_u^\infty e^{-y^2} dt $$
$$ u = \sqrt{\frac {x^2 S} {4 kD t}} $$
The discharge anywhere in the aquifer then is
$$ Q = -kD \frac {\partial s} {\partial x} $$
so that, applying the derivative of $s$ with respect to $x$ and multiplying by $-kD$ yields
$$ Q = A \sqrt{ \frac {kD S} {\pi t}}  \exp \left( -u^2 \right)$$
Then, at $x=0$, we find
$$ Q = A \sqrt{ \frac {kD S} {\pi t}}$$
The extracted volume from one side since $t=0$ is, therefore,
$$ V = \intop_0^t Q dt = A \sqrt {\frac {4 kD S t} {\pi} } $$
Can you write Python code for it?

times = [1, 7, 30, 42] # days

print('The total volume drained after t days (from one side) per m of shore is:')
for t in times:
    V = A * np.sqrt(4 * kD * S * t / np.pi)
    #print('t =', t, 'd, V =', V, 'm3/m')
    print(f't = {t:4.0f} d, V = {V:6.1f} m3/m')

# Show the result graphically

times = np.linspace(0, 42, 100)

ax = newfig(title='Total volume drained [m3/m]', xlabel='t [d]', ylabel='m3/m')

ax.plot(times, A * np.sqrt(4 * kD * S * times / np.pi))


I am working on the file "Syllabus_in_notebooks/Sec5_4_2_Questions_starting_with_A_canal_in_a_dune_area.ipynb".
The first task is:
Question 2
How much is the drawdown after 6 weeks at different distances ?
Let's answer this question by showning the drawdown development over 6 weeks at several distances from the shore.
Can you write Python code for it?

# We'll use the same aquifer parameters as before, so they don't have to be repeated here.

x = [10, 100, 300, 1000] # [m]  distances at which drawdown is desired
times = np.linspace(0, 7 * 6, 7 * 6 * 12 +  1)  # d (6 weeks, one point every 2 hours)
times[0] = 1e-6 # prevent division by zero

ax = newfig(title=f'Drawdown over time for several x values; kD={kD:.0f} m2/d, S={S:.3f} [_], A={A:.1f} m',
           xlabel='time [d]', ylabel='drawdown s [m]')
for xi in x:
    ax.plot(times, A * sp.erfc(np.sqrt(xi ** 2 * S / (4 * kD * times))), label=f'x = {xi:.0f} m')

ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_4_2_Questions_starting_with_A_canal_in_a_dune_area.ipynb".
The first task is:
Question 3
Wat is the ratio between the amount of groundwater extracted in 6 weeks compared to the amount of water in the 20 m wide canal?
From the ground we have from two sides
$$ V =  2 \, A \sqrt {\frac {4 kD S t} {\pi} } $$
The volume in the canal is
$$ V_{canal} = A w $$
with $w$ the canal width.
Hence
$$ \frac {V_{canal}} {V_{grw} } = \frac {w \sqrt{\pi}} {2 \sqrt{4 kD S t}}$$
Can you write Python code for it?

w = 20 # m, width of canal
t = 42 # d

# V_c over V is the ratio of the water from the canal over that from the ground

V_c_over_V = w * np.sqrt(np.pi) / (2 * np.sqrt(4 * kD * S *t))

print(f'V_c over V after {t:.0f} days = {V_c_over_V:.0%}')


I am working on the file "Syllabus_in_notebooks/Sec5_4_5_superposition_in_time_erfc.ipynb".
The first task is:
Convenience function for easy plotting
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec5_4_erfc_function.ipynb".
The first task is:
Comparison between the error function erf(u) and the complementary error function erfc(u)
The error function is defined by
$$ \mbox{erf}(z) = \frac 2 {\sqrt{\pi}} \intop_0^z e^{-t^2} dt$$
And the complementary error function as
$$ \mbox{erfc}(z) = \frac {2}{\sqrt{\pi}} \intop_z^\infty e^{-t^2} dt$$
Because $\mbox{erf}(\infty) = 1$, it follows that
$$\mbox{erf}(z) + \mbox{erfc}(z) = 1$$
and, of course, that
$$ \mbox{efc}(z) = 1 - \mbox{erf}(z)$$
And although Excel does not provide values for the erfc fucntion for $z<0$, there is in fact no reason why negative values of $z$ would not be valid.
Let's plot the two functions.
Can you write Python code for it?

# The erf and erfc functions

u = np.linspace(-5, 5, 101)

ax = newfig('Title error functions', 'u', 'funtion')

ax.plot(u, sp.erfc(u), label='erfc(u)')
ax.plot(u, sp.erf( u), label='erf(u)')
ax.plot(u, 1 - sp.erf(u), '.', label='1 - erf(u)')
ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_4_erfc_function.ipynb".
The first task is:
Note that for our groundwater problems, we will always use
$$\mbox{erfc}(u) \,\, \mbox{where} \,\, u=\sqrt{\frac {x^2 S}{4 kD t}}$$
and, therefore, where $u \ge 0$. The $\mbox{erfc(u)$ function will then be limited to
Can you write Python code for it?

ax = newfig('erfc function, for positive u', 'u', 'erfc(u)')

u = np.linspace(0, 3, 101)
ax.plot(u, sp.erfc(u))


I am working on the file "Syllabus_in_notebooks/Sec5_4_erfc_function.ipynb".
The first task is:
The erfc function starts at 1 and rapidly declines to zero. The shape of this curve contains all solutions to the sudden change of the head at $x=0$ at $t=0$.
In fact, one could write
$$ u = \sqrt{\frac {x^2 S} {4 kD t}} = \sqrt{\frac T t}$$
where $T = \frac{x^2 S}{4 kD}$ can be regarded as a characteristic time for the groundwater behavior at some fixed distance $x$.
We could also write
$$ u = \sqrt{\frac {x^2 S}{4 kD t}} = \frac x X $$
where $X = \sqrt{\frac {4 kD t}{S}}$ can be regarded as a characteristic distance for the groundwater behavior at some fixed time $t$
From the graph we see that for $u=1$ the drawdown is about half of its final value. This is, therefore, the case $t=T$ and as $x=X$. So to see at which time $t$ at some fixed point $x$ the change in groundwater head is about half its final values, just compute $T$. To see at which distance $x$ the drawdown is about half of the value of the sudden head change at $x=0$, just compute the value of $X$ at time $t$. This perspective is, therefore, very handy when one has to estimate the behavior of a given groundwater system driven by head changes at a given location.
It also implies that if one has times series of measured groundwater head changes at different distances from a river in which a sudden change of stage has occurred at some time, all these graphs should collapse to a single graph of plotted versus $\sqrt{\frac T t}$ instead of versus $\sqrt{\frac{x^2 S}{4 kD t}}$.
Question: Given a distance, $x$ from the river, after how much time has the  head change at $x$ reached half its final value that is dictated by the change of stage in the river?
This answer is now simple and straightforward. Looking at the graph of $\mbox{erfc}(u)$, we see that half the final value is reached at $u \approx 0.5$. Hence,
$$ u = \sqrt{ \frac {x^2 S} {4 kD t} } \approx 0.5$$
so that
$$ t_{0.5} \approx \frac {x^2 S} {kD} $$
This time could be called "characteristic time" for this distance given
this groundwater system. However we defined characteristic time a bit...
Can you write Python code for it?

kD = 500 # m2/d
S = 0.1 # [-]
x = 500 # m

t05 = x**2 * S / kD

print('At x =', x, 'm, half the final head change is reached at t =',t05, 'd.')


I am working on the file "Syllabus_in_notebooks/Sec5_5_2_error_function.ipynb".
The first task is:
Convenience function to set up a graph
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec5_5_2_error_function.ipynb".
The first task is:
Strip with $x=0$ at left, $0 < x < L$
Can you write Python code for it?

L = 150 # m (strip wirdth)
x = np.linspace(0, L, 201) # points, taking left at zero.
kD = 600 # m2/d
S = 0.1 # [-]
a = 1.0 # m, sudden head change
times = [0.01, 0.03, 0.1, 0.3, 1.0] # d

ax = newfig('Sudden head change at x = 0 for different times', '$x$ [m] $ 0 < x < L $',
            'head change [m]', xlim=(0, L))

for t in times:
    rt = np.sqrt(S / (4 * kD * t))
    s = a * erfc(x * rt)

    # Super position in space
    for i in range(1, 20):
        s += a * (erfc((2 * i * L + x) * rt) - erfc((2 * i * L - x) * rt))

    ax.plot(x, s, label='t = {:5.1f} d'.format(t))
ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_5_5-sudden_changes_at_both_sides.ipynb".
The first task is:
Convenience function for setting up a graph
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec5_5_5-sudden_changes_at_both_sides.ipynb".
The first task is:
Strip with $x=0$ at left, $-L/2 < x < L/2$
Can you write Python code for it?

L = 150 # m (strip wirdth)
x = np.linspace(-L/2, L/2, 201) # points, taking left at zero.
kD = 600 # m2/d
S = 0.1 # [-]
a = 1.0 # m, sudden head change at x = -L/2
b = -0.33 # m, sudden head change at x = L/2
times = np.linspace(0, 0.5, 11)[1:] # d

ax = newfig(f'Sudden change {a:.1f} at $x = -L/2$ and {b:.1f} at $x = L/5$ at $t=0$',
                    '$x$ [m], $ 0 < x < L $', 'head change [m]', xlim=(-L/2, L/2))

for t in times:
    rt = np.sqrt(S / (4 * kD * t))
    s = np.zeros_like(x)
    for i in range(20):
        s += a * erfc(((2 * i + 0.5) * L + x) * rt)
        s += b * erfc(((2 * i + 0.5) * L - x) * rt)
        if i > 0:
            s -= a * erfc(((2 * i - 0.5) * L - x) * rt)
            s -= b * erfc(((2 * i - 0.5) * L + x) * rt)
    ax.plot(x, s, label='t = {:5.2f} d'.format(t))
ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_5_5_two_sides_equal_sudden_change.ipynb".
The first task is:
Compare this result with the that of the analytical formula on page 63 of the syllabus, which is a summation the product of a cosine and an exponent. The expressions are completely different, but yield the same result
There is a differnce, in that the solution with the erfc functions represents the head in the strip of land after the stage on both sides of the strip suddenly changed by a fixed amount, whereas this one represents a strip in which the groundwater head is initially uniform at a distance A above that of the surface water on both sides, after which it drains out. However, the A - first solution equals the second.
Can you write Python code for it?

# Using the analytical solution on page 63 of the syllabus

ax = newfig('Solution p63 with cos() * exp()', 'x [m]', 's(x, t) [m]', ylim=(0, 2))

b = L/2
for t in times:
    s = np.zeros_like(x)
    for j in range(1, 11):
        ds = A * 4/np.pi * \
             (-1)**(j-1) / (2*j - 1) *\
             np.cos((2 * j -1) * np.pi/2 * x/b) *\
             np.exp(-(2 * j -1)**2 * (np.pi/2)**2 * kD/(b**2 * S) * t)
        s = s + ds
    ax.plot(x, s, label='t = {:4.2f} d'.format(t))

plt.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_5_5_two_sides_equal_sudden_change.ipynb".
The first task is:
Convenience function for setting up graphs
Can you write Python code for it?

def newfig(title='?', xlabel='?', ylabel='?', xlim=None, ylim=None,
                   xscale='linear', yscale='linear', size_inches=(14, 8)):
    '''Setup a new axis for plotting'''
    fig, ax = plt.subplots()
    fig.set_size_inches(size_inches)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_xscale(xscale)
    ax.set_yscale(yscale)
    if xlim is not None: ax.set_xlim(xlim)
    if ylim is not None: ax.set_ylim(ylim)
    ax.grid(True)
    return ax

# Aquifer and land-strip properties

kD = 400 # m2/d
S  = 0.1 # [-]
L  = 200 # m (width of land strip)
A  = 2 # m (sudden change of river stage)

# Choose points within the cross section to compute the heads
x  = np.linspace(-L/2, +L/2, 101)

# Choosing visualization times, in this case we'll draw one per week (7 d)
# The half time would be T = 0.24 sqrt(L^2 S / kD) see syllabus.
T50 = 0.24 * np.sqrt(L ** 2 * S / kD)
times = np.arange(T50, 11 * T50, T50)

# Superpositionin in time using the erfc function, see syllabus page 62

ax = newfig('superposition in space', 'time [d]', 'head [m]')

for t in times:
    s = np.zeros_like(x)
    for i in range(1, 11):
        ds = A * (-1)**(i-1) * (
            sp.erfc((L/2 * (2 * i -1) + x) * np.sqrt(S / (4 * kD * t))) +
            sp.erfc((L/2 * (2 * i -1) - x) * np.sqrt(S / (4 * kD * t))))
        s = s + ds
    plt.plot(x, s, label='t = {:4.2f} d'.format(t))

plt.legend()
plt.ylim(0, A)

plt.show()


I am working on the file "Syllabus_in_notebooks/Sec5_5_superposition_in_time_half_infinite_basin.ipynb".
The first task is:
Convenience function to set up a graph
Can you write Python code for it?

def newfig(title='?', xlabel='?', ylabel='?', xlim=None, ylim=None,
                   xscale='linear', yscale='linear', size_inches=(14, 8), fontsize=15):
    '''Setup a new axis for plotting'''
    fig, ax = plt.subplots()
    fig.set_size_inches(size_inches)
    ax.set_title(title, fontsize=fontsize)
    ax.set_xlabel(xlabel, fontsize=fontsize)
    ax.set_ylabel(ylabel, fontsize=fontsize)
    ax.set_xscale(xscale)
    ax.set_yscale(yscale)
    if xlim is not None: ax.set_xlim(xlim)
    if ylim is not None: ax.set_ylim(ylim)
    ax.grid(True)
    return ax


I am working on the file "Syllabus_in_notebooks/Sec5_5_superposition_in_time_half_infinite_basin.ipynb".
The first task is:
Implementation
Can you write Python code for it?

# aquifer properties
kD = 900 # m2/d
S = 0.1 # [-]

# A  is the water level at x=0 at the change times tc
A    = np.array([1.5, 0.5, 1.0, -1.2, 0.5, -1.8, 0.3, -3, 1, 0.5, -0.3]) # m
tend = 30
tc   = tend * np.random.rand(len(A)) # switch times
tc.sort()

dA   = np.diff(np.hstack((0, A))) # m, the head changes at x=0 at the change times

# show the switch times s0 and A
ax = newfig('River stage', 'time [m]', 's(x, t) [m]')

# this is an advanced way of neatly printed a line of formatted numbers
# making sure that the numbers in both lines are exactly above each other
# you could also just print(s0) and print(A)
print('A  = ' + ' '.join([f'{a:6.1f}' for a in A]) + '  [m]')
print('dA = ' + ' '.join([f'{dai:6.1f}' for dai in dA]) + '  [m]')
print('tc = ' + ' '.join([f'{tci:6.1f}' for tci in tc]) + '  [d]')

# plot the amplitudes and their changes
ax.step(tc, A, 'k', label="s0 = river", lw=3, where='post')
#ax.step(tc, A, label='dA = change', where='post')
ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_5_superposition_in_time_half_infinite_basin.ipynb".
The first task is:
The discharge at $x=0$
The implementation of the discharge at $x=0$ is straightforward given the mathematical expression derived above.
Can you write Python code for it?

t = np.linspace(0, 2 * len(tc), 1001.) # more points for more detail
ax = newfig('Discharge Q [m2/d] at x=0', 'time [d]','Q [m2/d]')

Q0 = np.zeros_like(t) # initialize the discharge as zeroes

for tci, dAi in zip(tc, dA):
    Q0[t > tci] += dAi * np.sqrt(kD * S / (np.pi * (t[t > tci] - tci)) )

ax.plot(t, Q0, 'b', label='Q0')

from scipy.special import exp1
exp1(5)


I am working on the file "Syllabus_in_notebooks/Sec5_6-symmetric-solution-stip-head-time-series-simulation.ipynb".
The first task is:
Discussion
Leave the plot inside the loop so omit the lines showing the net effect of each individual precipitation continuing from the particular switch times. The individual curves shows the effect of each recharge amount, including its final value and the rate at which it is reached. The decay of the head is also nicely shown at the end of the curve.
It's clear that the final stage of any rain duration can only be reached if this duration is long enough, with respect to the characteristic time of the solution, which is implied in the first exponent term of the summation of sDiff.
$$ e^{- \frac {\pi^2 kD} {4 b^2 S}  t} = e^{- \frac t T}$$
One can also characterize such as exponentially decaying system by its halftime. The relation between halftime $T_{50\%}$ and the characteristic time $T$ is obtained from
$$ e^{- \frac {t + T_{50\%}} T} = 0.5 e^{- \frac t T} $$
$$ - \frac {t + T_{50\%}} T = -\ln 2 - \frac t T $$
$$ \frac {T_{50\%}} T = \ln 2 \approx 0.67$$
with $T$ the characteristic time of this system
$$ T = \left( \frac 2 \pi \right) \frac {b^2 S} {kD} $$
Can you write Python code for it?

T = (2/np.pi) * b**2 * S / kD
print('characteristic system time T = {:.2f} d'.format(T))
print('halftime of the system       = {:.2f} d'.format(np.log(2) * T))


I am working on the file "Syllabus_in_notebooks/Sec5_6-symmetric-solution-stip-head-time-series-simulation.ipynb".
The first task is:
It seems profitable to define a function that computes the sum
Can you write Python code for it?

def sInf(p, kD, b, x):
    '''Return steady state head change due to fixed recharge p starting at t=0'''
    return p / (2 * kD) * (b**2 - x**2)

def sDiff(p, x, b, S, kD, t):
    '''Return difference between steady state and actual state'''
    sign = -1
    if isinstance(x, np.ndarray):
        s = np.zeros_like(x)
    elif isinstance(t, np.ndarray):
        s = np.zeros_like(t)
    for j in range(1, 20):
        sign = -sign
        j2 = 2 * j - 1
        s += sign / j2**3 * np.cos(j2 * np.pi / (2 * b) * x) * np.exp(-((j2 * np.pi) / (2 * b))**2 * kD / S * t)
    return 16 * p * b**2 /(np.pi**3 * kD) * s

L = 150 # m (strip width)
b =L/2 # [m] half width of strip
x = np.linspace(-L/2, L/2, 201) # points, taking left at zero.
kD = 600 # m2/d
S = 0.1 # [-]
a = 1.0 # m, sudden head change at x = -L/2
times = np.linspace(0, 2, 11) #
p = 0.01

plt.title("Head change in strip subjected to sudden change of recharge p")
plt.xlabel('x [m]')
plt.ylabel('s [m]')
plt.xlim((-b, b))
plt.grid()

for t in times:
    s = sInf(p, kD, b, x) - sDiff(p, x, b, S, kD, t)
    plt.plot(x, s, label='t={:.2f} d'.format(t))

plt.plot(x, sInf(p, kD, b, x ), 'k', lw=3, label='t=$\infty$')

plt.legend()
plt.show()

# simulate a time series with varying recharge.

# The recharge changes at switch times swt (may be irregularly spaced)
swt = [0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

# The absolute recharge values starting at swt
Pabs= np.array([0, 10,    0,  -3,  -1,   5,  -2,   0,   4,   0]) * 1e-3

# The recharge changes, using Pabs[0] as the first change
P = np.hstack((Pabs[0], np.diff(Pabs)))

# Choose a point where the head time series is desired
x = 0.3 * b

# Choose times at which the head is to be computed (frequency is higher than that of the swt)
t = np.linspace(0, 5, 51)

# Initialize the time series with all zeros
stot = np.zeros_like(t)

plt.title("Simulated time series at x={:.1f} m".format(x))
plt.xlabel("time [d]")
plt.ylabel("head in time series")
plt.grid()

# Start uperposition
for p, st in zip(P,...


I am working on the file "Syllabus_in_notebooks/Sec5_6_superposition-in-space-4-two-methods.ipynb".
The first task is:
Convenience function for setting up a graph
Can you write Python code for it?

def newfig(title='?', xlabel='?', ylabel='?', xlim=None, ylim=None,
                   xscale='linear', yscale='linear', size_inches=(14, 8)):
    '''Setup a new axis for plotting'''
    fig, ax = plt.subplots()
    fig.set_size_inches(size_inches)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_xscale(xscale)
    ax.set_yscale(yscale)
    if xlim is not None: ax.set_xlim(xlim)
    if ylim is not None: ax.set_ylim(ylim)
    ax.grid(True)
    return ax


I am working on the file "Syllabus_in_notebooks/Sec5_6_superposition-in-space-4-two-methods.ipynb".
The first task is:
Same, thing, now using the analytical solution with the cos and the exp
Can you write Python code for it?

b =L/2

ax = newfig('Symmertric solution for head decay in strip', 'x [m]', 'head [h]', xlim=(-b, b))

for t in times:
    h = np.zeros_like(x)
    for j in range(1,20):
        h += a * 4  / np.pi * ((-1)**(j-1) / (2 * j - 1) *
            np.cos((2 * j - 1) * np.pi / 2 * x / b) *
            np.exp(- (2 * j - 1)**2 * (np.pi / 2)**2 * kD /(b**2 * S) * t))
    ax.plot(x, h, label=f't={t:.1f}')

ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_6_superposition-in-space-4-two-methods.ipynb".
The first task is:
Super position with $\mathtt{erfc}()$ subtracted from initial head $a$
Can you write Python code for it?

L = 150 # m (strip wirdth)
x = np.linspace(-L/2, L/2, 201) # points, taking left at zero.
kD = 600 # m2/d
S = 0.1 # [-]
a = 1.0 # m, sudden head change at x = -L/2
times = np.linspace(0, 0.5, 11)[1:] # d

ax = newfig('Decay from initial head $a$ to 0 at $x = -L/2$ and at $x = L/5$',
                '$x$ [m], $ 0 < x < L $', 'head change [m]', xlim=(-L/2, L/2))

for t in times:
    rt = np.sqrt(S / (4 * kD * t))
    s = np.zeros_like(x) + a # intiial head
    for i in range(20):
        s -= a * erfc(((2 * i + 0.5) * L + x) * rt)
        s -= a * erfc(((2 * i + 0.5) * L - x) * rt)
        if i > 0:
            s += a * erfc(((2 * i - 0.5) * L - x) * rt)
            s += a * erfc(((2 * i - 0.5) * L + x) * rt)
    ax.plot(x, s, label=f't = {t:5.2f} d')
ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec5_6_symmetric-solution_sudden_change.ipynb".
The first task is:
As can be seen, we did not compute enough terms in the series to remove the waves in the first graph. It can be solved by taking more terms or by choosing a somewhat later first time. Just change the value in the loop (20) to for instance 200 to see the effect.
The individual terms of the analytic solution
Let's take a time and show how the individual terms add up to yield the final solution.
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec6_3_13_Alexandria_Egypte_desert_infiltration.ipynb".
The first task is:
Implementation
Can you write Python code for it?

# aquifer properties, because it make more sense here, we'll work in m and years instead of m and days
kD = 200 * 25 * (365) # m2/y (not m2/d)
S = 0.25 # [-], storage coefficient, does not change if we work in years or days

# place the wells, which are on a regular 1000x1000 grid to simulate the irrigation.
# The width and length of the irrigated area are both 30 km.
a = 1000. # m cell size
L = 30e3 # km
xw = np.linspace(-(L - a)/2, (L - a)/2, L/a) # m, coordinatres of the cell centers = well
yw = np.linspace(-(L - a)/2, (L - a)/2, L/a) # m, coordinaters of the cell centers = well

# Genrate full 2D arrays of the x and y coordinates of the wells. Becaus these are 2D arrays,
# I chose to use capital letters X and Y with a small w to show these are the irrigation well-coordinates
Xw, Yw = np.meshgrid(xw, yw) #

# Show the wells
ax = newfig('Location of the wells and the grid', 'x [m]', 'y [m]',
            xlim=(-(L + a) / 2 , (L + a) / 2), ylim=(-(L + a) / 2, (L + a) / 2))
ax.grid(False)
ax.plot(Xw, Yw, 'r.')
plt.show() # See what happens if you omit plt.show()

# Recharge is irrigation - vaportranspiration is the loss of irrigation wat to the deep groundwater
# in the aquifer
q = 0.25 # m/y, the yearly recharge
Q = a**2 * q  # irrigation per well in m3/y (with so many wells this is a lot of water)

# Observation points, where we want to see what happens to the groundwater
# We jus take a few points on the y-axis, some inside the irrigated area some outside of it
Y0 = np.array([0, 10, 20, 40, 60]) * 1e3 # note the 1e3 factor which converts km to m
X0 = np.zeros_like(Y0) # only one valuenp.zeros_like(Y0) # same

# effect on observation point

t = np.linspace(0, 50, 51)[1:] # time in years, omit t[0] which is 0, to prevend division by zero.

F = Q / (4 * np.pi * kD) # need to compute this only once

result = dict() # Use a dict to store the results (for convenience of plotting later)

for i, (x0, y0) in enumerate(zip(X0, Y0)):
    R = np.sqrt((x0 - Xw)**2 + (y0 - Yw)**2).ravel()[:, np.newaxis] # shape is...


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
Actual computation for all times
Can you write Python code for it?

# embellishment of the plot
ax = newfig(f'Total inflow from the river, well is {b:.0f} m from river', 'time [d]',
                                        '<--outflow, extraction | inflow, injection--> m3/d')

plt.ylim(-Q0, Q0) # maximum value is Q, the total extraction

Qriv = [] # empty list in which the values will be assembled
Qwell= []
for t in times: # t is the next value from times in the loop
    Qsum = 0.
    Qtot = 0.
    for tsw, Q in zip(Tsw, Qsw):
        if t > tsw:
            u1 = r1 ** 2 * S / (4 * kD * (t - tsw))
            u2 = r2 ** 2 * S / (4 * kD * (t - tsw))

            q1 = +Q / (2 * np.pi * r1) * np.exp(-u1)
            q2 = -Q / (2 * np.pi * r2) * np.exp(-u2)

            qx = q1 * np.cos(alpha1) + q2 * np.cos(alpha2)
            qy = q1 * np.sin(alpha1) + q2 * np.sin(alpha2)

            qin = qx

            qm = 0.5 * (qin[:-1] + qin[1:])
            Qsum = Qsum + np.sum(qm * dy)
            Qtot = Qtot + Q
    Qriv.append(-Qsum) # append next value
    Qwell.append(Qtot)

# plot the curve
ax.plot(times, Qwell, 'k', label='Qwell')
ax.plot(times, Qriv, 'r', label='Qriv')

ax.legend()


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
Compute the total inflow at a given time
We have to integrate the inflow along the y axis.
To do this, we take the pieces $\Delta y$ between the chosen point on the $y$ axis and multiply them with the averate value of $q_{in}$ at each piece on the $y$ axis.
Can you write Python code for it?

dy = y[1:] - y[:-1] # or np.diff(y)
qm = 0.5 * (qin[:-1] + qin[1:]) # or np.diff(qin)
Qin = np.sum(qm * dy) # total inflow through the river bank
print('Total inflow with Q={:.0f} at t={:.1f} d equals {:.1f} m3/d'\
     .format(Q, t, Qin))


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
Everything combined, many times, with varying well extraction
Every winter the extration is Q0 and during the summer it is -Q0
We simulate 10 years using superosition in time. The winter injection
will start each year in September and the summer extraction each year in april. Let's define the start times in days since the start of of the first year, assuming for simplicity that a month as always 30 days.
Can you write Python code for it?

Q0 = 2400 # m3/d
kD = 500 # m2/d
S  = 0.2 # [-]

# all variable that are constant in time:

b = 1500 # m, distance of well to river shore
x1, y1 = -b, 0 # location of well
x2, y2 = +b, 0 # location of mirror well

# all parameters that are constant in time
y = np.linspace(-1000, 1000, 81)
x = np.zeros_like(y)

dy = y[1:] - y[:-1]

r1 = np.sqrt((x - x1) ** 2 + (y - y1) ** 2)
r2 = np.sqrt((x - x2) ** 2 + (y - y2) ** 2)

alpha1 = np.arctan2(y, x - x1)
alpha2 = np.arctan2(y, x - x2)


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
Inflow from the river
First we'll show the flux flowing from the river into the aquifer at a point in time.
In the section thereafter, we'll integrate the flux along the bank of the river to get the total inflow.
The inflowing flux at the river bank is obtained from the x-component of the discharge vectors of both wells in point of the river bank. We just compute the vector for each well separately and add the x and y component of the well and its mirror in river-bank points. Because the river bank lies along the y-axis, we only need the x-component of the discharge vectors.
Can you write Python code for it?

Q = 1200 # m3/d

a = 125 # m, distance of well to river shore
x1, y1 = -a, 0 # location of well
x2, y2 = +a, 0 # location of mirror well

# Choose points along the river bank
y = np.linspace(-1000, 1000, 81)
x = np.zeros_like(y)

r1 = np.sqrt((x - x1) ** 2 + (y - y1) ** 2)
r2 = np.sqrt((x - x2) ** 2 + (y - y2) ** 2)

u1 = r1 ** 2 * S / (4 * kD * t)
u2 = r2 ** 2 * S / (4 * kD * t)

q1 = +Q / (2 * np.pi * r1) * np.exp(-u1)
q2 = -Q / (2 * np.pi * r2) * np.exp(-u2)

alpha1 = np.arctan2(y, x - x1)
alpha2 = np.arctan2(y, x - x2)

qx = q1 * np.cos(alpha1) + q2 * np.cos(alpha2)
qy = q1 * np.sin(alpha1) + q2 * np.sin(alpha2) # Will be zero for all bank points.

qin = qx # we only need the x-component.

ax = newfig('Inflow along the river', 'y [m]', 'inflow m2/d')
plt.plot(y, qin)

# show tyat qy is zero
print(f'Max qy = {np.min(qy):.4g}, min qy = {np.max(qy):4g} m2/d')


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
Many observation points
Instead of 1 observation point, we can take many observationi points, for instance, along a line throug both wells.
To prevent that the distance $r$ becomes zero at the well center, set the minimum distance equal to the well's radius $r_0$, which will normally be around 0.25 m.
Can you write Python code for it?

# Drawdown as arbitraty points x, y
x  = np.linspace(-2 * a, 2 * a, 201) # many x-coordinates
y  = np.zeros_like(x) # same number of y-coordinates, all value zero (line aloong x-axis)
rw = 0.25 # The well radius

# Compute the distance from each well to all observation points at once:
r1 = np.sqrt((x1 - x)**2 + (y1 - y)**2)
r2 = np.sqrt((x2 - x)**2 + (y2 - y)**2)

# Use logical indexing to set or exclude points or select specific ones. In this case
# we will put the distance of points closer than r_0 from the well (in fact inside the well) equal to
# r_0.
r1[r1<rw] = rw
r2[r2<rw] = rw

# Compute the argument of teh well function for all points at once:
u1 = r1**2 * S / (4 * kD * t)
u2 = r2**2 * S / (4 * kD * t)

# Compute the head due to the well and its mirror for all points at once
s = Q / (4 * np.pi *kD) * (exp1(u1) - exp1(u2))

ax = newfig('Drawdown along a line throug the well and its mirror', 'x [m], y=0', 's [m]', ylim=(2, -2))
# By using (2, -2) in ylim, we invert the vertical axis at the same time.

ax.plot(x, s)

plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
Of course, one could also draw contourlines.
Including the mirror well in the discharge vector
Can you write Python code for it?

# Well properties
a = 125
Q = 1200 # m3/d extraction
x1, y1 = -a, 0.  # the extractioni and location of the well
x2, y2 = +a, 0.  # the extractioni and location of the well

# Chose a set of randomly placed observation points
n = 500
x = 2 * a * (np.random.randn(n) - 0.5)  # choose 25 random values, with average value 0
y = 2 * a * (np.random.randn(n) - 0.5)  # same

# Distance from the well to each of the random points
r1 = np.sqrt((x-x1)**2 + (y-y1)**2)
r2 = np.sqrt((x-x2)**2 + (y-y2)**2)

# u
u1 = r1**2 * S /(4 * kD * t)
u2 = r2**2 * S /(4 * kD * t)

# the specific discharge
q1 =  Q0 / (2 * np.pi * r1) * np.exp(-u1)
q2 = -Q0 / (2 * np.pi * r2) * np.exp(-u2) # sum because flows add

alpha1 = np.arctan2(y - y1, x- x1) # angle between vector and horizontal
alpha2 = np.arctan2(y - y2, x- x2) # angle between vector and horizontal

qx = q1 * np.cos(alpha1) + q2 * np.cos(alpha2)  # x component of specific discharge
qy = q1 * np.sin(alpha1) + q2 * np.sin(alpha2)   # y component of specific discharge

scale = 20.0 # scale factor to plot of the specific discharge vectors (arbiytrary, just for visualization)

ax = newfig('discharge vector plot', 'x [m]', 'y [m]')

# plot the location of the well as  black ('k') circle ('o') of size 8
ax.plot(x1, y1, 'ko', markersize=8, label='well')
ax.plot(x2, y2, 'ko', markersize=8, label='mirror well')

# in a loop, plot one vector after another
for xi, yi, qxi, qyi in zip(x, y, qx, qy):
    ax.plot(xi, yi, 'o', markersize=4)    # plot marker at obs. point
    ax.plot([xi, xi - scale * qxi],
            [yi, yi - scale * qyi]) # plot vector


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
The compute inflow will approach the true inflow better, the longer the part of the y-axis is chosen.
Show the total inflow for many times but a constant well extraction
To to this, a loop it necessary to compute the inflow for each time at a time and collect the results.
Can you write Python code for it?

# The data

Q = 1200 # m3/d

b = 125 # m, distance of well to river shore
x1, y1 = -b, 0 # location of well
x2, y2 = +b, 0 # location of mirror well

# The times
times = np.linspace(0, 200, 2001)
times[0] = 0.0001  # to prevent computing for t=0 use a very small number

# all parameters that are constant in time
y = np.linspace(-1000, 1000, 81)
x = np.zeros_like(y)

dy = y[1:] - y[:-1]

r1 = np.sqrt((x - x1) ** 2 + (y - y1) ** 2)
r2 = np.sqrt((x - x2) ** 2 + (y - y2) ** 2)

alpha1 = np.arctan2(y, x - x1)
alpha2 = np.arctan2(y, x - x2)

ax = newfig('Total inflow from the river', 'time [d]', 'inflow m3/d', ylim=(0, Q))

# Compute for each time in turn
Qin = [] # empty list in which the values will be assembled
for t in times: # t is the next value from times in the loop
    u1 = r1 ** 2 * S / (4 * kD * t)
    u2 = r2 ** 2 * S / (4 * kD * t)

    q1 = +Q / (2 * np.pi * r1) * np.exp(-u1)
    q2 = -Q / (2 * np.pi * r2) * np.exp(-u2)

    qx = q1 * np.cos(alpha1) + q2 * np.cos(alpha2)
    qy = q1 * np.sin(alpha1) + q2 * np.sin(alpha2)

    qin = qx

    qm = 0.5 * (qin[:-1] + qin[1:])
    Qin.append(np.sum(qm * dy)) # append next value

ax.plot(times, Qin)  # plot the curve

# embellishment of the plot


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_a_river_extended_assignment.ipynb".
The first task is:
The first time the injection is Q.
Then at each time when extraction starts, is has -2Q
And then each time when we switch back to injection the flow is +2Q
The flows to be used for superposition in time are
Month  9,  15,  21,  27,  33,  39,  45, ...
Flow   Q, -2Q, 2Q, -2Q,  2Q, -2Q,  2Q, ...
Now define the times at which the total inflow is to be computed.
Can you write Python code for it?

# Set the times when pumping switches
#              start, end, step
month = np.arange(9, 120, 6) # month numbers at which flow switches
                            #counting from Jan 1 in first year
Tsw = 30 * month   # t at which flow switches in days

# set the pumping flow at the switch points
Qsw = Q0 * 2 * (-1) ** np.arange(len(Tsw))
Qsw[0] = Q0

# Show them
print('{:>6} {:>6}'.format('Tsw[d]', 'Qsw'))
for tsw, Q in zip(Tsw, Qsw):
    print('{:6.0f} {:6.0f}'.format(tsw, Q))


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_river_in_class_assignment.ipynb".
The first task is:
Choose times at which we want to see the total inflow
Can you write Python code for it?

# The small values prevents computing flows at the time the well starts
# t is now just a negilible time later.
#            start  end      step
times = np.arange(0, 30 * 120, 10) + 0.00001  # 10 years in days in 10 day steps

Q0 = 2400 # m3/d

b = 1500 # m, distance of well to river shore
x1, y1 = -b, 0 # location of well
x2, y2 = +b, 0 # location of mirror well

times = np.linspace(0, 30 * 120, 501) +0.0000001 # times

# points along the river
y = np.linspace(-1000, 1000, 801)
x = np.zeros_like(y)

r1 = np.sqrt((x - x1) ** 2 + (y - y1) ** 2)
r2 = np.sqrt((x - x2) ** 2 + (y - y2) ** 2)

alpha1 = np.arctan2(y, x - x1)
alpha2 = np.arctan2(y, x - x2)

Qps  = np.zeros_like(times)  # pumping station
Qriv = np.zeros_like(times)  # inflow from river
for i, t in enumerate(times):
    for tsw, qsw in zip(Tsw, Qsw):
        if t  > tsw:
            u1 = r1 ** 2 * S / (4 * kD * (t - tsw))
            u2 = r2 ** 2 * S / (4 * kD * (t - tsw))

            q1 = +qsw / (2 * np.pi * r1) * np.exp(-u1)
            q2 = +qsw / (2 * np.pi * r2) * np.exp(-u2)

            qx = q1 * np.cos(alpha1) - q2 * np.cos(alpha2)
            qy = q1 * np.sin(alpha1) - q2 * np.sin(alpha2)

            qin = qx

            Qps[i] = Qps[i]  + qsw
            Qriv[i]= Qriv[i] + np.sum((y[:-1] - y[1:]) * 0.5 * (qin[:-1] +qin[1:]))

plt.title('Total inflow from river')
plt.xlabel('t [d]')
plt.ylabel('total inflow m3/d')
plt.ylim(-2*Q0, 2*Q0)
plt.grid()
plt.plot(times, Qps, 'r')
plt.plot(times, Qriv, 'b')
plt.show()

Qsw


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_river_in_class_assignment.ipynb".
The first task is:
Compute the flow across a ring with radius r
The total flow across a ring with radius $r$ around a transient well is
$$ Q_r = Q_0 e^{-u} $$
And the specific discharge
$$ q_r = \frac {Q_0} {2 \pi r} e^{-u} $$
and points in the direction of the well.
The specific discharge as randomly chosen points around a well
Here we have a single well in location x0, y0 and a set random points with coordinates in the arrays x, and y.
The specific discharge is computed at all points. Then we compute the x and y components of these vectors. Finally we show them in a simple way, in which the point is indicated by a small circle and the discharge vector by a line with length proportional to its strength.
Can you write Python code for it?

Q0 = 1200 # m3/d Well
x0, y0 = 0., 0.  # the location of the well

# The random observation points
a = 50 # m  a length to scale randomly chosen points
x = a * (np.random.randn(25) - 0.5)  # choose 25 random values
y = a * (np.random.randn(25) - 0.5)  # same

# Distance from each point to the well
r = np.sqrt((x-x0)**2 + (y-y0)**2)

# u
u = r**2 * S /(4 * kD * t)

# the specific discharge
q = Q0 / (2 * np.pi * r) * np.exp(-u) # m2/d
alpha = np.arctan2(y - y0, x- x0) # angle between vector and horizontal
qx = q * np.cos(alpha)   # x component of specific discharge
qy = q * np.sin(alpha)   # y component of specific discharge

scale = 2.0 # scale factor to plot specific discharge vectors

# plot the location of the well as  black ('k') circle ('o') of size 8
plt.plot(x0, y0, 'ko', markersize=8)

# in a loop, plot one vector after another
for xx, yy, qxx, qyy in zip(x, y, qx, qy):
    plt.plot(x, y, 'o', markersize=4)    # plot marker at obs. point
    plt.plot([x, x - scale * qx], [y, y - scale * qy]) # plot vector

# embelishment of the plot
plt.title('Specific discharge vectors due to well at x0={:.1f}, y0={:.1f}'\
         .format(x0, y0))
plt.xlabel('x [m]')
plt.ylabel('y [m]')
plt.grid()

# show it all
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_river_in_class_assignment.ipynb".
The first task is:
Compute the total inflow for a single time
We have to integrate the inflow along the y axis.
To do this, we take the pieces $\Delta y$ between the chosen point on the $y$ axis and multiply them with the averate value of $q_{in}$ at each piece on the $y$ axis.
Can you write Python code for it?

dy = y[1:] - y[:-1]
qm = 0.5 * (qin[:-1] + qin[1:])
Qin = np.sum(qm * dy)
print('Total inflow with Q={:.0f} at t={:.1f} d equals {:.1f} m3/d'\
     .format(Q, t, Qin))


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_river_in_class_assignment.ipynb".
The first task is:
Everything combined, many times, with varying well extraction
Every winter the extration is Q0 and during the summer it is -Q0
We simulate 10 years using superosition in time. The winter injection
will start each year in September and the summer extraction each year in april. Let's define the start times in days since the start of of the first year, assuming for simplicity that a month as always 30 days.
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_river_in_class_assignment.ipynb".
The first task is:
Inflow from the river
Can you write Python code for it?

Q = 1200 # m3/d

b = 125 # m, distance of well to river shore
x1, y1 = -b, 0 # location of well
x2, y2 = +b, 0 # location of mirror well

y = np.linspace(-1000, 1000, 81)
x = np.zeros_like(y)

r1 = np.sqrt((x - x1) ** 2 + (y - y1) ** 2)
r2 = np.sqrt((x - x2) ** 2 + (y - y2) ** 2)

u1 = r1 ** 2 * S / (4 * kD * t)
u2 = r2 ** 2 * S / (4 * kD * t)

q1 = +Q / (2 * np.pi * r1) * np.exp(-u1)
q2 = -Q / (2 * np.pi * r2) * np.exp(-u2)

alpha1 = np.arctan2(y, x - x1)
alpha2 = np.arctan2(y, x - x2)

qx = q1 * np.cos(alpha1) + q2 * np.cos(alpha2)
qy = q1 * np.sin(alpha1) + q2 * np.sin(alpha2)

qin = qx

plt.title('Inflow along the river')
plt.xlabel('y [m]')
plt.ylabel('inflow m2/d')
plt.grid()
plt.plot(y, qin)
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_3_well_near_river_in_class_assignment.ipynb".
The first task is:
Many observation points
Instead of 1 observation point, we can take many, for instance along a line throug both wells.
To prevent that the distance r becomes zero at the cell center, we set the minimum distance equal to the well radius $r_w$, which will normally be around 0.25 m.
Can you write Python code for it?

# Drawdown as arbitraty points x, y
x  = np.linspace(-2 * a, 2 * a, 201)
y  = np.zeros_like(x)
rw = 0.25 # well radius (radius of borehole)

r1 = np.sqrt((x1 - x)**2 + (y1 - y)**2)
r2 = np.sqrt((x2 - x)**2 + (y2 - y)**2)

# Use logical indexing to set only a limited number of points
# Logical indexing gives as many values as the array that are
# True of correct and False otherwise. Only the True values will be set.
r1[r1<rw] = rw
r2[r2<rw] = rw

u1 = r1**2 * S / (4 * kD * t)
u2 = r2**2 * S / (4 * kD * t)

s = Q / (4 * np.pi *kD) * (W(u1) - W(u2))
plt.title('Drawdown along a line throug the well and its mirror')
plt.xlabel('x [m], y=0')
plt.ylabel('s [m]')
plt.grid()
plt.plot(x, s)
plt.ylim(2, -2) # invert the direction of the y-axis

plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_4_3_Theis_Hantush_implementations.ipynb".
The first task is:
Results of the timing
Theis:
W_theis0 :
6.06 µs ± 261 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
W_theis1(u) :
7.11 µs ± 163 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
W_theis2(u) :
299 µs ± 6.79 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
W_theis3(u) :
553 µs ± 33.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Their is almost no difference in speed between direcly using exp1 from scipy and integrating numerically using quad. Both are equally accurate.
Thex explicit integration is slow just as the summation.
Hantush:
W_hantush0(u, rho) :
86 µs ± 1.69 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
W_hantus1(, rho) :
7.53 ms ± 72.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
W_hantush2(u, rho) :
882 µs ± 26.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
W_hantush3(u, rho) :
8.64 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Note that my "smart" integration (W_hantush2) is about 9 time faster than the simple integration and th quad solution. So it turns aout to be smart enough after all.
The smart and normal integration methods are equally accurate to 5 didgets with 5000 points and haveing 1e10 as upper limit. The quad method has 10 didgets accuracy.
The series method is the slowest of all, 10 times slower than the quad and simple integration methods and 100 times slower than my smart method.
The series method is also not accurate. The number of terms to include must be
much larger, which would make it even slower te compute.
Can you write Python code for it?

rhos = [0.01, 0.03, 0.1, 0.3, 1,]
colors = 'brgkmc'
u = np.logspace(-6, 1, 71)

ax = newfig('Theis and Hantush type curves', "1/u", r"W(u), Wh(u, $\rho$)", xscale='log', yscale='linear',
           ylim=(10, -0.1))

ax.plot(1/u, W_theis0(u), lw=3, label='Theis', zorder=100)
for i, rho in enumerate(rhos):
    clr = colors[i % len(colors)]
    #ax.plot(1/u, W_hantush2(u, rho), '.', label='rho={:.1f}'.format(rho))
    ax.plot(1/u, W_hantush3(u, rho), color=clr, label=r'Hantush, $\rho$={:.2f}'.format(rho))
    ax.plot(1/(rho/2), W_hantush3(rho/2, rho), 'o', color=clr, zorder=100 + 1)
ax.legend()

rhos = [0., 0.1, 0.3, 1, 3]

u = np.logspace(-6, 1, 71)

plt.title('Hantush type curves')
plt.xscale('log')
plt.yscale('linear')
plt.grid()
for rho in rhos:
    plt.plot(1/u, W_hantush2(u, rho), '.', label='rho={:.1f}'.format(rho))
    plt.plot(1/u, W_hantush3(u, rho), label='rho={:.1f}'.format(rho))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_4_3_Theis_Hantush_implementations.ipynb".
The first task is:
The inflection point of the Hantush graphs, where $u=r/(2\lambda)=\rho/2$
Can you write Python code for it?

rhos = [0.01, 0.03, 0.1, 0.3, 1, 3, 5]

s = r"The inflection point is where $u = r/(2 \lambda) = \rho/2$"

u = np.logspace(-6, 1, 71)

ax = newfig('Hantush type curves, ' + s, "1/u", r"W($u, r/\lambda$)", xscale='log', figsize=(12, 6), size=15)

for rho, clr in zip(rhos, clrs()):
    ax.plot(1/u, W_hantush3(u, rho), color=clr, label='rho={:.2f}'.format(rho))
    ax.plot(2/rho, W_hantush3(rho/2, rho), 'o', color=clr, label="Inflection point.")
ax.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_4_3_Theis_Hantush_implementations.ipynb".
The first task is:
Timing the functions
Can you write Python code for it?

u  = np.logspace(-3, 1, 41)
rho = 0.003

theis_funcs = [W_theis0, W_theis1, W_theis2, W_theis3]
hantush_funcs = [W_hantush0, W_hantush1, W_hantush2, W_hantush3]

for i, f in enumerate(theis_funcs):
    print(f'W_theis{i}: ', f(u)[:3])

for i, f in enumerate(hantush_funcs):
    print(f'W_hantush{i}: ',f(u, rho)[:3])

print('W_theis0 :')
%timeit W_theis0(u)
print('W_theis1(u) :')
%timeit W_theis1(u)
print('W_theis2(u) :')
%timeit W_theis2(u)
print('W_theis3(u) :')
%timeit W_theis3(u)

print('W_hantush0(u, rho) :')
%timeit W_hantush0(u, rho)
print('W_hantus1(, rho) :')
%timeit W_hantush1(u, rho)
print('W_hantush2(u, rho) :')
%timeit W_hantush2(u, rho)
print('W_hantush3(u, rho) :')
%timeit W_hantush3(u, rho)


I am working on the file "Syllabus_in_notebooks/Sec6_4_3_intro_theis_well_simple.ipynb".
The first task is:
The plot of W(u) vs u is simply not intuitive
Can you write Python code for it?

plt.title('Theis type curve $W(u)$ vs $u$')
plt.xlabel('u')
plt.ylabel('W(u), exp1(u)')
plt.xscale('log')
plt.yscale('log')
plt.grid()
plt.plot(u, exp1(u))
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_4_4_Theis_Hantush_implementations.ipynb".
The first task is:
Results of the timing
Theis:
W_theis0 :
6.06 µs ± 261 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
W_theis1(u) :
7.11 µs ± 163 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
W_theis2(u) :
299 µs ± 6.79 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
W_theis3(u) :
553 µs ± 33.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Their is almost no difference in speed between direcly using exp1 from scipy and integrating numerically using quad. Both are equally accurate.
Thex explicit integration is slow just as the summation.
Hantush:
W_hantush0(u, rho) :
86 µs ± 1.69 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
W_hantus1(, rho) :
7.53 ms ± 72.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
W_hantush2(u, rho) :
882 µs ± 26.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
W_hantush3(u, rho) :
8.64 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Note that my "smart" integration (W_hantush2) is about 9 time faster than the simple integration and th quad solution. So it turns aout to be smart enough after all.
The smart and normal integration methods are equally accurate to 5 didgets with 5000 points and haveing 1e10 as upper limit. The quad method has 10 didgets accuracy.
The series method is the slowest of all, 10 times slower than the quad and simple integration methods and 100 times slower than my smart method. But it is as accurate as the quad method. The series method is also very effective.
The series method is also not accurate. The number of terms to include must be
much larger, which would make it even slower te compute.
Can you write Python code for it?

rhos = [0., 0.1, 0.3, 1, 3]

u = np.logspace(-6, 1, 71)

ax = newfig('Hantush type curves', '1/u', 'Wh(u, rho)', xscale='log', yscale='log')

ax.plot(1/u, W_theis0(u), lw=3, label='Theis', zorder=100)
for rho in rhos:
    ax.plot(1/u, W_hantush2(u, rho), '.', label='rho={:.1f}'.format(rho))
    ax.plot(1/u, W_hantush3(u, rho), label='rho={:.1f}'.format(rho))
ax.legend()
plt.show()

rhos = [0., 0.1, 0.3, 1, 3]

u = np.logspace(-6, 1, 71)

ax = newfig('Hantush type curves', '1/y', 'W(u, rho)', xscale='log')
for rho in rhos:
    ax.plot(1/u, W_hantush2(u, rho), '.', label='rho={:.1f}'.format(rho))
    ax.plot(1/u, W_hantush3(u, rho), label='rho={:.1f}'.format(rho))
ax.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_4_4_Theis_Hantush_implementations.ipynb".
The first task is:
Timing the functions
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec6_5_1_Hantush-well-1.ipynb".
The first task is:
Implementing the integral
Can you write Python code for it?

def Wh(u, rho):
    '''Numerical integration using quad'''
    def kernel(y, rho): return np.exp(-y - (rho / 2)**2 /y)  / y
    def wh(u, rho):     return quad(kernel, u, np.inf, (rho,))
    wh = np.frompyfunc(wh, 2, 2)
    return wh(u, rho)[0]

def Wh2(u, rho, tol=1e-14):
    '''Return Hantush using summation.

    This implementation works but has a limited reach; for very small
    values of u (u<0.001) the solution will deteriorate into nonsense,
    '''
    #import pdb
    #pdb.set_trace()
    tau = (rho/2)**2 / u
    f0 = 1
    E =  exp1(u)
    w0= f0 * E
    W = w0
    for n in range(1, 500):
        E = (1/n) * (np.exp(-u) - u * E)
        f0 = -f0 / n * tau
        w1 = f0 * E
        #print(w1)
        if np.max(abs(w0 + w1)) < tol: # use w0 + w1 because terms alternate sign
            #print('succes')
            break
        W += w1
        w0 = w1 # remember previous value
    return W

# Timing and test
u = np.array([1e-3, 1e-2, 1e-1, 1, 10])
rho=0.3
print("Hantush quad :")
%timeit Wh(u, rho)
print(Wh(u, rho))
print("Hantush series:")
%timeit Wh2(u, rho)
print(Wh2(u, rho))


I am working on the file "Syllabus_in_notebooks/Sec6_5_6_superposition_space_Theis-wells.ipynb".
The first task is:
Animation
Can you write Python code for it?

ttl = "kD = {:.0f} m2/d, Sy = {:.2f}, Q = {:.0f} m3/d per well.".format(kD, Sy, wells.iloc[0]["Q"])

ax = newfig("Drawdown over time, " + ttl, "x [m]", "y [m]", size_inches=(10,  8), fontsize=15)
fig = ax.figure
ax.set_aspect(1.0)

u = lambda R, t: R ** 2 * Sy  / (4 * kD * t)

times = wells['tstart'].values + 180
times = np.linspace(0, 5, 200) * 365.

for Id in wells.index:
    well = wells.loc[Id]
    ax.plot(well["x"], well["y"], 'ro')
    ax.text(well["x"], well["y"], "  well {}, tstart={:.0f} d".format(Id, well["tstart"]))

R = list()
for Id in wells.index:
    well = wells.loc[Id]
    r = np.sqrt((X - well['x']) ** 2 + (Y - well['y']) ** 2)
    R.append(np.fmax(well["r0"], r))

s = np.zeros((len(times), len(y), len(x)))

for it, t in enumerate(times):
    for iw in range(len(wells)):
        well = wells.iloc[iw]
        if t > well["tstart"]:
            s[it] += well["Q"] / (4 * np.pi * kD) * W(u(R[iw], t - well["tstart"]))
        #print(np.max(s[it]))

cvals = np.arange(0, 5.1, 0.2)
cont = ax.contour(x, y, s[0], levels=cvals)
ttxt = ax.text(0.5, 0.5, "time = {:5.0f} d".format(0), ha="center", va="center", transform=ax.transAxes,
               bbox=dict(boxstyle='round', facecolor='wheat'), fontsize=15)

def init():
    global cont, s
    return cont.collections + [ttxt]

clrs = 'brgkmcy'
def animate(it):
    global cont, s, times
    for tp in cont.collections:
        tp.remove()
    cont= ax.contour(x, y, s[it], levels=cvals, colors='k')
    ttxt.set_text("time = {:5.0f} d".format(times[it]))
    return cont.collections + [ttxt]

anim = FuncAnimation(fig, animate, frames=len(times),
                                  fargs=(), init_func=init, interval=100, blit=True, repeat=False)

plt.close(anim._fig)
out = HTML(anim.to_html5_video())
#out # to actually show the video.

fname = "FourWellsContours"
anim.save(fname + '.mp4', fps=20, extra_args=['-vcodec', 'libx264'], bitrate=1000)
print(anim.save_count, " frames saved.") # Shows the number of frames saved.

!ffmpeg -i FourWellsContours.mp4...


I am working on the file "Syllabus_in_notebooks/Sec6_5_6_superposition_space_Theis-wells.ipynb".
The first task is:
How does the drawdown behave for diffent distances from the well?
For this assume a real situation.
Can you write Python code for it?

kD = 600 # m2/d
Sy = 0.24 # [-]
L = 1000. # m

u = lambda t, r: r **2 * Sy / (4 * kD * t)

ax = newfig('Drawdown, in the center between the 4 wells', 't [d]', 'drawdow', xscale='linear')

Qs = -np.array([600, 600, 600, 600]) * 24 # m3/d
ts_start = np.array([1, 2, 3, 4]) * 365 # d

t = np.linspace(0, 10, 1001) * 365 # days
s = np.zeros_like(t)
for Q, t_start in zip(Qs, ts_start):
    r = L * np.sqrt(2) / 2
    s[t>t_start] += Q / (4 * np.pi * kD) * W(u(t[t>t_start] - t_start, r))
    ax.plot(t, s)
ax.plot(t, s, lw=3)


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
Drawdown on double log scale
Can you write Python code for it?

plt.title('Dalem pumping test measured drawdowns')
plt.xlabel('t [min]')
plt.ylabel('dd [m]')
plt.xscale('log')
plt.yscale('log')
plt.grid()

for r in distances:
    I = data[:,0] == r
    plt.plot(data[I,-2], data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
Drawdown on double log scale using $t/r^2$ on x-axis
Can you write Python code for it?

plt.title('Dalem pumping test measured drawdowns')
plt.xlabel('$t/r^2$ [min/m$^2$]')
plt.ylabel('dd [m]')
plt.xscale('log')
#plt.yscale('log')
plt.grid()

for r in distances:
    I = data[:,0] == r
    tr2 = data[I, -2] / r**2
    plt.plot(tr2, data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
Interpretation using the match on double log scales (Classical method)
The classical interpreation plots the measured drawdowns on double log paper (drawdown $s$ versus $t/r^2$ and compares them with the Theis type curve $W(u)$ versus $1/u$ also drawn on double log paper. Because $1/u = (4 kD t) / (r^2 S)$ it follows that on logarthmic scales $1/u$ and $t/r^2$ differ only by a constant factor, which represents a horizontal shift on the log scale. The drawdown $s$ only differs the constant $Q/(4 \pi kD$ from the well function $W(u)$, and so  this implies a vertical shift on logarithmic scale. Hence the measured drawdown versus $t/r^2$ on double log scale looks exactly the same as the theis type curve but it is only shifted a given distance along the horizontal axis and a given distance along the vertical axis. These two shifts yield the sought transmissivity and storage coefficient.
Below we draw the Theis type curve and the drawdown $s$ multiplied by a factor $A$ and the $t/r^2$ multiplied by a factor $B$, choosing $A$ and $B$ interactively untill the measured and the type curve match best.
In this worked out example, I already optmized the values of $A$ and $B$ by hand. Set them both to 1 and try optimizing them yourself.
Can you write Python code for it?

A = 30
B = 5.0e6
u = np.logspace(-4, 0, 41)
plt.title('Type curve and $A \times s$ vs $B \times t/r^2$, with $A$={}, $B$={}'.format(A, B))
plt.xlabel('$1/u$ and $B \, t/r^2$')
plt.ylabel('W(u) and $A \, s$')
plt.xscale('log')
plt.yscale('log')
plt.grid()

# the Theis type curve
plt.plot(1/u, exp1(u), 'k', lw=3, label='Theis')

for rho in [0.01, 0.03, 0.1, 0.3, 3]:
    plt.plot(1/u, Wh(u, rho), label='rho={:.2f}'.format(rho))

# The measurements
for r in distances :
    I = data[:,0] == r
    t = data[I,-2] / (24 * 60)
    s = data[I,-1] # Q /(4 * np.pi * kD) * exp1(r**2 * S / (4 * kD * t))
    plt.plot(B * t/r**2, A * s, 'o', label='$r$= {:.3g} m'.format(r))

plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
Read the data
Can you write Python code for it?

fname = './Dalem_data.txt'

with open(fname, 'r') as f:
    data = f.readlines() # read the data as a list of strings

hdr = data[0].split() # get the first line, i.e. the header
data = data[1:] # remove the header line from the data

# split each line (string) into its individual tokens
# each token is still a string not yet a number
toklist = [d.split() for d in data]

# convert this list of lines with string tokens into a list of lists with numbers
data = [] # start empty
for line in toklist:
    data.append([float(d) for d in line]) # convert this line

# when done, convert this list of lists of numbers into a numpy array
data = np.array(data)
#data # show what we've got

# get the piezometer distances from the first data column, the unique values
distances = np.unique(data[:,0])

plt.title('Dalem pumping test measured drawdowns')
plt.xlabel('t [min]')
plt.ylabel('dd [m]')
plt.grid()

for r in distances:
    I = data[:,0] == r # boolean array telling which data belong to this observation well
    plt.plot(data[I, -2], data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
Same, but using log scale
Can you write Python code for it?

plt.title('Dalem pumping test measured drawdowns')
plt.xlabel('t [min]')
plt.ylabel('dd [m]')
plt.xscale('log')
plt.grid()

for r in distances:
    I = data[:,0] == r
    plt.plot(data[I,-2], data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
The Hantush well function
$$ W(u, \frac r \lambda) = \intop _u ^\infty \frac {e^{-y - \frac {\left( \frac r {\lambda} \right) ^2} {4 y} }} y dy $$
The implementation is readily done by numeric integration using Simpsons rule, with sufficient points te make sure the function is computed accurately enough.
Can you write Python code for it?

def Wh(U, rho):
    '''Return Hantus well function for vector of u values and single rho'''
    W = np.zeros_like(U)
    for i, u in enumerate(U):
        W[i] = wh(u, rho)
    return W

def wh(u, rho):
    '''Return Wh(u, rho) for single value of u and rho'''
    uMax = 20 # sufficiently high
    y = np.logspace(np.log10(u), np.log10(uMax), 1000) # enough points, log axis
    ym = 0.5*(y[:-1] + y[1:])
    dy = np.diff(y)
    return np.sum(np.exp(-ym - rho**2 / (4 * ym)) / ym * dy)


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
The storage coefficient then follows from
$\frac 1 u = B \frac t {r^2}$, that is, $\frac {4 kD t} {r^2 S} = B \frac t {r^2}$ so that $S = \frac {4 kD} B$
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec6_5_Dalem-pumptest-Hantush.ipynb".
The first task is:
The vertical resistance is obtained from observing which of the lines depending on $\rho$ the measurements of the individual piezomters follow. In this case, the $r=30$ m piezometer seems to follow the type curve for $\rho = 0.03$ and the 90 m curve seems to follow the $\rho = 0.1$ type curve.
$$\rho = r / \lambda$$
then yields
$$ \lambda = \sqrt{kD c} = \frac r \rho $$
and
$$ c = \frac {\left( \frac r \rho \right)^2} {kD} $$
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec6_5_Korendijk-pumptest-Theis.ipynb".
The first task is:
Clearly, the result depends somewhat on the exact straigt line drawn through the bundel of curves for the observation wells. In the ideal situation, these curves fall onto each other. In this real-world case this is not true, which is due to non-uniformity of the real-world aquifer. There are many real-world pumping test where the match is mucht closer. But it is good to keep in mind that the real world is less homogeneous than the analytic solution presumes.
Interpretation using the match on double log scales (Classical method)
The classical interpreation plots the measured drawdowns on double log paper (drawdown $s$ versus $t/r^2$ and compares them with the Theis type curve $W(u)$ versus $1/u$ also drawn on double log paper. Because $1/u = (4 kD t) / (r^2 S)$ it follows that on logarthmic scales $1/u$ and $t/r^2$ differ only by a constant factor, which represents a horizontal shift on the log scale. The drawdown $s$ only differs the constant $Q/(4 \pi kD$ from the well function $W(u)$, and so  this implies a vertical shift on logarithmic scale. Hence the measured drawdown versus $t/r^2$ on double log scale looks exactly the same as the theis type curve but it is only shifted a given distance along the horizontal axis and a given distance along the vertical axis. These two shifts yield the sought transmissivity and storage coefficient.
Below we draw the Theis type curve and the drawdown $s$ multiplied by a factor $A$ and the $t/r^2$ multiplied by a factor $B$, choosing $A$ and $B$ interactively untill the measured and the type curve match best.
In this worked out example, I already optmized the values of $A$ and $B$ by hand. Set them both to 1 and try optimizing them yourself.
Can you write Python code for it?

A = 7
B = 1.0e7
u = np.logspace(-4, 1, 41)
plt.title('Type curve and $A \times s$ vs $B \times t/r^2$, with $A$={}, $B$={}'.format(A, B))
plt.xlabel('$1/u$ and $B \, t/r^2$')
plt.ylabel('W(u) and $A \, s$')
plt.xscale('log')
plt.yscale('log')
plt.grid()

# the Theis type curve
plt.plot(1/u, exp1(u), label='Theis')

# The measurements
for r in distances :
    I = data[:,0] == r
    t = data[I,-2] / (24 * 60)
    s = data[I,-1] # Q /(4 * np.pi * kD) * exp1(r**2 * S / (4 * kD * t))
    plt.plot(B * t/r**2, A * s, '.', label='$r$= {:.3g} m'.format(r))

plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Korendijk-pumptest-Theis.ipynb".
The first task is:
Drawdown on double log scale
Can you write Python code for it?

plt.title('Korendijk pumping test measured drawdowns')
plt.xlabel('t [min]')
plt.ylabel('dd [m]')
plt.xscale('log')
plt.yscale('log')
plt.grid()

for r in distances:
    I = data[:,0] == r
    plt.plot(data[I,-2], data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Korendijk-pumptest-Theis.ipynb".
The first task is:
Drawdown on double log scale using $t/r^2$ on x-axis
Can you write Python code for it?

plt.title('Korendijk pumping test measured drawdowns')
plt.xlabel('$t/r^2$ [min/m$^2$]')
plt.ylabel('dd [m]')
plt.xscale('log')
#plt.yscale('log')
plt.grid()

for r in distances:
    I = data[:,0] == r
    tr2 = data[I, -2] / r**2
    plt.plot(tr2, data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Korendijk-pumptest-Theis.ipynb".
The first task is:
Read the data
Can you write Python code for it?

fname = './Korendijk_data.txt'

with open(fname, 'r') as f:
    data = f.readlines() # read the data as a list of strings

hdr = data[0].split() # get the first line, i.e. the header
data = data[1:] # remove the header line from the data

# split each line (string) into its individual tokens
# each token is still a string not yet a number
toklist = [d.split() for d in data]

# convert this list of lines with string tokens into a list of lists with numbers
data = [] # start empty
for line in toklist:
    data.append([float(d) for d in line]) # convert this line

# when done, convert this list of lists of numbers into a numpy array
data = np.array(data)
#data # show what we've got

# get the piezometer distances from the first data column, the unique values
distances = np.unique(data[:,0])

plt.title('Korendijk pumping test measured drawdowns')
plt.xlabel('t [min]')
plt.ylabel('dd [m]')
plt.grid()

for r in distances:
    I = data[:,0] == r # boolean array telling which data belong to this observation well
    plt.plot(data[I, -2], data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Korendijk-pumptest-Theis.ipynb".
The first task is:
Same, but using log scale
Can you write Python code for it?

plt.title('Korendijk pumping test measured drawdowns')
plt.xlabel('t [min]')
plt.ylabel('dd [m]')
plt.xscale('log')
plt.grid()

for r in distances:
    I = data[:,0] == r
    plt.plot(data[I,-2], data[I,-1], '.-', label='r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Korendijk-pumptest-Theis.ipynb".
The first task is:
The storage coefficient then follows from
$\frac 1 u = B \frac t {r^2}$, that is, $\frac {4 kD t} {r^2 S} = B \frac t {r^2}$ so that $S = \frac {4 kD} B$
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec6_5_Theis-well.ipynb".
The first task is:
$$kD=\frac{2.3Q}{4\pi\left(s_{10t}-s_{t}\right)}$$
$$S_S=2.25kD\frac{t_{0}}{r^{2}}$$
Can you write Python code for it?

# Drawdown increase per log-cycle = 0.375 m
# intersection with y = 0, 2e-3 (orange line), 1.2 e-2 green line, 2 e-2 read line

#kD = 2.3 * Q / (4 np.pi * Ddelta_s)
kD = 2.3 * Q / (4 * np.pi * 0.375)

Sy = []
for t0, r in zip([2e-3, 1.2e-2, 3.5e-2],[50, 125, 250]):
    Sy.append(2.25 * kD * t0 / r ** 2)
print('kD= {:.0f} m2/d'.format(kD))
print('Sy = {:.4g}, {:.4g}, {:.4g} [-]'.format(*Sy))

ax = newfig("Drawdown piezometer, Q = {:.0f} m3/d".format(Q), "t/r^2 [d/m^2]", "drawdown [m]", xscale='log',
           ylim=(2, -0.1))
for pz, r in zip(['piez[10]', 'piez[50]', 'piez[125]', 'piez[250]'], [10, 50, 125, 250]):
    t = pdata['t[d]']
    ax.plot(t/r ** 2, pdata[pz], '.-', label=pz)
ax.legend()

# delta pp


I am working on the file "Syllabus_in_notebooks/Sec6_5_Theis-well.ipynb".
The first task is:
Approxmation of the Theis well function
$$ W(u) \approx  = -\gamma - \ln u + u - \frac {u^2} {3\times 2!}
+ \frac {u^3} {3\times 3!} - \frac {u^4} {4 \times 4!} + ... $$
with $\gamma$ Euler's constant
$$ \gamma = 0.577216... $$
It's straigt-forward to implement this power series. But one also see that for very small values of $u$, the series may be approximated by
$$ \mathtt{W}(u) \approx -\gamma - \ln u $$
This may be worked out as follows:
$$ W(u) \approx - \ln \left( e^\gamma \right) - \ln \left( \frac {r^2 S} {4 kD t} \right) $$
$$ W(u) \approx + \ln \left( \frac 1 {e^\gamma} \right) + \ln \left( \frac {4 kD t} {r^2 S }\right)$$
$$ W(u) \approx \ln \left(  \frac {2.25 kD t} {r^2 S}  \right) $$
This is a simple logarithm, valid for small enough values or $u$, hence, for large enough values of time $t$. (Or for small enough values of $r$ in case a fixed time is chosen.)
Let's compare the Theis well function with this approxmation for different values or distance $r$
Can you write Python code for it?

plt.title('Theis drawdown, full well function and logarithmic approximation')
plt.xlabel('t')
plt.ylabel('W(u), exp1(u)')
plt.xscale('log')
plt.yscale('linear')
plt.grid()

for r in distances:
    plt.plot(t, exp1(r**2 * S /(4 * kD * t)), label='theis, r={:.0f} m'.format(r))
    plt.plot(t, np.log(2.25 * kD * t/(r**2 * S)), '-.', label='approx r={:.0f} m'.format(r))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec6_5_Theis-well.ipynb".
The first task is:
In the graph above, the y-axis was turned upside down by using plt.ylim((min, max)) so that the drawdown is downward on the graph, which is more inuitive. Instead of a raidus of influence, one may now define and observe a time of influence
$$ t = \sqrt{\frac {r^2 S} {2.25 kD} } $$
One observes that with time the drawdown (straight lines) moves parallel to the right on this logarithmic distance axis.
Pumping test example
Can you write Python code for it?


I am working on the file "Syllabus_in_notebooks/Sec6_5_Theis-well.ipynb".
The first task is:
The graph shows that the proximation is indeed only valid after some time, but after that it falls on the true drawdown graph.
Radius of influence
The distance at which the drawdown starts to deviate from zero increases with time as can be seen on the previous graph. That distance is called the radius of influence. It can be set equal to the distance at which the straight drawdown approxmation intersects with the horizontal axis, the line of zero drawdown. If we do that, we can derive it immediately from our approximation of the Theis drawdown curve by setting it to zero, i.e. by setting the argument under the logarithm equal to 1.
$$ \frac {2.25 kD t} {r^2 S} = 1 $$
And so
$$ r = \sqrt {\frac {2.25 kD t}  S}$$
From which it follows that this radius is proportional to the root of $kD$ and $t$ and inversily proportional to the root of $S$. In fact, one may say that the area of influence is directly proportional to $kD t / S$
This radius of influence is a very practial tool to estimated the influence that a transient well has on its enviroment.
Dradown versus log $r$ is linear for small enough $r$
It should also be clear that the drawdown increases with $1/r$ on logarithmic scale for small enough $r$.
We wil show this for different times in this section.
Can you write Python code for it?

r = np.logspace(-1, 3, 41)
times = [0.1, 0.3, 0.5, 1, 2, 5]

plt.title('Theis drawdown, full well function and logarithmic approximation')
plt.xlabel('r [m]')
plt.ylabel('W(u), exp1(u)')
plt.xscale('log')
plt.yscale('linear')
plt.ylim((17, -7))
plt.grid()

for t in times:
    plt.plot(r, exp1(r**2 * S /(4 * kD * t)), label='theis, t={:.1f} d'.format(t))
    plt.plot(r, np.log(2.25 * kD * t/(r**2 * S)), '-.', label='approx t={:.1f} d'.format(t))
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec7_convolution.ipynb".
The first task is:
Compare the result for well s1 with regular superposition
We can obtain the same results by means of regular superpostion. We'll show this on the hand of the input of well Q1, which is less random, and so easier to compare.
Can you write Python code for it?

# extract the times that the flow changes and the change of the extractions themselves
sw = np.hstack((True, np.diff(Q1) != 0)) # bool array, where True is the change of flow
Q3 = Q1[sw]     # Get the flow changes at these times
swt = time[sw]  # Get the actual times at which the flow changes
dQ  = np.hstack((Q3[0], np.diff(Q3))) # Compute the flow changes themselves

# Just print out the above to see what has been obtained in each step.


I am working on the file "Syllabus_in_notebooks/Sec7_convolution.ipynb".
The first task is:
Convolution by means of scipy.signal.lfilter(..)
Can you write Python code for it?

# compute the time series response to each well at the given r
s1 = lfilter(B, 1, Q1)
s2 = lfilter(B, 1, Q2)

# show graph of the input
plt.title('extraction m3/d')
plt.xlabel('time [d]')
plt.ylabel('Q [m3/d]')
plt.step(time, Q1, 'k', label='Q1', where='post')
plt.step(time, Q2, 'r', label='Q2', where='post')
plt.grid()
plt.legend()
plt.show()

# show the result, i.e. the head change caused by the varying extraction
plt.title('s due to well with random extraction')
plt.xlabel('time [d]')
plt.ylabel('head [m]')
plt.ylim((-0.6, 0))
plt.grid()
plt.plot(time, s1, 'k', label='s1')
plt.plot(time, s2, 'r', label='s2')
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec7_convolution.ipynb".
The first task is:
What follows next is ordinary superposition in time
Can you write Python code for it?

plt.title("Ordinary superpostion of well extractions")
plt.xlabel('time [d]')
plt.ylabel('s [m]')
plt.grid()

s = np.zeros_like(time)

# loop over the switch times and the flow changes
for st, dq in zip(swt, dQ):
    I = time > st   # select the time points that are later than the current switch times

    # compute the head changes for these times
    s_ = dq /(4 * np.pi * kD) * exp1(r**2 * S / (4 * kD * (time[I] - st)))
    plt.plot(time[I], s_, label='dQ = {:.0f} m3/d'.format(dq))

    # add them to the total head changes
    s[I] += s_

plt.plot(time, s, 'k', lw=3, label='total')
plt.legend()
plt.show()


I am working on the file "Syllabus_in_notebooks/Sec7_head-simulation-convolution.ipynb".
The first task is:
Convolution
The steady state head is
$$ s_{steady} = \frac  p {2 kD}  (b^2 - x^2) $$
For comparison, we will also plot this steady head for $p$ equal to the average recharge.
Can you write Python code for it?

# new data

L = 6000 # m (strip width)
b =L/2 # [m] half width of strip
x = 0. # point taken at cross section
kD = 600 # m2/d
S = 0.25 # [-]
a = 1.0 # m, sudden head change at x = -L/2
times = np.linspace(0, 0.5, 11)[1:] #

# Get the dynamic response

# Get time tau directly from the column days, then the vector tau is of equal length as the total
# time series, which will yield maximum accuracy (no truncation errors).
tau = meteo['days'].values

# Then compute the response
response = step(x, tau, S, kD, b)

# Do the convolution

rch = meteo['P'] - meteo['E'] # recharge in m/d

# Design a vadose-zone filter (here just uniform, but any shape can be chosen)
n = 1 # length of vadose filter
vadose_filter = np.ones(n) / n

# Filter the recharge to get the recharge at the water table
rch = lfilter(vadose_filter, 1, rch)

# steady state for mean recharge
steady = np.mean(rch) / (2 * kD) * (b**2 - x**2)

# add the simulated result as a column to the data frame (this is not necessary, though)
meteo['sim'] = lfilter(response, 1, rch)

# set up the plot
plt.title('simulated head $b$={:.0f} m, $kD$={:.0f} m$^2$/d, $S$={:.3f} [-]'.format(b, kD, S))
plt.xlabel('time -->')
plt.ylabel('head [m]')
plt.grid()

# plot simulate result
plt.plot(meteo.index, meteo['sim'], label='simulated')

# plot the simulation for a constant recharge equal to the average value
plt.plot(meteo.index, lfilter(response, 1, np.mean(rch) * np.ones_like(rch)), label='constant mean recharge')

# plot the steady state result for the case of  average recharge
plt.plot(meteo.index[[0, -1]], [steady, steady], 'k', lw=3, label='steady state')

plt.legend()
plt.show()


I am working on the file "SymPyExample.ipynb".
The first task is:
Calculus
You can use sympy to perform symbolic integration or differentiation.
Can you write Python code for it?

expression = 5*x**2 * sp.sin(3*x**3)

expression

expression.diff(x)

expression = sp.cos(x)

expression.integrate(x)

expression.integrate((x, 0, sp.pi / 2))


I am working on the file "SymPyExample.ipynb".
The first task is:
Lambdify: Making python functions from sympy expressions
Can you write Python code for it?

expression

f = sp.lambdify((x,y), expression, 'numpy')

f(3,4)

8 * 3**2 + 26 * 3 * 4 + 15 * 4**2


I am working on the file "SymPyExample.ipynb".
The first task is:
Symbols and Expressions
We'll define x and y to be sympy symbols, then do some symbolic algebra.
Can you write Python code for it?

x, y = sp.symbols("x y")

expression = (x+y)**4

expression

sp.expand(expression)

expression = 8*x**2 + 26*x*y + 15*y**2

expression

sp.factor(expression)

expression - 20 *x*y - 14*y**2

sp.factor(expression - 20*x*y - 14*y**2)


I am working on the file "SymPyExample.ipynb".
The first task is:
You can also create unevalated integrals or derivatives. These can later be evaluated with their doit methods.
Can you write Python code for it?

deriv = sp.Derivative(expression)

deriv

deriv.doit()

inte = sp.Integral(expression, (x, 0, sp.pi / 2))

inte

inte.doit()


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
Evaluate this for a particular value of x and y:
Can you write Python code for it?

integrate(pi*sin(x*y), x).subs([(x, pi), (y, 1)])


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
For instance, you can benchmark the speed of a function:
Can you write Python code for it?


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
It renders nicely as $\frac{\sin\pi x}{\cos\pi x}$. Now a symbolic integral:
Can you write Python code for it?


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
List comprehension does not actually return a list, it returns an iterator. Iterators are essentially generating functions for list and they are very important to functional programming in Python. Iterators have a next() function to retrieve subsequent elements, making them very easy to loop over. For instance:
Can you write Python code for it?


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
List comprehensions
Python was retrofitted with some elements of functional programming, mainly building on ideas coming from Haskell. It nevertheless remains an object-oriented language, but it is highly opportunistic. This approach is a lot like Mathematica, which is quintessentially functional, but you can follow any programming paradigm when using it.
List comprehensions are probably the most used construct in Python from functional programming. In fact, it is considered a Pythonesque way of doing things. It is a quick way of generating transformed lists from other lists: list comprehension is a simple map function in disguise.
Can you write Python code for it?


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
Some other cool features to explore: LaTeX export of notebooks with support for Bibtex -- also works for HTML -- and launching parallel computations in Python interpreters distributed across a cluster.
Symbolic operations
First, let us bypass the debate over Python 2 and 3 by forcing us to write code that functions identically in either version:
Can you write Python code for it?

from __future__ import print_function, division


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
SymPy, just like Mathematica, keeps results symbolic as long as possible. If you want a numerical result, you specifically have to request it:
Can you write Python code for it?

N(integrate(pi*sin(x*y), x).subs([(x, pi), (y, 1)]))


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
Then we bulk-import everything we might need:
Can you write Python code for it?


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
There is no shortage of useful examples for using iterators. Take combinatoric functions, for instance:
Can you write Python code for it?

import itertools
for combination in itertools.combinations([1, 2, 3, 4, 5], 2):
    print(combination)


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
This is, of course, not very useful, as we could have just used the list itself. List comprehensions are a more sensible way of getting iterators. Another way of creating an iterator is by defining a function that returns values through yield rather than through return. This allows an internal state for the function and lets it continue where it left it off. For example:
Can you write Python code for it?

def squares(N):
    for i in range(N):
        yield(i**2)

for j in squares(5):
    print(j)


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
We also want nicely formatted output:
Can you write Python code for it?

from sympy.interactive import printing
printing.init_printing(use_latex='mathjax')


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
We can easily define Hamiltonians. For instance, the Hubbard model on a chain is as follows:
Can you write Python code for it?

t = 1.0
U = 4.0
n_sites = 2
cu = [Operator("%s_%s_u" % ("c", i + 1)) for i in range(n_sites)]
cd = [Operator("%s_%s_d" % ("c", i + 1)) for i in range(n_sites)]
hamiltonian = sum(U*Dagger(cu[r])*cu[r]*Dagger(cd[r])*cd[r] for r in range(n_sites))
hamiltonian += sum(-t*(Dagger(cu[r])*cu[r+1]+Dagger(cu[r+1])*cu[r]
                       +Dagger(cd[r])*cd[r+1]+Dagger(cd[r+1])*cd[r]) for r in range(n_sites-1))
expand(hamiltonian)


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
We define some symbols:
Can you write Python code for it?

x, y = symbols('x y')

sin(pi*x)/cos(pi*y)


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
You also have direct access to the operating system calls:
Can you write Python code for it?


I am working on the file "Symbolic calculations and functional programming.ipynb".
The first task is:
You can, of course, always access the result of the previous command:
Can you write Python code for it?


I am working on the file "SymbolicVsAD.ipynb".
The first task is:
Let's also compare to AD using a source code transformation method (I used Tapenade in Fortran)
Can you write Python code for it?

def funcad(x):
    xd = 1.0
    yd = xd
    y = x
    for i in range(30):
        yd = (xd + yd)*cos(x + y)
        y = sin(x + y)
    return yd

dfdx = funcad(xpt)

print('dfdx =', dfdx)


I am working on the file "SymbolicVsAD.ipynb".
The first task is:
Let's compare with automatic differentiation using operator overloading:
Can you write Python code for it?

from algopy import UTPM, sin

x_algopy = UTPM.init_jacobian(xpt)
y_algopy = func(x_algopy)
dfdx = UTPM.extract_jacobian(y_algopy)

print('dfdx =', dfdx)


I am working on the file "SymbolicVsAD.ipynb".
The first task is:
We can now evaluate the expression.
Can you write Python code for it?

xpt = 0.1

dfdx = dexp.subs(x, xpt)

print('dfdx =', dfdx)


I am working on the file "Sympy Tutorial.ipynb".
The first task is:
Can make a substitution to evaluate the function
Can you write Python code for it?

from sympy.abc import alpha
y.subs(x,alpha)


I am working on the file "Sympy Tutorial.ipynb".
The first task is:
Lets transform the left hand side in teh same way
$$ y $$
Take the natural log
$$ ln (y)$$
Take the derivative
$$ \frac{1}{y}dy $$
Now the complete equation is as follows
$$
\frac{1}{y} dy = \frac{3x^2 + 15x - 8}{2x(3x^2 + x -2)}
$$
The right hand side is given above, so to find the solution just multiply $ y $
Can you write Python code for it?


I am working on the file "Sympy Tutorial.ipynb".
The first task is:
Or we can plot it
Can you write Python code for it?

sympy.plot((sympy.diff(y, x), (x, 0.2, 10)), (y, (x, 0.5, 10)))

# To change colors
# show = False delays the plot until we can set all of the parameters
# legend turns on the legend and uses the labels we have later.
p = sympy.plot((sympy.diff(y,x),(x,0.2,10)), (y, (x,0.5,10)), show = False, legend = True)
p[0].line_color = 'blue'
p[0].label = '$\\frac{dy}{dx}$'
p[1].line_color = 'green'
p[1].label = '$y$'
p.show()


I am working on the file "Sympy Tutorial.ipynb".
The first task is:
This was a round about way to do something simple, but we can compare them directly to make sure
Can you write Python code for it?

sympy.simplify(sympy.diff(y, x)) == sympy.simplify(sympy.diff(sympy.ln(y),x)*y)


I am working on the file "Sympy Tutorial.ipynb".
The first task is:
We can simplify this as well and just take the derivative directly
Can you write Python code for it?


I am working on the file "Sympy Tutorial.ipynb".
The first task is:
We first try to solve a complicated problem
Given:
$$
y = \frac{x^2\sqrt{3x-2}}{(x+1)^2}
$$
Find: $ \dot{y} $
Now we'll define symbolic variables
Can you write Python code for it?

# must define symbolic variables before us
x, y = sympy.symbols('x y', real=True)

y = x**2*sympy.sqrt(3*x-2)/(x+1)**2
y # This causes it to output formatted nicely.

# Taking the natural log of this:
a = sympy.ln(x**2*sympy.sqrt(3*x-2)/(x+1)**2)
a # Keep in mind that the left hand side is ln(y)

# Taking its derivative with respect to x
# So, we took the log of the right side, then the derivative
sympy.diff(a,x)

# That is kind of messy. Let the computer simplify it
sympy.simplify(sympy.diff(a,x))

# We could have done this all at once
sympy.simplify(sympy.diff(sympy.ln(y),x))


I am working on the file "Sympy.ipynb".
The first task is:
$$ \frac{\partial f(x,y)}{\partial x} = \lim_{h\to 0}\frac{f(x+h,y)-f(x,y)}{h}$$
Can you write Python code for it?

h = Symbol("h")

limit((f.subs(x, x+h) - f)/h, h, 0)

limit(1/x, x, 0, dir="+")

limit(1/x, x, 0, dir="-")


I am working on the file "Sympy.ipynb".
The first task is:
(Taylorovi) redovi
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Algebarske manipulacije
Can you write Python code for it?

together(izraz)

cancel(together(izraz))

(x+1)*(x+2)*(x+3)

expand((x+1)*(x+2)*(x+3))


I am working on the file "Sympy.ipynb".
The first task is:
Analiza
Deriviranje
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Definitni integrali:
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Efikasniji kod se postiže funkcijom lambdify koja kompajlira Sympy izraz u funkciju:
Can you write Python code for it?

# prvi argument je lista varijabli funkcije f, u ovom slučaju funckcija je x -> f(x)
f = lambdify([x], (x + pi)**2, 'numpy')

y_vec = f(x_vec)


I am working on the file "Sympy.ipynb".
The first task is:
Eq kreira matematičke jednakosti, tj. jednadžbe.
Can you write Python code for it?

factorit(18)

interact(factorit,n=(2,20));

interact(factorit,n=(1,20,2));

interact(factorit,n=widgets.widget_int.IntSlider(min=2,max=20,step=1,value=2));


I am working on the file "Sympy.ipynb".
The first task is:
Integracija
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Kompleksni brojevi
Imaginarna jedinica se označava s I.
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Koristit ćemo i interaktivne widgete, pa ih ovdje učitavamo
Can you write Python code for it?

from IPython.display import display
from ipywidgets import interact, fixed, interact_manual
import ipywidgets as widgets


I am working on the file "Sympy.ipynb".
The first task is:
Limesi
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Možemo navoditi i dodatne pretpostavke:
Can you write Python code for it?

x = Symbol('x', real=True)

x.is_imaginary

x = Symbol('x', positive=True)

x > 0


I am working on the file "Sympy.ipynb".
The first task is:
No subs možemo korisiti i općenitije:
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Rastav oko $x=1$:
Can you write Python code for it?

series(exp(x), x, 1)

series(exp(x), x, 1, 10)

tan(x).series(x,pi/2)

s1 = cos(x).series(x, 0, 5)
s1

s2 = sin(x).series(x, 0, 2)
s2

expand(s1 * s2)


I am working on the file "Sympy.ipynb".
The first task is:
Razlika u brzini izvođenja:
Can you write Python code for it?

%%timeit

y_vec = numpy.array([N(((x + pi)**2).subs(x, xx)) for xx in x_vec])

%%timeit

y_vec = f(x_vec)


I am working on the file "Sympy.ipynb".
The first task is:
Razlomci
Postoje tri numerička tipa: Real, Rational, Integer:
Can you write Python code for it?

r1 = Rational(4,5)
r2 = Rational(5,4)

r1

r1+r2

r1/r2

denom(r1)


I am working on the file "Sympy.ipynb".
The first task is:
Reziduumi:
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
Rješavanje jednadžbi
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
S metodom removeO se možemo riješiti $\mathcal{O}$ dijela:
Can you write Python code for it?


I am working on the file "Sympy.ipynb".
The first task is:
expand prima dodatne argumente. Npr. trig=True:
Can you write Python code for it?

sin(a+b)

expand(sin(a+b), trig=True)

simplify(sin(a)**2 + cos(a)**2)

simplify(cos(x)/sin(x))

f1 = 1/((a+1)*(a+2))

apart(f1)

f2 = 1/(a+2) + 1/(a+3)

together(f2)


I am working on the file "Sympy.ipynb".
The first task is:
Želimo izračunati $$\frac{\partial^3f}{\partial  x \partial y^2}$$
Can you write Python code for it?

diff(f, x, 1, y, 2)

def deriv(f):
    display(diff(f,x))
interact_manual(deriv, f='x');


I am working on the file "T-Test vs Permutation Test, Sunday (Aug 27) .ipynb".
The first task is:
ANYWAY, results, results, results.

You can see on the Results + Blue-And-Yellow coloured figure that we have generated two sets of data (datasets A and B) and t-tested them.

You can also see that assuming equal variance and non-equal variance changes the statistical significance of the difference between the datasets A and B. #told-you-so #assumptions-matter
Can you write Python code for it?

p = 1

for iii in range(int(1e20)):

    dataset_A = np.random.normal(1.24, 1.75, 10)
    dataset_B = np.random.normal(1.78, 1.1, 15)
    t_stats , p = stats.ttest_ind(dataset_A, dataset_B, equal_var = False)
    if (abs(p - 0.05) < 0.001) & ((dataset_A > 0).all()) & ((dataset_B > 0).all()):
        break

a,b = stats.ttest_ind(dataset_A, dataset_B, equal_var = False)
c,d = stats.ttest_ind(dataset_A, dataset_B, equal_var = True)

print('Results:','\n','non-equal variance, t(9) = %.2f, p = %.3f'%(a,b),'\n',
      'equal variance, t(14) = %.2f, p = %.3f'%(c,d))

fig,ax = plt.subplots()
ax.hist(dataset_A,color = "blue", alpha = .3, label = 'dataset_A')
ax.hist(dataset_B,color = "yellow", alpha = .3, label = 'dataset_B')
ax.axvline(dataset_A.mean(), label = 'Mean of dataset_A', color = 'blue')
ax.axvline(dataset_B.mean(), label = 'Mean of dataset_B', color = 'yellow')
ax.legend(loc = "best")
ax.set(title = "Histogram of Datasets A and B", xlabel = "Value of generated data points",
       ylabel = "Count / Frequency of X")