import streamlit as st
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import altair as alt
from datetime import time,datetime,date
from io import StringIO
import time as tim
from PIL import Image

def write():
	with st.spinner("Cargando Elementos Básicos ..."):
		st.title("Elementos Básicos de Streamlit")

######################ELEMENTOS DE TEXTO########################################
	st.header("Elementos de Texto")
	with st.beta_expander('Elementos de encabezado'):
		st.title("Esto es un titulo")
		st.code("""st.title("Esto es un titulo")""")
		st.header('Esto es un encabezado')
		st.code("""st.header('Esto es un encabezado')""")
		st.subheader('Esto es un subencabezado')
		st.code("""st.subheader('Esto es un subencabezado')""")

	with st.beta_expander("Elementos de markdown"):
		st.markdown(
		'''
		# Esto es un título en markdown
		## Esto es un encabezado
		### Esto es subencabezado

		:+1: :sunglasses: Y esto es una lista:
		- Item 1
		- Item 2

		''')
		st.code("""st.markdown(
		'''
		# Esto es un título en markdown
		## Esto es un encabezado
		### Esto es subencabezado

		:+1: :sunglasses: Y esto es una lista:
		- Item 1
		- Item 2

		''')""")

	with st.beta_expander("Elementos de texto simple"):
		st.text('Tenemos un texto de ancho fijo')
		st.code("""st.text('Tenemos un texto de ancho fijo')""")
		st.text('Podemos además insertar listas de objetos:')
		st.write(['st', 'is <', 3])
		st.code("""st.write(['st', 'is <', 3])""")
		st.write("Y podemos tambien insertar ecuaciones de Látex:")
		st.latex(r''' e^{i\pi} + 1 = 0 ''')
		st.code("""st.latex(r''' e^{i\pi} + 1 = 0 ''')""")

######################ELEMENTOS DE DATA########################################
	st.header("Elementos de Data")
	with st.beta_expander("Elementos para mostrar data "):

		st.write("Podemos mostrar la información contenida en un dataframe de Pandas:")
		df = pd.DataFrame(np.random.randn(50, 20),columns=('col %d' % i for i in range(20)))
		st.dataframe(df.style.highlight_max(axis=0)) #resaltamos los máximos
		st.code("""df = pd.DataFrame(np.random.randn(50, 20),columns=('col %d' % i for i in range(20)))
st.dataframe(df.style.highlight_max(axis=0)) #seteamo el ancho y largo, resaltamos los máximos""")

		st.write("También podemos mostrar la misma información a través de una tabla simple:")
		st.table(df.iloc[0:10])
		st.code("""st.table(df.iloc[0:10])""")

		st.write("Y finalmente podemos también mostrar la información contenida en formato JSON:")
		st.json({'foo':'bar','fu':'ba'})
		st.code("""st.json({'foo':'bar','fu':'ba'})""")

######################ELEMENTOS GRÁFICOS########################################
	st.header("Elementos gráficos")
	with st.beta_expander("Elementos de Ploteo"):
		st.write("Podemos hacer gráficas lineales (¡y se puede interactuar con ellos!):")
		chart_data = pd.DataFrame(np.random.randn(20, 3),columns=['a', 'b', 'c'])
		st.line_chart(chart_data)
		st.code("""chart_data = pd.DataFrame(np.random.randn(20, 3),columns=['a', 'b', 'c'])
st.line_chart(chart_data)""")

		st.write("Podemos hacer gráficos de áreas:")
		chart_data = pd.DataFrame(np.random.randn(20, 3),columns=['a', 'b', 'c'])
		st.area_chart(chart_data)
		st.code("""chart_data = pd.DataFrame(np.random.randn(20, 3),columns=['a', 'b', 'c'])
st.area_chart(chart_data)""")

		st.write(" Podemos hacer gráficos de barras:")
		chart_data = pd.DataFrame(np.random.randn(50, 3),columns=["a", "b", "c"])
		st.bar_chart(chart_data)
		st.code("""chart_data = pd.DataFrame(np.random.randn(50, 3),columns=["a", "b", "c"])
st.bar_chart(chart_data)""")

		st.write("Streamlit además tiene integración con Matplotlib")
		arr = np.random.normal(1, 1, size=100)
		fig, ax = plt.subplots()
		ax.hist(arr, bins=20)
		st.pyplot(fig)
		st.code("""arr = np.random.normal(1, 1, size=100)
fig, ax = plt.subplots()
ax.hist(arr, bins=20)
st.pyplot(fig)""")

		st.write("También integración con Altair:")
		df = pd.DataFrame(np.random.randn(200, 3),columns=['a', 'b', 'c'])
		c = alt.Chart(df).mark_circle().encode(	x='a', y='b', size='c', color='c', tooltip=['a', 'b', 'c'])
		st.altair_chart(c, use_container_width=True)
		st.code("""df = pd.DataFrame(np.random.randn(200, 3),columns=['a', 'b', 'c'])
c = alt.Chart(df).mark_circle().encode(	x='a', y='b', size='c', color='c', tooltip=['a', 'b', 'c'])
st.altair_chart(c, use_container_width=True)""")

		st.write("Y finalmente puede integrarse además con mapas de OpenStreetMaps:")
		df = pd.DataFrame(
		np.random.randn(500, 2) / [50, 50] + [-33.46, -70.65],columns=['lat', 'lon'])#[-33.50, -70.55],columns=['lat', 'lon'])
		st.map(df)

		st.write("De la misma forma anterior, Streamlit puede integrarse con bibliotecas como Vega-Lite, Plotly, Bokeh, PyDeck, DEck_GL y Graphviz. ¡Los invitamos a testearlos! ")
		st.code("""st.vega_lite_chart(data)
st.plotly_chart(data)
st.bokeh_chart(data)
st.pydeck_chart(data)
st.deck_gl_chart(data)
st.graphviz_chart(data)""")

######################ELEMENTOS MULTIMEDIA########################################
	st.header("Elementos Multimedia")
	with st.beta_expander("Elementos de música, audio y video"):
		from PIL import Image
		st.write("En esta sección podemos cargar imágenes directamente desde nuestro PC y agregarle un comentario: ")
		image = Image.open(r'resources/sunrise.jpg')
		st.image(image, caption='Puesto de Sol en las montañas. Ojalá pudiese estar allí. ',use_column_width=True)
		st.code("""image = Image.open('sunrise.jpg')
st.image(image, caption='Puesto de Sol en las montañas. Ojalá pudiese estar allí. ',use_column_width=True)""")

		st.write("Podemos además agregar archivos de audio, como el Valse Opus 64 de Chopin (también conocido como el 'vals del perrito'):")
		audio_file = open(r'resources/Chopin-valse-opus64.ogg', 'rb')
		audio_bytes = audio_file.read()
		st.audio(audio_bytes, format='audio/ogg')
		st.code("""audio_file = open('Chopin-valse-opus64.ogg', 'rb')
audio_bytes = audio_file.read()
st.audio(audio_bytes, format='audio/ogg')""")

		st.write("""Podemos por otra parte agregar videos directamente desde nuestro PC:""")
		video_file = open(r'resources/Star - 6962.mp4', 'rb')
		video_bytes = video_file.read()
		st.video(video_bytes)
		st.code("""video_file = open('Star - 6962.mp4', 'rb')
video_bytes = video_file.read()
st.video(video_bytes)""")

		st.write("Y finalmente también podemos agregar videos de Youtube:")
		st.video('https://www.youtube.com/watch?v=NUYvbT6vTPs')

######################WIDGETS INTERACTIVOS########################################
	st.header("Widgets Interactivos")
	with st.beta_expander("Widgets"):
		st.write("Dentro de los widgets interactivos podemos incluir botones:")
		if st.button('Digamos hola',key="say_hello"):
		    st.write('Respuesta: ¡Hola quien quiera que seas!')
		else:
			st.write('Respuesta: ¡Adios!')
		st.code("""if st.button('Digamos hola',key="say_hello"):
	st.write('Respuesta: ¡Hola quien quiera que seas!')
else:
	st.write('Respuesta: ¡Adios!')""")

		st.write("Tambien tenemos checkbox para marcar alguna preferancia u opción:")
		agree = st.checkbox('Estoy de acuerdo.')
		if agree:
			st.write('¡Genial! Me acabas de vender tu alma :).')
			st.code("""agree = st.checkbox('Estoy de acuerdo.')
if agree:
	st.write('¡Genial! Me acabas de vender tu alma :).')""")

		st.write("O también radio buttons para marcar entre varias opciones:")
		ingrediente = st.radio("¿Que ingrediente prefieres en la pizza?",('Tocino', 'Piña', 'Choricillo'))
		if ingrediente == 'Tocino':
			st.write('¡Cuidado con el colesterol!.')
		elif ingrediente=='Piña':
			st.write('¡¿En serio?! ¿No me digas que también le echas Coca-Cola al vino?')
		else:
			st.write("Buena elección, padawan.")
		st.code("""ingrediente = st.radio("¿Que ingrediente prefieres en la pizza?",('Tocino', 'Piña', 'Choricillo'))
if ingrediente == 'Tocino':
	st.write('¡Cuidado con el colesterol!.')
elif ingrediente=='Piña':
	st.write('¡¿En serio?! ¿No me digas que también le echas Coca-Cola al vino?')
else:
	st.write("Buena elección, padawan.")""")

		st.write("Las selectboxes te permiten seleccionar una opción dentro de una lista desplegable:")
		opcion = st.selectbox('¿Cómo quieres ser contactado?',('Email', 'Teléfono de casa', 'Celular'))
		st.write('Seleccionaste:', opcion)
		st.code("""	st.write("Las selectboxes te permiten seleccionar más de una opción:")
opcion = st.selectbox('¿Cómo quieres ser contactado?',('Email', 'Teléfono de casa', 'Celular'))
st.write('Seleccionaste:', opcion)""")

		st.write("Existe la opción de elegir múltiples alternativas:")
		opciones= st.multiselect('¿Cuáles son tus colores favoritos?',['Green', 'Yellow', 'Red', 'Blue'],	['Yellow', 'Red'])
		st.write('You selected:', opciones)
		st.code("""	opciones= st.multiselect('¿Cuáles son tus colores favoritos?',['Green', 'Yellow', 'Red', 'Blue'],	['Yellow', 'Red'])
st.write('You selected:', opciones)""")

		st.write("Con el widget 'Slider' puedes elegir un valor,un rango de valores o incluso fechas:")
		x= st.slider("Valor de x:", min_value=1, max_value=100, value=5, step=1, format=None, key=None)
		st.write('Su cuadrado es:', x * x)
		valores = st.slider('Selecciona un rango de valores:',	0.0, 100.0, (25.0, 75.0))
		st.write('Valores:', valores)
		cita = st.slider("Agenda tu cita:",value=(time(11, 00), time(12, 45)))
		st.write("Agendaste tu cita para entre las ", cita[0].strftime("%H:%M")," y las ",cita[1].strftime("%H:%M"))
		st.code("""x= st.slider("Valor de x:", min_value=1, max_value=100, value=5, step=1, format=None, key=None)
st.write('Su cuadrado es:', x * x)
valores = st.slider('Selecciona un rango de valores:',	0.0, 100.0, (25.0, 75.0))
st.write('Valores:', valores)
cita = st.slider("Agenda tu cita:",value=(time(11, 00), time(12, 45)))
st.write("Agendaste tu cita para entre las ", cita[0].strftime("%H:%M")," y las ",cita[1].strftime("%H:%M"))""")

		st.write("Además de lo anterior, podemos también ingresar texto:")
		title_text = st.text_input('Ingresa tu película favorita:', 'Iron Man 3')
		st.write('Tu película favorita es ', title_text)
		st.code("""title_text = st.text_input('Ingresa tu película favorita:', 'Iron Man 3')
st.write('Tu película favorita es ', title_text)""")

		st.write("Podemos insertar números:")
		number = st.number_input('Inserta un número:')
		st.write('Tu número es el ', number)
		st.code("""number = st.number_input('Inserta un número:')
st.write('Tu número es el ', number)""")

		st.write("Podemos agregar tambien en un área de texto para, por ejemplo, analizar sentimientos:")
		txt = st.text_area('Texto a analizar', '''¡Me encanta lo que hace el candidato del partido azul! ¡Vota por el partido azul!''')
		st.write('Sentimiento: ', 'Favorable' )#run_sentiment_analysis(txt))
		st.code("""txt = st.text_area('Texto a analizar', '''¡Me encanta lo que hace el candidato del partido azul! ¡Vota por el partido azul!''')
st.write('Sentimiento: ', 'Favorable' )#run_sentiment_analysis(txt))""")

		st.write("Podemos ingresar fechas u horas:")
		d = st.date_input("¿Cuándo naciste?",date(2019, 7, 6))
		st.write('Naciste el ', d)
		t = st.time_input('Programar alarma para las ', time(8, 45))
		st.write('Alarma programda a las ', t)
		st.code("""d = st.date_input("'¿Cuándo naciste?'",date(2019, 7, 6))
st.write('Naciste el ', d)
t = st.time_input('Programar alarma para las ', time(8, 45))
st.write('Alarma programda a las ', t)""")

		st.write("Finalmente podemos subir un archivo o múltiples archivos:")
		uploaded_file = st.file_uploader("Elige un archivo:")
		if uploaded_file is not None:
		    bytes_data = uploaded_file.read()
		    st.write(bytes_data)
		uploaded_files = st.file_uploader("Elige tus archivos:", accept_multiple_files=True)
		for uploaded_file in uploaded_files:
		    bytes_data = uploaded_file.read()
		    st.write("filename:", uploaded_file.name)
		    st.write(bytes_data)
		st.code("""uploaded_file = st.file_uploader("Elige un archivo:")
if uploaded_file is not None:
    bytes_data = uploaded_file.read()
    st.write(bytes_data)
uploaded_files = st.file_uploader("Elige tus archivos:", accept_multiple_files=True)
for uploaded_file in uploaded_files:
    bytes_data = uploaded_file.read()
    st.write("filename:", uploaded_file.name)
    st.write(bytes_data)""")

######################ELEMENTOS DE PROCESO, INFORMACION Y CONTROL DE FLUJO########################################
	st.header("Procesamiento, Información y Control de Flujo")
	with st.beta_expander("Elementos de Procesamiento:"):
		st.write("Podemos crear elementos que se vayan actualizando con el tiempo:")
		if st.button("Aprietame para iniciar el conteo"):
			with st.empty():
				for seconds in range(3):
					st.write(f"⏳ {seconds} segundos han pasado")
					tim.sleep(1)
				st.write("✔ conteo de segundos terminado.")
		st.write("Podemos crear una barra de progreso:",key="msje2")
		if st.button("Aprietame para iniciar la barra de carga"):
			my_bar = st.progress(0)
			for percent_complete in range(100):
			    tim.sleep(0.03)
			    my_bar.progress(percent_complete + 1)

		st.write("Podemos también poner elementos de espera:")
		if st.button("Esperar"):
			with st.spinner('Esperando 3 segundos...'):
			    tim.sleep(3)
			st.success('Listo!')

	with st.beta_expander("Elementos de información:"):
		st.write("Además podemos crear distintos cuadros para comunicarnos con los usuarios:")
		st.info("Esto es un cuadro de información.")
		st.warning('Esto es un cuadro de advertencia.')
		st.success('Esto es un cuadro de éxito.')
		st.error("Esto es un cuadro de error.")
		e = RuntimeError('También podemos levantar Excepciones. Por ejemplo estos es un RuntimeError')
		st.exception(e)

	with st.beta_expander("Elementos de código:"):
		st.write("Podemos además mostrar código directamente:")
		st.code("""st.write('Este código será impreso en pantalla.')""")

	#######################################################################PLACEHOLDERS, HELPS & OPTIONS

	# placeholder = st.empty()
	# # Replace the placeholder with some text:
	# placeholder.text("Hello")
	# # Replace the text with a chart:
	# placeholder.line_chart({"data": [1, 5, 2, 6]})
	# # Replace the chart with several elements:
	# with placeholder.beta_container():
	#     st.write("This is one element")
	#     st.write("This is another")
	# # Clear all those elements:
	# placeholder.empty()

	# st.help(pd.DataFrame)

	#######################################################################MUTATE DATA
	# df1 = pd.DataFrame(
	#    np.random.randn(50, 20),
	#    columns=('col %d' % i for i in range(20)))
	# # my_table = st.table(df1)
	# df2 = pd.DataFrame(
	#    np.random.randn(50, 20),
	#    columns=('col %d' % i for i in range(20)))
	# # my_table.add_rows(df2)
	# # Now the table shown in the Streamlit app contains the data for
	# # df1 followed by the data for df2.

	# my_chart = st.line_chart(df1)
	# my_chart.add_rows(df2)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Jul 17 16:17:25 2017

@author: jorgemauricio
"""

# librerias
import numpy as np
from pandas import Series,DataFrame
import pandas as pd

# indices como objetos
my_ser = Series([1,2,3,4],index=['A','B','C','D'])

# Obtener los indices
my_index = my_ser.index

# desplegar
my_index

# obtener valores mediante rangosCan grab index ranges
my_index[2:]

# si tratamos de cambiar un index, que pasa?
my_index[0] = 'Z'

# Marca Error. Los indices no son mutables


import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import math

def plot_progression(S, T1=None, names=None, linestyles=None, xlabel="$t$", ylabel="Value", title=None, show=True):

	if S.ndim > 1:
		if T1 is None:
			T1 = range(1,len(S[0])+1)
		if linestyles is None:
			for s in S:
				plt.plot(T1, s)
		else:
			for s in S:
				plt.plot(T1, s, linestyle=linestyles[i])
	else:
		if T1 is None:
			T1 = range(1,len(S)+1)
		plt.plot(T1, S)

	if names is not None:
		plt.legend(names)

	if xlabel is not None:
		plt.xlabel(xlabel)

	if ylabel is not None:
		plt.ylabel(ylabel)

	if title is not None:
		plt.title(title)

	if show:
		plt.show()

def plot_freq(F, k=None, K1=None, n=None, bar_width=2, interpolation='hanning', cmap='gray_r', xlabel="$t$", ylabel="Frequency", title=None, show=True):

	if k is None:
		k = len(F)      #number of arms (series)

	if K1 is None:
		K1 = np.arange(1,k+1)      #range of arms (for labels)

	if n is None:
		n = len(F[0])   #time-horizon (number of samples)

	w = bar_width #bar width (>1)
	h = k*w+1     #fig height

	img_map = np.zeros([h,n])   #image map

	for i in range(h):
		if (i % w != 0):
			img_map[i] = F[i//w]

	plt.imshow(img_map, aspect="auto", interpolation=interpolation, cmap=cmap)
	plt.yticks(np.arange(1, h, step=w), K1)

	plt.colorbar()

	if xlabel is not None:
		plt.xlabel(xlabel)

	if ylabel is not None:
		plt.ylabel(ylabel)

	if title is not None:
		plt.title(title)

	if show:
		plt.show()

def plot_history(H=None, H1=None, k=None, K1=None, n=None, T1=None, xlabel='$t$', ylabel='Arm', title='History of pulled arms', alpha=0.5, markersize=None, show=True):

	#H is the history of pulls starting from index 0 for arms
	#H1 the same, just shifted to have the first arm as index 1
	if H1 is None:
		if H is None:
			raise ValueError("You must define either H or H1.")
		else:
			H1 = H+1

	#n is the time-horizon
	#and T1 is the range, starting from t=1
	if T1 is None:
		if n is None:
			n = len(H1)
		T1 = range(1,n+1)

	#k is the number of arms
	#and K1 is the range of arm indexes, starting from 1
	if K1 is None:
		if k is None:
			k = max(H1)
		K1 = range(1,k+1)
	else:
		if k is None:
			k = len(K1)

	plt.plot(T1, H1, 'o', markersize=markersize, alpha=alpha)

	plt.yticks(K1)
	plt.ylim([0.5, k+0.5])
	plt.gca().invert_yaxis()

	if xlabel is not None:
		plt.xlabel(xlabel)

	if ylabel is not None:
		plt.ylabel(ylabel)

	if title is not None:
		plt.title(title)

	if show:
		plt.show()

def plot_comp_algs(y, names, xlabel="Algorithm", ylabel="Value", title="Comparison", show=True):

	x = range(len(names))
	low = min(y)
	high = max(y)
	plt.ylim([low, high])
	plt.bar(x, y, align='center', alpha=0.5)
	plt.xticks(x, names, rotation='vertical')

	if xlabel is not None:
		plt.xlabel(xlabel)

	if ylabel is not None:
		plt.ylabel(ylabel)

	if title is not None:
		plt.title(title)

	if show:
		plt.show()

def plot_comp_arms(n_a, K1=None, xlabel="Arm (Selected Action)", ylabel="Number of Actions Taken", title="Selected Actions", show=True):

	if K1 is None:
		K1 = np.arange(1,len(n_a)+1, dtype='int')      #range of arms (for labels)

	plt.bar(K1, n_a)

	if xlabel is not None:
		plt.xlabel(xlabel)

	if ylabel is not None:
		plt.ylabel(ylabel)

	if title is not None:
		plt.title(title)

	if show:
		plt.show()

def plot_reward_regret(reward, regret, best_strategy, T1=None, names=['Cumulated Reward', 'Cumulated Regret', 'Best Strategy'], xlabel="$t$", ylabel="Reward and Regret", title="Best Strategy vs Cumulated Reward vs Regret ", show=True):

	#best, reward, regret
	Y = np.array([reward, -regret, best_strategy])

	plot_progression(Y, show=False, names=names, title=title, ylabel=ylabel)

	plt.fill_between(T1, 0, reward)
	plt.fill_between(T1, 0, -regret)

	if xlabel is not None:
		plt.xlabel(xlabel)

	if ylabel is not None:
		plt.ylabel(ylabel)

	if title is not None:
		plt.title(title)

	if show:
		plt.show()

def nrows_ncols(N):
    """(nrows, ncols) pour créer un subplots de N figures avec les bonnes dimensions."""
    nrows = int(np.ceil(np.sqrt(N)))
    ncols = N // nrows
    while N > nrows * ncols:
        ncols += 1
    nrows, ncols = max(nrows, ncols), min(nrows, ncols)
    return nrows, ncols

def plot_hist_regret(rewards, names, horizon, mustar=1):
    nrows, ncols = nrows_ncols(len(names))
    fig, axes = plt.subplots(nrows, ncols, sharex=False, sharey=False)
    fig.suptitle("Histogram of regret at $t = T = {}$".format(horizon))

    # XXX See https://stackoverflow.com/a/36542971/
    ax0 = fig.add_subplot(111, frame_on=False)  # add a big axes, hide frame
    ax0.grid(False)  # hide grid
    #ax0.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')  # hide tick and tick label of the big axes
    ax0.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)  # hide tick and tick label of the big axes
    # Add only once the ylabel, xlabel, in the middle
    ax0.set_ylabel("Distribution")
    ax0.set_xlabel("Regret")

    for i, r in enumerate(rewards):
        x, y = i % nrows, i // nrows
        ax = axes[x, y] if ncols > 1 else axes[x]
        regret = mustar * horizon - r
        #ax.hist(regret, normed=True, bins=25)
        ax.hist(regret, density=True, bins=25)
        ax.set_title(names[i])
    plt.show()


#!/usr/bin/env python
# coding: utf-8

# (ch:module)=
# # 모듈

# 캡슐화encapsulation?

# ## 모듈이란?

# 한 번 구현한 함수, 상수, 변수, 클래스, 객체 등을
# 다른 파이썬 파일에서 공유하여 사용하면 프로그램을 보다 효율적으로 구현할 수 있다.
# 이를 위해 **모듈**을 활용한다.
#
# 파이썬에서 모듈은 간단하게 말하면 하나의 파이썬 소스코드 파일이다.
# 파일의 확장자가 `py`이다.
# 모듈은 언제든지 불러와서(import) 모듈에 포함된 내용을 활용할 수 있다.

# ## 모듈 종류

# 모듈은 크게 세 종류로 나뉜다.
#
# * 내장 모듈(built-in module): 파이썬에서 기본적으로 제공하는 모듈
#     * 파이썬을 설치할 때 기본으로 제공되는 모듈
#     * 예제: `math`, `urllib.request`, `random`, `turtle`, `os`, `sys` 등등
# * 제3자 라이브러리 모듈: 제3자에 의해 제공된 라이브러리에 포함된 모듈
#     * 제3자가 제공한 라이브러리를 설치할 때 제공되는 모듈
#     * 예제: `numpy.random`, `matplotlib.pyplot`, `pygame.mixer` 등등
# * 사용자 정의 모듈: 개인 프로젝트를 진행하면서 작성한 모듈
#     * 프로젝트 관리를 위해 사용되는 모듈
#     * 예제: 아래 예제에서 소개되는 `wc.py` 파일

# ## 내장 모듈

# 파일썬을 설치하면 다양한 모듈이 함께 설치되며, 그런 모듈을 내장 모듈(built-in modules)이라 부른다.
# 지금까지 사용한 모듈은 `random`, `math`, `os`, `sys` 등이며, `import 모듈명` 형식으로 모듈을 불러왔다.
# 단, `.py` 확장자는 생략한다.
#
# 예를 들어, `math` 모듈을 불러오려면 다음과 같이 한다.

# In[1]:

import math

# 그러면 `math` 모듈에서 정의된 많은 수학 관련 함수들을 사용할 수 있다.
# 예를 들어, 로그(`log`) 함수의 사용은 다음과 같다.

# In[2]:

math.log(2)

# 무작위 수 생성 함수들을 모아놓은 `random` 모듈에 0과 1사이의 실수를 무작으로
# 생성하는 함수 `random`을 아래와 같이 사용한다.

# In[3]:

import random

random.random()

# `random` 함수는 실행할 때마다 새로운 수를 무작위로 생성한다.

# In[4]:

random.random()

# ## 제3자 라이브러리 모듈

# 제3자에 의해  제공된 라이브러리에 포함된 모듈을 사용하려면 먼저 추가 패키지를 설치해야 한다.
# 예를 들어, 단순한 2차원 그래프를 그리는 도구가 필요하면 맷플롯립(`matplotlib`)을,
# 데이터분석을 위한 다양한 도구가 필요하면 넘파이(`numpy`)를 기본적으로 설치해야 한다.
# 제3자 제공 파이썬 패키지를 설치하는 방법은 해당 패키지의 홈페이지를 참고해야 한다.
# 보통은 파이썬을 설치할 때 함께 제공되는 파이썬 패키지 매니저인 pip 명령어를 활용한다.
#
# 파이썬과 관련된 주요 패키지를 함께 제공하는 앱이나 웹서버 등이 있어서
# 개인적으로 추가하고 관리하기 어려운 경우 사용할 수 있다.
# 예를 들어, 아나콘다 패키지는 `matplotlib`, `numpy` 등 파이썬
# 데이터분석과 관련된 다수의 패키지를 함께 제공한다.
# 또한 Repl.it, 구글 코랩 등도 파이썬 기본 이외에 다양한 패키지를 함께 제공한다.
#
# 현재 이 노트북을 실행하는 서버 또한 `matplotlib`, `numpy` 등을 포함해서
# 다양한 제3자 제공 라이브러리가 함께 설치되어 있다.
# Repl.it 및 구글 코랩에서 문제없이 작동하는 코드만 여기에서 사용한다.

# ### 제3자 라이브러리 모듈 불러오기

# 예를 들어, 고차원 어레이를 편하게 다룰 수 있도록 도와주는 많은 도구를 담고 있는 넘파이 패키지를 활용하려면
# 아래와 같이 `numpy` 패키지를 불러와야 한다. 그러면 관련된 많은 모듈과 도구들을 활용할 수 있다.

# #### 별칭 사용하기

# 모듈이나 패키지를 불러올 때 별칭을 지정하여 활용할 수 있다.
# 많이 사용되는 모듈이나 패키지는 많은 사람들이 관습적으로 사용하는 별칭이 있다.
# `numpy` 패키지의 경우 보통 `np`로 줄여 부른다.
# 별칭을 사용하는 방식은 다음과 같다.

# In[5]:

import numpy as np

# 넘파이 패키지에 `random` 모듈이 포함되어 있다.
# 파이썬에서 기본으로 제공하는 내장 모듈인 `random` 보다 많은 기능을 갖춘 도구들이
# 넘파이 `random` 모듈이 제공한다.
#
# 앞서 언급했던 파이썬 내장 모듈 `random`에서 정의된 `random` 함수에 해당하는 함수가
# 넘파이 `random` 모듈에 동일한 이름으로 정의되어 있다.
# 하지만 넘파이 `random` 모듈을 사용하려면 반드시 `np`를 아래와 같이 추가해야 한다.
# 그렇지 않으면 파이썬 내장 모듈과 이름이 혼동되어 문제가 발생할 수 있다.

# In[6]:

np.random.random()

# In[7]:

np.random.random()

# 패키지나 모듈을 불러올 때 별칭을 지정하면 반드시 별칭으로 사용해야 한다.
# 그렇지 않으면 오류가 발생한다.

# In[8]:

numpy.random.random()

# 원래의 패키지 또는 모듈의 이름을 사용하려면 한 번 불러와야 한다.
# 그러면 원래 이름과 별칭 모두 사용할 수 있다.

# In[ ]:

import numpy

# In[ ]:

numpy.random.random()

# In[ ]:

np.random.random()

# ## 사용자 정의 모듈

# 먼저, 아래 코드를 담고 있는 `wc.py` 파일을 먼저 작성한다.
# `wc.py` 파일을 이용하여 사용자가 임의로 작성한 파이썬 코드 파일을 모듈로 활용하는 법을 설명한다.
#
# **주의:**
# 설명을 위해 `wc.py` 파일이 현재 작업 디렉토리의 하위 디렉토리인 `codes` 디렉토리에 저장되어 있다고 가정한다.
#
# 파일의 내용은 아래와 같다.

#

# ### `__init__.py` 파일 작성
#
# 현재 작업 디렉토리가 아닌 다른 디렉토리에 포함된 모듈을 불러오려면
# 해당 디렉토리에 `__init__.py` 파일이 생성되어 있어야 한다.
# 따라서 `codes` 라는 하위 디렉토리에 포함된 파일들의 리스를 확인하면 `wc.py` 와 `__init__.py`
# 두 개의 파일이 포함되어 있어야 한다.
#
# **주의:** `__init__.py` 파일은 아무 내용이 없는 빈 파일이어도 된다. 다른 용도는 여기서 다루지 않는다.

# #### 현재 작업 디렉토리 확인

# 파이썬 명령어를 이용하여 현재 작업 디렉토리(current working directory, 줄임말: cwd)를
# 확인하는 방법은 다음과 같다.

# In[ ]:

import os
cwd = os.getcwd()
print(cwd)

# 현재 작업 디렉토리에 `codes`라는 디렉토리 포함여부 확인은 다음과 같다.

# In[ ]:

'codes' in os.listdir(cwd)

# `codes` 디렉토리에 포함된 파일들의 리스트 확인하면 `wc.py`와 `__init__.py` 두 파일이
# 포함되어 있음을 볼 수 있다.

# In[ ]:

os.listdir("./codes")

# ### 사용자 정의 모듈 불러오기

# `wc.py` 모듈에 포함되어 있는 `linecount` 함수를 활용하기 위해서
# 먼저 `wc.py` 모듈을 불러와야 한다.

# #### 현재 작업 디렉토리 모듈 불러오기

# 만약 `wc.py` 모듈이 현재 디렉토리에 포함되어 있다면 아래와 같이 불러오면 된다.
#
# ```
# import wc
# ```
#
# 또한 `wc.py`에 작성된 코드 마지막 줄을 아래와 같이 작성해야 오류가 발생하지 않는다.
#
# ```python
# print(linecount('wc.py'))
# ```

# #### 경로 설정 문제

# 그런데 `wc.py` 모듈이 현재 디렉토리의 하위 디렉토리인 `codes` 에 포함되어 있기 때문에
# 불러오기 과정이 좀 더 복잡하다.
# 단순히 `import wc` 명령어를 사용하면 모듈을 찾을 수 없다는 오류(`ModuleNotFoundError`)가 발생한다.

# In[ ]:

import wc

# 이와 같이 현재 작업디렉토리가 아닌 곳에 포함된 사용자 정의 모듈은 다른 방식으로 불러와야 한다.
# 다양한 방식이 있지만 여기서는 __라이브러리 경로(library path)__에 특정 디렉토리를 추가하는 방식을 사용한다.
#
# **주의:** 여기서 라이브러리 경로에 특정 경로를 추가하는 방식은 임시적이다.
# 라이브러리 경로를 영구적으로 변경하려면 다른 방식을 따라야 한다.

# #### 파이썬 라이브러리 경로 확인하기

# 먼저 파이썬이 기본적으로 사용하는 라이브러리들의 경로를 확인해보자.
# `sys.path` 변수에 파이썬이 기본적으로 지원하는 라이브러리들의 경로가 리스트로 저장되어 있다.

# In[ ]:

import sys
sys.path

# #### 파이썬 라이브러리 경로에 임시 경로 추가하기

# `sys.path` 에 저장된 라이브러리들의 경로들의 리스트에 원하는 경로를 추가한다.
#
# 여기서는 현재 작업디렉토리의 하위 폴더인 `codes`를 경로에 추가하며,
# 리스트에 항목을 추가하는 `append` 메소드를 활용한다.

# In[ ]:

sys.path.append(cwd + "/codes")

# 이제 새로운 경로가 추가된 것을 확인할 수 있다.

# In[ ]:

sys.path

# #### 라이브러리 경로에 포함된 디렉토리의 모듈 불러오기

# 라이브러리 경로에 포함된 디렉토리의 모듈은 현재 디렉토리에 포함된 모듈을 불러오는 것처럼 하면 된다.

# In[ ]:

import wc

# 이제 `wc`가 누군지를 물으면 아래와 같이 답한다.

# In[ ]:

wc

# 즉, `wc`는 모듈이라는 정보와 `wc.py` 파일이 저장된 위치 정보를 보여준다.

# ### `__name__` 속성과 `__main__` 함수

# #### `__name__` 속성

# 파이썬에서 함수, 클래스, 모듈 등은 `__name__`이라는 특별한 속성을 가지며,
# 항상 자기 자신을 가리킨다.
#
# 예를 들어, 아래 함수를 살펴보자.

# In[ ]:

def myName():
    pass

# 이제 `myName` 함수의 `__name__` 속성을 확인해보자.
# 속성 확인은 자료형의 메서드를 호출하는 방식과 비슷하다.
# 다만, 속성은 함수가 아니기에 괄호를 사용하지 않는다.

# In[ ]:

myName.__name__

# 모듈도 `__name__` 속성을 갖는다. `wc.py` 모듈의 `__name__` 속성을 확인해보자.

# In[ ]:

wc.__name__

# 앞으로 좀 더 구체적으로 배우게 될 클래스 역시 `__name__` 속성을 갖는다.
# 사실 지금까지 살펴본 모든 자료형 역시 클래스이다.
# 예를 들어, 사전 자료형의 `__name__` 속성은 아래와 같다.

# In[ ]:

dict.__name__

# #### `__main__` 함수

# `wc.py` 모듈을 임포트할 때 앞서 `os`와 `sys` 모듈을 임포트할 때와는 달리 숫자 `7`을 출력한다.
# 이유는 모듈을 임포트할 때 모듈 안에 포함된 코드가 실행되기 때문인데,
# `wc.py` 파일의 경우에는 마지막 줄에 있는 아래 명령어가 실행되기 때문이다.
#
# ```python
# print(linecount('./codes/wc.py'))
# ```
#
# `linecount` 함수는 인자로 지정된 파일에 포함된 내용의 줄 수(line number)를 계산해서 내준다.
# 따라서 위 명령문은 `wc.py` 파일에 포함된 내용이 몇 줄인가를 확인해준다.
#
# 그런데 사실 `print(linecount('./codes/wc.py'))` 명령문은 `linecount` 함수가
# 제대로 작동하는가를 확인하는 용도로 작성된 코드이며,
# 모듈을 불러와서 활용하기 위해서는 굳이 실행할 필요가 없다.
# 이런 경우에 모듈의 `__name__` 속성을 이용하여 아래와 같이 작성하면 모듈을 임포트할 때 굳이 실행할 필요가
# 없는 코드를 모듈에 포함시킬 수 있다.
#
# ```python
# if __name__ == '__main__':
#     print(linecount('./codes/wc.py'))
# ```
#
# 위와 같이 작성하면 `import wc`를 실행해도 `if __name__ == '__main__':`에 포함된 코드는 실행되지 않는다.

#

# 반면에 터미널을 이용해서 현재 작업 디렉토리에서  `python codes/wc.py` 형태로 `wc.py` 코드를 직접 실행하면
# `if __name__ == '__main__'` 조건문의 본체가 실행된다.

#

# `__main__` 함수의 이런 기능은 C, Java 등에서 의무적으로 사용되는 `main` 함수와 유사한 기능을 수행한다.
# `Repl.it` 사이트에서 main 모듈이 기본적으로 실행되는 이유가 이런 전통에서 유래한다.

# ### 모듈 다시 불러오기

# 한 번 불러온 모듈을 다시 불러오면 모듈 내용이 또 실행되지는 않는다.

# In[ ]:

import wc

# ### 불러온 모듈 활용하기

# 어떤 종류의 모듈이든 한 번 불러온 모듈을 활용하는 방법은 동일하다.
# 우리가 작성하고 불러온 `wc` 모듈에 포함된 `linecount` 함수를 활용하는 방법도 동일하다.
# 예를 들어, `wc.py` 파일에 포함된 코드의 줄의 수를 알고자 하면 아래와 같이 실행한다.

# In[ ]:

wc.linecount('./codes/wc.py')

# 반면에 `codes` 디렉토리에 포함된 `__init__.py` 파일은 비어있음을 아래와 같이 확인할 수 있다.

# In[ ]:

wc.linecount('./codes/__init__.py')

# ### 별칭 사용

# 패키지나 모듈 또한 종류에 상관없이 별칭을 사용할 수 있다.
# 예를 들어, `wc` 모듈을 `wordCount` 라고 별칭을 지정하면서 불러올 수 있다.

# In[ ]:

import wc as wordCount

# 이제는 `wc` 대신에 `wordCount`를 사용할 수 있다.

# In[ ]:

wordCount.linecount('./codes/wc.py')

# In[ ]:

wordCount.linecount('./codes/__init__.py')

# ## 연습문제

# 1. ...


# coding: utf-8

import pytest
import numpy as np
import os

from pyccel.epyccel import epyccel
from pyccel.decorators import types

def clean_test():
    cmd = 'rm -rf __pycache__/*'
    os.system(cmd)

#------------------------------------------------------------------------------
def test_decorator_f1():
    @types('int')
    def f1(x):
        y = x - 1
        return y

    f = epyccel(f1)

    # ...
    assert f(3) == f1(3)
    # ...

#------------------------------------------------------------------------------
def test_decorator_f2():
    @types('int [:]')
    def f2(x):
        y = x[0] - 1
        return y

    f = epyccel(f2)

    # ...
    x = np.array([3, 4, 5, 6], dtype=int)
    assert f(x) == f2(x)
    # ...

    # ...
    x = [3, 4, 5, 6]
    assert f(x) == f2(x)
    # ...

#------------------------------------------------------------------------------
def test_decorator_f3():
    @types('int [:]')
    def f3(x):
        y = x - 1
        return y

    from pyccel.ast import AstFunctionResultError
    with pytest.raises(AstFunctionResultError):
        f = epyccel(f3)

#------------------------------------------------------------------------------
def test_decorator_f4():
    @types('real [:,:]')
    def f4(x):
        y = x - 1.0
        return y

    from pyccel.ast import AstFunctionResultError
    with pytest.raises(AstFunctionResultError):
        f = epyccel(f4)

#------------------------------------------------------------------------------
def test_decorator_f5():
    @types('int', 'real [:]')
    def f5(m1, x):
        x[:] = 0.
        for i in range(0, m1):
            x[i] = i * 1.

    f = epyccel(f5)

    # ...
    m1 = 3

    x = np.zeros(m1)
    f(m1, x)

    x_expected = np.zeros(m1)
    f5(m1, x_expected)

    assert np.allclose( x, x_expected, rtol=1e-15, atol=1e-15 )
    # ...

#------------------------------------------------------------------------------
def test_decorator_f6_1():
    @types('int', 'int', 'real [:,:]')
    def f6_1(m1, m2, x):
        x[:,:] = 0.
        for i in range(0, m1):
            for j in range(0, m2):
                x[i,j] = (2*i+j) * 1.

    # default value for assert_contiguous is False
    f = epyccel(f6_1, assert_contiguous=False)

    # ...
    m1 = 2 ; m2 = 3

    x = np.zeros((m1,m2))
    f(m1, m2, x)

    x_expected = np.zeros((m1,m2))
    f6_1(m1, m2, x_expected)

    assert np.allclose( x, x_expected, rtol=1e-15, atol=1e-15 )
    # ...

#------------------------------------------------------------------------------
# in order to call the pyccelized function here, we have either to
#   - give the transpose view of x: x.transpose()
#   - create x with Fortran ordering
def test_decorator_f6_2():
    @types('int', 'int', 'real [:,:]')
    def f6_2(m1, m2, x):
        x[:,:] = 0.
        for i in range(0, m1):
            for j in range(0, m2):
                x[i,j] = (2*i+j) * 1.

    f = epyccel(f6_2, assert_contiguous=True)

    # ...
    m1 = 2 ; m2 = 3

    x_expected = np.zeros((m1,m2))
    f6_2(m1, m2, x_expected)
    # ...

    # ... BAD CALL
    x = np.zeros((m1,m2))
    with pytest.raises(ValueError):
        #  in this case we should get the following error
        #  ValueError: failed to initialize intent(inout) array -- input not fortran contiguous
        f(m1, m2, x)
    # ...

    # ... GOOD CALL
    x = np.zeros((m1,m2))
    f(m1, m2, x.transpose())

    assert np.allclose( x, x_expected, rtol=1e-15, atol=1e-15 )
    # ...

#------------------------------------------------------------------------------
# in order to call the pyccelized function here, we have either to
#   - give the transpose view of x: x.transpose()
#   - create x with Fortran ordering
def test_decorator_f6_3():

    @types('int', 'int', 'real [:,:](order=F)')
    def f6_3(m1, m2, x):
        x[:,:] = 0.
        for i in range(0, m1):
            for j in range(0, m2):
                x[i,j] = (2*i+j) * 1.

    f = epyccel(f6_3, assert_contiguous=True)

    m1 = 2 ; m2 = 3
    x_expected = np.zeros((m1,m2))
    f6_3(m1, m2, x_expected)

    # ... GOOD CALL
    x = np.zeros((m1,m2), order='F')
    f(m1, m2, x)

    x = np.ascontiguousarray(x)
    assert np.allclose( x, x_expected, rtol=1e-15, atol=1e-15 )
    # ...

##==============================================================================
## CLEAN UP GENERATED FILES AFTER RUNNING TESTS
##==============================================================================
#
#def teardown_module():
#    clean_test()
#


#!/usr/bin/env python
# coding: utf-8

# In[1]:

import numpy as np
import panel as pn

pn.extension("katex")

# # Lecture 08 - Wells*
#
# (_The contents are based on the class lecture materials of Prof. R. Liedl. Modifications mostly to fit this specific format were done by Prof. Liedl and Dr. P. K. Yadav._)
#
# ---
#
# ## Motivation
#
# In the last lecture ({doc}`/contents/flow/lecture_07/17_quantify_flow` we derived system equations for different groundwater flow problems. We realized of the difficulties associated with solving flow problems specially at higher dimensions (2D/3D). Numerical methods are mostly used for solving groundwater problems but direct (analytical) solutions are also possible for some problems.
#
# **Wells** are the most common and also most extensively used method of utilizing (or affecting) groundwater. Thus, _wells_ represent a very common groundwater problem. We can now use our understanding of aquifer properties and groundwater system equations to analyze effect of _wells_ on the natural groundwater flow. We will however restrict our extent, in this lecture, to problems that can be directly solved. After learning numerical methods (in last part of this course), we will apply it to evaluate more complex groundwater problems also associated with wells.
#
# We begin this lecture recalling groundwater storage property _transmissivity._ Introducing _wells_ we then derive few relations that can help us understand the effect of wells in groundwater flow. To conclude, we will use the _wells_ to characterize aquifers, i.e., using so called pumping test.
#
# (**_The contents are based on the class lecture materials of Prof. R. Liedl. Modifications mostly to fit this specific format were done by Prof. Liedl and Dr. P. K. Yadav._**)

# ## Transmissivity ##
#
# When discussing storage properties in {doc}`/contents/flow/lecture_03/13_gw_storage`, we saw that aquifers or single layers may frequently be treated as two-dimensional systems. This is justified because the lateral extension of aquifers is usually much larger than the vertical extension. Thus, vertical variations of storage properties can be replaced by some average value without adversely affecting the quantification of groundwater storage.
#
# Similar things can be done with regard to conductivity properties and this brings us to the geohydraulic parameter of transmissivity ($T$,  L$^2$T$^{-1}$). The idea is to neglect vertical variations of hydraulic conductivity and to use vertically averaged values instead. This procedure does not eliminate horizontal variability, so transmissivity may still depend on horizontal coordinates  $(x, y)$.
#
# The vertically averaged $K$ value is then multiplied by the water-saturated thickness to obtain transmissivity. The concept of water-saturated thickness (or water-saturated depth) requires to distinguish whether _confined_ or _unconfined flow_ conditions prevail.
#
# In general, water-saturated thickness is the distance from the aquifer bottom to a level up to which all pores are filled with water. For _confined aquifers,_ this level is equal to aquifer top and water-saturated thickness is tantamount to aquifer thickness. For _unconfined aquifers,_ however, water-saturated thickness corresponds to the distance between aquifer bottom and groundwater level. We will see some illustrations below when we try to quantify transmissivity.
#
# Let us have a closer look at the confined case first. The black cuboid in {numref}`Trans_c_2D`  illustrates that water-saturated thickness extends from aquifer bottom to aquifer top. So, it is equal to aquifer thickness $m$. Transmissivity is calculated by $T_x = K_x \cdot m$ and $T_y = K_y \cdot m$. Here we allow for horizontal aquifer anisotropy with different hydraulic conductivities in $x-$ and $y-$ direction $(K_x\neq K_y)$. For horizontally isotropic aquifers $(K_x = K_y = K)$, transmissivity is given by $T = K \cdot m$.
#
# ```{figure} images/L08_f1.png
# ---
# scale: 40%
# align: center
# name: Trans_c_2D
# ---
# The transmissivity cuboid in confined aquifer.
# ```
# Things are a bit more complicated for unconfined aquifers.  {numref}`Trans_u_2D` illustrates that water-saturated thickness extends from the aquifer bottom to the groundwater table. It is important to note that transmissivity of unconfined aquifers depends on the vertical position of the groundwater table. For instance, if the groundwater table is lowered during to a draught period, transmissivity is decreasing. This is fundamentally different from the confined case where the water-saturated thickness is given by aquifer geometry only and is not affected by hydraulic head changes.
#
# ```{figure} images/L08_f2.png
# ---
# scale: 40%
# align: center
# name: Trans_u_2D
# ---
# The transmissivity cuboid in unconfined aquifer.
# ```
#
# Computing transmissivity of unconfined aquifers requires to determine the difference of hydraulic head h and the elevation of aquifer bottom $z_{bot}$. Based on this, transmissivity is given by $T_x = K_x\cdot(h - z_{bot})$ and $T_y = K_y\cdot(h - z_{bot})$. As above, we are allowing for horizontal aquifer anisotropy. For an isotropic unconfined aquifer we get $T = K\cdot(h - z_{bot})$.

# Two more remarks appear to be appropriate: First, transmissivity may be computed by the given equations even if the aquifer bottom is not horizontal. This case is not covered by the {numref}`Trans_u_2D`. Second, textbooks frequently present the equation **$T = K\cdot h$** for transmissivity of unconfined aquifers. It is to be noted that this equation only holds if two conditions are fulfilled:
#
# ```{admonition} Required conditions when $T = K\cdot h$ is valid
# :class: note
# 1. The aquifer bottom must be horizontal, and
#
# 2. hydraulic head values are expressed with respect to the elevation of aquifer bottom (= reference datum).
# ```
# Finally, we can try to compute transmissivity for isotropic aquifers and check how the result depends on several quantities like aquifer bottom, aquifer top, and hydraulic head.

# ### Example problem  ###
#
# ```{admonition} Transmissivity
# Find if the aquifer is confined or unconfined, and then calculate transmissivity of the aquifer.
# ```

# In[2]:

#print("\n\033[1m Provided are:\033[0m\n") # \033[1m \033[0m bold font, \n - new line

K_a = 8.5e-05 # m/s, Hydraulic conductivity
Z_bot = 120 # m, aquifer bottom
Z_top = 150 # m, aquifer top
h_a   =  139 # m, hydraulic head in aquifer

# interim calculation
A_t = Z_top-Z_bot # m, Aquifer thickness
A_wt = h_a - Z_bot # m, water_table level
S_t = min(A_t, A_wt) # m, saturated thickness

# result
if h_a<Z_top:
    print("It is Unconfined Aquifer \n")
else:
    print("It is Confined Aquifer \n")

T_a = K_a*S_t # m^2/s, transmissivity

print("The required transmissivity is {0:1.2e}".format(T_a), "m\u00b2/s")

# ## Wells - Overview ##
#
# ### What is a Well? ###
#
# A **well** is a shaft or a hole that has been sunk, dug or drilled into the earth to extract water (source: [Glossary of Hydrology](https://hydrologie.org/glu/HINDEN.HTM)).
#
# ```{figure} images/L08_f3.png
# ---
# scale: 50%
# align: center
# name: Well
# ---
# Well and its components.
# ```
#
# ### Using wells ###
#
# Wells are very extensively used around the globe. The contents below only highlights few of the use of wells.
#
# ```{margin}
# <img src=" images/L08_f4.png" alt="gw-sediment" class="bg-primary" width="300px">
# ```
#
# > Water supply: e.g., for households, agriculture, industry
#
# > Lowering the groundwater level: e.g., for excavations, open-pit mining
#
# > Remediation of aquifer contamination: e.g., applying pump and treat method.
#
# > Aquifer characterisation: e.g., using pumping test (this lecture)
#
# Apart from aquifer characterisation, wells are usually operated at steady-state i.e., at constant pumping rate. The figure below presents different uses of wells. {numref}`Well-ex` shows the case of lowering the groundwater level at the excavation site. The lowering is very often observed at the works that requires sub-surface construction works, e.g., high-rise building, tunnels. For this wells are placed close to excavation works and water is pumped out at higher discharge rates compare to the groundwater replenishing rate. This leads to decline of water level at the excavation site.
#
# ```{figure} images/L08_f5.png
# ---
# scale: 40%
# align: center
# name: Well-ex
# ---
# Well at the excavation site
# ```
#
# {numref}`Well-refuse` presents the case of using wells to delineate contamination site from the groundwater. These are part of the development of sanitary landfills sites or industries that pose threat to groundwater quality. Wells are used to lower groundwater table such that seepage of refuse is contained in the limited region.
#
# ```{figure} images/L08_f6z.png
# ---
# width: 30 cm
# height: 8 cm
# align: center
# name: Well-refuse
# ---
# Well at the refuse site
# ```

# ### Fully versus Partially Penetrating Wells ###
#
# Fully penetrating wells:
# : The fully penetrating wells are those which extends through the whole saturated depth of an aquifer and are constructed in such a manner that water is permitted to the well screen over its length [Glossary of Hydrology](https://hydrologie.org/glu/HINDEN.HTM)).
#
# {numref}`full-pen` shows a schematic of water flow in a fully penetrating wells.
#
# ```{figure} images/L08_f7x.png
# ---
# scale: 40%
# align: center
# name: full-pen
# ---
# Flownet in a fully penetrating wells
# ```
#
# Partially penetrating Wells:
# : The partially penetrating Wells are those in which the length of water entry is less than the thickness of the saturated aquifer which it penetrates ([Glossary of Hydrology](https://hydrologie.org/glu/HINDEN.HTM)).
#
# Partially penetrating wells are constructed when aquifer depths are very high; in which case a fully penetrating well may not also be economical. {numref}`par-pen` presents a schematic of a flownet that is likely to be observed in the partially penetrating well.
#
# ```{figure} images/L08_f7y.png
# ---
# scale: 40%
# align: center
# name: par-pen
# ---
# Flownet in a partially penetrating wells
# ```
#
# Comparing the Flownets in {numref}`full-pen` and {numref}`par-pen`, it can be observed that the vertical flow components can be significant in the partially penetrating case. This then lead to a 3D groundwater problem compared to the fully penetrating case, which can be treated as 2D groundwater problem as vertical flow component has limited effect.
#
# This course we will deal only with the fully penetrating steady-state cases.
#

# ## Groundwater Flow Near Wells Operated at Steady State ##
#
# As stated earlier wells are mostly operated under steady-state condition. Here will attempt to quantify the case. We will first attempt to identify the most relevant problems associated with the steadily pumping wells and then define our approach to solve them. The **two** most important points that need to be addressed here  are:
#
# > Which relevant quantities are needed to describe steady-state flow
# towards a well?
#
# > What is the quantitative relationship between the hydraulic
# parameters under steady-state conditions?
#
# To answer the above questions, we follow the following approach:
#
# > Find an appropriate way to apply the _law of continuity_
# (conservation of volume) and _Darcy‘s law._
#
# > We delineate the problem, e.g., study the _confined_ and the _unconfined_ cases separately.
#

# ### Cone of Depression in a Confined Aquifer ###
#
# A **cone of depression** (or drawdown cone) will result when the well pumps groundwater groundwater is pumped from the well. {numref}`cone-con-un` presents the schematic with relevant quantities of a well pumping case in both confined and unconfined aquifers. In both cases the well is fully penetrating. As can be understood from the figure that in the unconfined case the drawdown ($s$) magnitude depends on the water table level. This in the case of confined aquifer is dependent on the hydraulic head.
#
#
# ```{figure} images/L08_f8-9.png
# ---
# scale: 25%
# align: center
# name: cone-con-un
# ---
# Cone of depression in a (a) confined Aquifer and (b) unconfined aquifer (right)
# ```
#
# ```{margin} Relevant quantities in the figure:
# + pumping rate $Q$ [L$^3$T$^{-1}$]
# % free space
# + aquifer thickness $m$ [L]
# % free space
# + hydraulic conductivity $K$ [LT$^{-1}$]
# % free space
# + water level at rest $H$ [L]
# % free space
# + water-level in the well $h$ [L]
# % free space
# + Radius of influence $R$ [L]
# % free space
# + well radius $r_w$ (incl. gravel pack!)[L]
# % free space
# + drawdown $s = H - h$ [L]
# ```
#
# The Radius of influence ($R$) in the figure is the distance from the well at which the drawdown becomes negligible or unobservable. Thus $R$ delineates the influence of the well on the normal groundwater flow.
#
# #### Evolution of Cone of Depression with Time ####
#
# Transient models have to be used to observe the evolution of cone of depression. These are mostly only possible through use of numerical models (to be discussed in the later end of this course). The animation provides the computer simulation of evolving cone of depression as a function of time. From the figure it is easier to obtain the radius of influence ($R$). It is to be noted that $R$ will be maximum at the steady-state condition.

# In[3]:

video1 = pn.pane.Video("images/L08_f9X.mp4", width=400, height=150, loop=False)
spacer = pn.Spacer(width=50)
image = pn.pane.PNG("images/L08_f9Y.png", width=200)
pn.Row(video1, spacer, image)

# ### Law of Continuity ###
#
# Next we attempt to quantify the cone of depression. For this, and it is common in any hydraulics study, we begin with the _Law of Continuity._ Overall law of continuity implies:
#
# ```{admonition} Law of Continuity
# Discharge $Q_w = $ constant
# ```
#
# ```{figure} images/L08_f10X.png
# ---
# scale: 40%
# align: center
# name: law-cont
# ---
# Graphic visualization of Law of quantity.
# ```
#
# This, also seen in {numref}`law-cont`, implies that the pumping rate $Q$ corresponds to the discharge $Q_w$ near the well. Further, under steady-state conditions the law of continuity
# implies that there is the same discharge at all cross sections which
# completely surround the well ("mantle of a cylinder").
#
# ### Darcy's Law for Flow Towards a Well in a Confined Aquifer ###
#
# The Darcy's law, as we have known so far,  relates the Darcy's velocity $v_f$ and the hydraulic gradient $i$. If the hydraulic gradient was constant in space, the hydraulic gradient would be (also see {numref}`law-cont` )
#
# $$
# i = \frac{\Delta h}{\Delta r}
# $$
#
# where, $r$ represents the radial distance from the well axis. In this case the hydraulic gradient $i$ depends on the distance $r$ within a cone of depression {numref}`law-cont`). Qualitatively, it can be understood that $i$ is decreasing with increasing distance from the well. Since the hydraulic gradient is not constant in space, the ratio $\frac{\Delta h}{\Delta r}$ has to be replaced by
#
# $$
# i(r) = \frac{\textrm{d} h (r)}{\textrm{d} r} \tag{C1}
# $$
#
# With this defined, the Darcy's law should be expressed as function of $r$ as
#
# $$
# v_f (r) = - K\cdot i(r) = - K \cdot \frac{\textrm{d} h (r)}{\textrm{d} r} \tag{C2}
# $$
#
# And, now introducing the continuity equation we obtain:
#
# $$
# Q_w = A\cdot v_f (r) = - K\cdot i(r) = - K \cdot A\cdot\frac{\textrm{d} h (r)}{\textrm{d} r} \tag{C3}
# $$
#
# with $A$ is the area of a cross section at distance $r$ from the well axis. Therefore $A$ is
#
# $$
# A = 2\cdot \pi \cdot r \cdot m \tag{C4}
# $$
#
# $A$ is now inserted to eq. (C3), This results to
#
# $$
# Q_w =  - 2\cdot \pi \cdot r \cdot m \cdot K \cdot \frac{\textrm{d} h (r)}{\textrm{d} r} \tag{C5}
# $$
#
# ```{margin}
# <img src="images/L08_f8.png" alt="Confined Aquifer" class="bg-primary" width="400px">
# ```
#
# Eq (C5) is a first-order differential equation for hydraulic head $h(r)$. It can be solved by separation of variable. Doing that eq. (C5) becomes
# $$
# \textrm{d}h(r) = \frac{Q_w}{2\cdot \pi \cdot m \cdot K }\cdot \frac{\textrm{d}r}{r} \tag{C6}
# $$
#
# We now integrate eq. (C6). The limit of integration (see figure) along the vertical direction ($h$) will be from $h$ to $H$, and that along the radial axis will be from $r_w$ and $R$, i.e., we get
#
# $$
# \int\limits_h^H\textrm{d}h(r) = \frac{Q_w}{2\cdot \pi \cdot m \cdot K }\cdot \int\limits_{r_w}^R\frac{\textrm{d}r}{r} \tag{C7}
# $$
#
# The integrals in eq. (C7) are direct integrals and can be obtained from the table of integrals. The eq. (C7) with the indefinite integrals is thus
#
# $$
# \big[h(r)\big]_h^H = - \frac{Q_w}{2\cdot\pi\cdot m \cdot K}\cdot \big[\ln r\big]_{r_w}^R
# $$
#
# after inserting the limits of integration, we get
#
# $$
# H - h = - \frac{Q_w}{2\cdot\pi\cdot m \cdot K}\cdot (\ln R - \ln r_w) \tag{C8}
# $$
#
# Finally, we can solve for the discharge $Q_w$ and obtain
#
# $$
# Q_w = - \frac{2\cdot\pi\cdot m \cdot K \cdot (H-h)}{(\ln R - \ln r_w)}\tag{C9}
# $$
#
# The **negative** sign on the right-hand side of eq. (C9) indicates that flow is anti-parallel to the direction of the coordinate axis. Frequently, the pumping rate $Q$ is used instead of $Q_w$ and the negative sign is omitted. Thus we get a well known solution called Theim equation after Thiem (1906)<sup>[^Theim]</sup>
#
# ````{panels}
# :container: container pb-4
# :column: col-lg-8 p-2
#
# **The discharge from the well in confined aquifer after Thiem (1906)**
# ^^^
# $$
# Q = \frac{2\cdot\pi\cdot m \cdot K \cdot (H-h)}{(\ln R - \ln r_w)}  =  \frac{2\cdot\pi\cdot m \cdot K \cdot (H-h)}{\ln (R/r_w)}
# $$
# ````
#
# with decadic logarithm, we get,
#
# $$
# Q_w = \frac{2\cdot\pi\cdot m \cdot K \cdot (H-h)}{2.3\cdot(\log R - \log r_w)}  =  \frac{2\cdot\pi\cdot m \cdot K \cdot (H-h)}{2.3 \cdot\log (R/r_w)}
# $$
#
# [^Theim]: Thiem, G. (1906), _Hydrologische Methoden_, 56 pp., Gephardt, Leipzig, Germany.

# ### Example problem  ###
#
# ```{admonition} Well discharge from confined aquifer
# From the provided data, calculate the transmissivity of the aquifer.
# ```

# #### Solution ####
#
# For confined aquifer $T = K\cdot m$, the Thiem equation can be modified as:
#
# $$
# Q = \frac{2\cdot\pi\cdot T \cdot (H-h)}{\ln (R/r_w)}
# $$

# In[4]:

print("\n\033[1m Provided are:\033[0m\n")

Q = 9 # m^3/min, Given discharge
r1 = 8 # m, distance from well to point 1
h1 = 9 # m, head at well 1
R2 = 22 # m, distance from well to point 2
H2 = 10 # m, head at well 2

print(" The given dscharge is: {}".format(Q), "m\u00b3/min \n")
print(" The distance to Well 1 and well 2 are: {}m and {}m  \n".format(r1, R2))
print(" The head at Well 1 and well 2 are: {}m and {}m".format(h1, H2))

#interim calculation
Q_min = Q * 1440 # m^3/d

#Calculation

T = Q/(2*np.pi*(H2-h1))*np.log(R2/r1) # m^2/d, Transmissivity - inverting Theim equation

print("\n\033[1m Result:\033[0m\n")
print("The transmissivity in the aquifer is {0:0.2f} m\u00b2/d".format(T))

# ## Flow Towards a Well in an Unconfined Aquifer ##
#
# In the unconfined aquifer, the hydraulic head is function of $r$. Thus the discharge in the aquifer:
#
# $$
# Q_w = A \cdot v_f = - A \cdot K \frac{\textrm{d}h}{\textrm{d}r}(r) \tag{U1}
# $$
#
# ```{margin}
# <img src="images/L08_f9.png" alt="Unconfined Aquifer" class="bg-primary" width="400px">
# ```
# Also in this case, the cross section area $A$ also is dependent on $r$, i.e.,
#
# $$
# A = 2 \cdot \pi \cdot r \cdot h(r) \tag{U2}
# $$
#
# Next, we insert eq (U2) in eq. (U1) and get
#
# $$
# Q_w = A \cdot v_f = - 2 \cdot \pi \cdot r \cdot h(r)  \cdot K \frac{\textrm{d}h}{\textrm{d}r}(r) \tag{U3}
# $$
#
# Eq. (U3) is a first-order differential equation for hydraulic head $h(r)$, and it can be solved by separation of variables. Separating the variables of eq. (U3) leads to
#
# $$
# h(r) \cdot \textrm{d}h(r) = - \frac{Q_w}{2 \cdot \pi \cdot K }\cdot \frac{\textrm{d}r}{r} \tag{U4}
# $$
#
# Next we integrate eq. (U4) from limits $r_w$ to $R$ in the right-hand side, and from $h$ to $H$ (left-hand side), i.e.,
#
# $$
# \int\limits_h^H h(r) \cdot \textrm{d}h(r) = - \frac{Q_w}{2 \cdot \pi \cdot K }\cdot \int\limits_{r_w}^{R}\frac{\textrm{d}r}{r} \tag{U5}
# $$
#
# The integral in eq. (U5) can be obtained from the standard table of integrals. With that we get
#
# $$
# \frac{1}{2}(H^2 - h^2) = - \frac{Q_w}{2 \cdot \pi \cdot K }\cdot (\ln R - \ln r_w) \tag{U6}
# $$
#
# The discharge in aquifer $Q_w$ can be obtained from eq. (U6) and the expression is
#
# $$
# Q_w = -\frac{\pi\cdot K \cdot (H^2-h^2)}{\ln R - \ln r_w}\tag{U6}
# $$
#
# The negative sign on the right-hand side of the eq. (U6) indicates that flow is anti-parallel to the orientation of the coordinate axis. Frequently, the pumping rate $Q$ is used instead of $Q_w$ and the negative sign can thus be
# omitted, and we get
#
# ````{panels}
# :container: container pb-4
# :column: col-lg-10 p-2
#
# **Discharge $Q$ from well in unconfined aquifer modified from Thiem (1906)**
# ^^^
# $$
# Q_= \frac{\pi\cdot K \cdot (H^2-h^2)}{\ln R - \ln r_w} = \frac{\pi\cdot K \cdot (H^2-h^2)}{\ln (R/r_w)}
# $$
# ````
#
# In the decadic logarithm, the discharge from the well is:
#
# $$
# Q_= \frac{\pi\cdot K \cdot (H^2-h^2)}{2.3(\log R - \log r_w)} = \frac{\pi\cdot K \cdot (H^2-h^2)}{2.3\log (R/r_w)}
# $$

# ### Example problem  ###
#
# ```{admonition} Well discharge from unconfined aquifer
# From the provided data, calculate discharge of the aquifer.
# ```

# In[5]:

print("\n\033[1m Provided are:\033[0m\n")

K = 24.50 # m/d, conductivity
r_1 = 0.23 # m, distance from well to point 1
h_1 = 12 # m, head at well 1
R_2 = 275 # m, distance from well to point 2
H_2 = 18 # m, head at well 2

print(" The given conductivity is: {}".format(K), "m/d \n")
print(" The distance to Well 1 and well 2 are: {} m and {} m are \n".format(r_1, R_2))
print(" The head at Well 1 and well 2 are: {} m and {} m".format(h_1, H_2))

#Calculation

Q_1 = (np.pi*K*(H_2**2-h_1**2))/(np.log(R_2/r_1)) # m^2/d, Transmissivity - inverting Theim equation

print("\n\033[1m Result:\033[0m\n")
print("Discharge from the well is {0:0.2f} m\u00b3/d".format(Q_1))

# ### Radius of Influence ###
#
# The radius of influence ($R$) can also (instead of using numerical simulations) be obtained from empirical equations. The table below provides a list of few equations.
#
# ```{margin} The symbols in equations are:
# + $s$ = drawdown in pumping well,
# + $t$ = pumping time,
# + $N$ = groundwater recharge,
# + $S$ = storage coefficient,
# + $K$ = hydraulic conductivity,
# + $H$ = water level at rest (unconfined aquifer).
#
# In confined aquifers, $H$ has to be replaced by the aquifer thickness $m$.
# ```
#
# | Source                             |   | Equation                                  |
# |------------------------------------|---|-------------------------------------------|
# | Lembke (1886, 1887)                |   | $R = H (K/2\cdot N)^{1/2}$                |
# | Weber (Schultze, 1924)             |   | $R = 2.45 (H\cdot K\cdot t/S)^{1/2}$      |
# | Kusakin (Aravin and Numerov, 1953) |   | $R = 1.9 (H \cdot K \cdot t/s)^{1/2}$     |
# | Siechardt (Certousov, 1962)        |   | $R = 3000\cdot s \cdot K^{1/2}$           |
# | Kusakin (Certousov, 1949)          |   | $R = 575\cdot s \cdot (H \cdot K)^{1/2} $ |
#
#
# **Siechardt** and **Kusakin** equation are among the preferred equations by practitioners. In both equations, $K$ has to be expressed in _m/s_ and all other quantities must be expressed in _m._ _R_ depends on drawdown $s= H-h$ in both equations. Trial and error or iterative strategies have to be used to determine $R$ and $h$.
#
#

# ## Aquifer Characterisation by Pumping Tests ##
#
# Pumping tests are used to estimate aquifer properties such as hydraulic conductivity $(K)$, transmissivity $(T)$ or storativity $(S)$. Pumping results in an evolving cone of depression as was discussed earlier (see {numref}`cone-con-un`). The decrease in hydraulic head (or increase in drawdown) with time is recorded in one or more observation wells (and sometimes also in the pumping well itself).
#
# ```{margin}
# <img src="images/L08_f13.png" alt="Theis" class="bg-primary" width="200px">
# ```
#
# A variety of different schemes exist to evaluate pumping test data. The appropriate method has to be selected according to the specific setting (confined or unconfined, layered system, horizontal or inclined aquifer bottom etc.). A well known approach to derive $T$ and $S$ from pumping test data was developed by Theis (1935)<sup>[^Theis]</sup>
#
# ### Applicability of the Theis Method ###
#
# Pumping test data can be evaluated according to Theis (1935) if the following assumptions are (approximately) justified:
#
# + The aquifer is confined, homogeneous and isotropic.
# %
# + The aquifer thickness is uniform.
# %
# + The aquifer bottom is horizontal.
# %
# + The well is fully penetrating.
# %
# + The well radius is very small as compared to the radius of influence.
# %
# + The pumping rate is constant within the measurement period.
# %
# + There is no vertical flow component.
# %
# + The evolution of the cone of depression is not influenced by other hydraulic factors (surface water, impermeable boundaries etc.).
#
# [^Theis]: Theis, C.V., 1935. The relation between the lowering of the piezometric surface and the rate and duration of discharge of a well using groundwater storage, Am. Geophys. Union Trans., vol. 16, pp. 519-524.

# ### Drawdown According to Theis (1935) ###
#
# Theis (1935) deals with the transient flow (as opposed to steady-state methods discussed above) of water to a pumping well. The time-dependent drawdown $s$ in an observation well, which is a distance $r$ apart from the pumping well, is given by
#
# ```{margin}
# + $s$ is drawdown [L]
# %
# + $Q$ is pumping rate [L$^3$T$^{-1}$]
# %
# $T$ is transmissivity [L$^2$T$^{-1}$]
# %
# $W(u)$ is the well function
# ```
#
# $$
# s(r,t) = \frac{Q}{4\pi T}\cdot W(u) \tag{T1}
# $$
#
# where, $W(u)$ is the well function, which is given as
#
# $$
# W(u) = \int\limits_u^\infty \frac{\textrm{e}^{-\widetilde{u}}}{\widetilde{u}}\textrm{d}\widetilde{u}
# $$
#
# in which $u$ is defined by
#
# $$
# u = \frac{Sr^2}{4Tt} \tag{T2}
# $$
#
# where $S$ [-] is storage coefficient. For application Eq (T1) and eq. (T2), these equation are log-transformed. Eq (T1) then becomes
#
# $$
# \log s(r,t) = \log \frac{Q}{4\pi T}\cdot \log W(u) \tag{T3}
# $$
#
# and upon log-transformation and rearrangement of eq. (T2) results to
#
# $$
# \log \frac{t}{r^2} = \log\frac{S}{4T} + \log\frac{1}{u} \tag{T4}
# $$
#
# These two equations are used to derive $T$ and $S$ from drawdown data. This can either be done by applying special computer software or manually by a graphical method, which is discussed next.
#

# ### Manual Comparison of Data and Type Curve ###
#
# Theis (1935) provides a graphical approach to use the  Theis equation. The following steps are to be followed for using the graphical approach:
#
# + The logarithm of drawdown ($\log s$) is plotted against the $\log(t/r^2)$ in the data sheet
#
# + The logarithm of the well function $(\log W(u))$ is plotted against $\log (1/u)$ in a type curve sheet.
#
# ```{sidebar} Type Curve and Data sheet
# <img src="images/L08_f11.png" alt="Type-Curve" class="bg-primary" width="1000px">
# ```
#
# + Both sheets are put on top of each other such that the data coincide with some part of the type curve.
#
# + The shifts along the vertical and the horizontal axes correspond to the constant terms in the equations
#
# $$
# \log s = \log\frac{Q}{4\cdot \pi \cdot T} + \log W(u)
# $$
#
# $$
# \log \frac{t}{r^2} = \log\frac{S}{4 T} + \log \frac{1}{u}
# $$
#
# + The constant term in the upper equation ($\log\frac{Q}{4\cdot \pi \cdot T}$) can then be solved for $T$.
#
# + Finally, the constant term $(\log\frac{S}{4 T})$ in the lower equation can be used to solve for $S$.
#
# ```{important}
# The Type curve is independent from aquifer properties.
# ```

# ### Example  ###
#
# ```{admonition} Using Type curve
# For the provided pumping data, find the Transmissivity and Storage coefficient when the steady-discharge was 26.7 L/s.
# ```

# #### Solution ####
#
# The practical application of the Theis method is facilitated by selecting a match point in the range of the data such that corresponding values $W_A$
# and $1/u_A$ are "simple".
#
# <img src="images/L08_f14.png" alt="Type-Curve-Example" class="bg-primary" width="600px">
#
# In the example:
#
# $$
# W_A = 10^0 = 1
# $$
#
# $$
# 1/u_A = 10^2 = 100
# $$
#
# Next, values for $s$ and $t/r^2$ at the match points are determined. In this example:
#
# $$
# S = 0.2
# $$
#
# $$
# t/r^2 = 0.57
# $$
#
# Next, we obtain $T$ from $T = \frac{Q}{4\cdot\pi\cdot s }\cdot W_A$. For $Q = 26.7$ L/s, we obtain $T = 1.06 \cdot 10^{-2} $ m<sup>2</sup>/s. Finally, $S$ is obtained from $S = \frac{4 T\cdot t/r^2}{1/u_A}$. In this example:
# $S = 2.42 \cdot 10^{-4}$

# ### Computer-Based Comparison of Data and Type Curve ###
#
# The data in the type-curve can now easily be fitted using computer simulations. The {numref}`Type-Cur-com` is from the {doc}`/contents/tutorials/tutorial_07/tutorial_07`. The simulation tool {doc}`/contents/tools/type_curve_fit` provided can be used to fit user data to the type curve.
#
# ```{figure} images/L08_f15.png
# ---
# scale: 20%
# align: center
# name: Type-Cur-com
# ---
# Computationally fitted data to the Type curve
# ```

# In[ ]: