{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Projeto Capstone: Caracterização de Pêndulo Físico\n",
    "\n",
    "**Disciplina:** FSC1189 - Algoritmo e Programação\n",
    "\n",
    "**Objetivo:** Integrar todos os conceitos do curso em um projeto real de análise de dados experimentais.\n",
    "\n",
    "**Tema:** Caracterização de um pêndulo físico: da medição à publicação.\n",
    "\n",
    "Este projeto demonstra:\n",
    "- Coleta e tratamento de dados\n",
    "- Análise estatística com incertezas\n",
    "- Modelagem numérica (Euler vs Runge-Kutta)\n",
    "- Visualização científica\n",
    "- Comunicação de resultados"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy.integrate import odeint\n",
    "from scipy import stats\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "np.random.seed(42)\n",
    "\n",
    "# Configurar matplotlib para qualidade de publicação\nplt.rcParams['figure.dpi'] = 100\nplt.rcParams['savefig.dpi'] = 300\nplt.rcParams['font.size'] = 10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bloco 1: Campanha de Medição\n",
    "\n",
    "Simular uma campanha de medições do período de um pêndulo para 9 comprimentos diferentes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CampanhaPendulo:\n",
    "    \"\"\"Simula uma campanha de medições de período de pêndulo.\"\"\"\n",
    "    \n",
    "    def __init__(self, g_real=9.81, seed=42):\n",
    "        np.random.seed(seed)\n",
    "        self.g_real = g_real\n",
    "    \n",
    "    def periodo_teorico(self, L):\n",
    "        \"\"\"T = 2π√(L/g) para pequenos ângulos.\"\"\"\n",
    "        return 2 * np.pi * np.sqrt(L / self.g_real)\n",
    "    \n",
    "    def gerar_campanha(self, comprimentos, num_medidas=15):\n",
    "        \"\"\"Gera dados de uma campanha de medições.\"\"\"\n",
    "        dados = []\n",
    "        sigma_percent = 0.015  # 1.5% de incerteza\n",
    "        \n",
    "        for L in comprimentos:\n",
    "            T_teorico = self.periodo_teorico(L)\n",
    "            sigma = sigma_percent * T_teorico\n",
    "            \n",
    "            # Gerar medidas\n",
    "            for i in range(num_medidas):\n",
    "                T_med = np.random.normal(T_teorico, sigma)\n",
    "                # Adicionar 1-2 outliers por comprimento\n",
    "                if np.random.rand() < 0.07:\n",
    "                    T_med *= (1 + np.random.uniform(0.08, 0.12) * np.sign(np.random.randn()))\n",
    "                \n",
    "                dados.append({\n",
    "                    'L (m)': L,\n",
    "                    'T (s)': T_med,\n",
    "                    'T_teor (s)': T_teorico,\n",
    "                    'Medida': i + 1\n",
    "                })\n",
    "        \n",
    "        return pd.DataFrame(dados)\n\n# Executar campanha\ncampanha = CampanhaPendulo(g_real=9.81)\ncomprimentos = np.linspace(0.25, 2.0, 9)  # 0.25m a 2.0m\ndf = campanha.gerar_campanha(comprimentos, num_medidas=15)\n\nprint(\"=== CAMPANHA DE MEDIÇÃO ===\")\nprint(f\"Comprimentos: {len(comprimentos)}\")\nprint(f\"Medidas por comprimento: 15\")\nprint(f\"Total de pontos: {len(df)}\")\nprint()\nprint(\"Intervalo de comprimentos:\", f\"{comprimentos.min():.2f}m a {comprimentos.max():.2f}m\")\nprint(\"Intervalo de períodos:\", f\"{df['T (s)'].min():.3f}s a {df['T (s)'].max():.3f}s\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bloco 2: Análise Estatística com Propagação de Incerteza"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Remover outliers\ndef remover_outliers(dados, col, threshold=2.5):\n",
    "    z_scores = np.abs(stats.zscore(dados[col]))\n",
    "    return dados[z_scores < threshold].copy()\n\ndf_clean = remover_outliers(df, 'T (s)', threshold=2.5)\n\nprint(f\"Outliers removidos: {len(df) - len(df_clean)}\")\n\n# Calcular estatísticas por comprimento\nstats_por_L = []\n\nfor L in comprimentos:\n    mask = df_clean['L (m)'] == L\n    T_medidas = df_clean[mask]['T (s)'].values\n    \n    T_meio = T_medidas.mean()\n    sigma_T = T_medidas.std() / np.sqrt(len(T_medidas))  # erro da média\n    \n    # Estimar g a partir de T = 2π√(L/g) => g = 4π²L/T²\n    g_estimado = 4 * np.pi**2 * L / T_meio**2\n    \n    # Propagação de incerteza: σ_g = |dg/dT| * σ_T = -2g/T * σ_T\n    sigma_g = 2 * g_estimado / T_meio * sigma_T\n    \n    stats_por_L.append({\n    'L (m)': L,\n        'T_meio (s)': T_meio,\n        'sigma_T (s)': sigma_T,\n        'g (m/s²)': g_estimado,\n        'sigma_g (m/s²)': sigma_g,\n        'N': len(T_medidas)\n    })\n\ndf_stats = pd.DataFrame(stats_por_L)\n\nprint(\"\\n=== ESTIMATIVAS DE g POR COMPRIMENTO ===\")\nprint(df_stats[['L (m)', 'T_meio (s)', 'g (m/s²)', 'sigma_g (m/s²)']].to_string(index=False))\n\n# g global com erro\ng_global = df_stats['g (m/s²)'].mean()\nsigma_g_global = np.sqrt(np.sum(df_stats['sigma_g (m/s²)']**2)) / len(df_stats)\n\nprint(f\"\\nValor global de g: {g_global:.4f} ± {sigma_g_global:.4f} m/s²\")\nprint(f\"Erro percentual: {abs(g_global - 9.81) / 9.81 * 100:.2f}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bloco 3: Comparação Euler vs Runge-Kutta para Pêndulo Não-Linear\n",
    "\n",
    "Demonstrar quando a aproximação linear quebra."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pendulo_nao_linear(theta, t, L, g):\n",
    "    \"\"\"Pêndulo não-linear: d²θ/dt² = -(g/L)sin(θ)\"\"\"\n",
    "    dtheta_dt = theta[1]\n",
    "    d2theta_dt2 = -(g / L) * np.sin(theta[0])\n",
    "    return [dtheta_dt, d2theta_dt2]\n",
    "\ndef pendulo_linear(theta, t, L, g):\n",
    "    \"\"\"Pêndulo linear: d²θ/dt² = -(g/L)θ (aproximação para pequenos ângulos)\"\"\"\n",
    "    dtheta_dt = theta[1]\n",
    "    d2theta_dt2 = -(g / L) * theta[0]\n",
    "    return [dtheta_dt, d2theta_dt2]\n\n# Parâmetros\nL = 1.0  # 1 metro\ng = 9.81\nangulos = [5, 30, 60, 90]  # graus\nt = np.linspace(0, 3, 300)  # 3 segundos\n\nresultados = {}\n\nfor theta0_graus in angulos:\n    theta0_rad = np.radians(theta0_graus)\n    cond_ini = [theta0_rad, 0]\n    \n    # Resolver\n    sol_nao_linear = odeint(pendulo_nao_linear, cond_ini, t, args=(L, g))\n    sol_linear = odeint(pendulo_linear, cond_ini, t, args=(L, g))\n    \n    resultados[theta0_graus] = {\n        'nao_linear': sol_nao_linear[:, 0] * 180/np.pi,  # converter para graus\n        'linear': sol_linear[:, 0] * 180/np.pi,\n        'periodo_real': sol_nao_linear[1:, 0] * 180/np.pi,\n        'periodo_aprox': sol_linear[1:, 0] * 180/np.pi\n    }\n\n# Visualização\nfig, axes = plt.subplots(2, 2, figsize=(13, 9))\n\nfor idx, theta0 in enumerate(angulos):\n    ax = axes[idx // 2, idx % 2]\n    \n    ax.plot(t, resultados[theta0]['nao_linear'], 'b-', linewidth=2.5, label='Não-linear')\n    ax.plot(t, resultados[theta0]['linear'], 'r--', linewidth=2, alpha=0.7, label='Linear (aprox.)')\n    ax.fill_between(t, resultados[theta0]['nao_linear'], resultados[theta0]['linear'], \n                    alpha=0.2, color='yellow')\n    \n    ax.set_xlabel('Tempo (s)', fontsize=10)\n    ax.set_ylabel('Ângulo (graus)', fontsize=10)\n    ax.set_title(f'Pêndulo: θ₀ = {theta0}°', fontsize=11, fontweight='bold')\n    ax.legend(fontsize=10)\n    ax.grid(True, alpha=0.3)\n    ax.set_ylim(-theta0 - 5, theta0 + 5)\n\nplt.suptitle('Validação da Aproximação Linear: Pêndulo para Diferentes Amplitudes', \n             fontsize=12, fontweight='bold')\nplt.tight_layout()\nplt.show()\n\nprint(\"\\n=== VALIDAÇÃO DA APROXIMAÇÃO LINEAR ===\")\nfor theta0 in angulos:\n    erro_max = np.max(np.abs(resultados[theta0]['nao_linear'] - resultados[theta0]['linear']))\n    print(f\"θ₀ = {theta0:2}°: erro máximo = {erro_max:.3f}°\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bloco 4: Figura de Publicação (Grid 2×3)\n",
    "\n",
    "Criar uma figura profissional pronta para publicação ou apresentação."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(16, 10))\ngs = fig.add_gridspec(2, 3, hspace=0.35, wspace=0.35)\n\n# Painel 1: Regressão Linear T² vs L\nax1 = fig.add_subplot(gs[0, 0])\nL_stats = df_stats['L (m)'].values\nT2_stats = df_stats['T_meio (s)'].values ** 2\nsigma_T2 = 2 * df_stats['T_meio (s)'].values * df_stats['sigma_T (s)'].values\n\n# Regressão linear ponderada\nweights = 1 / sigma_T2**2\ncoeff = np.polyfit(L_stats, T2_stats, 1, w=weights)\np_fit = np.poly1d(coeff)\nL_fit = np.linspace(L_stats.min(), L_stats.max(), 100)\nT2_fit = p_fit(L_fit)\n\nax1.errorbar(L_stats, T2_stats, yerr=sigma_T2, fmt='bo', markersize=8, capsize=5, capthick=2)\nax1.plot(L_fit, T2_fit, 'r-', linewidth=2.5, label=f'Ajuste: T² = {coeff[0]:.4f}L + {coeff[1]:.4f}')\nax1.set_xlabel('Comprimento L (m)', fontsize=11, fontweight='bold')\nax1.set_ylabel('Período² T² (s²)', fontsize=11, fontweight='bold')\nax1.set_title('(a) Regressão Linear', fontsize=11, fontweight='bold')\nax1.legend(fontsize=10)\nax1.grid(True, alpha=0.3)\n\n# Painel 2: g por ponto com barras de erro\nax2 = fig.add_subplot(gs[0, 1])\nax2.errorbar(df_stats['L (m)'], df_stats['g (m/s²)'], yerr=df_stats['sigma_g (m/s²)'],\n            fmt='gs', markersize=8, capsize=5, capthick=2, alpha=0.7, elinewidth=2)\nax2.axhline(9.81, color='k', linestyle='--', linewidth=2, label='g teórico = 9.81 m/s²')\nax2.axhline(g_global, color='r', linestyle='-', linewidth=2, alpha=0.7, label=f'g obtido = {g_global:.3f} m/s²')\nax2.fill_between(df_stats['L (m)'], g_global - sigma_g_global, g_global + sigma_g_global,\n                 alpha=0.2, color='red')\nax2.set_xlabel('Comprimento L (m)', fontsize=11, fontweight='bold')\nax2.set_ylabel('Aceleração g (m/s²)', fontsize=11, fontweight='bold')\nax2.set_title('(b) Estimativa de g', fontsize=11, fontweight='bold')\nax2.legend(fontsize=9)\nax2.grid(True, alpha=0.3)\nax2.set_ylim(9.6, 10.1)\n\n# Painel 3: Resíduos\nax3 = fig.add_subplot(gs[0, 2])\nresiduos = T2_stats - p_fit(L_stats)\nax3.bar(L_stats, residuos, width=0.08, color='purple', alpha=0.7, edgecolor='black')\nax3.axhline(0, color='k', linestyle='-', linewidth=1)\nax3.set_xlabel('Comprimento L (m)', fontsize=11, fontweight='bold')\nax3.set_ylabel('Resíduos T²', fontsize=11, fontweight='bold')\nax3.set_title('(c) Resíduos do Ajuste', fontsize=11, fontweight='bold')\nax3.grid(True, alpha=0.3, axis='y')\n\n# Painel 4: Histograma dos valores de g\nax4 = fig.add_subplot(gs[1, 0])\nax4.hist(df_stats['g (m/s²)'], bins=5, color='orange', alpha=0.7, edgecolor='black')\nax4.axvline(g_global, color='r', linestyle='--', linewidth=2.5, label=f'Média: {g_global:.3f}')\nax4.axvline(9.81, color='k', linestyle=':', linewidth=2, label='Teórico: 9.81')\nax4.set_xlabel('g (m/s²)', fontsize=11, fontweight='bold')\nax4.set_ylabel('Frequência', fontsize=11, fontweight='bold')\nax4.set_title('(d) Distribuição de g', fontsize=11, fontweight='bold')\nax4.legend(fontsize=10)\nax4.grid(True, alpha=0.3, axis='y')\n\n# Painel 5: Retrato de fase (pêndulo a 30°)\nax5 = fig.add_subplot(gs[1, 1])\ntheta0 = np.radians(30)\ncond_ini = [theta0, 0]\nsol = odeint(pendulo_nao_linear, cond_ini, t, args=(L, g))\ntheta = sol[:, 0]\nomega = sol[:, 1]\n\nax5.plot(theta * 180/np.pi, omega, 'b-', linewidth=2.5)\nax5.scatter(theta[0] * 180/np.pi, omega[0], s=100, c='red', marker='o', zorder=5, label='Inicial')\nax5.scatter(0, 0, s=100, c='black', marker='x', linewidth=2, zorder=5, label='Equilíbrio')\nax5.set_xlabel('Ângulo θ (graus)', fontsize=11, fontweight='bold')\nax5.set_ylabel('Velocidade ω (rad/s)', fontsize=11, fontweight='bold')\nax5.set_title('(e) Retrato de Fase (30°)', fontsize=11, fontweight='bold')\nax5.legend(fontsize=10)\nax5.grid(True, alpha=0.3)\n\n# Painel 6: Resumo textual\nax6 = fig.add_subplot(gs[1, 2])\nax6.axis('off')\n\nresumo_texto = f\"\"\"RESUMO DOS RESULTADOS\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━\nMedições Realizadas\n━━━━━━━━━━━━━━━━━━━━━━━━━━━\n• Comprimentos: {len(comprimentos)}\n• Medidas/comprimento: 15\n• Total: {len(df)} pontos\n• Outliers removidos: {len(df) - len(df_clean)}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━\nResultado Principal\n━━━━━━━━━━━━━━━━━━━━━━━━━━━\ng = {g_global:.4f} ± {sigma_g_global:.4f} m/s²\n\nValor teórico: 9.8100 m/s²\nErro: {abs(g_global - 9.81):.4f} m/s²\nErro %: {abs(g_global - 9.81) / 9.81 * 100:.2f}%\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━\nQualidade do Ajuste\n━━━━━━━━━━━━━━━━━━━━━━━━━━━\nR² = {1 - np.sum(residuos**2) / np.sum((T2_stats - T2_stats.mean())**2):.4f}\n\"\"\"\n\nax6.text(0.05, 0.95, resumo_texto, transform=ax6.transAxes, fontsize=10,\n        verticalalignment='top', fontfamily='monospace',\n        bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))\n\nplt.suptitle('Projeto Capstone: Caracterização de Pêndulo Físico', fontsize=14, fontweight='bold', y=0.995)\nplt.tight_layout()\nplt.savefig('capstone_resultado.pdf', dpi=300, bbox_inches='tight')\nplt.show()\n\nprint(\"\\nFigura salva em 'capstone_resultado.pdf'\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bloco 5: Checklist de Competências"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "checklist = {\n    \"Capítulo 1 - Introdução a Algoritmos\": [\n        \"Utilizar estruturas de dados (arrays, DataFrames)\",\n        \"Implementar funções modulares para cálculos\"\n    ],\n    \"Capítulo 2 - Lógica de Programação\": [\n        \"Usar condicionais para validar dados\",\n        \"Implementar controles de fluxo em análises\"\n    ],\n    \"Capítulo 3 - Variáveis e Tipos\": [\n        \"Manipular arrays NumPy com eficiência\",\n        \"Trabalhar com DataFrames Pandas\"\n    ],\n    \"Capítulo 4-6 - Controle e Repetição\": [\n        \"Usar loops para processar múltiplas medições\",\n        \"Aplicar filtros e remoção de outliers\"\n    ],\n    \"Capítulo 7 - Funções\": [\n        \"Criar funções reutilizáveis para análises\",\n        \"Organizar código em módulos e classes\"\n    ],\n    \"Capítulo 8 - Métodos Numéricos\": [\n        \"Implementar integração numérica (odeint)\",\n        \"Comparar métodos: Euler vs Runge-Kutta\"\n    ],\n    \"Capítulo 9 - Tratamento de Dados\": [\n        \"Remover outliers estatisticamente\",\n        \"Calcular incertezas com propagação de erros\",\n        \"Realizar regressão linear ponderada\"\n    ],\n    \"Capítulo 10 - Projetos Integrados\": [\n        \"Conduzir uma campanha de medições simulada\",\n        \"Validar modelos lineares vs não-lineares\",\n        \"Produzir figura científica de qualidade\"\n    ]\n}\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"CHECKLIST DE COMPETÊNCIAS - PROJETO CAPSTONE\")\nprint(\"=\"*60)\n\ntotal_itens = 0\nfor capitulo, itens in checklist.items():\n    print(f\"\\n✓ {capitulo}\")\n    for item in itens:\n        print(f\"  ☑ {item}\")\n        total_itens += 1\n\nprint(f\"\\n{'='*60}\")\nprint(f\"Total de competências desenvolvidas: {total_itens}\")\nprint(f\"{'='*60}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
