diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1431c27 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.5 + hooks: + - id: ruff-format + types_or: [ python ] diff --git a/Notebooks/Jumper.ipynb b/Notebooks/Jumper.ipynb index 5abfb73..e408915 100644 --- a/Notebooks/Jumper.ipynb +++ b/Notebooks/Jumper.ipynb @@ -18,14 +18,15 @@ "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", - "import sys \n", + "import sys\n", "import os\n", "import pickle\n", - "import random \n", - "sys.path.insert(0,\"../lib/\")\n", + "import random\n", + "\n", + "sys.path.insert(0, \"../lib/\")\n", "from forecast import Predictions, Simulation, load_ts\n", - "#import importlib\n", - "#importlib.reload(jumper)" + "# import importlib\n", + "# importlib.reload(jumper)" ] }, { @@ -55,11 +56,11 @@ }, "outputs": [], "source": [ - "path = \"/scratchu/mtissot/SIMUp6Y\"\n", - "ye = True\n", + "path = \"/scratchu/mtissot/SIMUp6Y\"\n", + "ye = True\n", "start = 25\n", - "end = 125\n", - "comp = None #default = 0.9\n", + "end = 125\n", + "comp = None # default = 0.9\n", "steps = 30" ] }, @@ -109,24 +110,24 @@ } ], "source": [ - "simu_zos = Simulation(path=path,start=start,end=end,ye=ye,term=\"zos\") \n", + "simu_zos = Simulation(path=path, start=start, end=end, ye=ye, term=\"zos\")\n", "print(\"zos loaded\")\n", - "simu_so = Simulation(path=path,start=start,end=end,ye=ye,term=\"so\") \n", + "simu_so = Simulation(path=path, start=start, end=end, ye=ye, term=\"so\")\n", "print(\"so loaded\")\n", - "simu_thetao = Simulation(path=path,start=start,end=end,ye=ye,term=\"thetao\") \n", + "simu_thetao = Simulation(path=path, start=start, end=end, ye=ye, term=\"thetao\")\n", "print(\"thetao loaded\")\n", "\n", "\n", - "simu_zos.prepare() \n", + "simu_zos.prepare()\n", "print(\"\\nzos prepared\")\n", - "simu_so.prepare() \n", + "simu_so.prepare()\n", "print(\"so prepared\")\n", - "simu_thetao.prepare() \n", + "simu_thetao.prepare()\n", "print(\"thetao prepared\")\n", "\n", "\n", - "#LoadSimu : essayer avec des chunks plus gros et plus de jobs pour 3D \n", - "#Prepare : Cut spin Up - Remove Closed seas - Standardize - (old : Replace bathy nan values by the mean) - to float32" + "# LoadSimu : essayer avec des chunks plus gros et plus de jobs pour 3D\n", + "# Prepare : Cut spin Up - Remove Closed seas - Standardize - (old : Replace bathy nan values by the mean) - to float32" ] }, { @@ -149,14 +150,14 @@ } ], "source": [ - "simus = [simu_zos,simu_so,simu_thetao]\n", - "names = [\"zos\",\"so\",\"thetao\"]\n", + "simus = [simu_zos, simu_so, simu_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", "\n", - "fig, axes = plt.subplots(1,len(simus), figsize=(20,4))\n", + "fig, axes = plt.subplots(1, len(simus), figsize=(20, 4))\n", "\n", "for i, simu in enumerate(simus):\n", - " if simu.z_size is not None : \n", - " im = axes[i].pcolor(simu.simulation[0,0])\n", + " if simu.z_size is not None:\n", + " im = axes[i].pcolor(simu.simulation[0, 0])\n", " axes[i].set_title(f\"Surface {names[i]}\")\n", " else:\n", " im = axes[i].pcolor(simu.simulation[0])\n", @@ -164,14 +165,14 @@ " plt.colorbar(im, ax=axes[i])\n", "\n", "if False:\n", - " fig, axes = plt.subplots(1,len(simus), figsize=(20,4))\n", + " fig, axes = plt.subplots(1, len(simus), figsize=(20, 4))\n", "\n", " for i, simu in enumerate(simus):\n", - " if simu.z_size is not None : \n", - " plt.plot(np.mean(simu.desc[\"ssca\"],axis=(1,2,3)))\n", + " if simu.z_size is not None:\n", + " plt.plot(np.mean(simu.desc[\"ssca\"], axis=(1, 2, 3)))\n", " axes[i].set_title(f\"Average ssca - {names[i]}\")\n", " else:\n", - " plt.plot(np.mean(simu.desc[\"ssca\"],axis=(1,2)))\n", + " plt.plot(np.mean(simu.desc[\"ssca\"], axis=(1, 2)))\n", " axes[i].set_title(f\"Average ssca - {names[i]}\")\n", " plt.colorbar(im, ax=axes[i])" ] @@ -247,29 +248,29 @@ } ], "source": [ - "simus = [simu_zos,simu_so,simu_thetao]\n", - "names = [\"zos\",\"so\",\"thetao\"]\n", - "colors = [\"tab:blue\",\"tab:green\",\"tab:red\"]\n", + "simus = [simu_zos, simu_so, simu_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", + "colors = [\"tab:blue\", \"tab:green\", \"tab:red\"]\n", "fig, axes = plt.subplots(3, 3, figsize=(20, 10))\n", "\n", "for i, simu in enumerate(simus):\n", - " axes[0, i].plot(simu.pca.explained_variance_ratio_*100,\"ko\", markersize =4)\n", + " axes[0, i].plot(simu.pca.explained_variance_ratio_ * 100, \"ko\", markersize=4)\n", " axes[0, i].set_title(f\"Explained Variance Ratio - {names[i]}\")\n", "\n", " axes[1, i].plot(simu.components[:, 0], color=colors[i], alpha=0.9, label=\"1st comp\")\n", " axes[1, i].plot(simu.components[:, 1], color=colors[i], alpha=0.4, label=\"2nd comp\")\n", " axes[1, i].set_title(f\"Components - {names[i]}\")\n", " axes[1, i].legend()\n", - " \n", - " if simu.z_size is not None : \n", + "\n", + " if simu.z_size is not None:\n", " im = axes[2, i].pcolor(simu.getPC(0)[0])\n", - " plt.colorbar(im, ax=axes[2, i])#,label=units[i])\n", + " plt.colorbar(im, ax=axes[2, i]) # ,label=units[i])\n", " axes[2, i].set_title(f\"1st PC of the surface - {names[i]}\")\n", " else:\n", " im = axes[2, i].pcolor(simu.getPC(0))\n", - " plt.colorbar(im, ax=axes[2, i])#,label=units[i])\n", + " plt.colorbar(im, ax=axes[2, i]) # ,label=units[i])\n", " axes[2, i].set_title(f\"1st PC - {names[i]}\")\n", - " \n", + "\n", "fig.suptitle(\"PCA INFO\")\n", "plt.show()" ] @@ -294,10 +295,10 @@ ], "source": [ "n = len(simu_zos.pca.explained_variance_ratio_)\n", - "rec_zos, rmseV_zos, rmseM_zos = simu_zos.rmseOfPCA(n)\n", + "rec_zos, rmseV_zos, rmseM_zos = simu_zos.rmseOfPCA(n)\n", "print(\"RMSE compilated for zos\")\n", "n = len(simu_so.pca.explained_variance_ratio_)\n", - "rec_so, rmseV_so, rmseM_so = simu_so.rmseOfPCA(n)\n", + "rec_so, rmseV_so, rmseM_so = simu_so.rmseOfPCA(n)\n", "print(\"RMSE compilated for so\")\n", "n = len(simu_thetao.pca.explained_variance_ratio_)\n", "rec_thetao, rmseV_thetao, rmseM_thetao = simu_thetao.rmseOfPCA(n)\n", @@ -312,7 +313,10 @@ "outputs": [], "source": [ "import xarray as xr\n", - "array = xr.open_dataset(simu_so.files[-1], decode_times=False,chunks={\"time\": 200, \"x\":120})" + "\n", + "array = xr.open_dataset(\n", + " simu_so.files[-1], decode_times=False, chunks={\"time\": 200, \"x\": 120}\n", + ")" ] }, { @@ -335,16 +339,32 @@ "source": [ "rmseV_zos\n", "values = [rmseV_zos, rmseV_so.T, rmseV_thetao.T]\n", - "maps = [rmseM_zos, rmseM_so, rmseM_thetao]\n", - "names = [\"zos\", \"so\", \"thetao\"]\n", + "maps = [rmseM_zos, rmseM_so, rmseM_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", "colors = [\"tab:blue\", \"darkgreen\", \"darkred\"]\n", "\n", - "fig = plt.figure(figsize=(6,8))\n", + "fig = plt.figure(figsize=(6, 8))\n", "for i in range(3):\n", " if i == 0:\n", - " plt.errorbar(np.mean(values[i]), array.deptht[0], xerr=np.std(values[i]), fmt='.', label=names[i], color=colors[i], ecolor=\"grey\")\n", + " plt.errorbar(\n", + " np.mean(values[i]),\n", + " array.deptht[0],\n", + " xerr=np.std(values[i]),\n", + " fmt=\".\",\n", + " label=names[i],\n", + " color=colors[i],\n", + " ecolor=\"grey\",\n", + " )\n", " else:\n", - " plt.errorbar(np.mean(values[i], axis=1), array.deptht, xerr=np.std(values[i], axis=1), fmt='.', label=names[i], color=colors[i], ecolor=\"grey\")\n", + " plt.errorbar(\n", + " np.mean(values[i], axis=1),\n", + " array.deptht,\n", + " xerr=np.std(values[i], axis=1),\n", + " fmt=\".\",\n", + " label=names[i],\n", + " color=colors[i],\n", + " ecolor=\"grey\",\n", + " )\n", "\n", "plt.title(\"PCA EVALUATION - 1st COMP\")\n", "plt.ylabel(\"Depth\")\n", @@ -352,7 +372,7 @@ "plt.legend()\n", "\n", "plt.gca().invert_yaxis()\n", - "plt.show()\n" + "plt.show()" ] }, { @@ -375,22 +395,22 @@ } ], "source": [ - "values = [rmseV_zos,rmseV_so,rmseV_thetao]\n", - "maps = [rmseM_zos,rmseM_so,rmseM_thetao]\n", - "names = [\"zos\",\"so\",\"thetao\"]\n", + "values = [rmseV_zos, rmseV_so, rmseV_thetao]\n", + "maps = [rmseM_zos, rmseM_so, rmseM_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", "\n", "fig, axes = plt.subplots(1, 3, figsize=(20, 5))\n", "\n", "for i in range(3):\n", - " if len(np.shape(maps[i]))==2: \n", + " if len(np.shape(maps[i])) == 2:\n", " im = axes[i].pcolor(maps[i])\n", " plt.colorbar(im, ax=axes[i])\n", " axes[i].set_title(f\"Mean rmse map - {names[i]}\")\n", " else:\n", - " im = axes[i].pcolor(np.nanmean(maps[i],axis=0))\n", + " im = axes[i].pcolor(np.nanmean(maps[i], axis=0))\n", " plt.colorbar(im, ax=axes[i])\n", " axes[i].set_title(f\"Average rmse map - {names[i]}\")\n", - " \n", + "\n", "fig.suptitle(\"PCA EVALUATION - 1st COMP\")\n", "plt.show()" ] @@ -435,16 +455,16 @@ "if not os.path.exists(f):\n", " os.makedirs(f)\n", "\n", - "with open(f + 'pca_so', 'wb') as file:\n", + "with open(f + \"pca_so\", \"wb\") as file:\n", " pickle.dump(simu_so.pca, file)\n", - "with open(f + 'pca_thetao', 'wb') as file:\n", + "with open(f + \"pca_thetao\", \"wb\") as file:\n", " pickle.dump(simu_thetao.pca, file)\n", - "with open(f + 'pca_zos', 'wb') as file:\n", + "with open(f + \"pca_zos\", \"wb\") as file:\n", " pickle.dump(simu_zos.pca, file)\n", "\n", "np.savez(f + \"so\", **so_dico)\n", "np.savez(f + \"thetao\", **thetao_dico)\n", - "np.savez(f + \"zos\", **zos_dico)\n" + "np.savez(f + \"zos\", **zos_dico)" ] }, { @@ -484,15 +504,15 @@ "source": [ "f = \"/data/mtissot/spinup_data/simus_prepared/\"\n", "\n", - "df_zos,infos_zos = load_ts(f,\"zos\")\n", - "df_so,infos_so = load_ts(f,\"so\")\n", - "df_thetao,infos_thetao = load_ts(f,\"thetao\")\n", + "df_zos, infos_zos = load_ts(f, \"zos\")\n", + "df_so, infos_so = load_ts(f, \"so\")\n", + "df_thetao, infos_thetao = load_ts(f, \"thetao\")\n", "\n", "random.seed(20)\n", "\n", - "ts_zos = Predictions(\"zos\",df_zos,infos_zos) \n", - "ts_so = Predictions(\"so\",df_so,infos_so) \n", - "ts_thetao = Predictions(\"thetao\",df_thetao,infos_thetao) " + "ts_zos = Predictions(\"zos\", df_zos, infos_zos)\n", + "ts_so = Predictions(\"so\", df_so, infos_so)\n", + "ts_thetao = Predictions(\"thetao\", df_thetao, infos_thetao)" ] }, { @@ -557,16 +577,17 @@ ], "source": [ "import random\n", + "\n", "random.seed(100)\n", - "comp,train_len,steps=1,len(ts_zos),30#,20#len(ts_zos),30\n", + "comp, train_len, steps = 1, len(ts_zos), 30 # ,20#len(ts_zos),30\n", "\n", - "hat_zos, std_zos, metrics_zos = ts_zos.forecast_ts(comp,train_len,steps)\n", - "hat_so, std_so, metrics_so = ts_so.forecast_ts(comp,train_len,steps)\n", - "hat_thetao, std_thetao, metrics_thetao = ts_thetao.forecast_ts(comp,train_len,steps)\n", + "hat_zos, std_zos, metrics_zos = ts_zos.forecast_ts(comp, train_len, steps)\n", + "hat_so, std_so, metrics_so = ts_so.forecast_ts(comp, train_len, steps)\n", + "hat_thetao, std_thetao, metrics_thetao = ts_thetao.forecast_ts(comp, train_len, steps)\n", "\n", - "ts_zos.show(comp,hat_zos,std_zos,train_len)\n", - "ts_so.show(comp,hat_so,std_so,train_len,color=\"darkgreen\")\n", - "ts_thetao.show(comp,hat_thetao,std_thetao,train_len,color=\"darkred\")" + "ts_zos.show(comp, hat_zos, std_zos, train_len)\n", + "ts_so.show(comp, hat_so, std_so, train_len, color=\"darkgreen\")\n", + "ts_thetao.show(comp, hat_thetao, std_thetao, train_len, color=\"darkred\")" ] }, { @@ -578,12 +599,12 @@ }, "outputs": [], "source": [ - "hat_zos, hat_std_zos, metrics = ts_zos.Forecast(train_len,steps)\n", - "hat_so, hat_std_so, metrics = ts_so.Forecast(train_len,steps)\n", - "hat_thetao, hat_std_thetao, metrics = ts_thetao.Forecast(train_len,steps)\n", + "hat_zos, hat_std_zos, metrics = ts_zos.Forecast(train_len, steps)\n", + "hat_so, hat_std_so, metrics = ts_so.Forecast(train_len, steps)\n", + "hat_thetao, hat_std_thetao, metrics = ts_thetao.Forecast(train_len, steps)\n", "\n", "\n", - "#Utile de remettre la serie temporelle initiale pour l'analyse des erreurs\n", + "# Utile de remettre la serie temporelle initiale pour l'analyse des erreurs\n", "hat_zos[:-30] = df_zos[:]\n", "hat_so[:-30] = df_so[:]\n", "hat_thetao[:-30] = df_thetao[:]" @@ -606,17 +627,17 @@ } ], "source": [ - "#CHANGER PRENDRE SERIE TEMP TRUTH + PRED\n", + "# CHANGER PRENDRE SERIE TEMP TRUTH + PRED\n", "n = np.shape(ts_zos.info[\"ts\"])[1]\n", - "predictions_zos = ts_zos.reconstruct(hat_zos,n)\n", + "predictions_zos = ts_zos.reconstruct(hat_zos, n)\n", "print(\"zos reconstructed with all comp\")\n", "\n", "n = np.shape(ts_so.info[\"ts\"])[1]\n", - "predictions_so = ts_so.reconstruct(hat_so,n)\n", + "predictions_so = ts_so.reconstruct(hat_so, n)\n", "print(\"so reconstructed with all comp\")\n", "\n", "n = np.shape(ts_thetao.info[\"ts\"])[1]\n", - "predictions_thetao = ts_thetao.reconstruct(hat_thetao,n)\n", + "predictions_thetao = ts_thetao.reconstruct(hat_thetao, n)\n", "print(\"thetao reconstructed with all comp\")" ] }, @@ -640,19 +661,19 @@ } ], "source": [ - "maps = [predictions_zos,predictions_so,predictions_thetao]\n", - "names = [\"zos\",\"so\",\"thetao\"]\n", + "maps = [predictions_zos, predictions_so, predictions_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", "\n", - "fig, axes = plt.subplots(1,len(maps), figsize=(20,4))\n", + "fig, axes = plt.subplots(1, len(maps), figsize=(20, 4))\n", "\n", "for i, simu in enumerate(maps):\n", - " if len(np.shape(simu)) > 3 : \n", - " im = axes[i].pcolor(simu[0,0])\n", + " if len(np.shape(simu)) > 3:\n", + " im = axes[i].pcolor(simu[0, 0])\n", " axes[i].set_title(f\"Surface {names[i]}\")\n", " else:\n", " im = axes[i].pcolor(simu[0])\n", " axes[i].set_title(f\"{names[i]}\")\n", - " plt.colorbar(im, ax=axes[i])\n" + " plt.colorbar(im, ax=axes[i])" ] }, { @@ -695,14 +716,15 @@ "outputs": [], "source": [ "import sys\n", - "sys.path.insert(0,\"../lib/\")\n", + "\n", + "sys.path.insert(0, \"../lib/\")\n", "import forecast\n", "import numpy as np\n", "import xarray as xr\n", "import matplotlib.pyplot as plt\n", "from forecast import Predictions, Simulation, load_ts\n", - "#import importlib\n", - "#importlib.reload(p1)" + "# import importlib\n", + "# importlib.reload(p1)" ] }, { @@ -712,8 +734,8 @@ "metadata": {}, "outputs": [], "source": [ - "pred_zos = np.load(\"/data/mtissot/spinup_data/simus_predicted/pred_zos.npy\")\n", - "pred_so = np.load(\"/data/mtissot/spinup_data/simus_predicted/pred_so.npy\")\n", + "pred_zos = np.load(\"/data/mtissot/spinup_data/simus_predicted/pred_zos.npy\")\n", + "pred_so = np.load(\"/data/mtissot/spinup_data/simus_predicted/pred_so.npy\")\n", "pred_thetao = np.load(\"/data/mtissot/spinup_data/simus_predicted/pred_thetao.npy\")" ] }, @@ -740,23 +762,23 @@ } ], "source": [ - "id_,start2,end2 =\"106\",25,125+30#start,end+steps\n", + "id_, start2, end2 = \"106\", 25, 125 + 30 # start,end+steps\n", "ye = False\n", "\n", - "path =\"/scratchu/mtissot/SIMUp6Y\"\n", - "ref_zos = Simulation(path=path,start=start2,end=end2,ye=ye,term=\"zos\") \n", + "path = \"/scratchu/mtissot/SIMUp6Y\"\n", + "ref_zos = Simulation(path=path, start=start2, end=end2, ye=ye, term=\"zos\")\n", "print(\"zos loaded\")\n", - "ref_so = Simulation(path=path,start=start2,end=end2,ye=ye,term=\"so\") \n", + "ref_so = Simulation(path=path, start=start2, end=end2, ye=ye, term=\"so\")\n", "print(\"so loaded\")\n", - "ref_thetao = Simulation(path=path,start=start2,end=end2,ye=ye,term=\"thetao\") \n", + "ref_thetao = Simulation(path=path, start=start2, end=end2, ye=ye, term=\"thetao\")\n", "print(\"thetao loaded\")\n", "\n", - "#REMTTRE BIEN GET DATA APRES return grid[:1] => return grid\n", - "ref_zos.prepare(stand=False) \n", + "# REMTTRE BIEN GET DATA APRES return grid[:1] => return grid\n", + "ref_zos.prepare(stand=False)\n", "print(\"\\nzos prepared\")\n", - "ref_so.prepare(stand=False) \n", + "ref_so.prepare(stand=False)\n", "print(\"so prepared\")\n", - "ref_thetao.prepare(stand=False) \n", + "ref_thetao.prepare(stand=False)\n", "print(\"thetao prepared\")" ] }, @@ -767,7 +789,9 @@ "metadata": {}, "outputs": [], "source": [ - "array = xr.open_dataset(ref_so.files[-1], decode_times=False,chunks={\"time\": 200, \"x\":120})\n", + "array = xr.open_dataset(\n", + " ref_so.files[-1], decode_times=False, chunks={\"time\": 200, \"x\": 120}\n", + ")\n", "depth = array.deptht\n", "del array" ] @@ -791,27 +815,27 @@ "err_thetao = np.abs((ref_thetao.simulation - pred_thetao))\n", "err_zos = np.abs((ref_zos.simulation - pred_zos))\n", "\n", - "#predictions\n", - "i=15\n", - "mean_err_so_pred = np.nanmean(err_so[-i:],axis=(0,2,3))\n", - "std_err_so_pred = np.nanstd(err_so[-i:],axis=(0,2,3))\n", + "# predictions\n", + "i = 15\n", + "mean_err_so_pred = np.nanmean(err_so[-i:], axis=(0, 2, 3))\n", + "std_err_so_pred = np.nanstd(err_so[-i:], axis=(0, 2, 3))\n", "\n", - "mean_err_thetao_pred = np.nanmean(err_thetao[-i:],axis=(0,2,3))\n", - "std_err_thetao_pred = np.nanstd(err_thetao[-i:],axis=(0,2,3))\n", + "mean_err_thetao_pred = np.nanmean(err_thetao[-i:], axis=(0, 2, 3))\n", + "std_err_thetao_pred = np.nanstd(err_thetao[-i:], axis=(0, 2, 3))\n", "\n", - "mean_err_zos_pred = np.nanmean(err_zos[-i:],axis=(0,1,2))\n", - "std_err_zos_pred = np.nanstd(err_zos[-i:],axis=(0,1,2))\n", + "mean_err_zos_pred = np.nanmean(err_zos[-i:], axis=(0, 1, 2))\n", + "std_err_zos_pred = np.nanstd(err_zos[-i:], axis=(0, 1, 2))\n", "\n", - "#reference\n", - "i=30\n", - "mean_err_so_ref = np.nanmean(err_so[:-i],axis=(0,2,3))\n", - "std_err_thetao_ref = np.nanstd(err_so[:-i],axis=(0,2,3))\n", + "# reference\n", + "i = 30\n", + "mean_err_so_ref = np.nanmean(err_so[:-i], axis=(0, 2, 3))\n", + "std_err_thetao_ref = np.nanstd(err_so[:-i], axis=(0, 2, 3))\n", "\n", - "mean_err_thetao_ref = np.nanmean(err_thetao[:-i],axis=(0,2,3))\n", - "std_err_so_ref = np.nanstd(err_thetao[:-i],axis=(0,2,3))\n", + "mean_err_thetao_ref = np.nanmean(err_thetao[:-i], axis=(0, 2, 3))\n", + "std_err_so_ref = np.nanstd(err_thetao[:-i], axis=(0, 2, 3))\n", "\n", - "mean_err_zos_ref = np.nanmean(err_zos[:-i],axis=(0,1,2))\n", - "std_err_zos_ref = np.nanstd(err_zos[:-i],axis=(0,1,2))" + "mean_err_zos_ref = np.nanmean(err_zos[:-i], axis=(0, 1, 2))\n", + "std_err_zos_ref = np.nanstd(err_zos[:-i], axis=(0, 1, 2))" ] }, { @@ -832,16 +856,16 @@ } ], "source": [ - "categories = ['Prediction', 'PCA']\n", - "means = [mean_err_zos_pred, mean_err_zos_ref]\n", - "errors = [std_err_zos_pred, std_err_zos_ref]\n", + "categories = [\"Prediction\", \"PCA\"]\n", + "means = [mean_err_zos_pred, mean_err_zos_ref]\n", + "errors = [std_err_zos_pred, std_err_zos_ref]\n", "\n", - "fig, ax = plt.subplots(figsize=(6,3))\n", - "ax.bar(categories, means, yerr=errors, capsize=5, color=['tab:blue', 'grey'])\n", + "fig, ax = plt.subplots(figsize=(6, 3))\n", + "ax.bar(categories, means, yerr=errors, capsize=5, color=[\"tab:blue\", \"grey\"])\n", "\n", - "ax.set_title('Mean Error with Standard Error')\n", - "ax.set_ylabel('Error')\n", - "ax.set_xlabel('Categories')\n", + "ax.set_title(\"Mean Error with Standard Error\")\n", + "ax.set_ylabel(\"Error\")\n", + "ax.set_xlabel(\"Categories\")\n", "plt.show()" ] }, @@ -863,26 +887,59 @@ } ], "source": [ - "fig, axes = plt.subplots(2,1, figsize=(10,6))\n", - "\n", - "#Plot thetao infos\n", - "axes[0].plot(depth, mean_err_thetao_ref, color=\"black\", label=\"thetao\",linestyle=\"dashed\",alpha=0.6)\n", - "axes[0].fill_between(depth, mean_err_thetao_ref + std_err_thetao_ref, mean_err_thetao_ref - std_err_thetao_ref, color=\"black\", alpha=0.1)\n", + "fig, axes = plt.subplots(2, 1, figsize=(10, 6))\n", + "\n", + "# Plot thetao infos\n", + "axes[0].plot(\n", + " depth,\n", + " mean_err_thetao_ref,\n", + " color=\"black\",\n", + " label=\"thetao\",\n", + " linestyle=\"dashed\",\n", + " alpha=0.6,\n", + ")\n", + "axes[0].fill_between(\n", + " depth,\n", + " mean_err_thetao_ref + std_err_thetao_ref,\n", + " mean_err_thetao_ref - std_err_thetao_ref,\n", + " color=\"black\",\n", + " alpha=0.1,\n", + ")\n", "\n", "axes[0].plot(depth, mean_err_thetao_pred, color=\"darkred\", label=\"thetao\")\n", - "axes[0].fill_between(depth, mean_err_thetao_pred + std_err_thetao_pred, mean_err_thetao_pred - std_err_thetao_pred, color=\"darkred\", alpha=0.2)\n", - "axes[0].set_xlabel('Depth')\n", - "axes[0].set_ylabel('Mean Error')\n", + "axes[0].fill_between(\n", + " depth,\n", + " mean_err_thetao_pred + std_err_thetao_pred,\n", + " mean_err_thetao_pred - std_err_thetao_pred,\n", + " color=\"darkred\",\n", + " alpha=0.2,\n", + ")\n", + "axes[0].set_xlabel(\"Depth\")\n", + "axes[0].set_ylabel(\"Mean Error\")\n", "axes[0].legend()\n", "\n", - "#Plot so infos\n", - "axes[1].plot(depth, mean_err_so_ref, color=\"black\", label=\"so\",linestyle=\"dashed\",alpha=0.6)\n", - "axes[1].fill_between(depth, mean_err_so_ref + std_err_so_ref, mean_err_so_ref - std_err_so_ref, color=\"black\", alpha=0.1)\n", + "# Plot so infos\n", + "axes[1].plot(\n", + " depth, mean_err_so_ref, color=\"black\", label=\"so\", linestyle=\"dashed\", alpha=0.6\n", + ")\n", + "axes[1].fill_between(\n", + " depth,\n", + " mean_err_so_ref + std_err_so_ref,\n", + " mean_err_so_ref - std_err_so_ref,\n", + " color=\"black\",\n", + " alpha=0.1,\n", + ")\n", "\n", "axes[1].plot(depth, mean_err_so_pred, color=\"darkgreen\", label=\"so\")\n", - "axes[1].fill_between(depth, mean_err_so_pred + std_err_so_pred, mean_err_so_pred - std_err_so_pred, color=\"darkgreen\", alpha=0.2)\n", - "axes[1].set_xlabel('Depth')\n", - "axes[1].set_ylabel('Mean Error')\n", + "axes[1].fill_between(\n", + " depth,\n", + " mean_err_so_pred + std_err_so_pred,\n", + " mean_err_so_pred - std_err_so_pred,\n", + " color=\"darkgreen\",\n", + " alpha=0.2,\n", + ")\n", + "axes[1].set_xlabel(\"Depth\")\n", + "axes[1].set_ylabel(\"Mean Error\")\n", "axes[1].legend()\n", "\n", "fig.suptitle(f\"Absolute error on {i} last predictions\")\n", @@ -908,14 +965,14 @@ } ], "source": [ - "fig, axes = plt.subplots(1,2, figsize=(15,4))\n", + "fig, axes = plt.subplots(1, 2, figsize=(15, 4))\n", "\n", - "axes[0].plot(depth,mean_pred_so,label=\"predictions\")\n", - "axes[0].plot(depth,mean_ref_so,label=\"reference\")\n", + "axes[0].plot(depth, mean_pred_so, label=\"predictions\")\n", + "axes[0].plot(depth, mean_ref_so, label=\"reference\")\n", "axes[0].legend()\n", "\n", - "axes[1].plot(depth,mean_pred_thetao,label=\"predictions\")\n", - "axes[1].plot(depth,mean_ref_thetao,label=\"reference\")\n", + "axes[1].plot(depth, mean_pred_thetao, label=\"predictions\")\n", + "axes[1].plot(depth, mean_ref_thetao, label=\"reference\")\n", "axes[1].legend()\n", "\n", "fig.suptitle(f\"Average over depth\")\n", @@ -930,14 +987,14 @@ "metadata": {}, "outputs": [], "source": [ - "mean_pred_so = np.nanmean(pred_so,axis=(1,2,3))\n", - "mean_ref_so = np.nanmean(ref_so.simulation,axis=(1,2,3))\n", + "mean_pred_so = np.nanmean(pred_so, axis=(1, 2, 3))\n", + "mean_ref_so = np.nanmean(ref_so.simulation, axis=(1, 2, 3))\n", "\n", - "mean_pred_thetao = np.nanmean(pred_thetao,axis=(1,2,3))\n", - "mean_ref_thetao = np.nanmean(ref_thetao.simulation,axis=(1,2,3))\n", + "mean_pred_thetao = np.nanmean(pred_thetao, axis=(1, 2, 3))\n", + "mean_ref_thetao = np.nanmean(ref_thetao.simulation, axis=(1, 2, 3))\n", "\n", - "mean_pred_zos = np.nanmean(pred_zos,axis=(1,2))\n", - "mean_ref_zos = np.nanmean(ref_zos.simulation,axis=(1,2))" + "mean_pred_zos = np.nanmean(pred_zos, axis=(1, 2))\n", + "mean_ref_zos = np.nanmean(ref_zos.simulation, axis=(1, 2))" ] }, { @@ -958,15 +1015,15 @@ } ], "source": [ - "mean_pred = [mean_pred_zos,mean_pred_so, mean_pred_thetao]\n", - "mean_ref = [mean_ref_zos,mean_ref_so, mean_ref_thetao]\n", - "names = [\"zos\", \"so\", \"thetao\"]\n", + "mean_pred = [mean_pred_zos, mean_pred_so, mean_pred_thetao]\n", + "mean_ref = [mean_ref_zos, mean_ref_so, mean_ref_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", "colors = [\"tab:blue\", \"darkgreen\", \"darkred\"]\n", "\n", - "fig, axes = plt.subplots(3,1,figsize=(10,8))\n", - "for i,ax in enumerate(axes):\n", - " ax.plot(mean_pred[i],color=colors[i],label=names[i])\n", - " ax.plot(mean_ref[i],color=\"grey\",label=\"ref\",linestyle=\"dashed\")\n", + "fig, axes = plt.subplots(3, 1, figsize=(10, 8))\n", + "for i, ax in enumerate(axes):\n", + " ax.plot(mean_pred[i], color=colors[i], label=names[i])\n", + " ax.plot(mean_ref[i], color=\"grey\", label=\"ref\", linestyle=\"dashed\")\n", " ax.legend()" ] }, @@ -995,9 +1052,13 @@ } ], "source": [ - "ref_zos.simulation = (ref_zos.simulation - ref_zos.desc[\"mean\"]) /(2*ref_zos.desc[\"std\"])\n", - "ref_so.simulation = (ref_so.simulation - ref_so.desc[\"mean\"]) /(2*ref_so.desc[\"std\"])\n", - "ref_thetao.simulation = (ref_thetao.simulation - ref_thetao.desc[\"mean\"])/(2*ref_thetao.desc[\"std\"])\n", + "ref_zos.simulation = (ref_zos.simulation - ref_zos.desc[\"mean\"]) / (\n", + " 2 * ref_zos.desc[\"std\"]\n", + ")\n", + "ref_so.simulation = (ref_so.simulation - ref_so.desc[\"mean\"]) / (2 * ref_so.desc[\"std\"])\n", + "ref_thetao.simulation = (ref_thetao.simulation - ref_thetao.desc[\"mean\"]) / (\n", + " 2 * ref_thetao.desc[\"std\"]\n", + ")\n", "\n", "ref_zos.applyPCA()\n", "print(\"PCA applied on zos\")\n", @@ -1025,29 +1086,29 @@ } ], "source": [ - "simus = [ref_zos,ref_so,ref_thetao]\n", - "names = [\"zos\",\"so\",\"thetao\"]\n", - "colors = [\"tab:blue\",\"tab:green\",\"tab:red\"]\n", + "simus = [ref_zos, ref_so, ref_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", + "colors = [\"tab:blue\", \"tab:green\", \"tab:red\"]\n", "fig, axes = plt.subplots(3, 3, figsize=(20, 10))\n", "\n", "for i, simu in enumerate(simus):\n", - " axes[0, i].plot(simu.pca.explained_variance_ratio_*100,\"ko\", markersize =4)\n", + " axes[0, i].plot(simu.pca.explained_variance_ratio_ * 100, \"ko\", markersize=4)\n", " axes[0, i].set_title(f\"Explained Variance Ratio - {names[i]}\")\n", "\n", " axes[1, i].plot(simu.components[:, 0], color=colors[i], alpha=0.9, label=\"1st comp\")\n", " axes[1, i].plot(simu.components[:, 1], color=colors[i], alpha=0.4, label=\"2nd comp\")\n", " axes[1, i].set_title(f\"Components - {names[i]}\")\n", " axes[1, i].legend()\n", - " \n", - " if simu.z_size is not None : \n", + "\n", + " if simu.z_size is not None:\n", " im = axes[2, i].pcolor(simu.getPC(0)[0])\n", - " plt.colorbar(im, ax=axes[2, i])#,label=units[i])\n", + " plt.colorbar(im, ax=axes[2, i]) # ,label=units[i])\n", " axes[2, i].set_title(f\"1st PC of the surface - {names[i]}\")\n", " else:\n", " im = axes[2, i].pcolor(simu.getPC(0))\n", - " plt.colorbar(im, ax=axes[2, i])#,label=units[i])\n", + " plt.colorbar(im, ax=axes[2, i]) # ,label=units[i])\n", " axes[2, i].set_title(f\"1st PC - {names[i]}\")\n", - " \n", + "\n", "fig.suptitle(\"PCA INFO\")\n", "plt.show()" ] @@ -1072,20 +1133,22 @@ } ], "source": [ - "comp = 0\n", - "ref = [ref_zos,ref_so,ref_thetao]\n", - "pred = [hat_zos,hat_so,hat_thetao]#[df_zos,df_so,df_thetao]\n", - "names = [\"zos\",\"so\",\"thetao\"]\n", - "colors = [\"tab:blue\",\"tab:green\",\"tab:red\"]\n", + "comp = 0\n", + "ref = [ref_zos, ref_so, ref_thetao]\n", + "pred = [hat_zos, hat_so, hat_thetao] # [df_zos,df_so,df_thetao]\n", + "names = [\"zos\", \"so\", \"thetao\"]\n", + "colors = [\"tab:blue\", \"tab:green\", \"tab:red\"]\n", "\n", - "fig, axes = plt.subplots(3,1,figsize=(10,8))\n", + "fig, axes = plt.subplots(3, 1, figsize=(10, 8))\n", "\n", "for i, simu in enumerate(ref):\n", - " axes[i].plot(simu.components[:, comp], color=\"grey\",linestyle=\"dashed\",label=\"ref\")\n", - " axes[i].plot(pred[i].iloc[:,comp], color=colors[i], alpha=0.9, label=names[i])\n", + " axes[i].plot(\n", + " simu.components[:, comp], color=\"grey\", linestyle=\"dashed\", label=\"ref\"\n", + " )\n", + " axes[i].plot(pred[i].iloc[:, comp], color=colors[i], alpha=0.9, label=names[i])\n", " axes[i].set_title(f\"Components - {names[i]}\")\n", " axes[i].legend()\n", - " \n", + "\n", "fig.suptitle(\"PCA INFO\")\n", "plt.show()" ] @@ -1108,16 +1171,18 @@ } ], "source": [ - "comp = 1\n", + "comp = 1\n", "\n", - "fig, axes = plt.subplots(3,1,figsize=(10,8))\n", + "fig, axes = plt.subplots(3, 1, figsize=(10, 8))\n", "\n", "for i, simu in enumerate(ref):\n", - " axes[i].plot(simu.components[:, comp], color=\"grey\",linestyle=\"dashed\",label=\"ref\")\n", - " axes[i].plot(pred[i].iloc[:,comp], color=colors[i], alpha=0.9, label=names[i])\n", + " axes[i].plot(\n", + " simu.components[:, comp], color=\"grey\", linestyle=\"dashed\", label=\"ref\"\n", + " )\n", + " axes[i].plot(pred[i].iloc[:, comp], color=colors[i], alpha=0.9, label=names[i])\n", " axes[i].set_title(f\"Components - {names[i]}\")\n", " axes[i].legend()\n", - " \n", + "\n", "fig.suptitle(\"PCA INFO\")\n", "plt.show()" ] @@ -1140,16 +1205,18 @@ } ], "source": [ - "comp = 2\n", + "comp = 2\n", "\n", - "fig, axes = plt.subplots(3,1,figsize=(10,8))\n", + "fig, axes = plt.subplots(3, 1, figsize=(10, 8))\n", "\n", "for i, simu in enumerate(ref):\n", - " axes[i].plot(simu.components[:, comp], color=\"grey\",linestyle=\"dashed\",label=\"ref\")\n", - " axes[i].plot(pred[i].iloc[:,comp], color=colors[i], alpha=0.9, label=names[i])\n", + " axes[i].plot(\n", + " simu.components[:, comp], color=\"grey\", linestyle=\"dashed\", label=\"ref\"\n", + " )\n", + " axes[i].plot(pred[i].iloc[:, comp], color=colors[i], alpha=0.9, label=names[i])\n", " axes[i].set_title(f\"Components - {names[i]}\")\n", " axes[i].legend()\n", - " \n", + "\n", "fig.suptitle(\"PCA INFO\")\n", "plt.show()" ] diff --git a/Notebooks/Restart.ipynb b/Notebooks/Restart.ipynb index 61b9fd7..dbc8ca1 100644 --- a/Notebooks/Restart.ipynb +++ b/Notebooks/Restart.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 139, + "execution_count": 1, "id": "b7cde80b-3b01-42d6-9267-b61603c7bcd6", "metadata": {}, "outputs": [], @@ -10,7 +10,8 @@ "import numpy as np\n", "import xarray as xr\n", "import sys\n", - "sys.path.insert(0,\"/home/mtissot/SpinUp/jumper/lib\")\n", + "\n", + "sys.path.insert(0, \"/home/mtissot/SpinUp/jumper/lib\")\n", "import matplotlib.pyplot as plt" ] }, @@ -24,34 +25,30 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": null, "id": "7f5fa984-0bb5-4df8-8f1c-e10539f74e12", "metadata": {}, "outputs": [], "source": [ - "dataset1 = xr.open_dataset('/data/mtissot/infos4restart/data_restart/OCE_CM65v420-LR-CdL-pi-01_19141231_restart.nc',decode_times=False) \n", - "mask = xr.open_dataset('/data/mtissot/infos4restart/eORCA1.4.2_mesh_mask_modJD.nc',decode_times=False) \n", - "dataset2 = xr.open_dataset('/data/mtissot/infos4restart/data_restart/NEW_OCE_CM65v420-LR-CdL-pi-01_19141231_restart.nc',decode_times=False) " + "dataset1 = xr.open_dataset(\n", + " \"/data/mtissot/infos4restart/data_restart/OCE_CM65v420-LR-CdL-pi-01_19141231_restart.nc\",\n", + " decode_times=False,\n", + ")\n", + "mask = xr.open_dataset(\n", + " \"/data/mtissot/infos4restart/eORCA1.4.2_mesh_mask_modJD.nc\", decode_times=False\n", + ")\n", + "dataset2 = xr.open_dataset(\n", + " \"/data/mtissot/infos4restart/data_restart/NEW_OCE_CM65v420-LR-CdL-pi-01_19141231_restart.nc\",\n", + " decode_times=False,\n", + ")" ] }, { "cell_type": "code", - "execution_count": 128, + "execution_count": null, "id": "642bc6a4-c706-4cf0-9021-72b719038d0e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "restart features : \n", - " ['adatrj', 'avm_k', 'avt_k', 'dissl', 'e3t_ini', 'e3t_m', 'emp_b', 'en', 'fraqsr_1lev', 'frc_s', 'frc_t', 'frc_v', 'frq_m', 'fwfisf_par_b', 'hc_loc_ini', 'isf_hc_par_b', 'isf_sc_par_b', 'kt', 'ndastp', 'nn_fsbc', 'ntime', 'qns_b', 'qsr_hc_b', 'rdt', 'rhop', 'rnf_b', 'rnf_hc_b', 'rnf_sc_b', 'sb', 'sbc_hc_b', 'sbc_sc_b', 'sc_loc_ini', 'sfx_b', 'sn', 'ssh_ini', 'ssh_m', 'sshb', 'sshn', 'sss_m', 'sst_m', 'ssu_m', 'ssv_m', 'surf_ini', 'tb', 'tmask_ini', 'tn', 'ub', 'ub2_b', 'un', 'un_bf', 'utau_b', 'vb', 'vb2_b', 'vn', 'vn_bf', 'vtau_b', 'xx', 'yy']\n", - "\n", - "mask features : \n", - " ['e1f', 'e1t', 'e1u', 'e1v', 'e2f', 'e2t', 'e2u', 'e2v', 'e3f_0', 'e3t_0', 'e3t_1d', 'e3u_0', 'e3uw_0', 'e3v_0', 'e3vw_0', 'e3w_0', 'e3w_1d', 'ff_f', 'ff_t', 'fmask', 'gdept_0', 'gdept_1d', 'gdepw_0', 'gdepw_1d', 'glamf', 'glamt', 'glamu', 'glamv', 'gphif', 'gphit', 'gphiu', 'gphiv', 'mbathy', 'misf', 'nav_lev', 'time_counter', 'tmask', 'tmaskutil', 'umask', 'umaskutil', 'vmask', 'vmaskutil', 'gridx', 'gridy']\n" - ] - } - ], + "outputs": [], "source": [ "print(\"restart features : \\n\", list(dataset2.keys()))\n", "print(\"\\nmask features : \\n\", list(mask.keys()))" @@ -70,7 +67,7 @@ "id": "8bdd0df6-8ce7-4b6b-be26-0354c08a415f", "metadata": {}, "source": [ - "rhop,u,v,e3t,ssh,T,S," + "rhop,u,v,e3t,ssh,T,S." ] }, { @@ -83,7 +80,7 @@ }, { "cell_type": "code", - "execution_count": 206, + "execution_count": null, "id": "dc4dd751-7740-4f6c-aaae-b44fe3c6b8b9", "metadata": {}, "outputs": [], @@ -91,86 +88,58 @@ "new = dataset2.rhop.where(mask.tmask.values)\n", "old = dataset1.rhop.where(mask.tmask.values)\n", "\n", - "diff_new = np.diff(new.isel(time_counter=0), axis=0) \n", - "diff_old = np.diff(old.isel(time_counter=0), axis=0) \n", + "diff_new = np.diff(new.isel(time_counter=0), axis=0)\n", + "diff_old = np.diff(old.isel(time_counter=0), axis=0)\n", "\n", - "val = [old[0],new[0]]\n", - "diff = [diff_old,diff_new]" + "val = [old[0], new[0]]\n", + "diff = [diff_old, diff_new]" ] }, { "cell_type": "code", - "execution_count": 156, + "execution_count": null, "id": "063a406b-f1fc-44d7-bf3a-45c63ca27d1a", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_1826838/2392123758.py:4: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(val[0],axis=(1,2)),dataset1.nav_lev,linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "/tmp/ipykernel_1826838/2392123758.py:5: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(val[1],axis=(1,2)),dataset2.nav_lev,color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n" - ] - }, - { - "data": { - "image/png": "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7dSpU5O/02effdYdH5EaaXFAVFVVoU+fPli0aJHL+a+88goWLFiARYsWISsrCyaTCcOHD5evxXT69GmcPn0ar776Kvbs2YMVK1YgIyMDkyZNclrOqFGjYLPZsHHjRuzcuRPXX3890tLSUFBQcAUfU9ncsU4HDRqE/Px8p8ef//xndOrUCf369XPL53Q3d/2t/vvf/0Z6ejoefPBB/Pbbb/j5558xfvz4Nv98nuCudQoAc+bMcfp7fe6559r0s5EL4ioAEGvWrJGfOxwOYTKZxPz58+VptbW1wmg0isWLF190OZ9++qnQ6XTCarUKIYQ4c+aMACA2bdoktykvLxcAxHfffXc1JSteW63TC1ksFhEZGSnmzJnTarUrWVutV6vVKmJjY8XSpUvbrHalasu/1YSEBLFw4cK2KJtaoFX3QeTm5qKgoAAjRoyQp+n1egwePBhbtmy56OvMZjOCg4Oh0dRfGio8PBzdu3fH+++/j6qqKthsNixZsgRRUVFITk5uzZIVr7XW6YW+/PJLFBcXY+LEia1dsldorfX666+/4tSpU1CpVOjbty+io6Nx++23Ow2rtBet/bf68ssvIzw8HNdffz1eeuklWCyWNqudXGvVi/WdH/6Jiopymh4VFYXjx4+7fM3Zs2fxwgsv4JFHHpGnSZKEDRs24M4770RQUBBUKhWioqKQkZGBkJCQ1ixZ8VprnV5o2bJluO2229rt5ddba70ePXoUADB79mwsWLAAnTp1wmuvvYbBgwfj0KFDCAsLa6NPoDyt+bf65JNP4oYbbkBoaCh++eUX/P3vf0dubi6WLl3aNsWTS21yNVdJkpyeCyGaTAPqL/07atQo9OjRA7NmzXJq/9hjjyEyMhI//fQTDAYDli5dirS0NGRlZSE6Orotyla0q12njeXl5WHdunX49NNP26RWb3K169XhcAAAZs6cibvvvhsAsHz5csTFxeH//u//LhnSvqo1/lafeuop+ffevXsjNDQU99xzj9yrIPdo1SEmk8kEAE12JBcVFTX5VlFRUYGRI0ciMDAQa9asgVarledt3LgRX331FVavXo0bb7wRN9xwA9566y0YDAasXLmyNUtWvNZap40tX74c4eHhGD16dNsU7QVaa72e/7LSo0cPeZper0fnzp1x4sSJtipfkdrib/W8gQMHAgAOHz7cihXT5bRqQCQmJsJkMmHDhg3yNIvFgszMTAwaNEieVl5ejhEjRkCn0+HLL7+En5+f03Kqq6vri7vgBhwqlUr+xtZetNY6PU8IgeXLl+OBBx647P+Uvqy11mtycjL0ej0OHjwoT7NarTh27BgSEhLa/oMoSGv/rTa2a9cuAGiXowce1dK92hUVFWLXrl1i165dAoBYsGCB2LVrlzh+/LgQQoj58+cLo9EoPv/8c7Fnzx4xbtw4ER0dLcrLy4UQ9UcjpaSkiF69eonDhw+L/Px8+WGz2YQQ9UcxhYeHi7Fjx4rs7Gxx8OBBMWPGDKHVakV2dnar7J1XEnes0/O+++47AUDs27fP7Z/T3dy1Xp988kkRGxsr1q1bJw4cOCAmTZokIiMjRUlJiUc+d1tyxzrdsmWLvNyjR4+KTz75RMTExIjRo0d77HO3Vy0OiB9++EEAaPKYMGGCEKL+ULdZs2YJk8kk9Hq9uPnmm8WePXsu+3oAIjc3V26XlZUlRowYIcLCwkRQUJAYOHCg+Oabb676AyuRu9apEEKMGzdODBo0yI2fznPctV4tFouYPn26iIyMFEFBQWLYsGFi7969bv607uGOdbpz506RkpIijEaj8PPzE127dhWzZs0SVVVVHvjE7Rsv901ERC7xWkxEROQSA4KIiFxiQBARkUsMCCIicokBQURELjEgiIjIJQYEERG5xIAgIiKXGBBEROQSA4KIiFxiQBARkUsMCCIicun/A2ENIGg8YeBcAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(4, 5))\n", "ax = plt.gca()\n", "\n", - "ax.plot(np.nanmean(val[0],axis=()1,2),dataset1.nav_lev,linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "ax.plot(np.nanmean(val[1],axis=(1,2)),dataset2.nav_lev,color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n", + "ax.plot(\n", + " np.nanmean(val[0], axis=(1, 2)),\n", + " dataset1.nav_lev,\n", + " linestyle=\"dashed\",\n", + " color=\"black\",\n", + " alpha=0.7,\n", + " linewidth=3,\n", + " label=\"truth\",\n", + ")\n", + "ax.plot(\n", + " np.nanmean(val[1], axis=(1, 2)),\n", + " dataset2.nav_lev,\n", + " color=\"purple\",\n", + " alpha=0.8,\n", + " linewidth=2,\n", + " label=\"predictions\",\n", + ")\n", "ax.invert_yaxis()\n", "ax.invert_xaxis()\n", "\n", - "#ax.set_xlim(left=1)\n", + "# ax.set_xlim(left=1)\n", "\n", - "ax.yaxis.set_label_position('right')\n", + "ax.yaxis.set_label_position(\"right\")\n", "ax.yaxis.tick_right()\n", "ax.legend()\n", - "ax.set_title('Average\n", - "')\n", + "ax.set_title(\"Average\")\n", "plt.show()" ] }, { "cell_type": "code", - "execution_count": 208, + "execution_count": null, "id": "2b2b5ca2-7ba7-4409-bfd8-9da396e7a59b", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_1826838/3771032732.py:8: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(diff[0], axis=(1, 2)), dataset1.nav_lev[:-1], linestyle=\"dashed\", color=\"black\", alpha=0.7, linewidth=3, label=\"truth\")\n", - "/tmp/ipykernel_1826838/3771032732.py:9: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(diff[1], axis=(1, 2)), dataset2.nav_lev[:-1], color=\"purple\", alpha=0.8, linewidth=2, label=\"predictions\")\n" - ] - }, - { - "data": { - "image/png": "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8z/mtttuM08//bQBzLPPPttp+ZdfftkA5p133unwexiLxfom4OPU3f3syu+3W3X3nOqvx7Q36izpe73x+9qVvz19qbv7eOONN5q7777brF+/3rz77rvmP/7jP4zf7zdvvPFGskxXroH6mlPXEU6bPXu2GTdunFmzZo1Zs2aNGTdunJkzZ85Ry9fU1JhZs2aZ3/72t+btt982a9euNZMnTzYTJ07sUG769Olm/vz5HY5vTU1Nb+9On+h2wveJT3zCXHvttR2WjRkzxtx6663H/O706dOPmPBddtllZvbs2R2WXXDBBebyyy/vke0eS3fXfcstt5gxY8Z0WPa1r33NTJky5ajbuP/++012drZpaGhILrv99tvN6aeffvyBd6I39unRRx81oVCoR7fbVX1xjGKxmMnOzjaPPfZYcll/O0ZOn0vSPd1J+Kqrq/skpt7Qlf08nnPWLbp7TvXXY9oX9bD0vt74fe3K356+1BN/50499VRzxx13JN935Rqorzl1HeGkLVu2GKBDArp27VoDmLfffrvL61m/fr0BOiTHR8tTBoJudemMRCK8/vrrnH/++R2Wn3/++axZs+a4WxnXrl172DovuOCC5Dp7a7vHu+6jxfvaa68RjUaP+J1HHnmEyy+/nMzMzA7L33vvPcrKyhg5ciSXX34577///gnsTUJv7lNDQwPDhw9nyJAhzJkzh40bN57Qdp3en0M1NTURjUbJz8/vsLw/HSMnzyXpXWeeeSaDBg1i5syZvPzyy06H0+OO55x1gxM5p/rTMe2relh6V2/9vh7rb09f6om/c7ZtU19ff9j1QGfXQH3NqesIp61du5ZQKMTkyZOTy6ZMmUIoFOpWjLW1tViWRW5uboflTzzxBIWFhZx22mncfPPN1NfX91TojupWwrd//37i8TglJSUdlpeUlFBZWXncQVRWVna6zt7a7vGu+2jxxmIx9u/ff1j59evXs2nTJr761a92WD558mQef/xxXnjhBR5++GEqKyuZNm0aBw4ccOU+jRkzhmXLlvHHP/6Rp556irS0NM455xzee++9496uk/vzcbfeeiuDBw9m1qxZyWX97Rg5eS5J7xg0aBC//OUvefrpp3nmmWcYPXo0M2fO5G9/+5vTofWo4zln3eB4zqn+eEz7qh6W3tVbv6/H+tvTl3ri79yPfvQjGhsbueyyy5LLjnUN1Necuo5wWmVlJcXFxYctLy4u7nKMLS0t3HrrrcydO5ecnJzk8iuuuIKnnnqKVatW8d3vfpenn36aSy+9tMdid5LveL5kWVaH98aYw5b1xjp7Y7vHu+4jlT/Scki07o0bN45PfOITHZZfeOGFydfjx49n6tSpnHzyyTz22GN84xvf6PY+dCXGE9mnKVOmMGXKlOTn55xzDmeddRY//elP+clPfnLc2+2q3jxG99xzT/IkT0tLSy7vb8eoq+vszXNJetbo0aMZPXp08v3UqVPZuXMn9957L5/61KccjKzndeecdZvunFP9+Zj2Zj0sfac3fl/d9nfleON56qmnWLx4MX/4wx86JBZdvQbqa05dR/S0xYsXc8cdd3RaZsOGDcCR64+uxhiNRrn88suxbZuf//znHT6bP39+8vW4ceMoLy9n0qRJvPHGG5x11lld2Q3X6lYLX2FhIV6v97AMuqqq6rC7Ad1RWlra6Tp7a7vHu+6jxevz+SgoKOiwvKmpieXLlx/WunckmZmZjB8//oTvFvX2PrXxeDycffbZyXh76zj19v7ce++9LFmyhJUrVzJhwoROY3H7MXLyXJK+M2XKFMfuKveW46mD3KCnzim3H9O++rsivau3fl+P9benL53IPv72t7/lmmuu4Xe/+12H3j5H8vFroL7m1HVEb7n++uvZunVrpz/jxo2jtLSUvXv3Hvb9ffv2HTPGaDTKZZddxvbt23nxxRc7tO4dyVlnnYXf73d13dxV3Ur4AoEAEydO5MUXX+yw/MUXX2TatGnHHcTUqVMPW+fKlSuT6+yt7R7vuo8W76RJk/D7/R2W/+53vyMcDvPlL3/5mLGEw2G2bt3KoEGDurkXHfX2PrUxxlBRUZGMt7eOU2/uzw9/+EO+//3vs2LFCiZNmnTMWNx+jJw8l6TvbNy48YR/B93meOogN+ipc8rtx7Sv/q5I7+qt39dj/e3pS8e7j0899RRXXXUVTz75ZIcptI7m49dAfc2p64jeUlhYyJgxYzr9SUtLY+rUqdTW1naYRuvVV1+ltra20xjbkr333nuPv/zlL1266bR582ai0air6+Yu6+4oL21DwD7yyCNmy5YtZtGiRSYzM9N88MEHxhhjbr31VjNv3rwO39m4caPZuHGjmThxopk7d67ZuHGj2bx5c/Lzf/zjH8br9Zq77rrLbN261dx1111HHUr+aNs9Ed3dp7Zhbf/93//dbNmyxTzyyCNHHWr63HPPNV/84hePuN2bbrrJrFq1yrz//vtm3bp1Zs6cOSY7O9u1+7R48WKzYsUK889//tNs3LjRXH311cbn85lXX321y9t10/7cfffdJhAImN///vcdhuCtr69Plulvx8jpc0mOrb6+PlknAua+++4zGzduTI4U9vHjfv/995tnn33WvPvuu2bTpk3m1ltvNYB5+umnndqFLunufnanXnWb7p7L/fWY9ubfSuk7vfH72pW/PX2pu/v45JNPGp/PZ372s58ddUj+rlwD9TWnriOcNnv2bDNhwgSzdu1as3btWjN+/PjDpmUYPXq0eeaZZ4wxxkSjUXPxxRebIUOGmIqKig7HOBwOG2OM2bZtm7njjjvMhg0bzPbt282f//xnM2bMGHPmmWe6fsqcruh2wmeMMT/72c/M8OHDTSAQMGeddZZZvXp18rMrr7zSTJ8+veNG4LCf4cOHdyjz3//932b06NHG7/ebMWPGHPEPX2fbPVHd3adVq1aZM8880wQCATNixAjz4IMPHrbOd955xwBm5cqVR9zmF7/4RTNo0CDj9/tNWVmZufTSSzskwm7bp0WLFplhw4aZQCBgioqKzPnnn2/WrFnTre26aX+GDx9+xN/N22+/PVmmvx0jY5w/l6RzbUOcf/znyiuvNMYcftzvvvtuc/LJJ5u0tDSTl5dnzj33XPPnP//ZmeC7obv7aUzXfr/dqjvncn89psb0Tp0lfa83fl+78renL3VnH6dPn95pfWVM16+B+ppT1xFOOnDggLniiitMdna2yc7ONldcccVh04YAybmvt2/ffsTjC5iXX37ZGGPMhx9+aD71qU+Z/Px8EwgEzMknn2wWLlxoDhw40Lc710ssY1qf1hQREREREZEBpVvP8ImIiIiIiEj/oYRPRERERERkgFLCJyIiIiIiMkAp4RMRERERERmglPCJiIiIiIgMUEr4REREREREBiglfCIiIiIiIgOUEj4REREREZEBSgmfiIiIiIjIAKWET0REREREZIBSwiciIiIiIjJA/f9X4zF/IbhZnAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", @@ -179,8 +148,23 @@ "\n", "\n", "ax = axes[0]\n", - "ax.plot(np.nanmean(diff[0], axis=(1, 2)), dataset1.nav_lev[:-1], linestyle=\"dashed\", color=\"black\", alpha=0.7, linewidth=3, label=\"truth\")\n", - "ax.plot(np.nanmean(diff[1], axis=(1, 2)), dataset2.nav_lev[:-1], color=\"purple\", alpha=0.8, linewidth=2, label=\"predictions\")\n", + "ax.plot(\n", + " np.nanmean(diff[0], axis=(1, 2)),\n", + " dataset1.nav_lev[:-1],\n", + " linestyle=\"dashed\",\n", + " color=\"black\",\n", + " alpha=0.7,\n", + " linewidth=3,\n", + " label=\"truth\",\n", + ")\n", + "ax.plot(\n", + " np.nanmean(diff[1], axis=(1, 2)),\n", + " dataset2.nav_lev[:-1],\n", + " color=\"purple\",\n", + " alpha=0.8,\n", + " linewidth=2,\n", + " label=\"predictions\",\n", + ")\n", "ax.invert_yaxis()\n", "ax.invert_xaxis()\n", "ax.yaxis.tick_right()\n", @@ -209,7 +193,7 @@ "ax.yaxis.tick_right()\n", "\n", "plt.tight_layout()\n", - "plt.show()\n" + "plt.show()" ] }, { @@ -222,7 +206,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": null, "id": "7b9e842c-f1de-4ff8-bc12-d6ef2748bd6e", "metadata": {}, "outputs": [], @@ -230,52 +214,46 @@ "new = dataset2.un.where(mask.umask.values)\n", "old = dataset1.un.where(mask.umask.values)\n", "\n", - "diff_new = np.diff(new.isel(time_counter=0), axis=0) \n", - "diff_old = np.diff(old.isel(time_counter=0), axis=0) \n", + "diff_new = np.diff(new.isel(time_counter=0), axis=0)\n", + "diff_old = np.diff(old.isel(time_counter=0), axis=0)\n", "\n", - "val = [old[0],new[0]]\n", - "diff = [diff_old,diff_new]" + "val = [old[0], new[0]]\n", + "diff = [diff_old, diff_new]" ] }, { "cell_type": "code", - "execution_count": 131, + "execution_count": null, "id": "8ef08ac1-26ea-4211-9e54-754b9ab198a4", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_1826838/909273091.py:4: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(val[0],axis=(1,2)),dataset1.nav_lev,linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "/tmp/ipykernel_1826838/909273091.py:5: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(val[1],axis=(1,2)),dataset2.nav_lev,color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(4, 5))\n", "ax = plt.gca()\n", "\n", - "ax.plot(np.nanmean(val[0],axis=(1,2)),dataset1.nav_lev,linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "ax.plot(np.nanmean(val[1],axis=(1,2)),dataset2.nav_lev,color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n", + "ax.plot(\n", + " np.nanmean(val[0], axis=(1, 2)),\n", + " dataset1.nav_lev,\n", + " linestyle=\"dashed\",\n", + " color=\"black\",\n", + " alpha=0.7,\n", + " linewidth=3,\n", + " label=\"truth\",\n", + ")\n", + "ax.plot(\n", + " np.nanmean(val[1], axis=(1, 2)),\n", + " dataset2.nav_lev,\n", + " color=\"purple\",\n", + " alpha=0.8,\n", + " linewidth=2,\n", + " label=\"predictions\",\n", + ")\n", "ax.invert_yaxis()\n", "ax.invert_xaxis()\n", "\n", - "#ax.set_xlim(left=1)\n", + "# ax.set_xlim(left=1)\n", "\n", - "ax.yaxis.set_label_position('right')\n", + "ax.yaxis.set_label_position(\"right\")\n", "ax.yaxis.tick_right()\n", "ax.legend()\n", "plt.show()" @@ -283,44 +261,38 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": null, "id": "4d08052a-4fde-4b3a-a859-58afd6d56839", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_1826838/647516894.py:5: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(diff[0],axis=(1,2)),dataset1.nav_lev[:-1],linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "/tmp/ipykernel_1826838/647516894.py:6: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(diff[1],axis=(1,2)),dataset2.nav_lev[:-1],color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXgAAAGsCAYAAADXMryfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABVc0lEQVR4nO3deXwU9f0/8NfeR5Kd3NmEBAgQwxEuASF4gCAQBRGtBYWmWCnUA5AKHl+Pgt9WQKpYLeXwKNhvVfpTwKOt4RBBMeFIIEIIIEeAXEuuPXLsPZ/fH0smbC42IWE2m/eTxz6yM/uZ2fdkw2s/+5nZGQljjIEQQkjAkYpdACGEkM5BAU8IIQGKAp4QQgIUBTwhhAQoCnhCCAlQFPCEEBKgKOAJISRAyX1tyPM8SkpKEBISAolE0pk1EULITcEYQ3V1NeLi4iCVBl5/1+eALykpQUJCQmfWQgghoigsLER8fLzYZXQ4nwM+JCQEgOcXodPpOq0gQgi5WSwWCxISEoR8CzQ+B3z9sIxOp6OAJ4QElEAddg68QSdCCCEAKOAJISRgUcATQkiAooAnhJAARQFPCCEBigKeEEICFAU8IYQEKAp4QggJUBTwhBASoCjgCSEkQFHAE0JIgKKAJ4SQNli/fj0SExOhVqsxYsQI/PDDD2KX1CKfTzZGug6Hw4Hq6mowxsDzvHCrn3a73XA6nXC5XHC73XC73cJ8nudx6623Qi6nPw1CGvvXv/6FJUuWYP369bj99tuxadMm3HvvvcjPz0fPnj3FLq8JCWOM+dLQYrGA4ziYzeYueTZJxhgMBgMAz8VL6sON53n069ev2WUqKipw7tw5MMbadZswYUKzQVlWVoaDBw/i6y+/xhXDlasFAhJIhPtgAMdxiImO8ayPZwADGM+EacYYwANutxvMzcC7eTAbQx9jH5SfKAcv5eHQOmCJsAC4uv6r65YyKSS8BBLmmSf8hAQPP/Qw+k/tj563+98fLCEdqa25Nnr0aNx6663YsGGDMG/AgAGYMWMGVq1a1Zmltkund9NcLhdMJhOCg4MhkUgglUqFm0QiQWlpKcrLy4XAre9ZNjftcrmEaafTCZ1Oh5kzZ3o9H2MMzM3w7e5v8e2ub+FyulB0qQi8mUdMQQwUNgVcSheqI6oBAEq5Eo8/9jh4Fy/cGPOE6YULF5D5YyaAqwEoPEmjMG78+NX5jvsckMvlnnCur41nKC8rR/7ufIRd/Xc9wnP5yKFzQMJLIONl0Fg00Fg0bVr+/DfnEZUURQFPug2LxeI1rVKpoFKpvOY5HA7k5OTgxRdf9Jo/efJkZGZmdnqN7dGpAb979258uPJDRF6IbAjFqz1IydV/4D1tr+1BCj3Ka+e3MH1i0QnP/GvO5yyRSGC1WgEAUkjRE95BJXfKoa5VC21/+sdPwmNWqxU8z0MikaCmugah1aHt3v6izKJmLwNWV1fX7nXeLPVvSoR0B42vVrd8+XKsWLHCa15FRQXcbjdiYmK85sfExAijA/6m0wLearViy+tb0DO3c3uBdrv9hpZvPEJVWVkJm812Q+tsTnlZOaw2KyQSic81O1VO2IPsYJ53PgGT1H9suDp99XEmYZDwEujs3h81y3uWwx5k91peppRBppRBKpNCKpdCIpVAKpdCKpPi3pfuhT5Rf0PbS0hX0vhKdY1779dqfHEQxpjfXjCkc4do+E5de4do7sW6ESqVqtkX2+lywuFwtLosL+XBpMwT4FcDW2lVgkkZkgckQyL3hLBMI4NMLYNMI0PWkSzYeBt4GQ9ezoOX8ciT5QEKQBWsgipIhZDgEESFREGr00Kj1UCj0UCtVkOv12P69OnN1nLp0iWUny2HXC5v9iaTyaBQKCCTySCXy73u++sfOyEt8eVKdZGRkZDJZE1662VlZU169f6i0wJeo9Fg1rJZ+OTPnyD6QrQnsOp7ohLvXieAhlC7prfKpAyRUZGeXmb9TSKFTCLDpYuXvIZqmgzjXP0pYRLPzsSrNykvhVwqxy19bgFzsSZDETcS8BzHISoqqtnHfFmvlJc2+6YYHBwMuanhpeKv/nPCibjyOFTXVPtUXx3qUCut9bwZSHlcDr0M9+duyDVyKDQKSBWe3rxMIcPR3KM4/fNpMKnn9eAlPCD1vE5MwoQ3osbTkABR+ig89+JzkClknk8F9TeFFN8f+B7f7f8OSq0S2lAtgkKCoNVqERTU/M/6m0bjeWNSKpU+bSshHU2pVGLEiBHYvXs3HnzwQWH+7t278cADD4hYWcs6tQc/ffp0pKWloaioCMHBwV6H7Plyi4uLQ2RkZLPrzsrKgt1uv+466nfOOhwOOBwOOJ1O3HbbbRgxYgQAgHfxcDvccNldcDvceOP1N1BdVg2Xw+XZ6erk4Xa5hXZeR55cvV9/ixsah5F3jATv9OysdTvdwv1L/70Ec5m5YZlrl73ePLkEEqmk2XFxnrXtY1L9zlcZZJBZZCjPL2+2na3MhjDL9XcAN0d5Xon/Pv3fZh+rqqqCssoT0vVvOG65G7yMb/KTl/Nwy9zCJxO5Ro7XVr0GhVYBZZASco0cyiAlFFoFTp45iW+//RYajQZSqRQ6nQ59+vTByJEjIZPJ2rUdhDT27LPPIj09HSNHjkRqairee+89XL58GU888YTYpTWr2xwm2VGuPcTy2qN7XC4XtFotOI5rdrm8vDyYTKZmjwi63hFDycnJmDp1KhjP4Ha44axzwlHjgLPOiXVr1+Hnkz9D4pJA5pJB6pY23HjPT4lbItyvny9xS8BpOcRFxzX76aKsrKzJkQW+UqlUTXZa1ausrITRaGzXeuVyOXr37t3sY2aLGVcqr3jeCByefosx1oj+v+mPpc8vbdfzkcDXnlxbv3491qxZg9LSUqSkpODtt9/GXXfd1cmVtg8FfABgjMFms8FqtaKurg51dXWw2WzCzW63C4/X37fZbEhMTMTUqVPhtrvhsrk8nziufmr59ONPkXMkR/gE4nK4PMfaO3mAh/Dpon5o7NpPHbHRsXjowYeEZetvbqcbR7OP4udTP0PqlkLmlkHqknq9MbVGoVCgV69ezT5mNBpRWVnZZH5pv1L0m9YP4RHhCA0NhU6nQ2hoKCIjIzFo0KAO+f2TrivQc40CnrTZtUNf197qP9XI5XLExsY2u+z58+dx+fJl2Gw21NbWoq6uTvhZU10Dq9mKOnMd7BY7bNU2OGuckLgkkLqliI2MxayHZsFZ5/R8iql1wGV1wVHrwJkTZ1BUUASltfkxepfCBbfCDZfS8zMkKgSPznsUmjANNOEaqDiVMOyz94e9yP4pG+pgNYKCgqBWq4V9ANfe6udrtVqv+60dgUH8S6DnGgU88WuMMdjtdlitVrjd7hb3yeTk5GD37t3I2ZmDHqd6QGlrfWesRqNBjx49mn3s2uEpXsYLN7fM7TXd3C1CH4HfP/97KIIUkKvknsNRVTLIVXLs3b8XWYezoNQqoVApoFKpoFA0/FQqlcLt2vkqlQpBQUEYOnRos/U6nU4wxqBQKOgIpjYK9FyjgCcBpaSkBJ9+8ilytuUguCoYcoccMqcMcqcccqfcM6wEz5FJen3zx/pfMVzx+cikxtRqNeLj45t9rKKiAiaTSZgWDouVMuF+c/OYlEGr0+KXj/wSMqUMcrVc+B6DXC3HoexD2L13N3gp75mnlEOqkkKh9rzJKDQKyJVyKNQK4Vb/JqLValvcQVhcXIwzZ84Ibetv104391hzX+7zV4GeaxTwJCBVVVXh9OnTMJvNMJvNMJlMMJlMsFRaYLliwfD+w3HPHffAWmWFtcoKu8UuDP38sPcHlBWXNewnuGbH9fW09smgvLwcZrO5XdujVCpbPJlVVVUVqqqq2rS++sNapQopho0cJrxhXHsrKi1C7vFc8DK+4VDY+jeextPShsNlJXIJZEoZVq1ZBaVW2fBmpJJDqpDiXME5fL79c8hVcuETi1qtBsdxwk2n0wn3Q0JCOu1IqEDPNTplIAlI4eHhGDt2bLuWDTkSgpKSElitVlitVq8d2LYaG6wWK+w1dthrGt4U4AAkbgluSbwFI6eMhLPOCbfD7dmBffUQ3KqsKtRerPX6Tsa1PyVM0uKbSGtDL+357oaE95wqRAYZagw1zbaxmC0IqQxp87rr/ed3/2l2fk1NDawGz6lE6t8geBnv2UdydV9J4/sqTgVNuAa6aB2WvrS0xe+bEG8U8IQ0MmrUqDa1Z4zB6XTCZrOBMdbiobLRx6NRWFgofCejuZvT6YTD5oDD6oCjzgGnzQmnzQkuhsOMx2d4vrNh87xh1N/f9c0unD502nM47NWzhErd0ma/5Hft9yukvBRqhRraCK2wPrfDLbxh8Hz7v4ru6xuS8L0MlwwKu+K66+XBY/uB7QiKCIImTAN1mBrqUDW0UVr0f6A/wvq077sbgYqGaAjp4mpqalBdXe15c2j0ZmG32+F0OoVb/fz6+0FBQV5nZK0/G6vb4caenXuw87874bK7PLdr3ljcTjeYk3m9iUj5q28qbgnUCjV+/atfe71x1N8KzhXg5E8nvd5opC4p5E7f+pt9+/aF2+1u2BaHE2FhYQhPDMesHbPa9LsL9FyjgCeEtAvP882+aTidzlavs3Dx4kUcO3bM6w2ptrYWJqMJ1ZXVqCmvQW1lLZwWJ2ROmddOclWtCmqr2utTQEREBHQ6HdxuNyb+cyIGDBng8zYEeq5RwBNC/MLFixfx5ZdfovRSKSouVMBqsEJZp4TSqoTKqoLSqmx2H4UEEjB4dvIqfqvAyj+v9Pk5Az3XaAyeENKpbDYbioqKUFpaipKSEsyYMQNwAJZCC8yFZuHn5ROXcfmHy5A5ZQhHuM/rd8vcsGvtMMYaMXfy3M7bkC6IAp4Q0iHsdjsKCwtx+fJlXLp0CZcLLqP4VDFqC2s9PXGbpzduetcEib3pTli32w2Zs+XDIR1qBxwaBxxaB+waOxAKRPWLQmzfWCQkJGD48OFITk7uxC3seijgCSFt4nK5cPnyZeF2seAiSk6VoPpSNZS1SqjqVFDVeYZUQlkoQhHqtXxdVR2CgoKarFcmk4HX8LApbZ4gV3vCPCQ+BLHJsUjqmYQePXogPj4e8fHxCAsLo2/uXgcFPCGkCYfDAYlEAoWi4dBFl80Fc6EZF45dwPrX1zcEeZ0SOqaDDr6NYTMtQ9yoOHAJHHTxOugSdOB6ctD10GHbl9ugVCoRFxeH2NhY6PV6ugbADaCAJ6SbcjgcKC0tFcbGS0tLUXKpBGXny1BbUouHJz+MCHkELIUWWAotqC2vFZaNvhzd6hesmITBrrXDoXXAprXBEeTpiUf3jcatU27F+PHjm11u1qy2HeZIWkcBT0gA43ke5eXlKCoqQlFREYqLi1F8sRhl58pQW1oLpdUzLl4/Pi53yBF29d+58nOoCK1odr1KpRJ2ux1MwuDQOGDX2j2BHuyArpcOcQPi0Kt3L/Ts2RO9evVCjx49qCcuAgp4QgIQ4xksBgsW/WoRYIYwJq6qU3mFeGucTqdwXxOmgS7h6nBKAgfjESOuVF1Bj4E90CvRE+Q9e/ZEQkICnS7Zj1DAE9JFMMZQUVEh9MaLiopQdLEIdwy+A7dE3wLTRRNMF00wXzLDdMkEl82FhEsJXkHdGpfCBafGKRytoh6kxoOvPggugYMy2Lv3PezxYV3qrJHdFQU8IX6KMYZLly7h5MmTOHH8BM4eOgtnoVPoiSutSijsChwLPYbCyMJm16FUKr0C3i13w6H1HKFi19jh1Dih0WsQ1TcK8b3ihZ2b9T/VanWz66Vw7xoo4AnxE263GxcuXEBeXh7yjufh3MFzkBgk0Jq10Jq1iHBHNLucw+HwmpZIJdDF6xDaOxTWUivOnTsHabgUUUlRSOiXgB49eqBHjx6IjY1tNcRJ10cBT4gfeGftO8j+bzbk5XJozVpoLBrE8DGtLuOWu+HQOOCKcmHGwhngenEI7R0KXbwOMoXnC0O3GW/DXMlccBxHx4x3QxTwhIjEZrYh79M8lGSXoHpPNWJNzV/HFgDcCjfqdHWo4+rAIhiikqIQ1zcOCQkJiI+Px7Cxw5pdLiyMTp/bnVHAEyKS/a/tx6XvLwEA1Eo1LLAIj7mULtSG1qJOVwdtXy36j+mPlJQUDBo0CLGxsdQbJz6hgCdEJNXFDdd9jeoXhZ8lP6OOqwPXn8OA2wYgJSUFKSkpLV5onJDroYAnRCRSuVT4efu7tyPidATuvPPOFq8IRUhbUcATIpL6gOddPP4+8++4LL+MfaP34fYJt2Ps2LGIiWl9Jysh10MBT4hIQnqEoOxkGVwuF1wXXYhDHNhZhj1f78H2yO2IHBmJ1AmpuP3229GjRw+xyyVdEF3RiRCR2Ew2HP3wKA5+chCG84Zm29RxdbBEWsAN4zD2nrG46667KOw7UKDnGgU8ISL77P99hn///d+QF8qhq9BBYVc0265OVwdzrBmjZo/Cr+f+GqGhoTe30AAU6LlGAU+IH3C5XDh+/DgOHDiAY7uOQXJRAl2FDkpb0zMw2oJtMA0wYfrvpmP69Ol0lsYbEOi5RgFPiJ9xu904efIkDhw4gJxdOWAXGEIqQqCq8z5LoyXSAmmqFO9ufhdyOe1Oa49AzzUKeEL8GM/zOH36NA4cOIAf/vUDwk6HQV3bcO6YsMgwTPz9RAz/zXAotM0P7ZCWBXquUcAT0kVUVVXhoy0f4egnRxF9KRpKXolevXpBKpVCG6HFnS/fiV539RK7zC4l0HONAp6QLubs2bP4YP0H6G/tD/cxN3g3DwCQKWV4/MfH6TQGbRDouUYndSaki0lISMCkeydhQP8BQrgDQHRKtIhVEX9Ee2YI6UKKioqw6tVVkOyVIFGdiJCQEABA3yl9cdfLd1HvnXihgCekizh06BDWvbwOUcejIHfIUWYpg1qrxvj/GY+BvxxI4U6aoCEaQrqAzMxMrPv9OsRmx0Lu8PTLnAonClIKKNxJiyjgCfFzmZmZWL90PWLzGy4IUhtaC/cMN5auWUrhTlpEQzSE+LEff/wRG5ZtQNypOGGeKcaE5PnJWLR4EX2LlbSKAp4QP9VsuOtNGLZoGJ586knquZProiEaQvxQTk5Ok3A36o0U7qRNKOAJ8UOb129G7OmGMXej3ojhi4ZTuJM2oYAnxA81/oK5lJfCarWC5/kWliCkKQp4QvzQY08+BkP/houAcGUczm04hzdWvQGXyyViZaQroYAnxA+NGjUKT619CqUppWAST28+pDIEzn874bJTwBPfUMAT4qdSU1Ox5J0lMAw1gEkZNBoN9E499izdA2edU+zySBdAAU+IHxsxYgReWPcCgn4ZhJ59e0IikaAkuwS7n9/dZJyekMboOHhC/FxKSgpSNqXgyokr+PI3XwIAig8Xg/EMEhkdUUNaRj14QroIR41DuB8/Jh5SGf33Ja2jvxBCuojTO04L9/s/2F/ESkhXQUM0hPg5xhg+2vgRqndWQ6VQQROuQa876dJ85Poo4Anxc2fPnsW+9/Z5rsOqVCJxRCLczA0pfQAn10F/IYT4seLiYrz5hzcRXhwOAHA4HDhkPgS5nPpm5Poo4AnxU+fPn8crT76C4G+DhYt81ITVYOKDE+l8NCL5/vvvcf/99yMuLg4SiQRffPGF1+OMMaxYsQJxcXHQaDQYP348Tp486dXGbrdj0aJFiIyMRFBQEKZPn46ioiKvNkajEenp6eA4DhzHIT09HSaTqc31UsAT4odOnDiB1558DRE/RkDu9IS7XWuHZJwEDzzwgMjVdV+1tbUYOnQo1q1b1+zja9aswdq1a7Fu3TocOXIEer0ekyZNQnV1tdBmyZIl2LFjB7Zu3YoDBw6gpqYG06ZNg9vtFtrMnj0bubm5yMjIQEZGBnJzc5Gent72gpmPzGYzA8DMZrOvixBC2iErK4v96s5fsWVhy9hz3HPsOe45tjB+Ifv9U79nJpNJ7PICSn2uFRYWMrPZLNxsNtt1lwXAduzYIUzzPM/0ej1bvXq1MM9mszGO49jGjRsZY4yZTCamUCjY1q1bhTbFxcVMKpWyjIwMxhhj+fn5DAA7ePCg0CYrK4sBYKdPn27T9lEPnhA/smfPHqx7Zh1ij8dCwnuGYWrCaqB7VIfX33wdHMeJXGFgSkhIEIZDOI7DqlWr2ryOgoICGAwGTJ48WZinUqkwbtw4ZGZmAvCc59/pdHq1iYuLQ0pKitAmKysLHMdh9OjRQpsxY8aA4zihja9oTw0hfmLHjh344o9fIO5Cw0U+LFEW9JzbE8+98BwUCoWI1QW2wsJC6HQ6YVqlUrV5HQaD5+yfMTExXvNjYmJw6dIloY1SqURYWFiTNvXLGwwGREdHN1l/dHS00MZXFPCE+IEDBw7gi//9AjEFDeFgjDViyJND8PTCpyGV0oftzqTT6bwC/kY03gHOGLvuTvHGbZpr78t6GqO/GkJEVllZifdXvu8V7uU9y3HHi3dg4aKFFO5dhF6vB4AmveyysjKhV6/X6+FwOGA0Glttc+XKlSbrLy8vb/Lp4HroL4cQETHG8O7adxF2rOEje0VCBe5fcT/mzp1Lh0N2IYmJidDr9di9e7cwz+FwYP/+/Rg7diwAz9lBFQqFV5vS0lLk5eUJbVJTU2E2m3H48GGhzaFDh2A2m4U2vqIhGkJEVFJSgvJ/lyPYGgwAsAZbkTw7GQ899JDIlZHm1NTU4Ny5c8J0QUEBcnNzER4ejp49e2LJkiVYuXIlkpKSkJSUhJUrV0Kr1WL27NkAAI7jMG/ePCxduhQREREIDw/HsmXLMHjwYNxzzz0AgAEDBiAtLQ3z58/Hpk2bAAALFizAtGnTkJyc3KZ6KeAJERErYkiRpOCK5grq7HWoG12HpxY+JXZZpAXZ2dm4++67helnn30WADB37lxs2bIFzz//PKxWK5566ikYjUaMHj0au3btQkhIiLDM22+/DblcjpkzZ8JqtWLixInYsmULZDKZ0Objjz/G4sWLhaNtpk+f3uKx962RXD2e87osFgs4joPZbO6wnRGEdGd2ix2fP/I5astqAQDKu5UYOnsohg8fLnJl3Ueg5xr14AkRyYE3Dgjh3uO2HrjvjfsgkdKYO+k4tJOVEBEYfjLg/M7zAABViArjV4yncCcdjgKeEBGYCkzC/ZRHUxAUHSReMSRgUcATIoLwfuHC/eqS6lZaEtJ+FPCEiEAWLUOdrQ6VlZX47l/f4ZNPPhG7JBKAKOAJEcHOPTtx3nQeRqMR1lIrjmcfF7skEoAo4AkRwS233AJbiE2YLs4t9jofOCEdgQKeEBEkJSXBGmIVpmVVMhQWFopYEQlEFPCEiIDjOAQlNhw5E2QKws6dO+Hj9w4J8QkFPCEi6XtrX7jlnmGZIFMQDr97GJs3b6aQJx2GAp4QkTww4wFc6dNwWtiI4ghkvpmJDz74gEKedAgKeEJEkpycjLmvz0VpUqkwL7wkHIfXHsZ7m96jkCc3jAKeEBFNmjQJv3njNyi9pSHkw0rDkP2XbGxYv4FCntwQCnhCRDZhwgT89s+/RUlyiTAvzBCG3Hdz8bd1f6OQJ+1GAU+IHxg3bhx+9+bvUDKgIeRDr4TixF9P4N133gXP8yJWR7oqCnhC/MSdd96Jp956CqUDG4ZruDIOBX8vQGVFpYiVka6KAp4QPzJ27Fg8vfZplKaUgkkYZDIZkpCEy19cFrs00gVRwBPiZ8aMGYPFby9G9ZhqxCfEQ6FU4FzGuesvSEgjFPCE+KFRo0bh3S/fRfyIeACA+bJZuPoTIb6igCfETymVSsSNjBOmS3JKWmlNSFMU8IT4sbgR1wR8NgU8aRsKeEL8WMzQGMgUMgBAaU7pdVoT4o0CnhA/JlfJET04GgBQfr4c//l//xG5ItKVyMUugBDSMrPZjGNXjqHuQh14nsehNw/hjil3gOM4sUsjXQD14AnxY0FBQThbe1b4JqvWrMWJEydErop0FRTwhPgxuVyOniN6CtOqOhUMBoOIFZGuhAKeED8nVzSMpNZ/u5UQX1DAE+LnnHancJ9JGeRy2nVGfEMBT4ifc9quCXgJg0KhELEa0pVQwBPi59wOt3CfSSngie8o4Anxcy6HS7hPAU/aggKeED/nsl8T8BIagye+o4AnxM95BTz14EkbUMAT4ue8hmhoJytpAwp4QvwYYwx2i12Ydsvd0Gg0IlZEuhIKeEL8mMVigaROIky7lC5ERESIWBHpSijgCfFjlZWVUDgahmRcKhfCwsJErIh0JRTwhPixqqoqyB0NR81oIjV0qgLiMzreihA/NnjwYEy+fTKKM4vhcrkw7lfjxC6JdCEU8IT4MZVKBbldDq1WC4lUgvsevk/skkgXQkM0hPi52rJaAIA2whPyhPiKAp4QP8a7eVirrAAAbZRW5GpIV0MBT4gfs1ZawRgDQAFP2o4CnhA/Vnm2Uriv66ETsRLSFVHAE+KnCgsLsffjvXDYHQCAmCExIldEuhoKeEL81KFDh5D9n2xcLryMCxcuIONYhtglkS6GAp4QP5Wflw9Ntee8M3alHZJgOoKGtA0FPCF+iDGGC4cvQMJ7Qr1OV4cBAwaIXBXpaijgCfFDxcXFYKVMmLaGWDFw4EARKyJdEQU8IX7o1KlT0FoaDotU9lIiKipKxIpIV0QBT4gfysvLg8biGX/nZTz6jeoHiYTG4EnbUMAT4mcYY/j5yM+QOz2niqrT1WFQyiCRqyJdEQU8IX7ms88+g/uoW5i26qy0g9VPrFq1CqNGjUJISAiio6MxY8YMnDlzxqsNYwwrVqxAXFwcNBoNxo8fj5MnT3q1sdvtWLRoESIjIxEUFITp06ejqKjIq43RaER6ejo4jgPHcUhPT4fJZGpTvRTwhPiRn376CV+/+TVCDaEAPBfZVg5UIjExUdzCCABg//79ePrpp3Hw4EHs3r0bLpcLkydPRm1trdBmzZo1WLt2LdatW4cjR45Ar9dj0qRJqK6uFtosWbIEO3bswNatW3HgwAHU1NRg2rRpcLsb3thnz56N3NxcZGRkICMjA7m5uUhPT29bwcxHZrOZAWBms9nXRQghbVBRUcEeu/8xtixsGXuOe449xz3H5tw2h/30009ilxawbjTXysrKGAC2f/9+xhhjPM8zvV7PVq9eLbSx2WyM4zi2ceNGxhhjJpOJKRQKtnXrVqFNcXExk0qlLCMjgzHGWH5+PgPADh48KLTJyspiANjp06d9ro968IT4AZfLhTf++Aa4w5xw7LtRb8QDyx7AkCFDRK4u8FksFq+b3W6//kIAzGYzACA8PBwAUFBQAIPBgMmTJwttVCoVxo0bh8zMTABATk4OnE6nV5u4uDikpKQIbbKyssBxHEaPHi20GTNmDDiOE9r4ggKeED+wefNmVH9dDaVNCQCwBdsQPzMeDz/8sMiVdQ8JCQnCWDfHcVi1atV1l2GM4dlnn8Udd9yBlJQUAIDBYAAAxMR4nzcoJiZGeMxgMECpVDa5tm7jNtHR0U2eMzo6WmjjC7qiEyEiczgcOL/jPEIqQwAAbrkbtjttWPr8Ujo08iYpLCyETtdwtk6VSnXdZRYuXIjjx4/jwIEDTR5r/Loxxq77WjZu01x7X9ZzLerBEyKyypOV6FPeBxzHAQDKBpbhhT+9gODgYJEr6z50Op3X7XoBv2jRInz11Vf47rvvEB8fL8zX6/UA0KSXXVZWJvTq9Xo9HA4HjEZjq22uXLnS5HnLy8ubfDpoDQU8ISKqq6zDt//zLcCAqKgoDP/NcDy2/DH07dtX7NJIMxhjWLhwIbZv3469e/c2ObopMTERer0eu3fvFuY5HA7s378fY8eOBQCMGDECCoXCq01paSny8vKENqmpqTCbzTh8+LDQ5tChQzCbzUIbX9AQDSEi4d08vn3pW9RV1AEA4kbFYepbU+m6q37s6aefxieffIIvv/wSISEhQk+d4zhoNBpIJBIsWbIEK1euRFJSEpKSkrBy5UpotVrMnj1baDtv3jwsXboUERERCA8Px7JlyzB48GDcc889AIABAwYgLS0N8+fPx6ZNmwAACxYswLRp05CcnOx7wb4ebkOHSRLSsQ6vP8w2jdjENo3YxP6Z9k9WV1kndkndTltzDUCzt82bNwtteJ5ny5cvZ3q9nqlUKnbXXXexEydOeK3HarWyhQsXsvDwcKbRaNi0adPY5cuXvdpUVlayOXPmsJCQEBYSEsLmzJnDjEZjm7ZPcrXo67JYLOA4Dmaz2WtnBCGkfT6a8BHsFjukMimmvTcN+qF6sUvqdgI912gMnhCR8C4eAKBL0FG4k05BY/CEiCAjIwMXii+AVTOcLzkP03smLFiwQOyySIChgCdEBBUVFTDbzFA71GAu1uSQOUI6Ag3RECKC4OBg8HLPEI2El6DGVCNyRSQQUcATIoLg4GC45Q1nDqytqm2lNSHtQwFPiAiCgoK8Ar7OVCdiNSRQUcATIoJrh2gAwGa2iVgNCVQU8ISIoPEQjaPaAR+/kkKIzyjgCRFB44CXOqWoq6NhGtKxKOAJEUFQUBB4WcMQjdQt9brsGyEdgQKeEBHY7Xbhyk0AwCQMCoVCxIpIIKKAJ0QERqMRckfD9wzdKrdwPnhCOgoFPCEiMBqNkDsbAl4bqYVUSv8dSceivyhCRFBVVeXVgw+Opqs3kY5HAU+ICBoP0YTFhrXSmpD2oYAnRATXBrxb4UZYJAU86Xh0NklCRHD77bfDEmqB0+4EC2MYMGCA2CWRAEQBT4gIknomITMkEwgB4sfEY9KkSWKXRAIQDdEQIoK6yoZvrWoiNCJWQgIZBTwhIqiraAh4baRWxEpIIKOAJ0QEZXllwv2gqCARKyGBjAKekJuM8Qynt58GAEgkEvS8o6fIFZFARTtZCbmJzGYzNr20CeoCNdRqNeJT46GL14ldFglQFPCE3ES7du3Cxf9eRLAxGCqVCpF9IsUuiQQwGqIh5CbheR67PtuFYKPntAQ1khoUuApErooEMgp4Qm6SI0eOgJ1suGqTMdaIafdPE7EiEugo4Am5Sf7z5X/AXfGcEphJGcLHhOOWW24RuSoSyCjgCbkJSkpKULC7ADKXDABgibTgvl/cB4lEcp0lCWk/CnhCboL//Oc/CCtpOKGYrZ8Nd911l4gVke6AAp6QTma323Fg2wGoa9UAAGuwFeMeHgelUilyZSTQUcAT0skyMjKgPq8Wpk1xJtx7770iVkS6Cwp4QjpRcXExPvvLZ+DKPTtX3XI3+k7ui9jYWJErI90BBTwhncTtduPt1W8j+mS0MK+8VzkefuRhEasi3QkFPCGd5PPPP0f1zmrhyk21obVInZeKlJQUkSsj3QUFPCGd4Pz58/j63a/BlXmGZngZD/5OHr95/DciV0a6Ewp4QjrBoe8OIeZMjDBt6GfAkleWQK1Wt7IUIR2LAp6QDsYYQ3heOOKj4iGTyWCJtGDKwilITk4WuzTSzdDZJAnpYKe/OI3LBy4jKCgItwy9BexhhkcffVTsskg3RAFPSAeyFFtwcO1BYXri/06kC3oQ0dAQDSEdhPEM+5bvg9PqBAD0f7A/hTsRFfXgCekAbrcbeR/nwZBrAACExIUg9fepIldFujsKeEJukMPhwO/n/h59f+qLYG0wJBIJ7v7fu6HQKsQujXRzNERDyA3Ky8uDI9cBQ4kBhlIDkh5Ogn6YXuyyCKGAJ+RGlZWVQWH39NZramuw/ex2kSsixIMCnpAbVFVVJZyOAACiekaJWA0hDSjgCblB1wa8S+lCZFSkyBUR4kEBT8gNqqyoFALeqXQiLCzsOksQcnNQwBNyg0wlJuG+S+VCRESEeMUQcg0KeEJukMVgEe67lC6Eh4eLWA0hDSjgCbkBbrcb1kqrMO1SumiIhvgNCnhCboDZbIbMLhOmXUoaoiH+gwKekBtQWVnpdYgkr+ah0+lErIiQBhTwhNyAqqoqKBwNpyTQRmkhkUhErIiQBhTwhNwAo9EIub2hB2+rsuHYsWMiVkQ604YNGzBkyBDodDrodDqkpqbim2++ER5njGHFihWIi4uDRqPB+PHjcfLkSa912O12LFq0CJGRkQgKCsL06dNRVFTk1cZoNCI9PR0cx4HjOKSnp8NkMrW5Xgp4Qm5AcHAwXCqXMK3/SY+3nnqryX9qEhji4+OxevVqZGdnIzs7GxMmTMADDzwgvN5r1qzB2rVrsW7dOhw5cgR6vR6TJk1CdXW1sI4lS5Zgx44d2Lp1Kw4cOICamhpMmzYNbrdbaDN79mzk5uYiIyMDGRkZyM3NRXp6etsLZj4ym80MADObzb4uQkjAczqd7MVnX2RP9XqKPcc9x57jnmMrYlew7PezGc/zYpdHrqMjci0sLIx98MEHjOd5ptfr2erVq4XHbDYb4ziObdy4kTHGmMlkYgqFgm3dulVoU1xczKRSKcvIyGCMMZafn88AsIMHDwptsrKyGAB2+vTpNtVGPXhCboBcLsdrq15DwoIEmGJMUKlUiNHHIGdjDr7/4/fgXbzYJRIfWCwWr5vdbr/uMm63G1u3bkVtbS1SU1NRUFAAg8GAyZMnC21UKhXGjRuHzMxMAEBOTg6cTqdXm7i4OKSkpAhtsrKywHEcRo8eLbQZM2YMOI4T2viKAp6QG6RUKvHC/7yAe/54Dya/MhlSqee/1ZmvzuCbxd/AXn39sCDiSkhIEMa7OY7DqlWrWmx74sQJBAcHQ6VS4YknnsCOHTswcOBAGAyei73ExMR4tY+JiREeMxgMUCqVTb4r0bhNdHR0k+eNjo4W2viKLvhBSAeQSCSYOXMmACAuOQ77VuyD2+FG8eFifDXvK6T9JQ3BscF0hI2fKiws9Dq8VaVStdg2OTkZubm5MJlM2LZtG+bOnYv9+/cLjzd+jRlj133dG7dprr0v62mMevCEdLC+k/ti6oapUHNqAIDxghHbfrUNLzz+AvLy8kSujjSn/qiY+ltrAa9UKtGvXz+MHDkSq1atwtChQ/HOO+9Ar/dc5KVxL7usrEzo1ev1ejgcDhiNxlbbXLlypcnzlpeXN/l0cD0U8IR0Av1QPR7Y8gC4nhwYY7iQfwHsS4Y3nngD3333ndjlkQ7EGIPdbkdiYiL0ej12794tPOZwOLB//36MHTsWADBixAgoFAqvNqWlpcjLyxPapKamwmw24/Dhw0KbQ4cOwWw2C218RUM0hHQSLoHD9L9Px1tT3oLNZoMEEsTmxWLLs1tQsqwEs2fPpiGbLuall17Cvffei4SEBFRXV2Pr1q3Yt28fMjIyIJFIsGTJEqxcuRJJSUlISkrCypUrodVqMXv2bAAAx3GYN28eli5dioiICISHh2PZsmUYPHgw7rnnHgDAgAEDkJaWhvnz52PTpk0AgAULFmDatGlITk5uU70U8IR0IplWBnYvg9loBlfGAQBiCmKw70/7YCg1YPEzi6FQ0MW5u4orV64gPT0dpaWl4DgOQ4YMQUZGBiZNmgQAeP7552G1WvHUU0/BaDRi9OjR2LVrF0JCQoR1vP3225DL5Zg5cyasVismTpyILVu2QCZrOKfRxx9/jMWLFwtH20yfPh3r1q1rc70SxhjzpaHFYgHHcTCbzXSuDULagOd5fPjhh8j8ayaiLjdczq8mvAbcQxxeWv6SVwCQmyfQc43G4AnpZFKpFPPnz8cvVv4CJcklYBJPnyq4Khi1n9Zi+QvL4XK5rrMWQtqOAp6Qm2TatGl4Zt0zMIwwwC33fC1dXatG9d7qNn+BhRBfUMATchONGjUKr733GkzjTOBlnm+5hpaF4qvPvhK5MhKIKOAJucn69OmDeUvnwRRjAgBIeAkqDlTgzJkz4hZGAg4FPCEiGD16NDCoYTqsJAxffUm9eNKxKOAJEYFMJkParDTUhNUAABR2BX768idUVVWJXBkJJBTwhIhkypQpqO7dcJ7w0KJQr4tHEHKjKOAJEUlISAhue+g22DWes01qzVoc+PIAfPxqCiHXRQFPiIimT58OY5wRCoUCkZGReOiWh+j0BaTDUMATIqJevXph2XvLkDQoCaGhobi89zKsRqvYZZEAQQFPiMiGjBiC/jP6AwDcDjdO7zgtckUkUFDAE+IHBs0cBInUMzST/3k+XeqPdAgKeEL8QEhcCHrd1QsAUFtWi4K9BSJXRAIBBTwhfmLQzIZvPl0+cFnESkigoIAnxE+UmEpgs9ngcDhQZ62D0+kUuyTSxdEFPwjxExv/uhERRREAgJ/+/RN6zOmBYcOGiVsU6dKoB0+In3DUOoT7brkbGo1GxGpIIKCAJ8QP8DwPl63hoh+8jKeAJzeMAp4QP2Cz2SBzNVyTk5fxUKvVIlZEAgEFPCF+wGq1Qupu+O9IPXjSESjgCfEDNpsNUp4CnnQsCnhC/EBdXZ1XD16ilEAup4PcyI2hgCfEDzQeolFqlSJWQwIFBTwhfqBJwAdTwJMbRwFPiB+w2WxeAa8KVolYDQkUFPCE+AGLxQKZs+EwSVUIBTy5cRTwhPiBS5cuQVXnCXW33A19L73IFZFAQAFPiB+4ePoi5A7PUTP2IDv69OkjckUkEFDAEyIyxhiunLoiTNuCbOjdu7d4BZGAQQFPiMiMRiNCHCHCtD3ITgFPOgR9k4IQkYWHh2PWpFnIr8mHw+HAqMdGITQ0VOyySACgHjwhfqDqXBVkMhm0Wi1mPD4DEolE7JJIAKCAJ0RkjGeoOlcFANAl6KDQKESuiAQKCnhCRGYuNMPtcAMAwvuFi1wNCSQU8ISIrOpslXA/PIkCnnQcCnhCRFY/PAMAEUkRIlZCAg0dRUOIiHJzc/HjVz/CXeOGSqVCaN9QsUsiAYR68ISIKDs7GwVHC2AwGFBQVID3PnlP7JJIAKGAJ0RE1dXVwrVYXUoXIqMiRa6IBBIKeEJEVFtbKwS8W+aGVqsVuSISSCjgCRGR1WIV7tN1WElHo4AnRES2aptwn5fz1IMnHYoCnhAR2SzXBLyMAp50LAp4QkRkr7EL9yngSUejgCdERI4ah3DfLaedrKRjUcATIhKe5+GqczVMUw+edDAKeEJEYrVaIXU3/Beko2hIR6OAJ0QkdXV1TQKeevCkI1HAEyKSmpoaSF0N/wXdcjf14EmHooAnRASMMaz64yoEmYOEeYogBV3JiXQoCnhCRFBTWoOU8ykIMnkCnkkYQhNCxS2KBBwKeEJusuLDxdj+q+1Q16qhC9GBl/EwDDIg/cl0sUsjAYbOB0/ITcIYw/F/Hsfhvx4G4xkAoO+IvjjX5xzmzZ+HAQMGiFwhCTTUgyekk7lcLrhsLux9ZS8OvXNICPeed/TEQ//3EF5d+yqFexe0atUqSCQSLFmyRJjHGMOKFSsQFxcHjUaD8ePH4+TJk17L2e12LFq0CJGRkQgKCsL06dNRVFTk1cZoNCI9PR0cx4HjOKSnp8NkMrW5Rgp4QjpRQUEBFs5diI8e/Ajnd54X5t/621sxZe0UqEJUIlZH2uvIkSN47733MGTIEK/5a9aswdq1a7Fu3TocOXIEer0ekyZNQnV1tdBmyZIl2LFjB7Zu3YoDBw6gpqYG06ZNg9vtFtrMnj0bubm5yMjIQEZGBnJzc5Ge3o4hPOYjs9nMADCz2ezrIoR0a/v372dzxs9hz0Y8y14Mf5H9bejf2N/v/Du7sPeC2KWRq9qTa9XV1SwpKYnt3r2bjRs3jj3zzDOMMcZ4nmd6vZ6tXr1aaGuz2RjHcWzjxo2MMcZMJhNTKBRs69atQpvi4mImlUpZRkYGY4yx/Px8BoAdPHhQaJOVlcUAsNOnT7dp+6gHT0gHc7vd+Pvf/44Pn/kQccfiIHPJ4Ha7UVxTjPs23YfEuxPFLpE0YrFYvG52u73Ftk8//TSmTp2Ke+65x2t+QYHn0ouTJ08W5qlUKowbNw6ZmZkAgJycHDidTq82cXFxSElJEdpkZWWB4ziMHj1aaDNmzBhwHCe08RXtZCWkA1VXV2PNyjUo/7wcMeUxwvya8Bpo79NCHacWsTrSkoSEBK/p5cuXY8WKFU3abd26FUePHsWRI0eaPGYwGAAAMTExXvNjYmJw6dIloY1SqURYWFiTNvXLGwwGREdHN1l/dHS00MZXFPCEdJCCggKsemkV1N+roavVCfMrelZg+G+H44knn4BCoRCxQtKSwsJC6HQNr5lK1XTfSGFhIZ555hns2rULanXLb9SNv6zGGLvuF9gat2muvS/raYwCnpAO8P333+O95e8h5kSMcI1VXsbjyoArmPPKHKSlpYlcIWmNTqfzCvjm5OTkoKysDCNGjBDmud1ufP/991i3bh3OnDkDwNMDj42NFdqUlZUJvXq9Xg+HwwGj0ejViy8rK8PYsWOFNleuXGny/OXl5U0+HVwPjcETcgNcLleT8XYAcGgcqLqrCi+9/xKFe4CYOHEiTpw4gdzcXOE2cuRIzJkzB7m5uejTpw/0ej12794tLONwOLB//34hvEeMGAGFQuHVprS0FHl5eUKb1NRUmM1mHD58WGhz6NAhmM1moY2vqAdPSDtduHAB7655F7Y9NsRUNBpvn6bFmyveRHh4uIgVko4UEhKClJQUr3lBQUGIiIgQ5i9ZsgQrV65EUlISkpKSsHLlSmi1WsyePRsAwHEc5s2bh6VLlyIiIgLh4eFYtmwZBg8eLOy0HTBgANLS0jB//nxs2rQJALBgwQJMmzYNycnJbaqZAp6QNnI6nfjXv/6Fnet3IvpsNHROGm8nHs8//zysViueeuopGI1GjB49Grt27UJISIjQ5u2334ZcLsfMmTNhtVoxceJEbNmyBTKZTGjz8ccfY/HixcLRNtOnT8e6devaXI+EMcZ8aWixWMBxHMxm83XHqggJZJdOXcKfZ/wZ2isN5253y90o619G4+1dTKDnGvXgCfERYwznvjmHzDcz0YvvhXKUAwCqI6ohGy/DH178A5KSkkSukpAGFPCE+KC2rBbfv/49Cn8sBOAZS612VqMgrgBTnpqCWbNm0ZAM8TsU8IS0wGazAQAKvinAob8cgqPWITzWL60fJs2ZBKZi6NOnj1glEtIqCnhCmnH8+HGsW7UOSaVJ4Go4Yb42Uos7X7oTve7qJWJ1hPiGAp6Qa9TW1mLz3zfj8JbDiC6IRjlfDlW8Cmq1GsnTkzHm92PoDJCky6CAJ+SqnJwcbFi9AepDaugtemF+aXUpFmxYgMQ76SRhpGuhgCfdXnV1Nd5/730c/+dxRF2KgoRvON+HMdaIyKmR4AZxrayBEP9EAU+6taysLLz3xnsIzg5GdE3DGfwcagcqUyrx6LOP4t57723zSZ4I8QcU8KRbMpvN2PC3DTjzrzOIvBwJCWsI8KoeVYi9PxYvPftSs6dtJaSroIAn3U5BQQFWLFoB7hiHqNooYb5dY4dxiBHpy9Jxzz33UK+ddHkU8KTbeX/d+4jKioLU3XAy1YqECvR6sBf+8MwfEBERIWJ1hHQcCnjS7bicLq8dqVVxVRgybwieeeYZ6rWTgELngyfdziOPPQJDUsOlz8JLwlFzokbEigjpHBTwpNu59dZbMfPVmSjv6TlZGMdxiM2PRdmJMpErI6RjUcCTbun+++/HHc/cgd6TeyMqKgpupxs7n90Jc6FZ7NII6TAU8KTbmjdvHn738e8QNyoOAGAz2ZCxOAM2k03kygjpGBTwpFuTKWSYtGYSwvp4LoBsLjRj57M74bK7RK6MkBtHAU+6PVWICve+ey+0kZ4rNF05fgUZz2dg+7bt8PGCZ4T4JQp4QgAE64OR9k4aFFoFnE4nsj7Jwn//8F98/vnnYpdGSLtRwBNyVWRyJG558hYUFRXB6XQivDgcX6/+Gvv37xe7NELahQKekGtIeklQ3KdYmNaf1+P9V95HXl6eiFUR0j4U8IRcY+zYsfjFK79ARUKFMC/2ZCzWPLcGhYWFIlZGSNtRwBPSyPTp0zF64WiYozzHxEt4CaKyo7DypZVwu90iV0eI7yjgCWlEIpFg/vz5iJ8TjzquDgAgc8pgz7Xj3LlzIldHiO8o4AlphlQqRfLAZPBSXpjnCHIgJiZGxKoIaRsKeEKa4XA48M0/v0GwMRgA4FQ7MXjGYISGhopbGCFtQAFPSDP27NkD5SmlMF3ZoxK/nPlLESsipO0o4AlpxO1248v/+xJcuedC226FG4lpiejTp4/IlRHSNhTwhDTy448/gs9tGHuviqvCL2b9QsSKCGkfCnhCrsEYw7Z/bkPolVAAAC/jEX5nOAYPHixuYYS0AwU8IdcoKChAzcEa4ZJ+Jr0Jv5j9C7qUH+mSKOAJuUaCPgGjg0dDp9MBUkA5UonU1FSxyyKkXeii24Rc49T2U4ADiI6Oxsg5I9HnN32o9066LAp4Qq5yO9w48fEJYXr0gtEISwwTsSJCbgwN0RBy1bmMc6gtrwUA9B7fm8KddHkU8ITAc/TMTx/9JEwPnTtUxGoI6RgU8IQAcFldMF0yAQAikiIQM5jOOUO6Pgp4QgDINXLIg+SwWq24dOYSvv32W7FLIuSG0U5WQgDs3LkTOZdyEGQKAgAUbyvGxIkTRa6KkBtDPXhCAMTGxsIeZBemr+RfoYt7kC6PAp4QAImJibAF2YRpuVmOkpISESsi5MZRwBMCQKfTQROvEaZVNSpcuHBBxIoIuXEU8IRcFT84HkzCAADqWjUKCgpEroiQG0MBT8hVffr1gV3rGYdX1alw4Sz14EnXRgFPyFWJiYleO1qLThSJWA0hN44CnpCr+vTpA1tww45WZ6kTRqNRxIoIuTEU8IRcFRsbCz6s4UpOqloVjcOTLo0CnpCrJBIJ9IP0wrS6Rk1H0pAujQKekGv0GdAHTpUTgOdIGgp40pVRwBNyjfj4eDg0DgCA1C1FZUmlyBUR0n4U8IRcQ6fTwS1vOEWBzWxrpTUh/q3DTzbmdrvhdDo7erXEDykUCshkMrHL6FBBQUFeAV9nrhOxGkJuTIcFPGMMBoMBJpOpo1ZJuoDQ0FDo9fqAuW5pUFAQeLnnSBqpVIogRZDIFRF/smLFCrz22mte82JiYmAwGAB4cvC1117De++9B6PRiNGjR+Nvf/sbBg0aJLS32+1YtmwZPv30U1itVkycOBHr169HfHy80MZoNGLx4sX46quvAADTp0/HX//6V4SGhrap3g4L+Ppwj46OhlarDZj/8KR5jDHU1dWhrKwMgOcQw0CQlJSEBQsX4KcPPFd3mvzkZJErIv5m0KBB2LNnjzB97afYNWvWYO3atdiyZQtuueUW/OlPf8KkSZNw5swZhISEAACWLFmCr7/+Glu3bkVERASWLl2KadOmIScnR1jX7NmzUVRUhIyMDADAggULkJ6ejq+//rptxTIfmc1mBoCZzeYmj7lcLpafn88qKip8XR0JEBUVFSw/P5+5XC6xS+kwJz87yTaN2MQ2jdjETn91WuxySCdqLdeas3z5cjZ06NBmH+N5nun1erZ69Wphns1mYxzHsY0bNzLGGDOZTEyhULCtW7cKbYqLi5lUKmUZGRmMMcby8/MZAHbw4EGhTVZWFgPATp9u299jh+xkrR9z12q1HbE60oXUv+aBtN9FGaIU7juqHSJWQm4Wi8XidbPb7S22PXv2LOLi4pCYmIhHHnlEOJS2oKAABoMBkyc3fOpTqVQYN24cMjMzAQA5OTlwOp1ebeLi4pCSkiK0ycrKAsdxGD16tNBmzJgx4DhOaOOrDj2KhoZlup9AfM1VISrhvqOGAr47SEhIAMdxwm3VqlXNths9ejT+8Y9/YOfOnXj//fdhMBgwduxYVFZWCuPwMTHe1/O9dozeYDBAqVQiLCys1TbR0dFNnjs6Olpo4yu6ZB8hjSiDG3rw9uqWe3IkcBQWFkKn0wnTKpWq2Xb33nuvcH/w4MFITU1F37598dFHH2HMmDEAmnZ6GGPX7Qg1btNce1/W0xgdB09IIzRE0/3odDqvW0sB31hQUBAGDx6Ms2fPQq/3nOaicS+7rKxM6NXr9Xo4HI4mJ7Fr3ObKlStNnqu8vLzJp4ProYDvZPv27YNEIqHDR7uQwiuFqKmpgcViwcmjJ3Hu3DmxSyJ+ym6349SpU4iNjUViYiL0ej12794tPO5wOLB//36MHTsWADBixAgoFAqvNqWlpcjLyxPapKamwmw24/Dhw0KbQ4cOwWw2C2181e2HaMaPH49hw4bhL3/5i1+ti4jnsy8/g93gGZqps9ehR14P9OvXT+SqiD9YtmwZ7r//fvTs2RNlZWX405/+BIvFgrlz50IikWDJkiVYuXIlkpKSkJSUhJUrV0Kr1WL27NkAAI7jMG/ePCxduhQREREIDw/HsmXLMHjwYNxzzz0AgAEDBiAtLQ3z58/Hpk2bAHgOk5w2bRqSk5PbVG+nBrzZbG73shqNBkqlstnHLBYLGGNN5nMc1+7nawljDG63G3J5t38v7Da0Oi1sEhskTAKpS4qamhqxSyJ+oqioCI8++igqKioQFRWFMWPG4ODBg+jVqxcA4Pnnn4fVasVTTz0lfNFp165dwjHwAPD2229DLpdj5syZwhedtmzZ4nU8/ccff4zFixcLR9tMnz4d69ata3O9EtZcUjbDYrGA4ziYzWavnREAYLPZUFBQgMTERKjVamH+/fff3+aC6j3xxBOYOnVqs4/NmTMHFoulyfy2fgngsccew0cffeQ1b/PmzfjNb36DjIwMvPzyyzh+/Dh27tyJjz76CCaTCV988YXQdsmSJcjNzcW+ffuaXVdBQQEuXryIu+++G3v27MELL7yA/Px8DBs2DJs3b27zu7E/aum178o2bNiAs8vPQu6Qwy13I+GVBCxZskTsskgnaC3XAkG3HoN/5513kJqaivnz56O0tBSlpaVISEgA4HknXrVqFU6dOoUhQ4bc0LoA4OWXX8Zbb72F7OxsyOVyPP744522XeTGxMTEwKH27FyVuWQoKy4TuSJC2qdbjztwHAelUgmtVivsAT99+jQA4H//938xadKkG1rXtV5//XWMGzcOAPDiiy9i6tSpsNlsAdPrDSQxMTFwqp3A1Q+JlRfplMGka+rWPfjWjBw5skPXd+2ngPrzttSfx4X4l+joaKEHDwDVJdVwuVwiVkRI+3RqD/6f//xnu5fVaDQtPrZhw4Zmd7J2pKAg77MISqXSJs/Zlq/nKxQK4X79lxV4nm+pORGRXq/39OCvUlgVqKioaPaTGSH+rFMDvjOOagHQoTtDlEol3G73ddtFRUUhLy/Pa15ubq5XcPu6LuLfgoODIeUaPtwqbApcuXKFAp50Od1+iKZ37944dOgQLl68iIqKihZ71RMmTEB2djb+8Y9/4OzZs1i+fHmTwPd1XcS/SSQShCaECtMKu4KG00iX1O0DftmyZZDJZBg4cCCioqJw+fLlZttNmTIFr776Kp5//nmMGjUK1dXV+PWvf92udRH/F9U7CkziGZJT2BRtPskTIf6gU4+DJ4EvUF/7Dz74AHmv5EFpU4KX8Yh5MQbPPfec2GWRDkbHwRPSDUVHRws7WqVuKcoKaYiGdD0U8IQ0QzgW/qqqi1UiVkNI+1DAE9KMmJgYhMSFCMNO1nIrHA46dTDpWijgCWlOKdAP/RAVFQXAcyRNVRX14knX0q1PVUBIYzWGGhx85yAu7L4AGWSQqWTQcToUhxYjPDxc7PIIaRMKeNLtFRUVocZcg9rMWvy05Se47A2nJYjsH4m7374bJrWpxdNXE+KvKOBJt1VbW4tPP/0U+zbvQ8/inugd2Vt4TBOmwainRyF5ejIk0sC7sDjpHijgSbfDGMOePXvwybpPoM3VIs4cBxdcMCvMCAsPw6BZg3Dr/FuhCvHtupyE+CsKeNKtnD59Gu/99T1U761GVGmU12NFKMIj7z+ChCEJLSxNSNdCR9HcJL179/a6VqtEIvG6OlR7dMQ6uouqqiq89eZbWDlnJST/kiCsNEx4zKF2oGRwCYa/PBxRt0S1shZCuhbqwYuktLQUYWFh128IYMWKFfjiiy+Qm5vb7nV0V06nE19++SW+3Pglwk+FQ1/bcEZIXsajIqECfWf0xQu/e0E4Tz8hgYICvg0cDkeHHUnREaeepdPXtowxhsOHD2PzXzcDB4HYCu/wNkebIR8rx+LFi3HrrbeKVCUhnatbD9GMHz8eCxcuxMKFCxEaGoqIiAi88sorwoU9evfujT/96U947LHHwHEc5s+fDwDIzMzEXXfdBY1Gg4SEBCxevBi1tbXCesvKynD//fdDo9EgMTERH3/8cZPnbjy8UlRUhEceeQTh4eEICgrCyJEjcejQIWzZsgWvvfYafvrpJ0gkEkgkEmzZsqXZdZw4cQITJkyARqNBREQEFixYgJqaGuHxxx57DDNmzMCbb76J2NhYRERE4Omnn/a6cMn69euRlJQEtVqNmJgYPPzwwx3xq76pCgsLsfzl5dg4fyNC/hsCXUXDSaRswTYYRhtw35/vwzsfvEPhTgJap/Xgd6TvQF1lXWetvkXaCC0e/L8HfW7/0UcfYd68eTh06BCys7OxYMEC9OrVSwjzP//5z3j11VfxyiuvAPCE6JQpU/DHP/4RH374IcrLy4U3ic2bNwPwBGlhYSH27t0LpVKJxYsXt3o+8ZqaGowbNw49evTAV199Bb1ej6NHj4LnecyaNQt5eXnIyMjAnj17ADR/IZW6ujqkpaVhzJgxOHLkCMrKyvDb3/4WCxcuFN4QAOC7775DbGwsvvvuO5w7dw6zZs3CsGHDMH/+fGRnZ2Px4sX4v//7P4wdOxZVVVX44YcffP5d+gO32403FrwBxVEFouwN4+kuhQvlieUYNWcU5j42t9MuRkOIP+m0gK+rrENtWe31G4osISEBb7/9NiQSCZKTk3HixAm8/fbbQsBPmDABy5YtE9r/+te/xuzZs7FkyRIAQFJSEt59912MGzcOGzZswOXLl/HNN9/g4MGDGD16NADgww8/xIABA1qs4ZNPPkF5eTmOHDkifFuyX79+wuPBwcGQy+WtDsl8/PHHsFqt+Mc//iFcbnDdunW4//778cYbbyAmJgYAEBYWhnXr1kEmk6F///6YOnUqvv32W8yfPx+XL19GUFAQpk2bhpCQEPTq1QvDhw9vx29VHJU/VyLzzUz0KegDg91z/nYmYTDGGRE+KRzLFy73+r0SEug6LeC1EdrOWnWHPu+YMWOEa6QCQGpqKt566y3h0nuNL76dk5ODc+fOeQ27MMbA8zwKCgrw888/Qy6Xey3Xv39/hIaGtlhDbm4uhg8ffkNfhT916hSGDh3qdS3Z22+/HTzP48yZM0LADxo0CDKZTGgTGxuLEydOAAAmTZqEXr16oU+fPkhLS0NaWhoefPBBaLXivJa+splsyN6YjVPbT4HxDMHBwdBoNChXl8N+qx3pi9Ixbtw4r9eZkO6g0wK+LcMk/qzxxbd5nsfvfvc7LF68uEnbnj174syZMwDQpjBp7QLjvmKMtfic186/9hqy9Y/VX1owJCQER48exb59+7Br1y784Q9/wIoVK3DkyJFW36DEtmvZLhhyG664pIvXYfBzg/Gz7WfMmjUroC5EQkhbdOudrABw8ODBJtNJSUlevdxr3XrrrTh58iT69evX5KZUKjFgwAC4XC5kZ2cLy5w5cwYmk6nFGoYMGYLc3NwWz1boy8W8Bw4ciNzcXK+dvT/++COkUiluueWWVpe9llwuxz333IM1a9bg+PHjuHjxIvbu3evz8mK4db5nR6lCq8Bti27DL//fL5E6KxVz586lcCfdWrcP+MLCQjz77LM4c+YMPv30U/z1r3/FM88802L7F154AVlZWXj66aeRm5uLs2fP4quvvsKiRYsAAMnJyUhLS8P8+fNx6NAh5OTk4Le//W2rvfRHH30Uer0eM2bMwI8//ogLFy5g27ZtyMrKAuA5mqegoAC5ubmoqKiA3W5vso45c+ZArVZj7ty5yMvLw3fffYdFixYhPT1dGJ65nn//+9949913kZubi0uXLuEf//gHeJ5HcnKyT8uLJX50PMY+Nxazts/CsLnDIFM2/+ZMSHfT7QP+17/+NaxWK2677TY8/fTTWLRoERYsWNBi+yFDhmD//v04e/Ys7rzzTgwfPhyvvvqq15dkNm/ejISEBIwbNw4PPfQQFixYgOjo6BbXqVQqsWvXLkRHR+O+++7D4MGDsXr1auFTxC9+8QukpaXh7rvvRlRUFD799NMm69Bqtdi5cyeqqqowatQoPPzww5g4cSLWrVvn8+8iNDQU27dvx4QJEzBgwABs3LgRn376KQYNGuTzOsSSMisF2kj/3ldAyM3WrS+6PX78eAwbNszrFAKkbbrqa08IQBfdJoQQ0kVRwBNCSIDq1uei2bdvn9glEEJIp6EePCGEBKgODfj6L8yQ7oNec0L8V4cM0SiVSkilUpSUlCAqKgpKpZK+Fh7gGGNwOBwoLy+HVCqlC1IT4oc6JOClUikSExNRWlqKkpKSjlgl6SK0Wi169uwJqZRG+wjxNx22k1WpVKJnz55wuVzX/Vo9CQwymQxyuZw+rRHipzr0KBqJRAKFQtHkhFaEEEJuPvpcTQghAYoCnhBCAhQFPCGEBCifx+Drz0lmsVg6rRhCCLmZ6vPMx3Mudjk+B3x1dTUAzzVMCSEkkFRXVwfkhdh9Pl0wz/MoKSlBSEjIDR8WZ7FYkJCQgMLCwoA8Ree1aFsDE21rYGCMobq6GnFxcQH5XQ6fe/BSqRTx8fEd+uQ6nS7g/mBaQtsamGhbu75A7LnXC7y3LEIIIQAo4AkhJGCJEvAqlQrLly+HSqUS4+lvKtrWwETbSroCn3eyEkII6VpoiIYQQgIUBTwhhAQoCnhCCAlQFPCEEBKgKOAJISRAtSvg169fj8TERKjVaowYMQI//PBDq+3379+PESNGQK1Wo0+fPti4cWOTNtu2bcPAgQOhUqkwcOBA7Nixw+vxDRs2YMiQIcK36VJTU/HNN9+0p/w2EWNbV61ahVGjRiEkJATR0dGYMWMGzpw506Hb1RwxtvX777/H/fffj7i4OEgkEnzxxRcduUktEmNb2/O8Hc1oNCI9PR0cx4HjOKSnp8NkMrW6DGMMK1asQFxcHDQaDcaPH4+TJ096tbHb7Vi0aBEiIyMRFBSE6dOno6ioyKvN66+/jrFjx0Kr1SI0NLSDt4w0i7XR1q1bmUKhYO+//z7Lz89nzzzzDAsKCmKXLl1qtv2FCxeYVqtlzzzzDMvPz2fvv/8+UygU7PPPPxfaZGZmMplMxlauXMlOnTrFVq5cyeRyOTt48KDQ5quvvmL/+c9/2JkzZ9iZM2fYSy+9xBQKBcvLy2vrJvj9tk6ZMoVt3ryZ5eXlsdzcXDZ16lTWs2dPVlNTE3Db+t///pe9/PLLbNu2bQwA27FjR6dtYz2xtrWtz9sZ0tLSWEpKCsvMzGSZmZksJSWFTZs2rdVlVq9ezUJCQti2bdvYiRMn2KxZs1hsbCyzWCxCmyeeeIL16NGD7d69mx09epTdfffdbOjQoczlcglt/vCHP7C1a9eyZ599lnEc11mbSK7R5oC/7bbb2BNPPOE1r3///uzFF19stv3zzz/P+vfv7zXvd7/7HRszZowwPXPmTJaWlubVZsqUKeyRRx5ptZawsDD2wQcftKX8NvGXbS0rK2MA2P79+9u6CT7zh229WQEv1ra29Xk7Wn5+PgPg9aaTlZXFALDTp083uwzP80yv17PVq1cL82w2G+M4jm3cuJExxpjJZGIKhYJt3bpVaFNcXMykUinLyMhoss7NmzdTwN8kbRqicTgcyMnJweTJk73mT548GZmZmc0uk5WV1aT9lClTkJ2dDafT2WqbltbpdruxdetW1NbWIjU1tS2b4DN/2VYAMJvNAIDw8PA2b4cv/GlbO5tY29qe5+1oWVlZ4DgOo0ePFuaNGTMGHMe1WENBQQEMBoNX3SqVCuPGjROWycnJgdPp9GoTFxeHlJQUUV9r0sYx+IqKCrjdbsTExHjNj4mJgcFgaHYZg8HQbHuXy4WKiopW2zRe54kTJxAcHAyVSoUnnngCO3bswMCBA9uyCT4Te1vrMcbw7LPP4o477kBKSkp7N6dV/rKtN4NY29qe5+1oBoMB0dHRTeZHR0e3uu0AWq3bYDBAqVQiLCysxTZEHO3aydr4fPCMsVbPEd9c+8bzfVlncnIycnNzcfDgQTz55JOYO3cu8vPz27MJPhNrW+stXLgQx48fx6efftqmuttD7G29mcTa1s74faxYsQISiaTVW3Z2drPP72sN7anbX17r7szn88EDQGRkJGQyWZN35bKysibv8PX0en2z7eVyOSIiIlpt03idSqUS/fr1AwCMHDkSR44cwTvvvINNmza1ZTN8Iva2AsCiRYvw1Vdf4fvvv+/wc/Ffyx+29WYRa1vb87y+WrhwIR555JFW2/Tu3RvHjx/HlStXmjxWXl7e6rYDnl56bGysMP/auvV6PRwOB4xGo1cvvqysDGPHjm3z9pCO06YevFKpxIgRI7B7926v+bt3727xhUxNTW3SfteuXRg5ciQUCkWrba73x8EYg91ub8sm+EzMbWWMYeHChdi+fTv27t2LxMTEjtikFvnb69qZxNrW9jyvryIjI9G/f/9Wb2q1GqmpqTCbzTh8+LCw7KFDh2A2m1usITExEXq93qtuh8OB/fv3C8uMGDECCoXCq01paSny8vIo4MXW1r2y9Yd6ffjhhyw/P58tWbKEBQUFsYsXLzLGGHvxxRdZenq60L7+ELPf//73LD8/n3344YdNDjH78ccfmUwmY6tXr2anTp1iq1evbnKI2f/8z/+w77//nhUUFLDjx4+zl156iUmlUrZr1662boLfb+uTTz7JOI5j+/btY6WlpcKtrq4u4La1urqaHTt2jB07dowBYGvXrmXHjh3r1EMHxdrW6z3vzZCWlsaGDBnCsrKyWFZWFhs8eHCTwySTk5PZ9u3bhenVq1czjuPY9u3b2YkTJ9ijjz7a7GGS8fHxbM+ePezo0aNswoQJTQ6TvHTpEjt27Bh77bXXWHBwsPC6V1dXd/6Gd1NtDnjGGPvb3/7GevXqxZRKJbv11lu9Dt+bO3cuGzdunFf7ffv2seHDhzOlUsl69+7NNmzY0GSdn332GUtOTmYKhYL179+fbdu2zevxxx9/XHjOqKgoNnHixE4N93pibCuAZm+bN2/ujE0UiLGt3333XbPbOnfu3M7YRIEY23q9570ZKisr2Zw5c1hISAgLCQlhc+bMYUaj0atN4781nufZ8uXLmV6vZyqVit11113sxIkTXstYrVa2cOFCFh4ezjQaDZs2bRq7fPmyV5u5c+c2+1p/9913nbS1hM4HTwghAYrORUMIIQGKAp4QQgIUBTwhhAQoCnhCCAlQFPCEEBKgKOAJISRAUcATQkiAooAnhJAARQFPCCEBigKeEEICFAU8IYQEqP8PWfR2AK1hAzgAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(4, 5))\n", "ax = plt.gca()\n", "\n", - "#l = len(rhop_new[:,j,i])\n", - "ax.plot(np.nanmean(diff[0],axis=(1,2)),dataset1.nav_lev[:-1],linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "ax.plot(np.nanmean(diff[1],axis=(1,2)),dataset2.nav_lev[:-1],color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n", + "# l = len(rhop_new[:,j,i])\n", + "ax.plot(\n", + " np.nanmean(diff[0], axis=(1, 2)),\n", + " dataset1.nav_lev[:-1],\n", + " linestyle=\"dashed\",\n", + " color=\"black\",\n", + " alpha=0.7,\n", + " linewidth=3,\n", + " label=\"truth\",\n", + ")\n", + "ax.plot(\n", + " np.nanmean(diff[1], axis=(1, 2)),\n", + " dataset2.nav_lev[:-1],\n", + " color=\"purple\",\n", + " alpha=0.8,\n", + " linewidth=2,\n", + " label=\"predictions\",\n", + ")\n", "ax.invert_yaxis()\n", "ax.invert_xaxis()\n", "\n", - "#ax.set_xlim(left=1)\n", + "# ax.set_xlim(left=1)\n", "\n", - "ax.yaxis.set_label_position('right')\n", + "ax.yaxis.set_label_position(\"right\")\n", "ax.yaxis.tick_right()\n", "ax.legend()\n", "plt.show()" @@ -336,7 +308,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": null, "id": "bc767e27", "metadata": {}, "outputs": [], @@ -344,52 +316,46 @@ "new = dataset2.vn.where(mask.vmask.values)\n", "old = dataset1.vn.where(mask.vmask.values)\n", "\n", - "diff_new = np.diff(new.isel(time_counter=0), axis=0) \n", - "diff_old = np.diff(old.isel(time_counter=0), axis=0) \n", + "diff_new = np.diff(new.isel(time_counter=0), axis=0)\n", + "diff_old = np.diff(old.isel(time_counter=0), axis=0)\n", "\n", - "val = [old[0],new[0]]\n", - "diff = [diff_old,diff_new]" + "val = [old[0], new[0]]\n", + "diff = [diff_old, diff_new]" ] }, { "cell_type": "code", - "execution_count": 134, + "execution_count": null, "id": "fa946b44-fed9-492f-ad37-447e16ff7c94", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_1826838/909273091.py:4: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(val[0],axis=(1,2)),dataset1.nav_lev,linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "/tmp/ipykernel_1826838/909273091.py:5: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(val[1],axis=(1,2)),dataset2.nav_lev,color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(4, 5))\n", "ax = plt.gca()\n", "\n", - "ax.plot(np.nanmean(val[0],axis=(1,2)),dataset1.nav_lev,linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "ax.plot(np.nanmean(val[1],axis=(1,2)),dataset2.nav_lev,color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n", + "ax.plot(\n", + " np.nanmean(val[0], axis=(1, 2)),\n", + " dataset1.nav_lev,\n", + " linestyle=\"dashed\",\n", + " color=\"black\",\n", + " alpha=0.7,\n", + " linewidth=3,\n", + " label=\"truth\",\n", + ")\n", + "ax.plot(\n", + " np.nanmean(val[1], axis=(1, 2)),\n", + " dataset2.nav_lev,\n", + " color=\"purple\",\n", + " alpha=0.8,\n", + " linewidth=2,\n", + " label=\"predictions\",\n", + ")\n", "ax.invert_yaxis()\n", "ax.invert_xaxis()\n", "\n", - "#ax.set_xlim(left=1)\n", + "# ax.set_xlim(left=1)\n", "\n", - "ax.yaxis.set_label_position('right')\n", + "ax.yaxis.set_label_position(\"right\")\n", "ax.yaxis.tick_right()\n", "ax.legend()\n", "plt.show()" @@ -397,44 +363,38 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": null, "id": "66f6bfcf", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_1826838/647516894.py:5: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(diff[0],axis=(1,2)),dataset1.nav_lev[:-1],linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "/tmp/ipykernel_1826838/647516894.py:6: RuntimeWarning: Mean of empty slice\n", - " ax.plot(np.nanmean(diff[1],axis=(1,2)),dataset2.nav_lev[:-1],color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(4, 5))\n", "ax = plt.gca()\n", "\n", - "#l = len(rhop_new[:,j,i])\n", - "ax.plot(np.nanmean(diff[0],axis=(1,2)),dataset1.nav_lev[:-1],linestyle=\"dashed\",color=\"black\",alpha=0.7,linewidth=3,label=\"truth\")\n", - "ax.plot(np.nanmean(diff[1],axis=(1,2)),dataset2.nav_lev[:-1],color=\"purple\",alpha=0.8,linewidth=2,label=\"predictions\")\n", + "# l = len(rhop_new[:,j,i])\n", + "ax.plot(\n", + " np.nanmean(diff[0], axis=(1, 2)),\n", + " dataset1.nav_lev[:-1],\n", + " linestyle=\"dashed\",\n", + " color=\"black\",\n", + " alpha=0.7,\n", + " linewidth=3,\n", + " label=\"truth\",\n", + ")\n", + "ax.plot(\n", + " np.nanmean(diff[1], axis=(1, 2)),\n", + " dataset2.nav_lev[:-1],\n", + " color=\"purple\",\n", + " alpha=0.8,\n", + " linewidth=2,\n", + " label=\"predictions\",\n", + ")\n", "ax.invert_yaxis()\n", "ax.invert_xaxis()\n", "\n", - "#ax.set_xlim(left=1)\n", + "# ax.set_xlim(left=1)\n", "\n", - "ax.yaxis.set_label_position('right')\n", + "ax.yaxis.set_label_position(\"right\")\n", "ax.yaxis.tick_right()\n", "ax.legend()\n", "plt.show()" @@ -465,7 +425,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.9.12" } }, "nbformat": 4, diff --git a/README.md b/README.md index aa68357..24b164d 100644 --- a/README.md +++ b/README.md @@ -3,58 +3,58 @@ change update_rho -> thetao_new => Restart["tn"] # EMULATOR FRO NEMO MODEL ![img1](img/emulator.png) -Add retstart and prepare +Add retstart and prepare # Jumper.ipynb -### *Prepare and forecast simulations* +### *Prepare and forecast simulations* -The objective is to implement a Gaussian process forecast to forecast yearly simulations of NEMO coupled climate model. For this we need simulations files of the sea surface height (zos or ssh), the salinity (so) and temperature (thetao). +The objective is to implement a Gaussian process forecast to forecast yearly simulations of NEMO coupled climate model. For this we need simulations files of the sea surface height (zos or ssh), the salinity (so) and temperature (thetao). We apply PCA on each simulation to transform those features to time series. And we observe the trend in the first component. ![img1](img/jumper1.png) We forecast each component with a Gaussian process with the following kernel. -- Long term trend : 0.1*DotProduct(sigma_0=0.0) +- Long term trend : 0.1*DotProduct(sigma_0=0.0) - Periodic patterns : 10 * ExpSineSquared(length_scale=5/45, periodicity=5/45)#0.5**2*RationalQuadratic(length_scale=5.0, alpha=1.0) + 10 * ExpSineSquared(length_scale=5.0) - White noise : 2*WhiteKernel(noise_level=1) - + ![img2](img/jumper3.png) -And we evaluate the RMSE +And we evaluate the RMSE ![img2](img/jumper2.png) # Restart.ipynb -### *Update of restart files for NEMO* +### *Update of restart files for NEMO* The objective is to update the last restart file to initialize the jump. For this we need the 340 restarts files of the last simulated year. We also need the predictions of the sea surface height (zos or ssh), the salinity (so) and temperature (thetao). We also need the Mask dataset of the corresponding simulation where several informations are needed. ![img5](img/img3.png) -### 1 - Predicted features -- zos : Predicted sea surface height (ssh) - grid T - t,y,x -- so : Predicted salinity - grid T - t,z,y,x +### 1 - Predicted features +- zos : Predicted sea surface height (ssh) - grid T - t,y,x +- so : Predicted salinity - grid T - t,z,y,x - thetao : Predicted temperature - grid T - t,z,y,x -### 2 - Maskdataset +### 2 - Maskdataset **The Maskdataset contains mask on all grids, vectors and constants** - -- dimensions t:1 y:331 x:360 z:75 -- umask : continent mask for u grid (continent : 0, sea : 1) + +- dimensions t:1 y:331 x:360 z:75 +- umask : continent mask for u grid (continent : 0, sea : 1) - vmask : continent mask for v grid (continent : 0, sea : 1) - e3t_0 : initial thickness of cell on z axis on grid T (e:thickness, i:direction, t:grid, 0:initial state / ref) = e3t_ini restart -- e2t : thickness of cell on y axis on grid T +- e2t : thickness of cell on y axis on grid T - e1t : thickness of cell on y axis on grid T -- ff_f : corriolis force +- ff_f : corriolis force ### 3 - Necessary features to update restart -This is the list of features from the source restart file we need to exploit to update restart. +This is the list of features from the source restart file we need to exploit to update restart. - e3t : e3t_ini*(1+tmask4D*np.expand_dims(np.tile(ssh*ssmask/(bathy+(1-ssmask)),(75,1,1)),axis=0)) - deptht : depth of the z axis - grid T @@ -62,11 +62,11 @@ This is the list of features from the source restart file we need to exploit to ### 4 - Restart file update **The restart files contains all physical and dynamical features of the simulation** - -There is a total of 340 restart file per year. Each file contains a slice of x and y dimensions. Each files contains 58 data variables which 15 are updates using the predictions - -**NB** : difference between now (n) and before (b) arrays : they represent the same states, in practice the restart file save two successive states. In our code we set the two to the same state to use euler forward for the restart. +There is a total of 340 restart file per year. Each file contains a slice of x and y dimensions. Each files contains 58 data variables which 15 are updates using the predictions + + +**NB** : difference between now (n) and before (b) arrays : they represent the same states, in practice the restart file save two successive states. In our code we set the two to the same state to use euler forward for the restart. - ssh(n/b) : sea surface height => last prediction of zos - s(n/b) : sea salinity => last prediction of so @@ -95,9 +95,9 @@ F # main -### *Prepare and forecast simulations and initialize restarts files with one command line* +### *Prepare and forecast simulations and initialize restarts files with one command line* Prepare, forecats and predict -NB : En amont code de Guillaume pour obtenir des moyennes annuelles +NB : En amont code de Guillaume pour obtenir des moyennes annuelles #python main.py --ye True --start 25 --end 65 --comp 0.9 --steps 30 --path /scratchu/mtissot/SIMUp6Y diff --git a/lib/densite.py b/lib/density.py similarity index 53% rename from lib/densite.py rename to lib/density.py index 8558edb..19cd984 100644 --- a/lib/densite.py +++ b/lib/density.py @@ -1,52 +1,49 @@ #!/usr/bin/env python -#======================================================================= +# ======================================================================= # General Documentation -"""Equation of state of Sea-water and related utilities. -""" +"""Equation of state of Sea-water and related utilities.""" -#----------------------------------------------------------------------- +# ----------------------------------------------------------------------- # Additional Documentation # # Modification History: # - Oct 2007: Original by Julien Le Sommer, LEGI/CNRS -# - Jun 2008: Include pressure related utilities, JLS. +# - Jun 2008: Include pressure related utilities, JLS. # - Jan 2009: Include potential_energy_anomaly, JLS # - Feb 2009: improve dosctrings, JLS # # Notes: # - Written for Python 2.3, tested with Python 2.4 and Python 2.5 # -# Copyright (c) 2007, 2008,2009 by Julien Le Sommer. -# For licensing, distribution conditions, contact information, -# and additional documentation see the URL +# Copyright (c) 2007, 2008,2009 by Julien Le Sommer. +# For licensing, distribution conditions, contact information, +# and additional documentation see the URL # http://www.legi.hmg.inpg.fr/~lesommer/PyDom/doc/. -#======================================================================= +# ======================================================================= +# ---------------- Module General Import and Declarations --------------- -#---------------- Module General Import and Declarations --------------- - -#- Set module version to package version: - -#import package_version -#__version__ = package_version.version -#__author__ = package_version.author -#__date__ = package_version.date -#del package_version - +# - Set module version to package version: +# import package_version +# __version__ = package_version.version +# __author__ = package_version.author +# __date__ = package_version.date +# del package_version import numpy as N import numpy as npy import math as m -#from PyDom.__param__ import * +# from PyDom.__param__ import * -#---------------- Core Functions --------------------------------------- +# ---------------- Core Functions --------------------------------------- rau0 = 10e3 # We added rau0 constant -def insitu(theta0,S,Z): + +def insitu(theta0, S, Z): """In-situ density (kg/m**3) Compute in-situ density from `insitu_anom` with @@ -54,19 +51,20 @@ def insitu(theta0,S,Z): Parameters ---------- - theta0 : numpy.array + theta0 : numpy.array potential temperature - S : numpy.array + S : numpy.array salinity Z : numpy.array pressure (dB) or depth (m) """ - rho = rau0 * insitu_anom(theta0,S,Z) + rau0 + rho = rau0 * insitu_anom(theta0, S, Z) + rau0 # return rho -def sigma_n_old(theta0,S,n): + +def sigma_n_old(theta0, S, n): """Potential density referenced to pressure n*1000dB (kg/m**3) Parameters @@ -79,17 +77,17 @@ def sigma_n_old(theta0,S,n): reference pressure / 1000 dB """ - if n==0: - return sig0(theta0,S) + if n == 0: + return sig0(theta0, S) else: - theta_n=theta0_2_theta_n(theta0,S,n) - dep=N.zeros(theta0.shape)+n*1000 - sig_n=insitu(theta_n,S,dep)-1000 - # - return sig_n + theta_n = theta0_2_theta_n(theta0, S, n) + dep = N.zeros(theta0.shape) + n * 1000 + sig_n = insitu(theta_n, S, dep) - 1000 + # + return sig_n -def sigma_n(theta0,S,n): +def sigma_n(theta0, S, n): """Potential density referenced to pressure n*1000dB (kg/m**3) Parameters @@ -101,77 +99,65 @@ def sigma_n(theta0,S,n): n : int reference pressure / 1000 dB - Method + Method ------ A.-M. Treguier """ # - dpr4=4.8314e-4 - dpd=-2.042967e-2 - dprau0 = 1000. - pref = n * 1000. + dpr4 = 4.8314e-4 + dpd = -2.042967e-2 + dprau0 = 1000.0 + pref = n * 1000.0 dlref = pref - #sigmai = 0.*theta0 + # sigmai = 0.*theta0 dlrs = N.sqrt(N.abs(S)) dlt = theta0 dls = S # Compute the volumic mass of pure water at atmospheric pressure. - dlr1=((((6.536332e-9*dlt-1.120083e-6)\ - *dlt+1.001685e-4)\ - *dlt-9.095290e-3)\ - *dlt+6.793952e-2)\ - *dlt+999.842594e0 + dlr1 = ( + (((6.536332e-9 * dlt - 1.120083e-6) * dlt + 1.001685e-4) * dlt - 9.095290e-3) + * dlt + + 6.793952e-2 + ) * dlt + 999.842594e0 # Compute the seawater volumic mass at atmospheric pressure. - dlr2=(((5.3875e-9*dlt-8.2467e-7)\ - *dlt+7.6438e-5)\ - *dlt-4.0899e-3)\ - *dlt+0.824493e0 + dlr2 = ( + ((5.3875e-9 * dlt - 8.2467e-7) * dlt + 7.6438e-5) * dlt - 4.0899e-3 + ) * dlt + 0.824493e0 - dlr3=(-1.6546e-6*dlt+1.0227e-4)\ - *dlt-5.72466e-3 + dlr3 = (-1.6546e-6 * dlt + 1.0227e-4) * dlt - 5.72466e-3 # Compute the potential volumic mass (referenced to the surface). - dlrhop=(dpr4*dls+dlr3*dlrs+dlr2)*dls+dlr1 + dlrhop = (dpr4 * dls + dlr3 * dlrs + dlr2) * dls + dlr1 # Compute the compression terms. - dle=(-3.508914e-8*dlt-1.248266e-8)\ - *dlt-2.595994e-6 + dle = (-3.508914e-8 * dlt - 1.248266e-8) * dlt - 2.595994e-6 - dlbw=(1.296821e-6*dlt-5.782165e-9)\ - *dlt+1.045941e-4 + dlbw = (1.296821e-6 * dlt - 5.782165e-9) * dlt + 1.045941e-4 - dlb=dlbw+dle*dls + dlb = dlbw + dle * dls - dlc=(-7.267926e-5*dlt+2.598241e-3)\ - *dlt+0.1571896e0 + dlc = (-7.267926e-5 * dlt + 2.598241e-3) * dlt + 0.1571896e0 - dlaw=((5.939910e-6*dlt+2.512549e-3)\ - *dlt-0.1028859e0)\ - *dlt-4.721788e0 + dlaw = ((5.939910e-6 * dlt + 2.512549e-3) * dlt - 0.1028859e0) * dlt - 4.721788e0 - dla=(dpd*dlrs+dlc)*dls+dlaw + dla = (dpd * dlrs + dlc) * dls + dlaw - dlb1=(-0.1909078e0*dlt+7.390729e0)\ - *dlt-55.87545e0 + dlb1 = (-0.1909078e0 * dlt + 7.390729e0) * dlt - 55.87545e0 - dla1=((2.326469e-3*dlt+1.553190e0)\ - *dlt-65.00517e0)\ - *dlt+1044.077e0 + dla1 = ((2.326469e-3 * dlt + 1.553190e0) * dlt - 65.00517e0) * dlt + 1044.077e0 - dlkw=(((-1.361629e-4*dlt-1.852732e-2)\ - *dlt-30.41638e0)\ - *dlt+2098.925e0)\ - *dlt+190925.6e0 + dlkw = ( + ((-1.361629e-4 * dlt - 1.852732e-2) * dlt - 30.41638e0) * dlt + 2098.925e0 + ) * dlt + 190925.6e0 - dlk0=(dlb1*dlrs+dla1)*dls+dlkw + dlk0 = (dlb1 * dlrs + dla1) * dls + dlkw # Compute the potential density anomaly. - sigmai=dlrhop/(1.0e0-dlref/(dlk0-dlref*(dla-dlref*dlb)))\ - -dprau0 + sigmai = dlrhop / (1.0e0 - dlref / (dlk0 - dlref * (dla - dlref * dlb))) - dprau0 return sigmai -def sig0(theta0,S): +def sig0(theta0, S): """Surface referenced potential density (kg/m**3) Parameters @@ -186,12 +172,12 @@ def sig0(theta0,S): Use `insitu` """ - dep=N.zeros(theta0.shape) - sig=insitu(theta0,S,dep)-1000 + dep = N.zeros(theta0.shape) + sig = insitu(theta0, S, dep) - 1000 return sig -def insitu_anom(theta0,S,Z): +def insitu_anom(theta0, S, Z): """In-situ density anomaly. In situ density is computed directly as a function of @@ -206,7 +192,7 @@ def insitu_anom(theta0,S,Z): Z : numpy.array pressure (dB) or depth (m) - Notes + Notes ----- We use Jackett and McDougall (1995)'s [1]_ equation of state. the in situ density is computed directly as a function of @@ -230,7 +216,7 @@ def insitu_anom(theta0,S,Z): - in situ volumic mass rho kg/m**3 - in situ density anomaly prd no units - + Examples -------- >>> insitu(40,40,10000) @@ -238,56 +224,58 @@ def insitu_anom(theta0,S,Z): References ---------- - .. [1] Jackett, D. R., and T. J. McDougall, Minimal adjustment of - hydrographic profiles to achieve static stability, J. Atmos. - Ocean. Technol., 12(4), 381-389, 1995. + .. [1] Jackett, D. R., and T. J. McDougall, Minimal adjustment of + hydrographic profiles to achieve static stability, J. Atmos. + Ocean. Technol., 12(4), 381-389, 1995. """ + zsr = N.sqrt(N.abs(S)) + zt = theta0 + zs = S + zh = Z - - zsr=N.sqrt(N.abs(S)) - zt=theta0 - zs=S - zh=Z - # compute volumic mass pure water at atm pressure - zr1= ( ( ( ( 6.536332e-9*zt-1.120083e-6 )*zt+1.001685e-4)*zt - -9.095290e-3 )*zt+6.793952e-2 )*zt+999.842594 + zr1 = ( + (((6.536332e-9 * zt - 1.120083e-6) * zt + 1.001685e-4) * zt - 9.095290e-3) * zt + + 6.793952e-2 + ) * zt + 999.842594 # seawater volumic mass atm pressure - zr2= ( ( ( 5.3875e-9*zt-8.2467e-7 ) *zt+7.6438e-5 ) *zt - -4.0899e-3 ) *zt+0.824493 - zr3= ( -1.6546e-6*zt+1.0227e-4 ) *zt-5.72466e-3 - zr4= 4.8314e-4 + zr2 = ( + ((5.3875e-9 * zt - 8.2467e-7) * zt + 7.6438e-5) * zt - 4.0899e-3 + ) * zt + 0.824493 + zr3 = (-1.6546e-6 * zt + 1.0227e-4) * zt - 5.72466e-3 + zr4 = 4.8314e-4 # potential volumic mass (reference to the surface) - zrhop= ( zr4*zs + zr3*zsr + zr2 ) *zs + zr1 + zrhop = (zr4 * zs + zr3 * zsr + zr2) * zs + zr1 # add the compression terms - ze = ( -3.508914e-8*zt-1.248266e-8 ) *zt-2.595994e-6 - zbw= ( 1.296821e-6*zt-5.782165e-9 ) *zt+1.045941e-4 + ze = (-3.508914e-8 * zt - 1.248266e-8) * zt - 2.595994e-6 + zbw = (1.296821e-6 * zt - 5.782165e-9) * zt + 1.045941e-4 zb = zbw + ze * zs zd = -2.042967e-2 - zc = (-7.267926e-5*zt+2.598241e-3 ) *zt+0.1571896 - zaw= ( ( 5.939910e-6*zt+2.512549e-3 ) *zt-0.1028859 ) *zt - 4.721788 - za = ( zd*zsr + zc ) *zs + zaw + zc = (-7.267926e-5 * zt + 2.598241e-3) * zt + 0.1571896 + zaw = ((5.939910e-6 * zt + 2.512549e-3) * zt - 0.1028859) * zt - 4.721788 + za = (zd * zsr + zc) * zs + zaw - zb1= (-0.1909078*zt+7.390729 ) *zt-55.87545 - za1= ( ( 2.326469e-3*zt+1.553190)*zt-65.00517 ) *zt+1044.077 - zkw= ( ( (-1.361629e-4*zt-1.852732e-2 ) *zt-30.41638 ) - *zt + 2098.925 ) *zt+190925.6 - zk0= ( zb1*zsr + za1 )*zs + zkw + zb1 = (-0.1909078 * zt + 7.390729) * zt - 55.87545 + za1 = ((2.326469e-3 * zt + 1.553190) * zt - 65.00517) * zt + 1044.077 + zkw = ( + ((-1.361629e-4 * zt - 1.852732e-2) * zt - 30.41638) * zt + 2098.925 + ) * zt + 190925.6 + zk0 = (zb1 * zsr + za1) * zs + zkw - prd=( zrhop / ( 1.0 - zh / ( zk0 - zh * ( za - zh * zb ) ) ) - - rau0 ) / rau0 + prd = (zrhop / (1.0 - zh / (zk0 - zh * (za - zh * zb))) - rau0) / rau0 return prd -def delta(t,s,p): + +def delta(t, s, p): """Specific volume anomaly. - Specific volume anomaly with respect to a standart ocean + Specific volume anomaly with respect to a standart ocean of salinity S = Sstd and T = Tstd. Parameters @@ -299,15 +287,16 @@ def delta(t,s,p): p : numpy.array pressure (dB) or depth (m) - References + References ---------- see e.g. page 2188 of Watts et al. JPO, 2001. """ - delta = 1. / insitu(t,s,p) - 1. / insitu(Tstd, Sstd, p) + delta = 1.0 / insitu(t, s, p) - 1.0 / insitu(Tstd, Sstd, p) return delta -def spice(t,s): + +def spice(t, s): """Spiciness. A state variable for characterizing water masses and their @@ -320,8 +309,8 @@ def spice(t,s): s : numpy.array salinity - Notes - ----- + Notes + ----- following Flament (2002) [1]_. We could also have used [2]_. **caution** This state variable is only valid close to the surface. @@ -332,74 +321,74 @@ def spice(t,s): References ---------- - .. [1] Flament (2002) A state variable for characterizing water - masses and their diffusive stability: spiciness. Progress + .. [1] Flament (2002) A state variable for characterizing water + masses and their diffusive stability: spiciness. Progress in Oceanography Volume 54, 2002, Pages 493-501. .. [2] Jackett and McDougall, Deep Sea Research, 32A, 1195-1208, 1985. Examples -------- >>> spice(15,33) - 0.54458641375 + 0.54458641375 """ - B = numpy.zeros((7,6)) - B[1,1] = 0 - B[1,2] = 7.7442e-001 - B[1,3] = -5.85e-003 - B[1,4] = -9.84e-004 - B[1,5] = -2.06e-004 - - B[2,1] = 5.1655e-002 - B[2,2] = 2.034e-003 - B[2,3] = -2.742e-004 - B[2,4] = -8.5e-006 - B[2,5] = 1.36e-005 - - B[3,1] = 6.64783e-003 - B[3,2] = -2.4681e-004 - B[3,3] = -1.428e-005 - B[3,4] = 3.337e-005 - B[3,5] = 7.894e-006 - - B[4,1] = -5.4023e-005 - B[4,2] = 7.326e-006 - B[4,3] = 7.0036e-006 - B[4,4] = -3.0412e-006 - B[4,5] = -1.0853e-006 - - B[5,1] = 3.949e-007 - B[5,2] = -3.029e-008 - B[5,3] = -3.8209e-007 - B[5,4] = 1.0012e-007 - B[5,5] = 4.7133e-008 - - B[6,1] = -6.36e-010 - B[6,2] = -1.309e-009 - B[6,3] = 6.048e-009 - B[6,4] = -1.1409e-009 - B[6,5] = -6.676e-010 - # + B = numpy.zeros((7, 6)) + B[1, 1] = 0 + B[1, 2] = 7.7442e-001 + B[1, 3] = -5.85e-003 + B[1, 4] = -9.84e-004 + B[1, 5] = -2.06e-004 + + B[2, 1] = 5.1655e-002 + B[2, 2] = 2.034e-003 + B[2, 3] = -2.742e-004 + B[2, 4] = -8.5e-006 + B[2, 5] = 1.36e-005 + + B[3, 1] = 6.64783e-003 + B[3, 2] = -2.4681e-004 + B[3, 3] = -1.428e-005 + B[3, 4] = 3.337e-005 + B[3, 5] = 7.894e-006 + + B[4, 1] = -5.4023e-005 + B[4, 2] = 7.326e-006 + B[4, 3] = 7.0036e-006 + B[4, 4] = -3.0412e-006 + B[4, 5] = -1.0853e-006 + + B[5, 1] = 3.949e-007 + B[5, 2] = -3.029e-008 + B[5, 3] = -3.8209e-007 + B[5, 4] = 1.0012e-007 + B[5, 5] = 4.7133e-008 + + B[6, 1] = -6.36e-010 + B[6, 2] = -1.309e-009 + B[6, 3] = 6.048e-009 + B[6, 4] = -1.1409e-009 + B[6, 5] = -6.676e-010 + # t = numpy.array(t) s = numpy.array(s) # - coefs = B[1:7,1:6] + coefs = B[1:7, 1:6] sp = numpy.zeros(t.shape) - ss = s - 35. + ss = s - 35.0 bigT = numpy.ones(t.shape) for i in range(6): bigS = numpy.ones(t.shape) for j in range(5): - sp+= coefs[i,j]*bigT*bigS - bigS*= ss - bigT*=t + sp += coefs[i, j] * bigT * bigS + bigS *= ss + bigT *= t return sp -def lapse_rate(t,s,p): +def lapse_rate(t, s, p): """Adiabatic lapse rate (deg C/dBar). - - Adiabatic lapse rate (deg C/dBar) from salinity (psu), + + Adiabatic lapse rate (deg C/dBar) from salinity (psu), temperature (deg C) and pressure (dbar) Parameters @@ -412,37 +401,37 @@ def lapse_rate(t,s,p): pressure (dbar) - Notes + Notes ----- - This calculator is based on an algorithm for the speed of sound + This calculator is based on an algorithm for the speed of sound published by Chen and Millero (1977) [1]_. - - The underlying equations are valid for temperatures from -2 to 35 deg C, - pressures from 0 to 10,000 dbar, + + The underlying equations are valid for temperatures from -2 to 35 deg C, + pressures from 0 to 10,000 dbar, and practical salinity from 2 to 42. - - See also + + See also -------- http://fermi.jhuapl.edu/denscalc/spdcalc.html - + References ---------- .. [1] Chen and Millero, Speed of sound in seawater at high pressures, J. Acoust. Soc. Am., Vol. 62, No. 5, 1129-1135, Nov 1977. - """ + """ # ds = s - 35.0 atg = ((-2.1687e-16 * t + 1.8676e-14) * t - 4.6206e-13) * p * p - atg+= (2.7759e-12 * t - 1.1351e-10 ) * ds * p - atg+= (((-5.4481e-14 * t + 8.7330e-12) * t - 6.7795e-10) * t + 1.8741e-8) * p - atg+= (-4.2393e-8 * t + 1.8932e-6 ) * ds - atg+= ((6.6228e-10 * t - 6.8360e-8) * t + 8.5258e-6) * t + 3.5803e-5 + atg += (2.7759e-12 * t - 1.1351e-10) * ds * p + atg += (((-5.4481e-14 * t + 8.7330e-12) * t - 6.7795e-10) * t + 1.8741e-8) * p + atg += (-4.2393e-8 * t + 1.8932e-6) * ds + atg += ((6.6228e-10 * t - 6.8360e-8) * t + 8.5258e-6) * t + 3.5803e-5 return atg -def theta_n(theta0,s,n): - """Potential temperature. +def theta_n(theta0, s, n): + """Potential temperature. Potential temperature with reference pressure n x 1000dB @@ -455,14 +444,15 @@ def theta_n(theta0,s,n): n : int reference pressure / 1000 dB - """ - Pr=n*1000 - theta=theta0_2_theta(theta0,s,Pr) - return theta + """ + Pr = n * 1000 + theta = theta0_2_theta(theta0, s, Pr) + return theta + -def theta0_2_theta_n(theta0,s,Pr): +def theta0_2_theta_n(theta0, s, Pr): """Potential temperature. - + Potential temperature with reference pressure Pr. Parameters @@ -479,17 +469,17 @@ def theta0_2_theta_n(theta0,s,Pr): **caution** This function uses a very rough estimate of potential temperature """ - P0=0 - dP=Pr-P0 - dT= dP*lapse_rate(theta0,s,(Pr+P0)/2.) - theta_n=theta0+dT + P0 = 0 + dP = Pr - P0 + dT = dP * lapse_rate(theta0, s, (Pr + P0) / 2.0) + theta_n = theta0 + dT ## # # print '********************************************************************' ## # print ' theta0_2_theta_n is now deprecated. Please do not use it.' ## # print '********************************************************************' return theta_n -def beta(theta0,s,p): +def beta(theta0, s, p): """Haline contraction coeficient beta. Haline contraction coeficient (unit : 1/psu) from McDougall (1987) [1]_ @@ -503,31 +493,33 @@ def beta(theta0,s,p): p : numpy.array pressure (dB) or depth (m) - Notes + Notes ----- adapted from McDougall (1987) [1]_. - - References + + References ---------- - .. [1] McDougall, Neutral surfaces - Journal of Physical Oceanography, - vol.17,pp.1950-1964, 1987. - - """ - th=theta0 - beta = 0.785567E-3 - 0.301985E-5*th - beta+= 0.555579E-7*th**2 - 0.415613E-9*th**3 - beta+=(s-35.0)*(-0.356603E-6 + 0.788212E-8*th \ - + 0.408195E-10*p - 0.602281E-15*p**2) - beta+=(s-35.0)**2*0.515032E-8 + p*(-0.121555E-7 \ - + 0.192867E-9*th - 0.213127E-11*th**2) - beta+= p**2*(0.176621E-12 - 0.175379E-14*th) + p**3*(0.121551E-17) + .. [1] McDougall, Neutral surfaces - Journal of Physical Oceanography, + vol.17,pp.1950-1964, 1987. + + """ + th = theta0 + beta = 0.785567e-3 - 0.301985e-5 * th + beta += 0.555579e-7 * th**2 - 0.415613e-9 * th**3 + beta += (s - 35.0) * ( + -0.356603e-6 + 0.788212e-8 * th + 0.408195e-10 * p - 0.602281e-15 * p**2 + ) + beta += (s - 35.0) ** 2 * 0.515032e-8 + p * ( + -0.121555e-7 + 0.192867e-9 * th - 0.213127e-11 * th**2 + ) + beta += p**2 * (0.176621e-12 - 0.175379e-14 * th) + p**3 * (0.121551e-17) return beta -def alpha_over_beta(theta0,s,p): +def alpha_over_beta(theta0, s, p): """Thermal expansion to haline contraction ratio. - Ratio of the thermal expansion coefficient to the saline + Ratio of the thermal expansion coefficient to the saline contraction coeficient (unit: psu/deg). Parameters @@ -549,19 +541,20 @@ def alpha_over_beta(theta0,s,p): vol.17,pp.1950-1964, 1987. """ - th=theta0 - ab= 0.665157E-1 + 0.170907E-1*th - ab+= -0.203814E-3*th**2 + 0.298357E-5*th**3 \ - - 0.255019E-7*th**4 - ab+= (s-35.0)*(0.378110E-2 - 0.846960E-4*th \ - - 0.164759E-6*p - 0.251520E-11*p**2) - ab+= (s-35.0)**2*(-0.678662E-5) + p*(0.380374E-4 \ - - 0.933746E-6*th + 0.791325E-8*th**2) - ab+= 0.512857E-12*p**2*th**2 - 0.302285E-13*p**3 + th = theta0 + ab = 0.665157e-1 + 0.170907e-1 * th + ab += -0.203814e-3 * th**2 + 0.298357e-5 * th**3 - 0.255019e-7 * th**4 + ab += (s - 35.0) * ( + 0.378110e-2 - 0.846960e-4 * th - 0.164759e-6 * p - 0.251520e-11 * p**2 + ) + ab += (s - 35.0) ** 2 * (-0.678662e-5) + p * ( + 0.380374e-4 - 0.933746e-6 * th + 0.791325e-8 * th**2 + ) + ab += 0.512857e-12 * p**2 * th**2 - 0.302285e-13 * p**3 return ab -def alpha(theta0,s,p): +def alpha(theta0, s, p): """Thermal expansion coefficient. Thermal expansion coeficient (unit : 1/deg) from McDougall (1987) [1]_ @@ -581,16 +574,18 @@ def alpha(theta0,s,p): vol.17,pp.1950-1964, 1987. """ - alpha=beta(theta0,s,p)*alpha_over_beta(theta0,s,p) + alpha = beta(theta0, s, p) * alpha_over_beta(theta0, s, p) return alpha -#---------------- Derivatives of Core Funtions --------------------------- -def rhoalpha(theta0,s,p): +# ---------------- Derivatives of Core Funtions --------------------------- + + +def rhoalpha(theta0, s, p): """rho x alpha. Return the product rho x alpha. - + Parameters ---------- theta0 : numpy.array @@ -601,11 +596,12 @@ def rhoalpha(theta0,s,p): pressure (dB) or depth (m) """ - zrho = insitu(theta0,s,p) - za = alpha(theta0,s,p) + zrho = insitu(theta0, s, p) + za = alpha(theta0, s, p) return zrho * za -def rhobeta(theta0,s,p): + +def rhobeta(theta0, s, p): """rho x beta. Return the product rho x beta. @@ -620,11 +616,12 @@ def rhobeta(theta0,s,p): pressure (dB) or depth (m) """ - zrho = insitu(theta0,s,p) - zb = beta(theta0,s,p) + zrho = insitu(theta0, s, p) + zb = beta(theta0, s, p) return zrho * zb -def beta_p(theta0,s,p): + +def beta_p(theta0, s, p): """Partial derivative of beta with respect to pressure. Parameters @@ -641,16 +638,14 @@ def beta_p(theta0,s,p): analytic derivation of McDougall (1987)'s formula. """ - th=theta0 - zbeta_p=(s-35.0)*( 0.408195E-10 - 0.602281E-15*p*2) - zbeta_p+=+ (-0.121555E-7 + 0.192867E-9*th \ - - 0.213127E-11*th**2) - zbeta_p+= 2*p*(0.176621E-12 - 0.175379E-14*th) \ - + 3*p**2*(0.121551E-17) + th = theta0 + zbeta_p = (s - 35.0) * (0.408195e-10 - 0.602281e-15 * p * 2) + zbeta_p += +(-0.121555e-7 + 0.192867e-9 * th - 0.213127e-11 * th**2) + zbeta_p += 2 * p * (0.176621e-12 - 0.175379e-14 * th) + 3 * p**2 * (0.121551e-17) return zbeta_p -def rho_p(theta0,s,p): +def rho_p(theta0, s, p): """Partial derivative of in situ density with respect to pressure. Parameters @@ -667,49 +662,53 @@ def rho_p(theta0,s,p): analytic derivation of Jackett and McDougall (1995)'s formula. """ - zsr=N.sqrt(N.abs(s)) - zt=theta0 - zs=s - zh=p + zsr = N.sqrt(N.abs(s)) + zt = theta0 + zs = s + zh = p # compute volumic mass pure water at atm pressure - zr1= ( ( ( ( 6.536332e-9*zt-1.120083e-6 )*zt+1.001685e-4)*zt - -9.095290e-3 )*zt+6.793952e-2 )*zt+999.842594 + zr1 = ( + (((6.536332e-9 * zt - 1.120083e-6) * zt + 1.001685e-4) * zt - 9.095290e-3) * zt + + 6.793952e-2 + ) * zt + 999.842594 # seawater volumic mass atm pressure - zr2= ( ( ( 5.3875e-9*zt-8.2467e-7 ) *zt+7.6438e-5 ) *zt - -4.0899e-3 ) *zt+0.824493 - zr3= ( -1.6546e-6*zt+1.0227e-4 ) *zt-5.72466e-3 - zr4= 4.8314e-4 + zr2 = ( + ((5.3875e-9 * zt - 8.2467e-7) * zt + 7.6438e-5) * zt - 4.0899e-3 + ) * zt + 0.824493 + zr3 = (-1.6546e-6 * zt + 1.0227e-4) * zt - 5.72466e-3 + zr4 = 4.8314e-4 # potential volumic mass (reference to the surface) - zrhop= ( zr4*zs + zr3*zsr + zr2 ) *zs + zr1 + zrhop = (zr4 * zs + zr3 * zsr + zr2) * zs + zr1 # add the compression terms - ze = ( -3.508914e-8*zt-1.248266e-8 ) *zt-2.595994e-6 - zbw= ( 1.296821e-6*zt-5.782165e-9 ) *zt+1.045941e-4 + ze = (-3.508914e-8 * zt - 1.248266e-8) * zt - 2.595994e-6 + zbw = (1.296821e-6 * zt - 5.782165e-9) * zt + 1.045941e-4 zb = zbw + ze * zs zd = -2.042967e-2 - zc = (-7.267926e-5*zt+2.598241e-3 ) *zt+0.1571896 - zaw= ( ( 5.939910e-6*zt+2.512549e-3 ) *zt-0.1028859 ) *zt - 4.721788 - za = ( zd*zsr + zc ) *zs + zaw - - zb1= (-0.1909078*zt+7.390729 ) *zt-55.87545 - za1= ( ( 2.326469e-3*zt+1.553190)*zt-65.00517 ) *zt+1044.077 - zkw= ( ( (-1.361629e-4*zt-1.852732e-2 ) *zt-30.41638 ) - *zt + 2098.925 ) *zt+190925.6 - zk0= ( zb1*zsr + za1 )*zs + zkw - - #zrho= zrhop / ( 1.0 - zh / ( zk0 - zh * ( za - zh * zb ) ) ) - zk= zk0 - zh * ( za - zh * zb ) - zu=zh/zk - zk_p = -za + 2*zh * zb - zu_p=( 1 - ( zh/zk ) * zk_p ) / zk - zrho_p=(zrhop / ( 1.0 - zu )**2 ) * zu_p + zc = (-7.267926e-5 * zt + 2.598241e-3) * zt + 0.1571896 + zaw = ((5.939910e-6 * zt + 2.512549e-3) * zt - 0.1028859) * zt - 4.721788 + za = (zd * zsr + zc) * zs + zaw + + zb1 = (-0.1909078 * zt + 7.390729) * zt - 55.87545 + za1 = ((2.326469e-3 * zt + 1.553190) * zt - 65.00517) * zt + 1044.077 + zkw = ( + ((-1.361629e-4 * zt - 1.852732e-2) * zt - 30.41638) * zt + 2098.925 + ) * zt + 190925.6 + zk0 = (zb1 * zsr + za1) * zs + zkw + + # zrho= zrhop / ( 1.0 - zh / ( zk0 - zh * ( za - zh * zb ) ) ) + zk = zk0 - zh * (za - zh * zb) + zu = zh / zk + zk_p = -za + 2 * zh * zb + zu_p = (1 - (zh / zk) * zk_p) / zk + zrho_p = (zrhop / (1.0 - zu) ** 2) * zu_p return zrho_p -def Tb(theta0,s,p): +def Tb(theta0, s, p): """Thermobaric parameter Tb. Parameters @@ -721,7 +720,7 @@ def Tb(theta0,s,p): p : numpy.array pressure (dB) or depth (m) - Notes + Notes ----- Tb = beta*(alpha/beta)_p = alpha_p - (alpha/beta)*beta_p @@ -729,10 +728,11 @@ def Tb(theta0,s,p): """ # # # # print 'eos.Tb has not been checked yet...' - return beta(theta0,s,p)*alpha_over_beta_p(theta0,s,p) - #return alpha_p(theta0,s,p)-alpha_over_beta(theta0,s,p)*beta_p(theta0,s,p) + return beta(theta0, s, p) * alpha_over_beta_p(theta0, s, p) + # return alpha_p(theta0,s,p)-alpha_over_beta(theta0,s,p)*beta_p(theta0,s,p) + -def alpha_over_beta_p(theta0,s,p): +def alpha_over_beta_p(theta0, s, p): """Partial derivative of alpha/beta with respect to pressure. Parameters @@ -749,13 +749,14 @@ def alpha_over_beta_p(theta0,s,p): analytic derivation of McDougall (1987)'s formula. """ - th=theta0 - zab_p= (s-35.0)*( - 0.164759E-6 - 0.251520E-11*2*p) - zab_p+= (0.380374E-4 - 0.933746E-6*th + 0.791325E-8*th**2) - zab_p+= 0.512857E-12*2*p*th**2 - 0.302285E-13*3*p**2 - return zab_p + th = theta0 + zab_p = (s - 35.0) * (-0.164759e-6 - 0.251520e-11 * 2 * p) + zab_p += 0.380374e-4 - 0.933746e-6 * th + 0.791325e-8 * th**2 + zab_p += 0.512857e-12 * 2 * p * th**2 - 0.302285e-13 * 3 * p**2 + return zab_p -def alpha_p(theta0,s,p): + +def alpha_p(theta0, s, p): """Partial derivative of alpha with respect to pressure. Parameters @@ -767,19 +768,19 @@ def alpha_p(theta0,s,p): p : numpy.array pressure (dB) or depth (m) - Notes + Notes ----- computes alpha_p = (alpha/beta)_p * beta + beta_p * (alpha/beta) """ - zb = beta(theta0,s,p) - zb_p = beta_p(theta0,s,p) - zab = alpha_over_beta(theta0,s,p) - zab_p = alpha_over_beta_p(theta0,s,p) - return zab_p * zb + zab * zb_p + zb = beta(theta0, s, p) + zb_p = beta_p(theta0, s, p) + zab = alpha_over_beta(theta0, s, p) + zab_p = alpha_over_beta_p(theta0, s, p) + return zab_p * zb + zab * zb_p -def rhobeta_p(theta0,s,p): +def rhobeta_p(theta0, s, p): """Partial derivative of rho*beta with respect to pressure. Parameters @@ -792,13 +793,14 @@ def rhobeta_p(theta0,s,p): pressure (dB) or depth (m) """ - zrho= insitu(theta0,s,p) - zbeta= beta(theta0,s,p) - zbeta_p= beta_p(theta0,s,p) - zrho_p= rho_p(theta0,s,p) - return zrho*zbeta_p+zbeta*zrho_p + zrho = insitu(theta0, s, p) + zbeta = beta(theta0, s, p) + zbeta_p = beta_p(theta0, s, p) + zrho_p = rho_p(theta0, s, p) + return zrho * zbeta_p + zbeta * zrho_p -def rhoalpha_p(theta0,s,p): + +def rhoalpha_p(theta0, s, p): """Partial derivative of rho*alpha with respect to pressure. Parameters @@ -811,16 +813,16 @@ def rhoalpha_p(theta0,s,p): pressure (dB) or depth (m) """ - zrhobeta= rhobeta(theta0,s,p) - zab= alpha_over_beta(theta0,s,p) - zrhobeta_p= rhobeta_p(theta0,s,p) - zab_p= alpha_over_beta_p(theta0,s,p) - return zrhobeta*zab_p+zab*zrhobeta_p + zrhobeta = rhobeta(theta0, s, p) + zab = alpha_over_beta(theta0, s, p) + zrhobeta_p = rhobeta_p(theta0, s, p) + zab_p = alpha_over_beta_p(theta0, s, p) + return zrhobeta * zab_p + zab * zrhobeta_p -#---------------- Functions involving Derivatives or integrals -------------- -#- ie : "dom" dependent utilities. -def bn2(dom,theta0,S): +# ---------------- Functions involving Derivatives or integrals -------------- +# - ie : "dom" dependent utilities. +def bn2(dom, theta0, S): """Brunt-Vaissala frequency. Brunt-Vaissala frequency on the w-grid. @@ -841,31 +843,32 @@ def bn2(dom,theta0,S): N2 = grav*(alpha d_z theta - beta d_z S). """ - ralpha= alpha(theta0,S,dom.depthT_3D) - rbeta= beta(theta0,S,dom.depthT_3D) - ra=dom.lamVz(ralpha,dom.dro_z(theta0)) - rb=dom.lamVz(rbeta,dom.dro_z(S)) - gz=grav*(ra-rb) - mgz=dom.set_mask(gz,dom.wmask) + ralpha = alpha(theta0, S, dom.depthT_3D) + rbeta = beta(theta0, S, dom.depthT_3D) + ra = dom.lamVz(ralpha, dom.dro_z(theta0)) + rb = dom.lamVz(rbeta, dom.dro_z(S)) + gz = grav * (ra - rb) + mgz = dom.set_mask(gz, dom.wmask) return mgz -def isoslope(dom,theta0,S): - """Slope of isopycnals. - """ - ralpha= alpha(theta0,S,dom.depthT_3D) - rbeta= beta(theta0,S,dom.depthT_3D) - tx,ty,tz = dom.grad3D(theta0) - sx,sy,sz = dom.grad3D(S) - atx,aty,atz = dom.lamV(ralpha,(tx,ty,tz)) - bsx,bsy,bsz = dom.lamV(rbeta,(sx,sy,sz)) - ghsig = (atx-bsx,aty-bsy,0. * atz) + +def isoslope(dom, theta0, S): + """Slope of isopycnals.""" + ralpha = alpha(theta0, S, dom.depthT_3D) + rbeta = beta(theta0, S, dom.depthT_3D) + tx, ty, tz = dom.grad3D(theta0) + sx, sy, sz = dom.grad3D(S) + atx, aty, atz = dom.lamV(ralpha, (tx, ty, tz)) + bsx, bsy, bsz = dom.lamV(rbeta, (sx, sy, sz)) + ghsig = (atx - bsx, aty - bsy, 0.0 * atz) gzsig = atz - bsz ngh = N.sqrt(dom.normV(ghsig)) - denom = dom.gridW_2_gridT(gzsig) - s = dom.set_mask(ngh / denom,dom.tmask) + denom = dom.gridW_2_gridT(gzsig) + s = dom.set_mask(ngh / denom, dom.tmask) return s -def Gz(dom,theta0,S): + +def Gz(dom, theta0, S): """Vertical component of the neutral vector. Vertical component of the neutral vector on the w-grid. @@ -880,18 +883,16 @@ def Gz(dom,theta0,S): salinity """ - ralpha= rhoalpha(theta0,S,dom.depthT_3D) - rbeta= rhobeta(theta0,S,dom.depthT_3D) - ra=dom.lamVz(ralpha,dom.dro_z(theta0)) - rb=dom.lamVz(rbeta,dom.dro_z(S)) - gz=(ra-rb) - mgz=dom.set_mask(gz,dom.wmask) + ralpha = rhoalpha(theta0, S, dom.depthT_3D) + rbeta = rhobeta(theta0, S, dom.depthT_3D) + ra = dom.lamVz(ralpha, dom.dro_z(theta0)) + rb = dom.lamVz(rbeta, dom.dro_z(S)) + gz = ra - rb + mgz = dom.set_mask(gz, dom.wmask) return mgz - - -def hydrostatic(dom,T=None,S=None,rho=None,p0=1E5): +def hydrostatic(dom, T=None, S=None, rho=None, p0=1e5): """Hydrostatic pressure (Pa). Parameters @@ -902,15 +903,15 @@ def hydrostatic(dom,T=None,S=None,rho=None,p0=1E5): potential temperature S : numpy.array salinity - rho : numpy.array, optional + rho : numpy.array, optional in situ density - p0 : numpy.array + p0 : numpy.array surface pressure - Notes + Notes ----- according to \partial_z P = - \rho g - + **caution** P unit is Pa (1 dbar = 1e4 Pascal) """ @@ -918,29 +919,30 @@ def hydrostatic(dom,T=None,S=None,rho=None,p0=1E5): jpk = dom.jpk # ph = N.zeros(dom.tmask.shape) - if rho is not None: - arho = (rho - rau0) / rau0 - else: - arho = insitu_anom(T,S,dom.depthT_3D) + if rho is not None: + arho = (rho - rau0) / rau0 + else: + arho = insitu_anom(T, S, dom.depthT_3D) # - ph[...,0,:,:] = grav * dom.depthT_3D[0,:,:] * arho[...,0,:,:] - ph[...,1:jpk,:,:] = grav * dom.m_k(arho) * dom.e3w_3D[1:jpk,:,:] + ph[..., 0, :, :] = grav * dom.depthT_3D[0, :, :] * arho[..., 0, :, :] + ph[..., 1:jpk, :, :] = grav * dom.m_k(arho) * dom.e3w_3D[1:jpk, :, :] ph = ph.cumsum(axis=-3) - ph*= rau0 - ph+= rau0 * grav * dom.depthT_3D - ph+= p0 - ph= dom.set_mask(ph,dom.tmask) + ph *= rau0 + ph += rau0 * grav * dom.depthT_3D + ph += p0 + ph = dom.set_mask(ph, dom.tmask) return ph -def surface(dom,ssh,rho=rau0): + +def surface(dom, ssh, rho=rau0): """Surface pressure associated with sea surface height. Parameters ---------- dom : OPA_C_Grid instance domain - ssh : numpy.array - sea surface height + ssh : numpy.array + sea surface height rho : numpy.array, optional surface in situ density surface pressure @@ -948,10 +950,11 @@ def surface(dom,ssh,rho=rau0): """ ps = rho * grav * ssh - ps = dom.set_mask(ps,dom.tmask[0,:]) + ps = dom.set_mask(ps, dom.tmask[0, :]) return ps -def pressure(dom,T=None,S=None,ssh=None,rho=None): + +def pressure(dom, T=None, S=None, ssh=None, rho=None): """Pressure. Returns the surface + hydrostatic pressure @@ -972,49 +975,52 @@ def pressure(dom,T=None,S=None,ssh=None,rho=None): """ if ssh is None: - ssh=0.*T - rho_s = rau0 + ssh = 0.0 * T + rho_s = rau0 else: - rho_s = insitu(T[0],S[0],0.) + rho_s = insitu(T[0], S[0], 0.0) if rho is not None: - p = hydrostatic(dom,rho=rho) + p = hydrostatic(dom, rho=rho) else: - p = hydrostatic(dom,T,S) - p+= surface(dom,ssh,rho=rho_s) - p = dom.set_mask(p,dom.tmask) + p = hydrostatic(dom, T, S) + p += surface(dom, ssh, rho=rho_s) + p = dom.set_mask(p, dom.tmask) return p -def chi(dom,T,S): - """Return chi = g/rho0 \int (z * rho) dz - """ + +def chi(dom, T, S): + """Return chi = g/rho0 \int (z * rho) dz""" z3d = dom.depthT_3D - rho = insitu(T,S,z3d) - integral = dom.integrate_dz(rho * z3d,dom.tmask) + rho = insitu(T, S, z3d) + integral = dom.integrate_dz(rho * z3d, dom.tmask) return integral * grav / rau0 -def jebar_old(dom,T,S): + +def jebar_old(dom, T, S): """Return the JEBAR term. (weak signal to noise ratio...) see e.g. Mertz and Wright JPO 1992 """ dom.get_bottom_depth() - invH = 1./dom.bottom_depth - mchi = chi(dom,T,S) - return dom.jacobian(mchi,invH) + invH = 1.0 / dom.bottom_depth + mchi = chi(dom, T, S) + return dom.jacobian(mchi, invH) + -def jebar(dom,T,S): +def jebar(dom, T, S): """Return the JEBAR term. (not checked yet...) see e.g. Mertz and Wright JPO 1992 """ dom.get_bottom_depth() z3d = dom.depthT_3D - rho = insitu(T,S,z3d) + rho = insitu(T, S, z3d) h3d = dom.stretch(dom.bottom_depth) - locjac = dom.jacobian(rho,h3d) - mymask = npy.abs(locjac)< 1. # not very satisfactory but well... - integral = dom.integrate_dz(locjac * z3d,where=mymask) - return (integral * grav / rau0 ) / (-dom.bottom_depth**2) + locjac = dom.jacobian(rho, h3d) + mymask = npy.abs(locjac) < 1.0 # not very satisfactory but well... + integral = dom.integrate_dz(locjac * z3d, where=mymask) + return (integral * grav / rau0) / (-(dom.bottom_depth**2)) -def geopotential_height_anomaly(dom,T,S,z,zref=0.): + +def geopotential_height_anomaly(dom, T, S, z, zref=0.0): """Geopotential heigh anomaly. Return geopotential heigh anomaly at pressure z (in dbar) with respect @@ -1031,7 +1037,7 @@ def geopotential_height_anomaly(dom,T,S,z,zref=0.): z : float zref : float - Notes + Notes ----- see e.g. Watt et al. (2001) [1]_ @@ -1040,17 +1046,18 @@ def geopotential_height_anomaly(dom,T,S,z,zref=0.): .. [1] Watt et al. JPO 2001. """ - delt = delta(T,S,dom.depthT_3D) + delt = delta(T, S, dom.depthT_3D) dep = dom.depthT_3D - if z>zref: - intdom = (dep>zref) * (dep zref: + intdom = (dep > zref) * (dep < z) + int = -dom.integrate_dz(delt, intdom) # not sure about the sign here... else: - intdom = (depz) - int = dom.integrate_dz(delt,intdom) - return int * dbar2pascal/grav # to get dynamic meters... + intdom = (dep < zref) * (dep > z) + int = dom.integrate_dz(delt, intdom) + return int * dbar2pascal / grav # to get dynamic meters... + -def potential_energy_anomaly(dom,T,S,z=0,zref=2500.): +def potential_energy_anomaly(dom, T, S, z=0, zref=2500.0): """Potential energy anomaly (PEA). Return PEA integrated between pressure zref (in dbar) and pressure zref (in dbar). @@ -1075,20 +1082,19 @@ def potential_energy_anomaly(dom,T,S,z=0,zref=2500.): .. [1] Rintoul et al. JGR 2002. """ - delt = delta(T,S,dom.depthT_3D) + delt = delta(T, S, dom.depthT_3D) pdelt = dom.depthT_3D * delt dep = dom.depthT_3D - if z>zref: - intdom = (dep>zref) * (dep zref: + intdom = (dep > zref) * (dep < z) + int = -dom.integrate_dz(pdelt, intdom) else: - intdom = (depz) - int = dom.integrate_dz(pdelt,intdom) - return int * dbar2pascal/grav + intdom = (dep < zref) * (dep > z) + int = dom.integrate_dz(pdelt, intdom) + return int * dbar2pascal / grav - -def geopotential_height_anomaly3D(dom,T,S): +def geopotential_height_anomaly3D(dom, T, S): """Geopotential heigh anomaly. Return geopotential heigh anomaly at pressure z (in dbar) with respect @@ -1115,9 +1121,10 @@ def geopotential_height_anomaly3D(dom,T,S): """ name = miscutils.whoami() # print name + ' is not implemented yet...' - return + return + -def montgomery(dom,t,s,ssh=None,href=0.): +def montgomery(dom, t, s, ssh=None, href=0.0): """Montgomery potential. Parameters @@ -1139,11 +1146,12 @@ def montgomery(dom,t,s,ssh=None,href=0.): """ - b = pressure(dom,t,s,ssh=ssh) / rau0 - b+= grav * (href - dom.depthT_3D)# should be ok now... - return dom.set_mask(b,dom.tmask) + b = pressure(dom, t, s, ssh=ssh) / rau0 + b += grav * (href - dom.depthT_3D) # should be ok now... + return dom.set_mask(b, dom.tmask) -def bernoulli(dom,t,s,uv,ssh=None,href=0.,method='PM07'): + +def bernoulli(dom, t, s, uv, ssh=None, href=0.0, method="PM07"): """Bernoulli potential. Parameters @@ -1165,13 +1173,14 @@ def bernoulli(dom,t,s,uv,ssh=None,href=0.,method='PM07'): Notes ----- Different methods are available. Note that they do not - provide the same results. + provide the same results. """ - exec('_bernoulli = _bernoulli_' + method) - return _bernoulli(dom,t,s,uv,ssh=ssh,href=href) + exec("_bernoulli = _bernoulli_" + method) + return _bernoulli(dom, t, s, uv, ssh=ssh, href=href) + -def _bernoulli_PM07(dom,t,s,uv,ssh=None,href=0.): +def _bernoulli_PM07(dom, t, s, uv, ssh=None, href=0.0): """Bernoulli potential. Parameters @@ -1188,8 +1197,8 @@ def _bernoulli_PM07(dom,t,s,uv,ssh=None,href=0.): sea surface height href : float, optional reference depth - - Notes + + Notes ----- following Polton and Marshall (2007) [1]_ @@ -1199,13 +1208,14 @@ def _bernoulli_PM07(dom,t,s,uv,ssh=None,href=0.): """ - uvw = (uv[0],uv[1],0.*uv[0]) - b = pressure(dom,t,s,ssh=ssh) / rau0 - b+= dom.dot(uvw,uvw) / 2. - b+= grav * (href - dom.depthT_3D) - return dom.set_mask(b,dom.tmask) + uvw = (uv[0], uv[1], 0.0 * uv[0]) + b = pressure(dom, t, s, ssh=ssh) / rau0 + b += dom.dot(uvw, uvw) / 2.0 + b += grav * (href - dom.depthT_3D) + return dom.set_mask(b, dom.tmask) + -def _bernoulli_MJN01(dom,t,s,uv,ssh=None,href=0.): +def _bernoulli_MJN01(dom, t, s, uv, ssh=None, href=0.0): """Bernoulli potential. Parameters @@ -1232,23 +1242,23 @@ def _bernoulli_MJN01(dom,t,s,uv,ssh=None,href=0.): .. [1] Marshall Jamous and Niilson, J. of Phys. Ocean. 2001 """ - uvw = (uv[0],uv[1],0.*uv[0]) - rho = insitu(t,s,dom.depthT_3D) - b = pressure(dom,T=t,S=s,rho=rho,ssh=ssh) / rau0 - b+= grav * (href - dom.depthT_3D) * rho /rau0 - return dom.set_mask(b,dom.tmask) - -def _bernoulli_test(dom,t,s,uv,ssh=None,href=0.): - """You should not use this function. - """ - uvw = (uv[0],uv[1],0.*uv[0]) - rho = insitu(t,s,dom.depthT_3D) - b = pressure(dom,rho=rho,ssh=ssh) / rho - b+= grav * (href - dom.depthT_3D) - return dom.set_mask(b,dom.tmask) + uvw = (uv[0], uv[1], 0.0 * uv[0]) + rho = insitu(t, s, dom.depthT_3D) + b = pressure(dom, T=t, S=s, rho=rho, ssh=ssh) / rau0 + b += grav * (href - dom.depthT_3D) * rho / rau0 + return dom.set_mask(b, dom.tmask) + +def _bernoulli_test(dom, t, s, uv, ssh=None, href=0.0): + """You should not use this function.""" + uvw = (uv[0], uv[1], 0.0 * uv[0]) + rho = insitu(t, s, dom.depthT_3D) + b = pressure(dom, rho=rho, ssh=ssh) / rho + b += grav * (href - dom.depthT_3D) + return dom.set_mask(b, dom.tmask) -def helicity(dom,T,S): + +def helicity(dom, T, S): """Neutral helicity. Parameters @@ -1271,27 +1281,28 @@ def helicity(dom,T,S): gS = grad(S) gT = grad(T) # get A - alpha = alpha(T,S,dom.depthT_3D) - beta = beta(T,S,dom.depthT_3D) - ra = dom.lamV(alpha,gT) - rb = dom.lamV(beta,gS) - A = (rb[0]-ra[0],rb[1]-ra[1],rb[2]-ra[2]) + alpha = alpha(T, S, dom.depthT_3D) + beta = beta(T, S, dom.depthT_3D) + ra = dom.lamV(alpha, gT) + rb = dom.lamV(beta, gS) + A = (rb[0] - ra[0], rb[1] - ra[1], rb[2] - ra[2]) # get curl A - alpha_p= alpha_p(T,S,dom.depthT_3D) - beta_p= beta_p(T,S,dom.depthT_3D) - P = pressure(dom,T,S) - gP = grad(P/dbar2pascal) - gPxgT = dom.cross(gP,gT,True) - gPxgS = dom.cross(gP,gS,True) - ta=dom.lamV(alpha_p,gPxgT,True) - tb=dom.lamV(beta_p,gPxgS,True) - cA=(tb[0]-ta[0],tb[1]-ta[1],tb[2]-ta[2]) + alpha_p = alpha_p(T, S, dom.depthT_3D) + beta_p = beta_p(T, S, dom.depthT_3D) + P = pressure(dom, T, S) + gP = grad(P / dbar2pascal) + gPxgT = dom.cross(gP, gT, True) + gPxgS = dom.cross(gP, gS, True) + ta = dom.lamV(alpha_p, gPxgT, True) + tb = dom.lamV(beta_p, gPxgS, True) + cA = (tb[0] - ta[0], tb[1] - ta[1], tb[2] - ta[2]) # get H - H = dom.dot(cA,A,stag_grd=True) - mH = N.core.ma.masked_where((H==0.)+(H>1.)+(H<-1.),H) # happy hard-coding... + H = dom.dot(cA, A, stag_grd=True) + mH = N.core.ma.masked_where( + (H == 0.0) + (H > 1.0) + (H < -1.0), H + ) # happy hard-coding... # return mH # ===== end file ===== - diff --git a/lib/forecast.py b/lib/forecast.py index 2e30de8..9328e1b 100644 --- a/lib/forecast.py +++ b/lib/forecast.py @@ -11,18 +11,25 @@ from joblib import Parallel, delayed, parallel_backend from sklearn.preprocessing import StandardScaler from sklearn.gaussian_process import GaussianProcessRegressor -from sklearn.gaussian_process.kernels import RBF, ExpSineSquared, RationalQuadratic, WhiteKernel, DotProduct#, Matérn +from sklearn.gaussian_process.kernels import ( + RBF, + ExpSineSquared, + RationalQuadratic, + WhiteKernel, + DotProduct, +) # , Matérn import pickle import warnings + warnings.filterwarnings("ignore") -#file #Select the file where the prepared simu was saved -#var #Select the var you want to forecast -def load_ts(file,var): +# file #Select the file where the prepared simu was saved +# var #Select the var you want to forecast +def load_ts(file, var): """ Load time series data from the file where are saved the prepared simulations. - This function is used the get the prepared data info in order to instanciate a prediction class + This function is used the get the prepared data info in order to instantiate a prediction class Parameters: file (str): The path to the file containing the time series data. @@ -33,12 +40,15 @@ def load_ts(file,var): - df (DataFrame): DataFrame containing the time series data. - dico (dict): A dictionary containing all informations on the simu (pca, mean, std, time_dim)... """ - dico = np.load(file+f"/{var}.npz",allow_pickle=True) + dico = np.load(file + f"/{var}.npz", allow_pickle=True) dico = {key: dico[key] for key in dico.keys()} - df = pd.DataFrame(dico["ts"],columns=[f"{var}-{i+1}" for i in range(np.shape(dico["ts"])[1])]) - with open(file+f"/pca_{var}", 'rb') as file: + df = pd.DataFrame( + dico["ts"], columns=[f"{var}-{i+1}" for i in range(np.shape(dico["ts"])[1])] + ) + with open(file + f"/pca_{var}", "rb") as file: dico["pca"] = pickle.load(file) - return df,dico + return df, dico + ################################## ## ## @@ -46,10 +56,10 @@ def load_ts(file,var): ## ## ################################## -class Simulation: +class Simulation: """!!!Modified version where we apply a script to get yearly average for the simu before!!!""" - + """ A class for loading and preparing simulation data. @@ -68,10 +78,12 @@ class Simulation: x_size (int) : The size of the x dimension. z_size (int or None) : The size of the z dimension, if available. shape (tuple) : The shape of the simulation data. - simulation (xarray.DataArray) : The loaded simulation data + simulation (xarray.DataArray) : The loaded simulation data """ - - def __init__(self,path,term,start=0,end=None,comp=0.9,ye=True,ssca=False): #choose jobs 3 if 2D else 1 + + def __init__( + self, path, term, start=0, end=None, comp=0.9, ye=True, ssca=False + ): # choose jobs 3 if 2D else 1 """ Initialize Simulation with specified parameters. @@ -82,23 +94,23 @@ def __init__(self,path,term,start=0,end=None,comp=0.9,ye=True,ssca=False): #choo end (int, optional) : The end index for data slicing. Defaults to None. comp (float, optional) : The comp value for the simulation. Defaults to 0.9. ye (bool, optional) : Flag indicating whether to use ye or not. Defaults to True. - ssca (bool, optional) : Flag indicating whether ssca is used. Defaults to False. #Not used in this class + ssca (bool, optional) : Flag indicating whether ssca is used. Defaults to False. #Not used in this class """ - self.path = path - self.term = term - self.files = Simulation.getData(path,term) + self.path = path + self.term = term + self.files = Simulation.getData(path, term) self.start = start - self.end = end - self.ye = ye - self.comp = comp - self.len = 0 - self.desc = {} - self.getAttributes() #self time_dim, y_size, x_size, - self.getSimu() #self simu , desc {"mean","std","min","max"} + self.end = end + self.ye = ye + self.comp = comp + self.len = 0 + self.desc = {} + self.getAttributes() # self time_dim, y_size, x_size, + self.getSimu() # self simu , desc {"mean","std","min","max"} #### Load files and dimensions info ### - - def getData(path,term): + + def getData(path, term): """ Get the files related to the simulation in the right directory @@ -114,43 +126,50 @@ def getData(path,term): """ grid = [] for file in sorted(os.listdir(path)): - if term+"." in file: #add param!="" - grid.append(path+"/"+file) + if term + "." in file: # add param!="" + grid.append(path + "/" + file) return grid - + def getAttributes(self): """ Get attributes of the simulation data. """ - array = xr.open_dataset(self.files[-1], decode_times=False,chunks={"time": 200, "x":120}) - self.time_dim = list(array.dims)[0] - self.y_size = array.sizes['y'] - self.x_size = array.sizes['x'] + array = xr.open_dataset( + self.files[-1], decode_times=False, chunks={"time": 200, "x": 120} + ) + self.time_dim = list(array.dims)[0] + self.y_size = array.sizes["y"] + self.x_size = array.sizes["x"] if "deptht" in array[self.term].dims: - self.z_size = array.sizes['deptht'] - self.shape = (self.z_size,self.y_size,self.x_size) + self.z_size = array.sizes["deptht"] + self.shape = (self.z_size, self.y_size, self.x_size) elif "olevel" in array[self.term].dims: - self.z_size = array.sizes['olevel'] - self.shape = (self.z_size,self.y_size,self.x_size) + self.z_size = array.sizes["olevel"] + self.shape = (self.z_size, self.y_size, self.x_size) else: self.z_size = None - self.shape = (self.y_size,self.x_size) - #self.getSSCA(array) - + self.shape = (self.y_size, self.x_size) + # self.getSSCA(array) + #### Load simulation ### - + def getSimu(self): """ Load simulation data. """ - #array = list(Parallel(jobs)(delayed(self.loadFile)(file) for file in self.files)) - array = [self.loadFile(file) for file in self.files if self.len self.end: + # if self.len + array.sizes[self.time_dim] > self.end: # array = array[0:self.end-self.len] # self.len = self.len + array.sizes[self.time_dim] - #else: + # else: # self.len = self.len + array.sizes[self.time_dim] return array.load() - ######################### # prepare simulation # ######################### - - def prepare(self,stand=True): + + def prepare(self, stand=True): """ Prepare the simulation data selecting indices from start to end, updating length and obtaining statistics, standardizing if specified. @@ -188,69 +208,85 @@ def prepare(self,stand=True): stand (bool, optional): Flag indicating whether to standardize the simulation data. Defaults to True. """ if self.end is not None: - self.simulation = self.simulation[self.start:self.end] + self.simulation = self.simulation[self.start : self.end] else: - self.simulation = self.simulation[self.start:] - self.len = np.shape(self.simulation)[0] - #self.removeClosedSeas() - self.desc.update({"mean":np.nanmean(self.simulation),"std":np.nanstd(self.simulation), - "min":np.nanmin(self.simulation),"max":np.nanmax(self.simulation)}) + self.simulation = self.simulation[self.start :] + self.len = np.shape(self.simulation)[0] + # self.removeClosedSeas() + self.desc.update( + { + "mean": np.nanmean(self.simulation), + "std": np.nanstd(self.simulation), + "min": np.nanmin(self.simulation), + "max": np.nanmax(self.simulation), + } + ) if stand: - self.standardize() + self.standardize() self.simulation = self.simulation.values - def getSSCA(self,array): + def getSSCA(self, array): """ Extract the seasonality data from the simulation. Not used : we import yearly data Parameters: - array (xarray.Dataset): The last dataset containing simulation data in the simulation file. + array (xarray.Dataset): The last dataset containing simulation data in the simulation file. """ array = array[self.term].values - n = np.shape(array)[0]//12 *12 + n = np.shape(array)[0] // 12 * 12 array = array[-n:] - ssca = np.array(array).reshape((n//12, 12)+ self.shape) #np.array(array[self.term]) - ssca = np.mean(ssca, axis=0) - ssca_extended = np.tile(ssca, (n//12, 1, 1)) + ssca = np.array(array).reshape( + (n // 12, 12) + self.shape + ) # np.array(array[self.term]) + ssca = np.mean(ssca, axis=0) + ssca_extended = np.tile(ssca, (n // 12, 1, 1)) self.desc["ssca"] = sscad - if self.ye==False: + if self.ye == False: self.simulation = array - ssca_extended - - + def removeClosedSeas(self): """ - Remove closed seas from the simulation data. Not used : we don't have the specific mask to fill with predictions - """ - array = self.simulation - y_range = [slice(240, 266),slice(235, 276),slice(160, 201)] #mer noir, grands lacs, lac victoria - x_range = [slice(195, 213),slice(330, 351),slice(310, 325)] - for y,x in zip(y_range,x_range): - array = array.where((array['x'] < x.start) | (array['x'] >= x.stop) | - (array['y'] < y.start) | (array['y'] >= y.stop),drop=True) + Remove closed seas from the simulation data. Not used : we don't have the specific mask to fill with predictions + """ + array = self.simulation + y_range = [ + slice(240, 266), + slice(235, 276), + slice(160, 201), + ] # mer noir, grands lacs, lac victoria + x_range = [slice(195, 213), slice(330, 351), slice(310, 325)] + for y, x in zip(y_range, x_range): + array = array.where( + (array["x"] < x.start) + | (array["x"] >= x.stop) + | (array["y"] < y.start) + | (array["y"] >= y.stop), + drop=True, + ) self.simulation = array - + def standardize(self): """ Standardize the simulation data. """ - self.simulation = (self.simulation - self.desc["mean"]) / (2*self.desc["std"]) - + self.simulation = (self.simulation - self.desc["mean"]) / (2 * self.desc["std"]) + ################## # Compute PCA # ################## - + def applyPCA(self): """ Apply Principal Component Analysis (PCA) to the simulation data. """ - array = self.simulation.reshape(self.len,-1) - self.bool_mask = np.asarray(np.isfinite(array[0,:]), dtype=bool) - array_masked = array[:,self.bool_mask] + array = self.simulation.reshape(self.len, -1) + self.bool_mask = np.asarray(np.isfinite(array[0, :]), dtype=bool) + array_masked = array[:, self.bool_mask] pca = PCA(self.comp, whiten=False) self.components = pca.fit_transform(array_masked) - self.pca = pca - - def getPC(self,n): + self.pca = pca + + def getPC(self, n): """ Get principal component map for the specified component. @@ -260,41 +296,40 @@ def getPC(self,n): Returns: (numpy.ndarray): The principal component map. """ - map_ = np.zeros((np.product(self.shape)), dtype=float) + map_ = np.zeros((np.prod(self.shape)), dtype=float) map_[~self.bool_mask] = np.nan - map_[self.bool_mask] = self.pca.components_[n] + map_[self.bool_mask] = self.pca.components_[n] map_ = map_.reshape(self.shape) - map_ = 2 * map_ * self.desc["std"] + self.desc["mean"] + map_ = 2 * map_ * self.desc["std"] + self.desc["mean"] return map_ - - ############################### NOT USED IN THE MAIN.PY ############################### - def rmseOfPCA(self,n): + ############################### NOT USED IN THE MAIN.PY ############################### + def rmseOfPCA(self, n): reconstruction = self.reconstruct(n) - rmse_values = self.rmseValues(reconstruction) * 2 * self.desc["std"] - rmse_map = self.rmseMap(reconstruction) * 2 * self.desc["std"] + rmse_values = self.rmseValues(reconstruction) * 2 * self.desc["std"] + rmse_map = self.rmseMap(reconstruction) * 2 * self.desc["std"] return reconstruction, rmse_values, rmse_map - - def rmseValues(self,reconstruction): - truth = self.simulation# * 2 * self.desc["std"] + self.desc["mean"] - rec = reconstruction # * 2 * self.desc["std"] + self.desc["mean"] - if len(np.shape(truth))==3: + + def rmseValues(self, reconstruction): + truth = self.simulation # * 2 * self.desc["std"] + self.desc["mean"] + rec = reconstruction # * 2 * self.desc["std"] + self.desc["mean"] + if len(np.shape(truth)) == 3: n = np.count_nonzero(~np.isnan(truth[0])) - rmse_values = np.sqrt(np.nansum((truth-rec)**2,axis=(1,2))/n) + rmse_values = np.sqrt(np.nansum((truth - rec) ** 2, axis=(1, 2)) / n) else: - n = np.count_nonzero(~np.isnan(self.simulation[0]),axis=(1,2)) - rmse_values = np.nansum((truth-rec)**2,axis=(2,3)) + n = np.count_nonzero(~np.isnan(self.simulation[0]), axis=(1, 2)) + rmse_values = np.nansum((truth - rec) ** 2, axis=(2, 3)) for i in range(len(n)): - rmse_values[:,i] = rmse_values[:,i]/n[i] + rmse_values[:, i] = rmse_values[:, i] / n[i] rmse_values = np.sqrt(rmse_values) - return rmse_values - - def rmseMap(self,reconstruction): + return rmse_values + + def rmseMap(self, reconstruction): t = self.len truth = self.simulation - reconstruction = reconstruction - return np.sqrt(np.sum((self.simulation[:]-reconstruction)**2,axis=0)/t) - + reconstruction = reconstruction + return np.sqrt(np.sum((self.simulation[:] - reconstruction) ** 2, axis=0) / t) + def reconstruct(self, n): """ Reconstruct data using a specified number of principal components. @@ -306,13 +341,17 @@ def reconstruct(self, n): (numpy.array) : The reconstructed data. """ rec = [] - #int_mask = # Convert the boolean mask to int mask once - self.int_mask = self.bool_mask.astype(np.int32).reshape(self.shape) # Reshape to match the shape of map_ + # int_mask = # Convert the boolean mask to int mask once + self.int_mask = self.bool_mask.astype(np.int32).reshape( + self.shape + ) # Reshape to match the shape of map_ for t in range(len(self.components)): map_ = np.zeros(self.shape, dtype=np.float32) - arr = np.array(list(self.components[t, :n]) + [0] * (len(self.pca.components_) - n)) - map_[self.int_mask==1] = self.pca.inverse_transform(arr) - map_[self.int_mask==0] = np.nan + arr = np.array( + list(self.components[t, :n]) + [0] * (len(self.pca.components_) - n) + ) + map_[self.int_mask == 1] = self.pca.inverse_transform(arr) + map_[self.int_mask == 0] = np.nan rec.append(map_) return np.array(rec) @@ -321,7 +360,7 @@ def reconstruct(self, n): ################## # Save in db # ################## - + def makeDico(self): """ Create a dictionary containing simulation data, descriptive statistics, and other relevant information. @@ -330,18 +369,18 @@ def makeDico(self): (dict) : A dictionary containing simulation data and information. """ dico = dict() - dico["ts"] = self.components.tolist() - dico["mask"] = self.bool_mask - dico["desc"] = self.desc - dico["cut"] = self.start - dico["x_size"]= self.x_size - dico["y_size"]= self.y_size + dico["ts"] = self.components.tolist() + dico["mask"] = self.bool_mask + dico["desc"] = self.desc + dico["cut"] = self.start + dico["x_size"] = self.x_size + dico["y_size"] = self.y_size if self.z_size is not None: - dico["z_size"]= self.z_size - dico["shape"]= self.shape + dico["z_size"] = self.z_size + dico["shape"] = self.shape return dico - - def save(self,file,term): + + def save(self, file, term): """ Save the simulation data and information to files. @@ -350,9 +389,9 @@ def save(self,file,term): term (str): The term used in the filenames. """ simu_dico = self.makeDico() - if not os.path.exists(file): #save infos + if not os.path.exists(file): # save infos os.makedirs(file) - with open(f"{file}/{term}/pca_{term}", 'wb') as f: + with open(f"{file}/{term}/pca_{term}", "wb") as f: pickle.dump(self.pca, f) np.savez(f"{file}/{term}/{term}", **simu_dico) @@ -362,10 +401,9 @@ def save(self,file,term): ## Forecast & reconstruct ## ## ## ################################# - -class Predictions: +class Predictions: """ Class for forecasting and reconstructing time series data using Gaussian Processes. @@ -376,8 +414,8 @@ class Predictions: gp (GaussianProcessRegressor) : The Gaussian Process regressor. w (int) : Width for moving average and metrics calculation. """ - - def __init__(self,var,data=None,info=None,gp=None,w=12): + + def __init__(self, var, data=None, info=None, gp=None, w=12): """ Initialize the Predictions object. @@ -388,21 +426,21 @@ def __init__(self,var,data=None,info=None,gp=None,w=12): gp (GaussianProcessRegressor) : The Gaussian Process regressor. w (int) : Width for moving average and metrics calculation. """ - self.var = var - self.gp = Predictions.defineGP() if gp is None else gp - self.w = w + self.var = var + self.gp = Predictions.defineGP() if gp is None else gp + self.w = w self.data = data - self.info =info + self.info = info self.info["desc"] = self.info["desc"].item() self.len_ = len(self.data) - + def __len__(self): return len(self.data) - + ################## # Forecast # ################## - + @staticmethod def defineGP(): """ @@ -411,18 +449,25 @@ def defineGP(): - an irregularities_kernel for periodic patterns CHANGER 5/45 1/len(data)? - a noise_kernel We also set a n_restarts_optimizer to optimize hyperparameters - + Returns: GaussianProcessRegressor: The Gaussian Process regressor. """ - long_term_trend_kernel = 0.1*DotProduct(sigma_0=0.0) #+ 0.5*RBF(length_scale=1/2)# + - irregularities_kernel = 10 * ExpSineSquared(length_scale=5/45, periodicity=5/45)#0.5**2*RationalQuadratic(length_scale=5.0, alpha=1.0) + 10 * ExpSineSquared(length_scale=5.0) - noise_kernel = 2*WhiteKernel(noise_level=1)#0.1**2*RBF(length_scale=0.01) + 2*WhiteKernel(noise_level=1) - kernel = irregularities_kernel + noise_kernel +long_term_trend_kernel - return GaussianProcessRegressor(kernel=kernel, normalize_y=False,n_restarts_optimizer=8) - - - def Forecast(self,train_len,steps,jobs=1): + long_term_trend_kernel = 0.1 * DotProduct( + sigma_0=0.0 + ) # + 0.5*RBF(length_scale=1/2)# + + irregularities_kernel = ( + 10 * ExpSineSquared(length_scale=5 / 45, periodicity=5 / 45) + ) # 0.5**2*RationalQuadratic(length_scale=5.0, alpha=1.0) + 10 * ExpSineSquared(length_scale=5.0) + noise_kernel = 2 * WhiteKernel( + noise_level=1 + ) # 0.1**2*RBF(length_scale=0.01) + 2*WhiteKernel(noise_level=1) + kernel = irregularities_kernel + noise_kernel + long_term_trend_kernel + return GaussianProcessRegressor( + kernel=kernel, normalize_y=False, n_restarts_optimizer=8 + ) + + def Forecast(self, train_len, steps, jobs=1): """ Parallel forecast of time series data/eofs using an independent GP for each time series @@ -437,13 +482,20 @@ def Forecast(self,train_len,steps,jobs=1): - y_stds (DataFrame) : Standard deviations of the forecasts. - metrics (list of dict) : One dict of metrics by forecast """ - r = Parallel(n_jobs=jobs)(delayed(self.forecast_ts)(c,train_len,steps) for c in range(1,self.data.shape[1]+1)) - y_hats = pd.DataFrame(np.array([r[i][0] for i in range(len(r))]).T, columns=self.data.columns) - y_stds = pd.DataFrame(np.array([r[i][1] for i in range(len(r))]).T, columns=self.data.columns) - metrics = [r[i][2] for i in range(len(r))] + r = Parallel(n_jobs=jobs)( + delayed(self.forecast_ts)(c, train_len, steps) + for c in range(1, self.data.shape[1] + 1) + ) + y_hats = pd.DataFrame( + np.array([r[i][0] for i in range(len(r))]).T, columns=self.data.columns + ) + y_stds = pd.DataFrame( + np.array([r[i][1] for i in range(len(r))]).T, columns=self.data.columns + ) + metrics = [r[i][2] for i in range(len(r))] return y_hats, y_stds, metrics - - def forecast_ts(self,n,train_len,steps=0): + + def forecast_ts(self, n, train_len, steps=0): """ Forecast of one time series, function called in parallel in Forecast @@ -459,16 +511,20 @@ def forecast_ts(self,n,train_len,steps=0): metrics (dict) : Dictionary of metrics defined in the corresponding function """ random.seed(20) - mean,std,y_train,y_test,x_train,x_pred = self.prepare(n,train_len,steps) - self.gp.fit(x_train,y_train) - y_hat,y_hat_std = self.gp.predict(x_pred,return_std=True) - y_train,y_hat,y_hat_std = y_train*2*std+mean, y_hat*2*std+mean, y_hat_std*2*std + mean, std, y_train, y_test, x_train, x_pred = self.prepare(n, train_len, steps) + self.gp.fit(x_train, y_train) + y_hat, y_hat_std = self.gp.predict(x_pred, return_std=True) + y_train, y_hat, y_hat_std = ( + y_train * 2 * std + mean, + y_hat * 2 * std + mean, + y_hat_std * 2 * std, + ) metrics = None if y_test is not None: - metrics = Predictions.getMetrics(2,y_hat[train_len:len(self)],y_test) - return y_hat,y_hat_std,metrics + metrics = Predictions.getMetrics(2, y_hat[train_len : len(self)], y_test) + return y_hat, y_hat_std, metrics - def prepare(self,n,train_len,steps): + def prepare(self, n, train_len, steps): """ Prepare data for forecasting. @@ -486,18 +542,22 @@ def prepare(self,n,train_len,steps): x_train (numpy.array): Training features. x_pred (numpy.array): Prediction features. """ - x_train = np.linspace(0,1,train_len).reshape(-1,1) - pas = x_train[1,0]-x_train[0,0] - x_pred = np.arange(0,(len(self)+steps)*pas,pas).reshape(-1,1) - y_train = self.data[self.var+"-"+str(n)].iloc[:train_len].to_numpy() - mean, std = np.nanmean(y_train), np.nanstd(y_train) - y_train = (y_train-mean)/(2.0*std) - y_test = None - if train_len self.current_score_eval] - results=[] + gps_test = [ + process + for process, score in self.scores_eval + if score > self.current_score_eval + ] + results = [] for simu in self.simu_test: - print(f"Processing simulation {simu.id}",end="") - if self.min_train < len(simu)-self.min_test: - train_lens = np.arange(self.min_train,len(simu)-self.min_test,self.steps) - results.append([self.evaluateProcess(simu,train_lens,process) for process in gps_test]) - print("",end="\n") - results = [(process,score) for process,score in zip(gps_test, np.sum(results,axis=0))] + print(f"Processing simulation {simu.id}", end="") + if self.min_train < len(simu) - self.min_test: + train_lens = np.arange( + self.min_train, len(simu) - self.min_test, self.steps + ) + results.append( + [ + self.evaluateProcess(simu, train_lens, process) + for process in gps_test + ] + ) + print("", end="\n") + results = [ + (process, score) + for process, score in zip(gps_test, np.sum(results, axis=0)) + ] self.scores_test = sorted(results, key=lambda item: item[1], reverse=True) - - - - - - diff --git a/lib/restart.py b/lib/restart.py index 2ef9e9b..136eded 100644 --- a/lib/restart.py +++ b/lib/restart.py @@ -1,37 +1,40 @@ import numpy as np import xarray as xr -import densite +import density import matplotlib.pyplot as plt -import copy +import copy import os import glob + # SUPER LONG PEUT ETRE LE FAIR EN BASH OU ERREUR -def getRestartFiles(path,radical,puzzled=False): +def getRestartFiles(path, radical, puzzled=False): """ Get the restart files of the last simulation step Parameters: - path (str): The path to the restarts files - radical (str): Radical of the file name - (e.g. for "OCE_CM65-LR-pi-SpinupRef_19141231_00390.nc", it’s "OCE_CM65-LR-pi-SpinupRef_19141231") + path (str): The path to the restarts files + radical (str): Radical of the file name + (e.g. for "OCE_CM65-LR-pi-SpinupRef_19141231_00390.nc", it's "OCE_CM65-LR-pi-SpinupRef_19141231") puzzled (bool): Return Complete Restart File (False) or List of windowed files (True) Returns: list (of str) or str : List of restart puzzled files paths, or unique restart file path """ print("Retrieving Restart File(s)") - if puzzled==True: - return glob.glob(path+radical+"_*.nc").sorted() + if puzzled == True: + return glob.glob(path + radical + "_*.nc").sorted() else: try: - return glob.glob(path+radical+".nc")[0] + return glob.glob(path + radical + ".nc")[0] except IndexError: - print("No Full Restart Found : Use NEMO_REBUILD from NEMO tools if you didn’t do it yet.") + print( + "No Full Restart Found : Use NEMO_REBUILD from NEMO tools if you didn't do it yet." + ) raise -def getMaskFile(maskpath,restart): +def getMaskFile(maskpath, restart): """ Get the mask file and adapt it to fit the restart file coordinate system. @@ -42,78 +45,83 @@ def getMaskFile(maskpath,restart): Returns: mask (xarray.Dataset) : The mask dataset adapted to the restart file. """ - mask = xr.open_dataset(maskpath,decode_times=False) + mask = xr.open_dataset(maskpath, decode_times=False) # Harmonizing the structure of mask with that of restart - mask = mask.swap_dims(dims_dict={"z": "nav_lev","t":"time_counter"}) - mask["time_counter"]=restart["time_counter"] + mask = mask.swap_dims(dims_dict={"z": "nav_lev", "t": "time_counter"}) + mask["time_counter"] = restart["time_counter"] return mask -def recordFullRestart(path,radical,restart): + +def recordFullRestart(path, radical, restart): """ Record the Modified Full Restart Dataset to a file in the input directory for analysis. Parameters: - path (str): The path to the restart file directory - radical (str): Radical of the original restart file name + path (str): The path to the restart file directory + radical (str): Radical of the original restart file name (e.g. for "OCE_CM65-LR-pi-SpinupRef_19141231_restart_00390.nc", it’s "OCE_CM65-LR-pi-SpinupRef_19141231_restart") restart (xarray.Dataset): The full restart file we are modifying. Returns: str : Recording Completion Message """ - restart.to_netcdf(path+"NEW_"+radical+".nc") - print("Restart saved as : "+ path+"NEW_"+radical+".nc") + restart.to_netcdf(path + "NEW_" + radical + ".nc") + print("Restart saved as : " + path + "NEW_" + radical + ".nc") return "Recording Complete" -def recordPiecedRestart(path,radical,restart): + +def recordPiecedRestart(path, radical, restart): """ Record the Modified Puzzled Restart Datasets to files in the input directory for analysis. It is done by iterating on the existing puzzled dataset files, and creating new ones by appending "NEW_" in front of the filename. If the user want to overwrite the old files, they will need to do it manually (a 4 line bash script is available). Parameters: - path (str): The path to the restart file directory - radical (str): Radical of the original restart file name + path (str): The path to the restart file directory + radical (str): Radical of the original restart file name (e.g. for "OCE_CM65-LR-pi-SpinupRef_19141231_restart_00390.nc", it’s "OCE_CM65-LR-pi-SpinupRef_19141231_restart") restart (xarray.Dataset): The full restart file we are modifying. Returns: str : Recording Completion Message """ - size=len(glob.glob(path+radical+"_*.nc")) + size = len(glob.glob(path + radical + "_*.nc")) for index in range(size): - Restart_NEW=xr.open_dataset(path+radical+"_%04d.nc"%(index)) - x_slice,y_slice = getXYslice(Restart_NEW) - Restart_NEW["un"]=restart["un"][:,:,y_slice,x_slice] - Restart_NEW["vn"]=restart["vn"][:,:,y_slice,x_slice] - Restart_NEW["ub"]=restart["ub"][:,:,y_slice,x_slice] - Restart_NEW["vb"]=restart["vb"][:,:,y_slice,x_slice] - Restart_NEW["sn"]=restart["sn"][:,:,y_slice,x_slice] - Restart_NEW["tn"]=restart["tn"][:,:,y_slice,x_slice] - Restart_NEW["sb"]=restart["sb"][:,:,y_slice,x_slice] - Restart_NEW["tb"]=restart["tb"][:,:,y_slice,x_slice] - - Restart_NEW["rhop"]=restart["rhop"][:,:,y_slice,x_slice] - - Restart_NEW["sshn"]=restart["sshn"][:,y_slice,x_slice] - Restart_NEW["sshb"]=restart["sshb"][:,y_slice,x_slice] - - Restart_NEW["ssv_m"]=restart["ssv_m"][:,y_slice,x_slice] - Restart_NEW["ssu_m"]=restart["ssu_m"][:,y_slice,x_slice] - Restart_NEW["sst_m"]=restart["sst_m"][:,y_slice,x_slice] - Restart_NEW["sss_m"]=restart["sss_m"][:,y_slice,x_slice] - Restart_NEW["ssh_m"]=restart["ssh_m"][:,y_slice,x_slice] - Restart_NEW["e3t_m"]=restart["e3t_m"][:,y_slice,x_slice] - - Restart_NEW.to_netcdf(path+"NEW_"+radical+"_%04d.nc"%(index)) - print("Restart Piece saved as : "+ path+"NEW_"+radical+"_%04d.nc"%(index)) + Restart_NEW = xr.open_dataset(path + radical + "_%04d.nc" % (index)) + x_slice, y_slice = getXYslice(Restart_NEW) + Restart_NEW["un"] = restart["un"][:, :, y_slice, x_slice] + Restart_NEW["vn"] = restart["vn"][:, :, y_slice, x_slice] + Restart_NEW["ub"] = restart["ub"][:, :, y_slice, x_slice] + Restart_NEW["vb"] = restart["vb"][:, :, y_slice, x_slice] + Restart_NEW["sn"] = restart["sn"][:, :, y_slice, x_slice] + Restart_NEW["tn"] = restart["tn"][:, :, y_slice, x_slice] + Restart_NEW["sb"] = restart["sb"][:, :, y_slice, x_slice] + Restart_NEW["tb"] = restart["tb"][:, :, y_slice, x_slice] + + Restart_NEW["rhop"] = restart["rhop"][:, :, y_slice, x_slice] + + Restart_NEW["sshn"] = restart["sshn"][:, y_slice, x_slice] + Restart_NEW["sshb"] = restart["sshb"][:, y_slice, x_slice] + + Restart_NEW["ssv_m"] = restart["ssv_m"][:, y_slice, x_slice] + Restart_NEW["ssu_m"] = restart["ssu_m"][:, y_slice, x_slice] + Restart_NEW["sst_m"] = restart["sst_m"][:, y_slice, x_slice] + Restart_NEW["sss_m"] = restart["sss_m"][:, y_slice, x_slice] + Restart_NEW["ssh_m"] = restart["ssh_m"][:, y_slice, x_slice] + Restart_NEW["e3t_m"] = restart["e3t_m"][:, y_slice, x_slice] + + Restart_NEW.to_netcdf(path + "NEW_" + radical + "_%04d.nc" % (index)) + print( + "Restart Piece saved as : " + path + "NEW_" + radical + "_%04d.nc" % (index) + ) return "Recording Complete" -def load_predictions(restart,dirpath="/data/mtissot/simus_predicted"): + +def load_predictions(restart, dirpath="/data/mtissot/simus_predicted"): """ Load predicted data from saved NumPy files into the restart array. - We use the same prediction step for now and before steps (e.g sshn/sshb). - We also update the intermediate step surface variables (e.g. sst_m). + We use the same prediction step for now and before steps (e.g sshn/sshb). + We also update the intermediate step surface variables (e.g. sst_m). Returns: restart (xarray.Dataset) with the following primary variables modified : @@ -121,31 +129,37 @@ def load_predictions(restart,dirpath="/data/mtissot/simus_predicted"): so (xarray.DataArray) : salinity predictions - (t,z,y,x) thetao (xarray.DataArray) : temperature predictions - (t,z,y,x) """ - ## Loading new SSH in directly affected variables - ## (loading zos.npy, selecting last snapshot, then converting to fitting xarray.DataArray, and cleaning the nans) + ## Loading new SSH in directly affected variables + ## (loading zos.npy, selecting last snapshot, then converting to fitting xarray.DataArray, and cleaning the nans) try: - zos=np.load(dirpath+"/zos.npy")[-1:] - restart["sshn"] = xr.DataArray(zos, dims=("time_counter", "y", "x"), name="sshn").fillna(0) + zos = np.load(dirpath + "/zos.npy")[-1:] + restart["sshn"] = xr.DataArray( + zos, dims=("time_counter", "y", "x"), name="sshn" + ).fillna(0) restart["sshb"] = restart["sshn"].copy() restart["ssh_m"] = restart["sshn"].copy() except FileNotFoundError: print("Couldn’t find a SSH/ZOS prediction file, keeping the original SSH.") - ## Loading new SO in directly affected variables - ## (loading so.npy, selecting last snapshot, then converting to fitting xarray.DataArray, and cleaning the nans) + ## Loading new SO in directly affected variables + ## (loading so.npy, selecting last snapshot, then converting to fitting xarray.DataArray, and cleaning the nans) try: - so = np.load(dirpath+"/so.npy")[-1:] - restart["sn"] = xr.DataArray(so, dims=("time_counter", "nav_lev","y", "x"), name="sn").fillna(0) + so = np.load(dirpath + "/so.npy")[-1:] + restart["sn"] = xr.DataArray( + so, dims=("time_counter", "nav_lev", "y", "x"), name="sn" + ).fillna(0) restart["sb"] = restart["sn"].copy() restart["sss_m"] = restart["sn"].isel(nav_lev=0).copy() except FileNotFoundError: print("Couldn’t find a SO prediction file, keeping the original SO.") - ## Loading new THETAO in directly affected variables - ## (loading thetao.npy, selecting last snapshot, then converting to fitting xarray.DataArray, and cleaning the nans) + ## Loading new THETAO in directly affected variables + ## (loading thetao.npy, selecting last snapshot, then converting to fitting xarray.DataArray, and cleaning the nans) try: - thetao=np.load(dirpath+"/thetao.npy")[-1:] - restart["tn"] = xr.DataArray(thetao, dims=("time_counter", "nav_lev","y", "x"), name="tn").fillna(0) + thetao = np.load(dirpath + "/thetao.npy")[-1:] + restart["tn"] = xr.DataArray( + thetao, dims=("time_counter", "nav_lev", "y", "x"), name="tn" + ).fillna(0) restart["tb"] = restart["tn"].copy() restart["sst_m"] = restart["tn"].isel(nav_lev=0).copy() except FileNotFoundError: @@ -161,21 +175,21 @@ def getXYslice(restart): Parameters: restart (xarray.Dataset) : Restart file - + Returns x_slice (slice dtype=float) : range of x positions y_slice (slice dtype=float) : range of y positions """ - First = restart.DOMAIN_position_first - Last = restart.DOMAIN_position_last - x_slice = slice(First[0]-1,Last[0]) - y_slice = slice(First[1]-1,Last[1]) + First = restart.DOMAIN_position_first + Last = restart.DOMAIN_position_last + x_slice = slice(First[0] - 1, Last[0]) + y_slice = slice(First[1] - 1, Last[1]) return x_slice, y_slice -def toXarray(var,name,dep=True,fillna=True): +def toXarray(var, name, dep=True, fillna=True): """ - Converts a numpy array into an xarray DataArray and replace nan values by 0 to obtain the same data format as in restart files. + Converts a numpy array into an xarray DataArray and replace nan values by 0 to obtain the same data format as in restart files. Parameters: var (numpy array) : The array to be converted. @@ -189,60 +203,61 @@ def toXarray(var,name,dep=True,fillna=True): array (xarray.DataArray): An xarray DataArray object representing the input numpy array. """ if dep: - if len(np.shape(var))==4: - array = xr.DataArray(var, dims=("time_counter", "nav_lev", "y", "x"), name=name) - elif len(np.shape(var))==3: + if len(np.shape(var)) == 4: + array = xr.DataArray( + var, dims=("time_counter", "nav_lev", "y", "x"), name=name + ) + elif len(np.shape(var)) == 3: array = xr.DataArray(var, dims=("nav_lev", "y", "x"), name=name) else: - if len(np.shape(var))==3: + if len(np.shape(var)) == 3: array = xr.DataArray(var, dims=("time_counter", "y", "x"), name=name) - elif len(np.shape(var))==2: + elif len(np.shape(var)) == 2: array = xr.DataArray(var, dims=("y", "x"), name=name) return array.fillna(0) -def propagate_pred(restart,mask): + +def propagate_pred(restart, mask): """ - Update the variables indirectly affected by the prediction on primary variables (e.g. geostrophic velocities and density). - + Update the variables indirectly affected by the prediction on primary variables (e.g. geostrophic velocities and density). + Parameters: - restart (xarray.Dataset) : Full Restart file - + restart (xarray.Dataset) : Full Restart file + Returns: restart (xarray.Dataset) : Full Restart file with all variables modified according to the predictions. """ - thetao=restart.tn - so=restart.sn + thetao = restart.tn + so = restart.sn - deptht = get_deptht(restart,mask) - rhop_new,_= get_density(thetao,so,deptht,mask.tmask) + deptht = get_deptht(restart, mask) + rhop_new, _ = get_density(thetao, so, deptht, mask.tmask) - e3t_new = update_e3t(restart,mask) - u_new = update_u_velocity(restart,mask,e3t_new).fillna(0) - v_new = update_v_velocity(restart,mask,e3t_new).fillna(0) + e3t_new = update_e3t(restart, mask) + u_new = update_u_velocity(restart, mask, e3t_new).fillna(0) + v_new = update_v_velocity(restart, mask, e3t_new).fillna(0) - restart["un"]=u_new.copy() - restart["vn"]=v_new.copy() - restart["ub"]=u_new.copy() - restart["vb"]=v_new.copy() - restart["rhop"]=rhop_new.fillna(0) - restart["ssv_m"]=v_new.isel(nav_lev=0) - restart["ssu_m"]=u_new.isel(nav_lev=0) - restart["e3t_m"]=e3t_new.isel(nav_lev=0).fillna(0) + restart["un"] = u_new.copy() + restart["vn"] = v_new.copy() + restart["ub"] = u_new.copy() + restart["vb"] = v_new.copy() + restart["rhop"] = rhop_new.fillna(0) + restart["ssv_m"] = v_new.isel(nav_lev=0) + restart["ssu_m"] = u_new.isel(nav_lev=0) + restart["e3t_m"] = e3t_new.isel(nav_lev=0).fillna(0) return restart - - -def update_e3t(restart,mask): +def update_e3t(restart, mask): """ Update e3t_m : the cell thickness of the top layer. - Get e3t : the cell thickness for all dimensions, we can use e3t to get the new bathymetry and to update u and velocities + Get e3t : the cell thickness for all dimensions, we can use e3t to get the new bathymetry and to update u and velocities e3t = e3t_initital * (1+tmask4D*np.expand_dims(np.tile(ssh*ssmask/(bathy+(1-ssmask)),(75,1,1)),axis=0)) - Get deptht : The depth of each cell on grid. we use it to update the density rhop. - + Get deptht : The depth of each cell on grid. We use it to update the density rhop. + Parameters: mask (xarray.Dataset) : Mask Dataset containing tmask values restart (xarray.Dataset) : Restart file @@ -250,19 +265,20 @@ def update_e3t(restart,mask): Returns: e3t (numpy.ndarray) : Updated array of z-axis cell thicknesses. """ - e3t_ini = restart.e3t_ini # initial z axis cell's thickness on grid T - (t,z,y,x) - ssmask = mask.tmask.max(dim="nav_lev") # continent mask - (t,y,x) - bathy = e3t_ini.sum(dim="nav_lev") # inital Bathymetry - (t,y,x) - ssh = restart.sshn # Sea Surface Height - (t,y,x) - tmask = mask.tmask # bathy mask on grid T - (t,z,y,x) - e3t = e3t_ini*(1+ssh*ssmask/(bathy+1-ssmask)) # - (t,y,x) + e3t_ini = restart.e3t_ini # initial z axis cell's thickness on grid T - (t,z,y,x) + ssmask = mask.tmask.max( + dim="nav_lev" + ) # continent mask - (t,y,x) + bathy = e3t_ini.sum( + dim="nav_lev" + ) # inital Bathymetry - (t,y,x) + ssh = restart.sshn # Sea Surface Height - (t,y,x) + tmask = mask.tmask # bathy mask on grid T - (t,z,y,x) + e3t = e3t_ini * (1 + ssh * ssmask / (bathy + 1 - ssmask)) # - (t,y,x) return e3t - - - -def get_deptht(restart,mask): +def get_deptht(restart, mask): """ Calculate the depth of each vertical level on grid T in the 3D grid. @@ -273,22 +289,22 @@ def get_deptht(restart,mask): Returns: deptht (numpy.array) : The depth of each vertical level. """ - ssh=restart.sshn - e3w_0 = mask.e3w_0 #initial z axis cell's thickness on grid W - (t,z,y,x) - e3t_0 = mask.e3t_0 #initial z axis cell's thickness on grid T - (t,z,y,x) - tmask = mask.tmask #grid T continent mask - (t,z,y,x) - ssmask = tmask[:,0] #bathymetry - (t,y,x) - bathy = e3t_0.sum(dim="nav_lev") #initial condition depth 0 - (t,z,y,x) + ssh = restart.sshn + e3w_0 = mask.e3w_0 # initial z axis cell's thickness on grid W - (t,z,y,x) + e3t_0 = mask.e3t_0 # initial z axis cell's thickness on grid T - (t,z,y,x) + tmask = mask.tmask # grid T continent mask - (t,z,y,x) + ssmask = tmask[:, 0] # bathymetry - (t,y,x) + bathy = e3t_0.sum( + dim="nav_lev" + ) # initial condition depth 0 - (t,z,y,x) depth_0 = e3w_0.copy() - depth_0[:,0] = 0.5 * e3w_0[:,0] - depth_0[:,1:] = depth_0[:,0:1].data + e3w_0[:,1:].cumsum(dim="nav_lev") - deptht = depth_0 * (1+ssh/(bathy + 1 - ssmask )) * tmask + depth_0[:, 0] = 0.5 * e3w_0[:, 0] + depth_0[:, 1:] = depth_0[:, 0:1].data + e3w_0[:, 1:].cumsum(dim="nav_lev") + deptht = depth_0 * (1 + ssh / (bathy + 1 - ssmask)) * tmask return deptht - - -def update_rhop(restart,mask): +def update_rhop(restart, mask): """ Update the rhop variable in the array based on temperature (thetao) and salinity (so). @@ -297,19 +313,19 @@ def update_rhop(restart,mask): mask (xarray.Dataset) : Mask file Returns: - rhop (xarray.DataArray) - """ - x_slice,y_slice = getXYslice(array) - so = restart['sn'] - thetao = restart['tn'] - tmask = mask["tmask"][-1:,:,y_slice,x_slice] - deptht = get_depth(restart,mask) - - rhop, rho_insitu = get_density(thetao,so,deptht,tmask) + rhop (xarray.DataArray) + """ + x_slice, y_slice = getXYslice(array) + so = restart["sn"] + thetao = restart["tn"] + tmask = mask["tmask"][-1:, :, y_slice, x_slice] + deptht = get_depth(restart, mask) + + rhop, rho_insitu = get_density(thetao, so, deptht, tmask) return rhop -def get_density(thetao,so,depth,tmask): +def get_density(thetao, so, depth, tmask): """ Compute potential density referenced at the surface and density anomaly. @@ -325,44 +341,44 @@ def get_density(thetao,so,depth,tmask): array: Density anomaly. """ rdeltaS = 32.0 - r1_S0 = 0.875/35.16504 - r1_T0 = 1./40. - r1_Z0 = 1.e-4 - - EOS000 = 8.0189615746e+02 - EOS100 = 8.6672408165e+02 - EOS200 = -1.7864682637e+03 - EOS300 = 2.0375295546e+03 - EOS400 = -1.2849161071e+03 - EOS500 = 4.3227585684e+02 - EOS600 = -6.0579916612e+01 - EOS010 = 2.6010145068e+01 - EOS110 = -6.5281885265e+01 - EOS210 = 8.1770425108e+01 - EOS310 = -5.6888046321e+01 - EOS410 = 1.7681814114e+01 + r1_S0 = 0.875 / 35.16504 + r1_T0 = 1.0 / 40.0 + r1_Z0 = 1.0e-4 + + EOS000 = 8.0189615746e02 + EOS100 = 8.6672408165e02 + EOS200 = -1.7864682637e03 + EOS300 = 2.0375295546e03 + EOS400 = -1.2849161071e03 + EOS500 = 4.3227585684e02 + EOS600 = -6.0579916612e01 + EOS010 = 2.6010145068e01 + EOS110 = -6.5281885265e01 + EOS210 = 8.1770425108e01 + EOS310 = -5.6888046321e01 + EOS410 = 1.7681814114e01 EOS510 = -1.9193502195 - EOS020 = -3.7074170417e+01 - EOS120 = 6.1548258127e+01 - EOS220 = -6.0362551501e+01 - EOS320 = 2.9130021253e+01 + EOS020 = -3.7074170417e01 + EOS120 = 6.1548258127e01 + EOS220 = -6.0362551501e01 + EOS320 = 2.9130021253e01 EOS420 = -5.4723692739 - EOS030 = 2.1661789529e+01 - EOS130 = -3.3449108469e+01 - EOS230 = 1.9717078466e+01 + EOS030 = 2.1661789529e01 + EOS130 = -3.3449108469e01 + EOS230 = 1.9717078466e01 EOS330 = -3.1742946532 EOS040 = -8.3627885467 - EOS140 = 1.1311538584e+01 + EOS140 = 1.1311538584e01 EOS240 = -5.3563304045 EOS050 = 5.4048723791e-01 EOS150 = 4.8169980163e-01 EOS060 = -1.9083568888e-01 - EOS001 = 1.9681925209e+01 - EOS101 = -4.2549998214e+01 - EOS201 = 5.0774768218e+01 - EOS301 = -3.0938076334e+01 + EOS001 = 1.9681925209e01 + EOS101 = -4.2549998214e01 + EOS201 = 5.0774768218e01 + EOS301 = -3.0938076334e01 EOS401 = 6.6051753097 - EOS011 = -1.3336301113e+01 + EOS011 = -1.3336301113e01 EOS111 = -4.4870114575 EOS211 = 5.0042598061 EOS311 = -6.5399043664e-01 @@ -381,27 +397,72 @@ def get_density(thetao,so,depth,tmask): EOS003 = -2.3342758797e-02 EOS103 = -1.8507636718e-02 EOS013 = 3.7969820455e-01 - - zh = depth * r1_Z0 # depth - zt = thetao * r1_T0 # temperature - zs = np.sqrt(np.abs(so + rdeltaS ) * r1_S0 ) # square root salinity + + zh = depth * r1_Z0 # depth + zt = thetao * r1_T0 # temperature + zs = np.sqrt(np.abs(so + rdeltaS) * r1_S0) # square root salinity ztm = tmask - - zn3 = EOS013*zt + EOS103*zs+EOS003 - zn2 = (EOS022*zt + EOS112*zs+EOS012)*zt + (EOS202*zs+EOS102)*zs+EOS002 - zn1 = (((EOS041*zt + EOS131*zs+EOS031)*zt + (EOS221*zs+EOS121)*zs+EOS021)*zt + ((EOS311*zs+EOS211)*zs+EOS111)*zs+EOS011)*zt + (((EOS401*zs+EOS301)*zs+EOS201)*zs+EOS101)*zs+EOS001 - zn0 = (((((EOS060*zt + EOS150*zs+EOS050)*zt + (EOS240*zs+EOS140)*zs+EOS040)*zt + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000 - - zn = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0 - - rhop = zn0 * ztm # potential density referenced at the surface - rho_insitu = zn * ztm # density anomaly (masked) + + zn3 = EOS013 * zt + EOS103 * zs + EOS003 + zn2 = ( + (EOS022 * zt + EOS112 * zs + EOS012) * zt + (EOS202 * zs + EOS102) * zs + EOS002 + ) + zn1 = ( + ( + ( + (EOS041 * zt + EOS131 * zs + EOS031) * zt + + (EOS221 * zs + EOS121) * zs + + EOS021 + ) + * zt + + ((EOS311 * zs + EOS211) * zs + EOS111) * zs + + EOS011 + ) + * zt + + (((EOS401 * zs + EOS301) * zs + EOS201) * zs + EOS101) * zs + + EOS001 + ) + zn0 = ( + ( + ( + ( + ( + (EOS060 * zt + EOS150 * zs + EOS050) * zt + + (EOS240 * zs + EOS140) * zs + + EOS040 + ) + * zt + + ((EOS330 * zs + EOS230) * zs + EOS130) * zs + + EOS030 + ) + * zt + + (((EOS420 * zs + EOS320) * zs + EOS220) * zs + EOS120) * zs + + EOS020 + ) + * zt + + ((((EOS510 * zs + EOS410) * zs + EOS310) * zs + EOS210) * zs + EOS110) + * zs + + EOS010 + ) + * zt + + ( + ((((EOS600 * zs + EOS500) * zs + EOS400) * zs + EOS300) * zs + EOS200) * zs + + EOS100 + ) + * zs + + EOS000 + ) + + zn = ((zn3 * zh + zn2) * zh + zn1) * zh + zn0 + + rhop = zn0 * ztm # potential density referenced at the surface + rho_insitu = zn * ztm # density anomaly (masked) return rhop, rho_insitu -def plot_density_infos(array,e3t_new,min_=1017): +def plot_density_infos(array, e3t_new, min_=1017): """ - Plot density (rhop) information: surface density, density as a function of depth, and the difference in density as a function of depth. + Plot density (rhop) information: surface density, density as a function of depth, and the difference in density as a function of depth. The difference provides insights into density errors, particularly where it decreases instead of increasing Parameters: @@ -413,26 +474,26 @@ def plot_density_infos(array,e3t_new,min_=1017): None """ fig, axes = plt.subplots(1, 3, figsize=(18, 4)) - a = axes[0].pcolor(array["rhop"][0,0],vmin=min_) + a = axes[0].pcolor(array["rhop"][0, 0], vmin=min_) fig.colorbar(a, ax=axes[0]) - - rhop = array['rhop'].where(array["rhop"][0] != 0., np.nan) - diff_rhop = np.diff(rhop.isel(time_counter=0), axis=0) / e3t_new[0,:-1] - - for i in range(array["rhop"].sizes['x']): - for j in range(array["rhop"].sizes['y']): - rhop.isel(time_counter=0, x=i, y=j).plot(ax=axes[1]) - axes[2].plot(diff_rhop[:,j,i]) - - axes[0].set_title('Surface density') - axes[1].set_title('Density as a function of depth') - axes[2].set_title('Diff Density as a function of depth') + + rhop = array["rhop"].where(array["rhop"][0] != 0.0, np.nan) + diff_rhop = np.diff(rhop.isel(time_counter=0), axis=0) / e3t_new[0, :-1] + + for i in range(array["rhop"].sizes["x"]): + for j in range(array["rhop"].sizes["y"]): + rhop.isel(time_counter=0, x=i, y=j).plot(ax=axes[1]) + axes[2].plot(diff_rhop[:, j, i]) + + axes[0].set_title("Surface density") + axes[1].set_title("Density as a function of depth") + axes[2].set_title("Diff Density as a function of depth") # FONCTIONNE MAIS A VOIR def regularize_rho(rho): """ - Regularize the rho variable to ensure the density is alway superieur or equal at a lower depth. + Regularize the rho variable to ensure the density is alway superieur or equal at a lower depth. If the value found at k-1 depth is lower than the value found at k. k-1 value is replaces by k value. Parameters: @@ -441,20 +502,18 @@ def regularize_rho(rho): Returns: numpy.ndarray : Regularized array of density. """ - t,z,y,x = np.shape(rho) + t, z, y, x = np.shape(rho) for i in range(x): for j in range(y): - for k in range(z-1): - if rho[0,k,j,i]>rho[0,k+1,j,i]: - rho[0,k+1,j,i] = rho[0,k,j,i] + for k in range(z - 1): + if rho[0, k, j, i] > rho[0, k + 1, j, i]: + rho[0, k + 1, j, i] = rho[0, k, j, i] return rho - - - - -def update_u_velocity(restart,mask,e3t_new): #e3t_new = maskarray["e3t_0"].values[0,:,y_slice,x_slice] +def update_u_velocity( + restart, mask, e3t_new +): # e3t_new = maskarray["e3t_0"].values[0,:,y_slice,x_slice] """ Update the v-component velocity array.Meridional @@ -466,29 +525,34 @@ def update_u_velocity(restart,mask,e3t_new): #e3t_new = maskarray[" Returns: None """ - un = restart.un.copy() #initial v velocity of the restart - (t,z,y,x) - thetao = restart.tn - so = restart.sn - deptht = get_deptht(restart,mask) - e2t = mask.e2t #initial y axis cell's thickness on grid T - (y,x) - ff_f = mask.ff_f #corriolis force - (y,x) - tmask = mask.tmask - umask = mask.umask - vmask = mask.vmask - - _,rho_insitu = get_density(thetao,so,deptht,tmask) - rho_insitu = rho_insitu.where(tmask) #updated density - (t,z,y,x) - - ind_prof_u=(umask.argmin(dim="nav_lev")-1)*umask.isel(nav_lev=0) - - diff_y = rho_insitu.roll(y=-1) - rho_insitu # - (t,z,y,x) - u_new = 9.81/ff_f * (diff_y/rho_insitu*e3t_new/e2t).cumsum(dim="nav_lev") + un = restart.un.copy() # initial v velocity of the restart - (t,z,y,x) + thetao = restart.tn + so = restart.sn + deptht = get_deptht(restart, mask) + e2t = mask.e2t # initial y axis cell's thickness on grid T - (y,x) + ff_f = mask.ff_f # corriolis force - (y,x) + tmask = mask.tmask + umask = mask.umask + vmask = mask.vmask + + _, rho_insitu = get_density(thetao, so, deptht, tmask) + rho_insitu = rho_insitu.where( + tmask + ) # updated density - (t,z,y,x) + + ind_prof_u = (umask.argmin(dim="nav_lev") - 1) * umask.isel(nav_lev=0) + + diff_y = rho_insitu.roll(y=-1) - rho_insitu # - (t,z,y,x) + u_new = 9.81 / ff_f * (diff_y / rho_insitu * e3t_new / e2t).cumsum(dim="nav_lev") u_new = u_new - u_new.isel(nav_lev=ind_prof_u) - un_new = add_bottom_velocity(un,u_new,umask[0]) # add V_0 - (t,z,y,x) + un_new = add_bottom_velocity(un, u_new, umask[0]) # add V_0 - (t,z,y,x) return un_new -def update_v_velocity(restart,mask,e3t_new): #e3t_new = maskarray["e3t_0"].values[0,:,y_slice,x_slice] + +def update_v_velocity( + restart, mask, e3t_new +): # e3t_new = maskarray["e3t_0"].values[0,:,y_slice,x_slice] """ Update the v-component velocity array.Meridional @@ -500,31 +564,33 @@ def update_v_velocity(restart,mask,e3t_new): #e3t_new = maskarray[" Returns: New v_velocity """ - vn = restart.vn.copy() #initial v velocity of the restart - (t,z,y,x) - thetao = restart.tn - so = restart.sn - deptht = get_deptht(restart,mask) - e1t = mask.e1t #initial y axis cell's thickness on grid T - (y,x) - ff_f = mask.ff_f #corriolis force - (y,x) - tmask = mask.tmask - vmask = mask.vmask - - _,rho_insitu = get_density(thetao,so,deptht,tmask) - rho_insitu = rho_insitu.where(tmask) #updated density - (t,z,y,x) - - ind_prof_v=(vmask.argmin(dim="nav_lev")-1)*vmask.isel(nav_lev=0) - - diff_x = -rho_insitu.roll(x=1) + rho_insitu # - (t,z,y,x) - v_new = 9.81/ff_f * (diff_x/rho_insitu*e3t_new/e1t).cumsum(dim="nav_lev") # v without V_0 - (t,z,y,x) C: On intègre vers le fond puis on retire la valeur au fond sur toute la colonne pour avoir v_fond=vo + vn = restart.vn.copy() # initial v velocity of the restart - (t,z,y,x) + thetao = restart.tn + so = restart.sn + deptht = get_deptht(restart, mask) + e1t = mask.e1t # initial y axis cell's thickness on grid T - (y,x) + ff_f = mask.ff_f # corriolis force - (y,x) + tmask = mask.tmask + vmask = mask.vmask + + _, rho_insitu = get_density(thetao, so, deptht, tmask) + rho_insitu = rho_insitu.where( + tmask + ) # updated density - (t,z,y,x) + + ind_prof_v = (vmask.argmin(dim="nav_lev") - 1) * vmask.isel(nav_lev=0) + + diff_x = -rho_insitu.roll(x=1) + rho_insitu # - (t,z,y,x) + v_new = ( + 9.81 / ff_f * (diff_x / rho_insitu * e3t_new / e1t).cumsum(dim="nav_lev") + ) # v without V_0 - (t,z,y,x) C: On intègre vers le fond puis on retire la valeur au fond sur toute la colonne pour avoir v_fond=vo v_new = v_new - v_new.isel(nav_lev=ind_prof_v) - vn_new = add_bottom_velocity(vn,v_new,vmask[0]) + vn_new = add_bottom_velocity(vn, v_new, vmask[0]) return vn_new - - -def add_bottom_velocity(v_restart,v_update,mask): +def add_bottom_velocity(v_restart, v_update, mask): """ Add bottom velocity values to the updated velocity array. @@ -536,9 +602,8 @@ def add_bottom_velocity(v_restart,v_update,mask): Returns: v_restart (numpy.array): Velocity array with bottom velocity values added. """ - ind_prof=(mask.argmin(dim="nav_lev")-1)*mask.isel(nav_lev=0) - v_fond=v_restart.isel(nav_lev=ind_prof,time_counter=0) + ind_prof = (mask.argmin(dim="nav_lev") - 1) * mask.isel(nav_lev=0) + v_fond = v_restart.isel(nav_lev=ind_prof, time_counter=0) mask_nan_update = np.isnan(v_update) - v_new = mask_nan_update * v_restart + (1-mask_nan_update) * (v_fond + v_update) + v_new = mask_nan_update * v_restart + (1 - mask_nan_update) * (v_fond + v_update) return v_restart - diff --git a/main_forecast.py b/main_forecast.py index 56f74ba..090fb24 100644 --- a/main_forecast.py +++ b/main_forecast.py @@ -2,64 +2,90 @@ import pandas as pd import os import pickle -import sys -import random +import sys +import random import argparse -sys.path.insert(0,"../lib/") + +sys.path.insert(0, "../lib/") from forecast import Predictions, Simulation, load_ts -file_simu_prepared = "/data/mtissot/spinup_data/simus_prepared" +file_simu_prepared = "/data/mtissot/spinup_data/simus_prepared" file_simu_predicted = "/data/mtissot/spinup_data/simus_predicted" -def prepare(term,simu_path, start, end, ye, comp): - simu = Simulation(path=simu_path,start=start,end=end,ye=ye,comp=comp,term=term) #Load yearly or monthly simulations - print(f"{term} loaded") - simu.prepare() #Prepare simulations : start to end - removeClosedSeas - (removeSSCA) - standardize - to numpy - print(f"{term} prepared") - simu.applyPCA() #Exctract time series through PCA +def prepare(term, simu_path, start, end, ye, comp): + simu = Simulation( + path=simu_path, start=start, end=end, ye=ye, comp=comp, term=term + ) # Load yearly or monthly simulations + print(f"{term} loaded") + simu.prepare() # Prepare simulations : start to end - removeClosedSeas - (removeSSCA) - standardize - to numpy + print(f"{term} prepared") + simu.applyPCA() # Exctract time series through PCA print(f"PCA applied on {term}") - simu.save(file_simu_prepared,term) #Create dico and save: time series - mask - desc -(ssca) - cut(=start) - x_size - y_size - (z_size) - shape + simu.save( + file_simu_prepared, term + ) # Create dico and save: time series - mask - desc -(ssca) - cut(=start) - x_size - y_size - (z_size) - shape print(f"{term} saved at {file_simu_prepared}/{term}") - del simu #Clean RAM + del simu # Clean RAM + -def jump(term,steps): - df,infos = load_ts(f"{file_simu_prepared}/{term}",term) #load dataframe and infos - simu_ts = Predictions(term,df,infos) #create the class to predict - print(f"{term} time series loaded") - y_hat, y_hat_std, metrics = simu_ts.Forecast(len(simu_ts),steps) #Forecast - print(f"{term} time series forcasted") - n = len(simu_ts.info["pca"].components_) #Reconstruct n predicted components - predictions_zos = simu_ts.reconstruct(y_hat,n,begin=len(simu_ts)) - print(f"{term} predictions reconstructed") - np.save(f"{file_simu_predicted}/{term}.npy", predictions_zos) #Save - print(f"{term} predictions saved at {file_simu_predicted}") +def jump(term, steps): + df, infos = load_ts( + f"{file_simu_prepared}/{term}", term + ) # load dataframe and infos + simu_ts = Predictions(term, df, infos) # create the class to predict + print(f"{term} time series loaded") + y_hat, y_hat_std, metrics = simu_ts.Forecast(len(simu_ts), steps) # Forecast + print(f"{term} time series forcasted") + n = len(simu_ts.info["pca"].components_) # Reconstruct n predicted components + predictions_zos = simu_ts.reconstruct(y_hat, n, begin=len(simu_ts)) + print(f"{term} predictions reconstructed") + np.save(f"{file_simu_predicted}/{term}.npy", predictions_zos) # Save + print(f"{term} predictions saved at {file_simu_predicted}") del simu_ts -def emulate(simu_path,steps,ye,start,end,comp): - for term in ["zos","so","thetao"]: + +def emulate(simu_path, steps, ye, start, end, comp): + for term in ["zos", "so", "thetao"]: print(f"Preparing {term}...") - prepare(term,simu_path, ye, start, end, comp) + prepare(term, simu_path, ye, start, end, comp) print() print(f"Forecasting {term}...") - jump(term,steps) + jump(term, steps) print() -if __name__ == '__main__': - #simu_path = "/scratchu/mtissot/SIMUp6Y" +if __name__ == "__main__": + # simu_path = "/scratchu/mtissot/SIMUp6Y" parser = argparse.ArgumentParser(description="Emulator") - parser.add_argument("--path", type=str, help= "Enter the simulation pathn") #Path - parser.add_argument("--ye", type=bool, help= "Transform monthly simulation to yearly simulation") #Transform monthly simulation to yearly simulation - parser.add_argument("--start", type=int, help = "Start of the training") #Start of the simu : 0 to keep spin up / t to cut the spin up - parser.add_argument("--end", type=int, help = "End of the training") #End of the simu (end-strat = train len) - parser.add_argument("--steps", type=int, help = "Number of steps to emulate") #Number of years you want to forecast - parser.add_argument("--comp", type=float, help="Explained variance ratio for the pca") #Explained variance ratio for the pca + parser.add_argument("--path", type=str, help="Enter the simulation pathn") # Path + parser.add_argument( + "--ye", type=bool, help="Transform monthly simulation to yearly simulation" + ) # Transform monthly simulation to yearly simulation + parser.add_argument( + "--start", type=int, help="Start of the training" + ) # Start of the simu : 0 to keep spin up / t to cut the spin up + parser.add_argument( + "--end", type=int, help="End of the training" + ) # End of the simu (end-strat = train len) + parser.add_argument( + "--steps", type=int, help="Number of steps to emulate" + ) # Number of years you want to forecast + parser.add_argument( + "--comp", type=float, help="Explained variance ratio for the pca" + ) # Explained variance ratio for the pca args = parser.parse_args() - emulate(simu_path=args.path,steps=args.steps,ye=args.ye,start=args.start,end=args.end,comp=args.comp) + emulate( + simu_path=args.path, + steps=args.steps, + ye=args.ye, + start=args.start, + end=args.end, + comp=args.comp, + ) + + # update_restart_files - #update_restart_files - - #python SpinUp/jumper/main/main_forecast.py --ye True --start 25 --end 65 --comp 0.9 --steps 30 --path /scratchu/mtissot/SIMUp6Y + # python SpinUp/jumper/main/main_forecast.py --ye True --start 25 --end 65 --comp 0.9 --steps 30 --path /scratchu/mtissot/SIMUp6Y diff --git a/main_restart.py b/main_restart.py index bb2b224..e903a72 100644 --- a/main_restart.py +++ b/main_restart.py @@ -3,15 +3,17 @@ import matplotlib.pyplot as plt import os import pickle -import sys -import random +import sys +import random import argparse -sys.path.insert(0,"/home/mtissot/Spinup-NEMO/lib") + +sys.path.insert(0, "/home/mtissot/Spinup-NEMO/lib") from lib.restart import * import xarray as xr -def update_restart_slice(restart_file,restart_name,mask_file): -#restart file "/thredds/idris/work/ues27zx/Restarts/" mask file '/thredds/idris/work/ues27zx/eORCA1.4.2_mesh_mask_modJD.nc' + +def update_restart_slice(restart_file, restart_name, mask_file): + # restart file "/thredds/idris/work/ues27zx/Restarts/" mask file '/thredds/idris/work/ues27zx/eORCA1.4.2_mesh_mask_modJD.nc' """ Update a restart file with new predictions and related variables. @@ -23,21 +25,35 @@ def update_restart_slice(restart_file,restart_name,mask_file): Returns: None """ - restart_array = xr.open_dataset(restart_file+restart_name,decode_times=False) #load restart file - mask_array = xr.open_dataset(mask_file,decode_times=False) #load mask file - zos_new,so_new,thetao_new = restart.load_predictions() #load ssh, so and thetao predictions - restart.update_pred(restart_array,zos_new,so_new,thetao_new) #update restart with ssh, so and thetao predictions - e3t_new = restart.update_e3tm(restart_array,mask_array) #update e3tm and gete e3t - deptht_new = restart.get_deptht(restart_array,mask_array) #get new deptht for density - restart.update_rhop(restart_array,mask_array,deptht_new) #update density - restart.update_v_velocity(restart_array,mask_array,e3t_new[0]) #update meridional velocity - restart.update_u_velocity(restart_array,mask_array,e3t_new[0]) #update zonal velocity - array = array.rename_vars({'xx': 'x','yy':'y'}) #inverse transformation of x and y vars - #Restart.to_netcdf(restart_file+restart_name) # save file - - -#PAs EU LE TEMPS D'ESSAYER -def update_Restarts(restarts_file,mask_file,jobs=10) : + restart_array = xr.open_dataset( + restart_file + restart_name, decode_times=False + ) # load restart file + mask_array = xr.open_dataset(mask_file, decode_times=False) # load mask file + zos_new, so_new, thetao_new = ( + restart.load_predictions() + ) # load ssh, so and thetao predictions + restart.update_pred( + restart_array, zos_new, so_new, thetao_new + ) # update restart with ssh, so and thetao predictions + e3t_new = restart.update_e3tm(restart_array, mask_array) # update e3tm and gete e3t + deptht_new = restart.get_deptht( + restart_array, mask_array + ) # get new deptht for density + restart.update_rhop(restart_array, mask_array, deptht_new) # update density + restart.update_v_velocity( + restart_array, mask_array, e3t_new[0] + ) # update meridional velocity + restart.update_u_velocity( + restart_array, mask_array, e3t_new[0] + ) # update zonal velocity + array = array.rename_vars( + {"xx": "x", "yy": "y"} + ) # inverse transformation of x and y vars + # Restart.to_netcdf(restart_file+restart_name) # save file + + +# PAs EU LE TEMPS D'ESSAYER +def update_Restarts(restarts_file, mask_file, jobs=10): """ Update multiple restart files in parallel. @@ -49,40 +65,45 @@ def update_Restarts(restarts_file,mask_file,jobs=10) : Returns: None """ - restart_names = restart.getRestartFiles(restarts_file) # SUPER LONG PEUT ETRE LE FAIR EN BASH OU ERREUR - Parallel(jobs)(delayed(update_restart_slice)(restarts_file,file,mask_file) for file in restart_names) - - + restart_names = restart.getRestartFiles( + restarts_file + ) # SUPER LONG PEUT ETRE LE FAIR EN BASH OU ERREUR + Parallel(jobs)( + delayed(update_restart_slice)(restarts_file, file, mask_file) + for file in restart_names + ) -if __name__ == '__main__': - +if __name__ == "__main__": parser = argparse.ArgumentParser(description="Update of restart files") - parser.add_argument("--restart_path", type=str, help= "path of restart file directory") - parser.add_argument("--radical", type=str, help= "radical of restart filename") - parser.add_argument("--mask_file", type=str, help= "adress of mask file") - parser.add_argument("--prediction_path", type=str, help= "path of prediction directory") + parser.add_argument( + "--restart_path", type=str, help="path of restart file directory" + ) + parser.add_argument("--radical", type=str, help="radical of restart filename") + parser.add_argument("--mask_file", type=str, help="adress of mask file") + parser.add_argument( + "--prediction_path", type=str, help="path of prediction directory" + ) args = parser.parse_args() - restart=xr.open_dataset(getRestartFiles(args.restart_path,args.radical),decode_times=False) - mask=getMaskFile(args.mask_file,restart) - restart=load_predictions(restart,dirpath=args.prediction_path) - restart=propagate_pred(restart,mask) - recordFullRestart(args.restart_path,args.radical,restart) - recordPiecedRestart(args.restart_path,args.radical,restart) + restart = xr.open_dataset( + getRestartFiles(args.restart_path, args.radical), decode_times=False + ) + mask = getMaskFile(args.mask_file, restart) + restart = load_predictions(restart, dirpath=args.prediction_path) + restart = propagate_pred(restart, mask) + recordFullRestart(args.restart_path, args.radical, restart) + recordPiecedRestart(args.restart_path, args.radical, restart) - - print("""All done. Now you just need to : + print("""All done. Now you just need to : - Back transform the coordinates of the pieced restart files using ncks to the original version (see bash script xarray_to_CMIP.sh) - Rename/Overwrite the "NEW_" restart files to their old version if you’re happy with them (see other bash script rewrite.sh) - Point to the restart directory in your simulation config.card (if all your non-NEMO restart files are also in the restart_path directory, of course). You might need to reorganize them in a ./OCE/Restart/CM....nc structure instead of ./OCE_CM...nc (there’s the rename.sh bash script for that) but normally it should work without. You can see the example script Jumper.sh for how to do most of that. See you soon. :) """) + # update_Restarts(restarts_file=args.restarts_file,mask_file=args.mask_file) - - #update_Restarts(restarts_file=args.restarts_file,mask_file=args.mask_file) + # update_restart_files - #update_restart_files - - #python SpinUp/jumper/main/main_restart.py --restart_files '/thredds/idris/work/ues27zx/eORCA1.4.2_mesh_mask_modJD.nc' --mask_file '/thredds/idris/work/ues27zx/eORCA1.4.2_mesh_mask_modJD.nc' + # python SpinUp/jumper/main/main_restart.py --restart_files '/thredds/idris/work/ues27zx/eORCA1.4.2_mesh_mask_modJD.nc' --mask_file '/thredds/idris/work/ues27zx/eORCA1.4.2_mesh_mask_modJD.nc' diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8aea04a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +numpy==2.0.2 +pandas==2.2.2 +python-dateutil==2.9.0.post0 +pytz==2024.1 +six==1.16.0 +matplotlib==3.9.2 +scipy==1.13.1 +xarray[complete]==2024.7.0 +scikit-learn==1.5.1 diff --git a/standalone/restarter.py b/standalone/restarter.py index b4ac3c0..d27b37d 100644 --- a/standalone/restarter.py +++ b/standalone/restarter.py @@ -1,10 +1,8 @@ - -#$1 : Radical +# $1 : Radical # # - import numpy as np import xarray as xr import matplotlib.pyplot as plt @@ -20,20 +18,19 @@ def getXYslice(array): Parameters: array (xarray.DataArray) : Restart file - + Returns x_string (slice dtype=float) : range of x positions y_string (slice dtype=float) : range of y positions """ - First = array.DOMAIN_position_first - Last = array.DOMAIN_position_last - x_slice = slice(First[0]-1,Last[0]) - y_slice = slice(First[1]-1,Last[1]) + First = array.DOMAIN_position_first + Last = array.DOMAIN_position_last + x_slice = slice(First[0] - 1, Last[0]) + y_slice = slice(First[1] - 1, Last[1]) return x_slice, y_slice - -def density(thetao,so,depth,tmask): +def density(thetao, so, depth, tmask): """ Compute potential density referenced at the surface and in-situ density. C : Add source for the formula ? @@ -50,45 +47,45 @@ def density(thetao,so,depth,tmask): array: In Situ Density. """ rdeltaS = 32.0 - r1_S0 = 0.875/35.16504 - r1_T0 = 1./40. - r1_Z0 = 1.e-4 + r1_S0 = 0.875 / 35.16504 + r1_T0 = 1.0 / 40.0 + r1_Z0 = 1.0e-4 # C : What are thode values ? - EOS000 = 8.0189615746e+02 - EOS100 = 8.6672408165e+02 - EOS200 = -1.7864682637e+03 - EOS300 = 2.0375295546e+03 - EOS400 = -1.2849161071e+03 - EOS500 = 4.3227585684e+02 - EOS600 = -6.0579916612e+01 - EOS010 = 2.6010145068e+01 - EOS110 = -6.5281885265e+01 - EOS210 = 8.1770425108e+01 - EOS310 = -5.6888046321e+01 - EOS410 = 1.7681814114e+01 + EOS000 = 8.0189615746e02 + EOS100 = 8.6672408165e02 + EOS200 = -1.7864682637e03 + EOS300 = 2.0375295546e03 + EOS400 = -1.2849161071e03 + EOS500 = 4.3227585684e02 + EOS600 = -6.0579916612e01 + EOS010 = 2.6010145068e01 + EOS110 = -6.5281885265e01 + EOS210 = 8.1770425108e01 + EOS310 = -5.6888046321e01 + EOS410 = 1.7681814114e01 EOS510 = -1.9193502195 - EOS020 = -3.7074170417e+01 - EOS120 = 6.1548258127e+01 - EOS220 = -6.0362551501e+01 - EOS320 = 2.9130021253e+01 + EOS020 = -3.7074170417e01 + EOS120 = 6.1548258127e01 + EOS220 = -6.0362551501e01 + EOS320 = 2.9130021253e01 EOS420 = -5.4723692739 - EOS030 = 2.1661789529e+01 - EOS130 = -3.3449108469e+01 - EOS230 = 1.9717078466e+01 + EOS030 = 2.1661789529e01 + EOS130 = -3.3449108469e01 + EOS230 = 1.9717078466e01 EOS330 = -3.1742946532 EOS040 = -8.3627885467 - EOS140 = 1.1311538584e+01 + EOS140 = 1.1311538584e01 EOS240 = -5.3563304045 EOS050 = 5.4048723791e-01 EOS150 = 4.8169980163e-01 EOS060 = -1.9083568888e-01 - EOS001 = 1.9681925209e+01 - EOS101 = -4.2549998214e+01 - EOS201 = 5.0774768218e+01 - EOS301 = -3.0938076334e+01 + EOS001 = 1.9681925209e01 + EOS101 = -4.2549998214e01 + EOS201 = 5.0774768218e01 + EOS301 = -3.0938076334e01 EOS401 = 6.6051753097 - EOS011 = -1.3336301113e+01 + EOS011 = -1.3336301113e01 EOS111 = -4.4870114575 EOS211 = 5.0042598061 EOS311 = -6.5399043664e-01 @@ -108,29 +105,75 @@ def density(thetao,so,depth,tmask): EOS103 = -1.8507636718e-02 EOS013 = 3.7969820455e-01 - zh = depth * r1_Z0 # depth - zt = thetao * r1_T0 # temperature - zs = np.sqrt(np.abs(so + rdeltaS ) * r1_S0 ) # square root salinity + zh = depth * r1_Z0 # depth + zt = thetao * r1_T0 # temperature + zs = np.sqrt(np.abs(so + rdeltaS) * r1_S0) # square root salinity ztm = tmask - zn3 = EOS013*zt + EOS103*zs+EOS003 - zn2 = (EOS022*zt + EOS112*zs+EOS012)*zt + (EOS202*zs+EOS102)*zs+EOS002 - zn1 = (((EOS041*zt + EOS131*zs+EOS031)*zt + (EOS221*zs+EOS121)*zs+EOS021)*zt + ((EOS311*zs+EOS211)*zs+EOS111)*zs+EOS011)*zt + (((EOS401*zs+EOS301)*zs+EOS201)*zs+EOS101)*zs+EOS001 - zn0 = (((((EOS060*zt + EOS150*zs+EOS050)*zt + (EOS240*zs+EOS140)*zs+EOS040)*zt + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000 - - zn = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0 - - rhop = zn0 * ztm # potential density referenced at the surface - rho_insitu = zn * ztm # in-situ density (masked) + zn3 = EOS013 * zt + EOS103 * zs + EOS003 + zn2 = ( + (EOS022 * zt + EOS112 * zs + EOS012) * zt + (EOS202 * zs + EOS102) * zs + EOS002 + ) + zn1 = ( + ( + ( + (EOS041 * zt + EOS131 * zs + EOS031) * zt + + (EOS221 * zs + EOS121) * zs + + EOS021 + ) + * zt + + ((EOS311 * zs + EOS211) * zs + EOS111) * zs + + EOS011 + ) + * zt + + (((EOS401 * zs + EOS301) * zs + EOS201) * zs + EOS101) * zs + + EOS001 + ) + zn0 = ( + ( + ( + ( + ( + (EOS060 * zt + EOS150 * zs + EOS050) * zt + + (EOS240 * zs + EOS140) * zs + + EOS040 + ) + * zt + + ((EOS330 * zs + EOS230) * zs + EOS130) * zs + + EOS030 + ) + * zt + + (((EOS420 * zs + EOS320) * zs + EOS220) * zs + EOS120) * zs + + EOS020 + ) + * zt + + ((((EOS510 * zs + EOS410) * zs + EOS310) * zs + EOS210) * zs + EOS110) + * zs + + EOS010 + ) + * zt + + ( + ((((EOS600 * zs + EOS500) * zs + EOS400) * zs + EOS300) * zs + EOS200) * zs + + EOS100 + ) + * zs + + EOS000 + ) + + zn = ((zn3 * zh + zn2) * zh + zn1) * zh + zn0 + + rhop = zn0 * ztm # potential density referenced at the surface + rho_insitu = zn * ztm # in-situ density (masked) return rhop, rho_insitu +# C : Why do we need to modify inplace ? Can't we just return vn_new ? +# C : What is the difference between vn and vb ? -# C : Why do we need to modify inplace ? Can't we just return vn_new ? -# C : What is the difference between vn and vb ? - -def update_v_velocity(array,maskarray,e3t_new): #e3t_new = maskarray["e3t_0"].values[0,:,y_slice,x_slice] +def update_v_velocity( + array, maskarray, e3t_new +): # e3t_new = maskarray["e3t_0"].values[0,:,y_slice,x_slice] """ Update the v-component velocity array.Meridional @@ -142,31 +185,45 @@ def update_v_velocity(array,maskarray,e3t_new): #e3t_new = maskarra Returns: None """ - x_slice,y_slice = getXYslice(array) - vn = array.copy().variables['vn'] #initial v velocity of the restart - (t,z,y,x) - e1t = maskarray["e1t"].values[0,y_slice,x_slice] #initial y axis cell's thickness on grid T - (y,x) - vmask = maskarray["vmask"].values[0,:,y_slice,x_slice] #bathy mask on grid V - (z,y,x) - ff_f = maskarray["ff_f"].values[0,y_slice,x_slice] #corriolis force - (y,x) - tmask = maskarray["tmask"].values[0,:,y_slice,x_slice] - - rhop_new = array.variables['rhop'][0] - rhop_new = rhop_new.where(tmask).values #updated density - (t,z,y,x) - - diff_x = -np.roll(rhop_new,shift=1,axis=2) + rhop_new # - (t,z,y,x) - v_new = 9.81/(rhop_new*ff_f) * np.cumsum(diff_x*e3t_new/e1t,axis=1) # v without V_0 - (t,z,y,x) - v_new = np.expand_dims(v_new, axis=0) - vn_new = add_bottom_velocity(vn.values,v_new,vmask) # add V_0 - (t,z,y,x) - - array['vn'] = toXarray(vn_new,"vn") - array['vb'] = toXarray(vn_new,"vb") - array['ssv_m'] = toXarray(vn_new[:,0],"vb",dep=False) - #return v_new,vn_new - - -# C : What is "restart velocity array" ? Is it the source one ? -# C : v_update and v_restart should have a time dimension of 1 no ? + x_slice, y_slice = getXYslice(array) + vn = array.copy().variables[ + "vn" + ] # initial v velocity of the restart - (t,z,y,x) + e1t = maskarray["e1t"].values[ + 0, y_slice, x_slice + ] # initial y axis cell's thickness on grid T - (y,x) + vmask = maskarray["vmask"].values[ + 0, :, y_slice, x_slice + ] # bathy mask on grid V - (z,y,x) + ff_f = maskarray["ff_f"].values[ + 0, y_slice, x_slice + ] # corriolis force - (y,x) + tmask = maskarray["tmask"].values[0, :, y_slice, x_slice] + + rhop_new = array.variables["rhop"][0] + rhop_new = rhop_new.where( + tmask + ).values # updated density - (t,z,y,x) + + diff_x = ( + -np.roll(rhop_new, shift=1, axis=2) + rhop_new + ) # - (t,z,y,x) + v_new = ( + 9.81 / (rhop_new * ff_f) * np.cumsum(diff_x * e3t_new / e1t, axis=1) + ) # v without V_0 - (t,z,y,x) + v_new = np.expand_dims(v_new, axis=0) + vn_new = add_bottom_velocity(vn.values, v_new, vmask) # add V_0 - (t,z,y,x) + + array["vn"] = toXarray(vn_new, "vn") + array["vb"] = toXarray(vn_new, "vb") + array["ssv_m"] = toXarray(vn_new[:, 0], "vb", dep=False) + # return v_new,vn_new + + +# C : What is "restart velocity array" ? Is it the source one ? +# C : v_update and v_restart should have a time dimension of 1 no ? # C : In restart.py Maud have the same function with "ERROR MAUVAIS", what is the difference ? -def add_bottom_velocity(v_restart,v_update,mask): +def add_bottom_velocity(v_restart, v_update, mask): """ Add bottom velocity values to the updated velocity array. @@ -178,47 +235,54 @@ def add_bottom_velocity(v_restart,v_update,mask): Returns: v_restart (numpy.array): Velocity array with bottom velocity values added. """ - time,deptht,y,x = np.shape(v_update) + time, deptht, y, x = np.shape(v_update) for i in range(x): for j in range(y): - v0=False - for k in range(deptht)[::-1]: # From the bottom to the top : - if mask[k,j,i]==1 and v0==False: # If first cell of sea in the water column - v0 = v_restart[-1,k,j,i] # set V0 to the corresponding value - elif mask[k,j,i]==1 and v0!=False: # If cell is not in the bottom - v_restart[-1,k,j,i] = v0 + v_update[-1,k,j,i] # cell is equal to new v cell + v0 + v0 = False + for k in range(deptht)[::-1]: # From the bottom to the top : + if ( + mask[k, j, i] == 1 and v0 == False + ): # If first cell of sea in the water column + v0 = v_restart[ + -1, k, j, i + ] # set V0 to the corresponding value + elif ( + mask[k, j, i] == 1 and v0 != False + ): # If cell is not in the bottom + v_restart[-1, k, j, i] = ( + v0 + v_update[-1, k, j, i] + ) # cell is equal to new v cell + v0 return v_restart - - -if __name__ == '__main__' : +if __name__ == "__main__": # C : What is Radical here ? -> Name of the file # C : Use argparse radical = sys.argv[1] - MASKdataset = xr.open_dataset('../eORCA1.4.2_mesh_mask_modJD.nc',decode_times=False) - Restart = xr.open_dataset(radical+".nc",decode_times=False) + MASKdataset = xr.open_dataset( + "../eORCA1.4.2_mesh_mask_modJD.nc", decode_times=False + ) + Restart = xr.open_dataset(radical + ".nc", decode_times=False) Restart_NEW = Restart.copy() # C : In Maskdataset rename vertical axis with "nav_lev" and t in "time_counter" - - x_slice,y_slice = getXYslice(Restart) + x_slice, y_slice = getXYslice(Restart) - thetao=Restart.tn # C: Temperature - so=Restart.sn # C : Salinity - ssh=Restart.sshn # C : SSH - un=Restart.un.copy() # C : Zonal velocity (lat) - vn=Restart.vn.copy() # C : Meridional Velocity (lon) - e3t_ini=Restart.e3t_ini # C : Initial cell thickness ? + thetao = Restart.tn # C: Temperature + so = Restart.sn # C : Salinity + ssh = Restart.sshn # C : SSH + un = Restart.un.copy() # C : Zonal velocity (lat) + vn = Restart.vn.copy() # C : Meridional Velocity (lon) + e3t_ini = Restart.e3t_ini # C : Initial cell thickness ? # C : If e3t_ini and e3t_0 are the same, delete and use e3t_0 - ff_f=MASKdataset.ff_f # C : Coriolis ? + ff_f = MASKdataset.ff_f # C : Coriolis ? - e2t=MASKdataset.e2t # C : Zonal cell size ? - e1t=MASKdataset.e1t # C : Meridional cell size ? + e2t = MASKdataset.e2t # C : Zonal cell size ? + e1t = MASKdataset.e1t # C : Meridional cell size ? - # C: Vertical cell size ? + # C: Vertical cell size ? e3w_0 = MASKdataset.e3w_0 e3u_0 = MASKdataset.e3u_0 e3v_0 = MASKdataset.e3v_0 @@ -228,44 +292,42 @@ def add_bottom_velocity(v_restart,v_update,mask): umask = MASKdataset.umask vmask = MASKdataset.vmask - # Same as get_depth in restart.py - ssmask = tmask[:,0] #bathymetry - (t,y,x) - bathy = e3t_0.sum(dim="z") #initial condition depth 0 - (t,z,y,x) + ssmask = tmask[:, 0] # bathymetry - (t,y,x) + bathy = e3t_0.sum(dim="z") # initial condition depth 0 - (t,z,y,x) depth_0 = e3w_0.copy() - depth_0[:,0] = 0.5 * e3w_0[:,0] - depth_0[:,1:] = depth_0[:,0] + e3w_0[:,1:].cumsum(dim="z") - deptht = depth_0 * (1+ssh/(bathy + 1 - ssmask )) * tmask - rhop_new,rho_insitu_new=density(thetao,so,deptht,tmask) + depth_0[:, 0] = 0.5 * e3w_0[:, 0] + depth_0[:, 1:] = depth_0[:, 0] + e3w_0[:, 1:].cumsum(dim="z") + deptht = depth_0 * (1 + ssh / (bathy + 1 - ssmask)) * tmask + rhop_new, rho_insitu_new = density(thetao, so, deptht, tmask) - #C : No Rho regularisation / checking ? + # C : No Rho regularisation / checking ? # C : Here comment also (update_e3tm in restart.py) - e3t_new = e3t_ini*(1+(ssh*ssmask/(bathy+1-ssmask))) - + e3t_new = e3t_ini * (1 + (ssh * ssmask / (bathy + 1 - ssmask))) - rho_insitu=rho_insitu_new.where(tmask) - diff_y = rhop_new.roll(y=-1) - rhop_new # - (t,z,y,x) - u_new = 9.81/(rhop_new*ff_f) * (diff_y*e3t_new/e2t).cumsum(dim="z") - un_new = add_bottom_velocity(un,u_new,umask[0]) + rho_insitu = rho_insitu_new.where(tmask) + diff_y = rhop_new.roll(y=-1) - rhop_new # - (t,z,y,x) + u_new = 9.81 / (rhop_new * ff_f) * (diff_y * e3t_new / e2t).cumsum(dim="z") + un_new = add_bottom_velocity(un, u_new, umask[0]) - diff_x = -rhop_new.roll(x=1) + rhop_new # - (t,z,y,x) - v_new = 9.81/(rhop_new*ff_f) * (diff_x*e3t_new/e1t).cumsum(dim="z") # v without V_0 - (t,z,y,x) - vn_new = add_bottom_velocity(vn,v_new,vmask) + diff_x = -rhop_new.roll(x=1) + rhop_new # - (t,z,y,x) + v_new = ( + 9.81 / (rhop_new * ff_f) * (diff_x * e3t_new / e1t).cumsum(dim="z") + ) # v without V_0 - (t,z,y,x) + vn_new = add_bottom_velocity(vn, v_new, vmask) - Restart_NEW["un"]=un_new - Restart_NEW["vn"]=vn_new - Restart_NEW["rhop"]=rhop_new - Restart_NEW["ub"]=un_new - Restart_NEW["vb"]=vn_new + Restart_NEW["un"] = un_new + Restart_NEW["vn"] = vn_new + Restart_NEW["rhop"] = rhop_new + Restart_NEW["ub"] = un_new + Restart_NEW["vb"] = vn_new # C : To Add e3tm, ssu_m, ssv_m - # Not modified yet ( no forecast ) but for later Restart_NEW["tn"] = thetao Restart_NEW["sn"] = so Restart_NEW["sshn"] = ssh - - Restart_NEW.to_netcdf(radical+"_NEW.nc") + Restart_NEW.to_netcdf(radical + "_NEW.nc") diff --git a/standalone/restarter_V2.py b/standalone/restarter_V2.py index 22ec3f4..a134b78 100644 --- a/standalone/restarter_V2.py +++ b/standalone/restarter_V2.py @@ -1,17 +1,17 @@ -#$1 : Radical +# $1 : Radical # # - import numpy as np import xarray as xr import matplotlib.pyplot as plt -import copy +import copy import os import sys from ipdb import set_trace + def getXYslice(array): """ Given a Restart array with 'DOMAIN_position_first' and 'DOMAIN_position_last' attributes, @@ -19,20 +19,19 @@ def getXYslice(array): Parameters: array (xarray.DataArray) : Restart file - + Returns x_string (slice dtype=float) : range of x positions y_string (slice dtype=float) : range of y positions """ - First = array.DOMAIN_position_first - Last = array.DOMAIN_position_last - x_slice = slice(First[0]-1,Last[0]) - y_slice = slice(First[1]-1,Last[1]) + First = array.DOMAIN_position_first + Last = array.DOMAIN_position_last + x_slice = slice(First[0] - 1, Last[0]) + y_slice = slice(First[1] - 1, Last[1]) return x_slice, y_slice - -def density(thetao,so,depth,tmask): +def density(thetao, so, depth, tmask): """ Compute potential density referenced at the surface and in-situ density. @@ -48,44 +47,44 @@ def density(thetao,so,depth,tmask): array: In Situ Density. """ rdeltaS = 32.0 - r1_S0 = 0.875/35.16504 - r1_T0 = 1./40. - r1_Z0 = 1.e-4 - - EOS000 = 8.0189615746e+02 - EOS100 = 8.6672408165e+02 - EOS200 = -1.7864682637e+03 - EOS300 = 2.0375295546e+03 - EOS400 = -1.2849161071e+03 - EOS500 = 4.3227585684e+02 - EOS600 = -6.0579916612e+01 - EOS010 = 2.6010145068e+01 - EOS110 = -6.5281885265e+01 - EOS210 = 8.1770425108e+01 - EOS310 = -5.6888046321e+01 - EOS410 = 1.7681814114e+01 + r1_S0 = 0.875 / 35.16504 + r1_T0 = 1.0 / 40.0 + r1_Z0 = 1.0e-4 + + EOS000 = 8.0189615746e02 + EOS100 = 8.6672408165e02 + EOS200 = -1.7864682637e03 + EOS300 = 2.0375295546e03 + EOS400 = -1.2849161071e03 + EOS500 = 4.3227585684e02 + EOS600 = -6.0579916612e01 + EOS010 = 2.6010145068e01 + EOS110 = -6.5281885265e01 + EOS210 = 8.1770425108e01 + EOS310 = -5.6888046321e01 + EOS410 = 1.7681814114e01 EOS510 = -1.9193502195 - EOS020 = -3.7074170417e+01 - EOS120 = 6.1548258127e+01 - EOS220 = -6.0362551501e+01 - EOS320 = 2.9130021253e+01 + EOS020 = -3.7074170417e01 + EOS120 = 6.1548258127e01 + EOS220 = -6.0362551501e01 + EOS320 = 2.9130021253e01 EOS420 = -5.4723692739 - EOS030 = 2.1661789529e+01 - EOS130 = -3.3449108469e+01 - EOS230 = 1.9717078466e+01 + EOS030 = 2.1661789529e01 + EOS130 = -3.3449108469e01 + EOS230 = 1.9717078466e01 EOS330 = -3.1742946532 EOS040 = -8.3627885467 - EOS140 = 1.1311538584e+01 + EOS140 = 1.1311538584e01 EOS240 = -5.3563304045 EOS050 = 5.4048723791e-01 EOS150 = 4.8169980163e-01 EOS060 = -1.9083568888e-01 - EOS001 = 1.9681925209e+01 - EOS101 = -4.2549998214e+01 - EOS201 = 5.0774768218e+01 - EOS301 = -3.0938076334e+01 + EOS001 = 1.9681925209e01 + EOS101 = -4.2549998214e01 + EOS201 = 5.0774768218e01 + EOS301 = -3.0938076334e01 EOS401 = 6.6051753097 - EOS011 = -1.3336301113e+01 + EOS011 = -1.3336301113e01 EOS111 = -4.4870114575 EOS211 = 5.0042598061 EOS311 = -6.5399043664e-01 @@ -104,25 +103,70 @@ def density(thetao,so,depth,tmask): EOS003 = -2.3342758797e-02 EOS103 = -1.8507636718e-02 EOS013 = 3.7969820455e-01 - - zh = depth * r1_Z0 # depth - zt = thetao * r1_T0 # temperature - zs = np.sqrt(np.abs(so + rdeltaS ) * r1_S0 ) # square root salinity + + zh = depth * r1_Z0 # depth + zt = thetao * r1_T0 # temperature + zs = np.sqrt(np.abs(so + rdeltaS) * r1_S0) # square root salinity ztm = tmask - - zn3 = EOS013*zt + EOS103*zs+EOS003 - zn2 = (EOS022*zt + EOS112*zs+EOS012)*zt + (EOS202*zs+EOS102)*zs+EOS002 - zn1 = (((EOS041*zt + EOS131*zs+EOS031)*zt + (EOS221*zs+EOS121)*zs+EOS021)*zt + ((EOS311*zs+EOS211)*zs+EOS111)*zs+EOS011)*zt + (((EOS401*zs+EOS301)*zs+EOS201)*zs+EOS101)*zs+EOS001 - zn0 = (((((EOS060*zt + EOS150*zs+EOS050)*zt + (EOS240*zs+EOS140)*zs+EOS040)*zt + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000 - - zn = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0 - - rhop = zn0 * ztm # potential density referenced at the surface - rho_insitu = zn * ztm # in-situ density (masked) + + zn3 = EOS013 * zt + EOS103 * zs + EOS003 + zn2 = ( + (EOS022 * zt + EOS112 * zs + EOS012) * zt + (EOS202 * zs + EOS102) * zs + EOS002 + ) + zn1 = ( + ( + ( + (EOS041 * zt + EOS131 * zs + EOS031) * zt + + (EOS221 * zs + EOS121) * zs + + EOS021 + ) + * zt + + ((EOS311 * zs + EOS211) * zs + EOS111) * zs + + EOS011 + ) + * zt + + (((EOS401 * zs + EOS301) * zs + EOS201) * zs + EOS101) * zs + + EOS001 + ) + zn0 = ( + ( + ( + ( + ( + (EOS060 * zt + EOS150 * zs + EOS050) * zt + + (EOS240 * zs + EOS140) * zs + + EOS040 + ) + * zt + + ((EOS330 * zs + EOS230) * zs + EOS130) * zs + + EOS030 + ) + * zt + + (((EOS420 * zs + EOS320) * zs + EOS220) * zs + EOS120) * zs + + EOS020 + ) + * zt + + ((((EOS510 * zs + EOS410) * zs + EOS310) * zs + EOS210) * zs + EOS110) + * zs + + EOS010 + ) + * zt + + ( + ((((EOS600 * zs + EOS500) * zs + EOS400) * zs + EOS300) * zs + EOS200) * zs + + EOS100 + ) + * zs + + EOS000 + ) + + zn = ((zn3 * zh + zn2) * zh + zn1) * zh + zn0 + + rhop = zn0 * ztm # potential density referenced at the surface + rho_insitu = zn * ztm # in-situ density (masked) return rhop, rho_insitu -def add_bottom_velocity(v_restart,v_update,mask): +def add_bottom_velocity(v_restart, v_update, mask): """ Add bottom velocity values to the updated velocity array. @@ -134,153 +178,151 @@ def add_bottom_velocity(v_restart,v_update,mask): Returns: v_restart (numpy.array): Velocity array with bottom velocity values added. """ - time,deptht,y,x = np.shape(v_update) - ind_prof=(mask.argmin(dim="nav_lev")-1)*mask.isel(nav_lev=0) -# vu=v_update.groupby(np.isnan(v_update)) - v_fond=v_restart.isel(nav_lev=ind_prof,time_counter=0) -# v_restart=v_restart.groupby(np.isnan(v_update)).map(parser, vu=vu, v_fond=v_fond) + time, deptht, y, x = np.shape(v_update) + ind_prof = (mask.argmin(dim="nav_lev") - 1) * mask.isel(nav_lev=0) + # vu=v_update.groupby(np.isnan(v_update)) + v_fond = v_restart.isel(nav_lev=ind_prof, time_counter=0) + # v_restart=v_restart.groupby(np.isnan(v_update)).map(parser, vu=vu, v_fond=v_fond) mask_nan_update = np.isnan(v_update) - v_new = mask_nan_update * v_restart + (1-mask_nan_update) * (v_fond + v_update) + v_new = mask_nan_update * v_restart + (1 - mask_nan_update) * (v_fond + v_update) return v_restart - - - #for i in range(x): + + # for i in range(x): # for j in range(y): # v0=False # print("i,j=",i,' ',j) - # + # # if mask[0,j,i]==1: # for k in range(deptht)[::-1]:# From the bottom to the top : # if mask[k,j,i]==1 and v0==False: # If first cell of sea in the water column # v0 = v_restart[-1,k,j,i] # print(v0,' ',k) # set V0 to the corresponding value # elif mask[k,j,i]==1 and v0!=False: # If cell is not in the bottom - # v_restart[-1,k,j,i] = v0 + v_update[-1,k,j,i] # cell is equal to new v cell + v0 + # v_restart[-1,k,j,i] = v0 + v_update[-1,k,j,i] # cell is equal to new v cell + v0 + -#def parser(gb_da, vu, v_fond): +# def parser(gb_da, vu, v_fond): # if gb_da.name="1": # return vu["1"]+ v_fond # else: # return gb_da - -if __name__ == '__main__' : +if __name__ == "__main__": radical = sys.argv[1] - MASKdataset = xr.open_dataset('../eORCA1.4.2_mesh_mask_modJD.nc',decode_times=False) - Restart = xr.open_dataset(radical+".nc",decode_times=False) - MASKdataset = MASKdataset.swap_dims(dims_dict={"z": "nav_lev","t":"time_counter"}) - MASKdataset["time_counter"]=Restart["time_counter"] + MASKdataset = xr.open_dataset( + "../eORCA1.4.2_mesh_mask_modJD.nc", decode_times=False + ) + Restart = xr.open_dataset(radical + ".nc", decode_times=False) + MASKdataset = MASKdataset.swap_dims(dims_dict={"z": "nav_lev", "t": "time_counter"}) + MASKdataset["time_counter"] = Restart["time_counter"] Restart_NEW = Restart.copy() -#Part to replace with ML-extrapolated data ### - thetao=Restart.tn # - so=Restart.sn # - ssh=Restart.sshn # -############################################## - un=Restart.un.copy() - vn=Restart.vn.copy() - e3t_ini=Restart.e3t_ini - - ff_f=MASKdataset.ff_f - - e2t=MASKdataset.e2t - e1t=MASKdataset.e1t - + # Part to replace with ML-extrapolated data ### + thetao = Restart.tn # + so = Restart.sn # + ssh = Restart.sshn # + ############################################## + un = Restart.un.copy() + vn = Restart.vn.copy() + e3t_ini = Restart.e3t_ini + + ff_f = MASKdataset.ff_f + + e2t = MASKdataset.e2t + e1t = MASKdataset.e1t + e3w_0 = MASKdataset.e3w_0 e3u_0 = MASKdataset.e3u_0 e3v_0 = MASKdataset.e3v_0 e3t_0 = MASKdataset.e3t_0 - + tmask = MASKdataset.tmask umask = MASKdataset.umask vmask = MASKdataset.vmask - ssmask = tmask[:,0] #bathymetry - (t,y,x) - bathy = e3t_0.sum(dim="nav_lev") #initial condition depth 0 - (t,z,y,x) + ssmask = tmask[:, 0] # bathymetry - (t,y,x) + bathy = e3t_0.sum( + dim="nav_lev" + ) # initial condition depth 0 - (t,z,y,x) depth_0 = e3w_0.copy() - depth_0[:,0] = 0.5 * e3w_0[:,0] - #print(depth_0) - #print(depth_0[:,0]) - #print(e3w_0[:,1:]) - #print(e3w_0[:,1:].cumsum(dim="nav_lev")) - #set_trace() - depth_0[:,1:] = depth_0[:,0:1].data + e3w_0[:,1:].cumsum(dim="nav_lev") - -# set_trace() - deptht = depth_0 * (1+ssh/(bathy + 1 - ssmask )) * tmask - rhop_new,rho_insitu_new=density(thetao,so,deptht,tmask) - - e3t_new = e3t_ini*(1+ssh*ssmask/(bathy+1-ssmask)) - - ind_prof_u=(umask.argmin(dim="nav_lev")-1)*umask.isel(nav_lev=0) - ind_prof_v=(vmask.argmin(dim="nav_lev")-1)*vmask.isel(nav_lev=0) - - rho_insitu=rho_insitu_new.where(tmask) - diff_y = rho_insitu.roll(y=-1) - rho_insitu # - (t,z,y,x) - u_new = 9.81/ff_f * (diff_y/rho_insitu*e3t_new/e2t).cumsum(dim="nav_lev") + depth_0[:, 0] = 0.5 * e3w_0[:, 0] + # print(depth_0) + # print(depth_0[:,0]) + # print(e3w_0[:,1:]) + # print(e3w_0[:,1:].cumsum(dim="nav_lev")) + # set_trace() + depth_0[:, 1:] = depth_0[:, 0:1].data + e3w_0[:, 1:].cumsum(dim="nav_lev") + + # set_trace() + deptht = depth_0 * (1 + ssh / (bathy + 1 - ssmask)) * tmask + rhop_new, rho_insitu_new = density(thetao, so, deptht, tmask) + + e3t_new = e3t_ini * (1 + ssh * ssmask / (bathy + 1 - ssmask)) + + ind_prof_u = (umask.argmin(dim="nav_lev") - 1) * umask.isel(nav_lev=0) + ind_prof_v = (vmask.argmin(dim="nav_lev") - 1) * vmask.isel(nav_lev=0) + + rho_insitu = rho_insitu_new.where(tmask) + diff_y = rho_insitu.roll(y=-1) - rho_insitu # - (t,z,y,x) + u_new = 9.81 / ff_f * (diff_y / rho_insitu * e3t_new / e2t).cumsum(dim="nav_lev") u_new = u_new - u_new.isel(nav_lev=ind_prof_u) - un_new = add_bottom_velocity(un,u_new,umask[0]) + un_new = add_bottom_velocity(un, u_new, umask[0]) - diff_x = -rho_insitu.roll(x=1) + rho_insitu # - (t,z,y,x) - v_new = 9.81/ff_f * (diff_x/rho_insitu*e3t_new/e1t).cumsum(dim="nav_lev") # v without V_0 - (t,z,y,x) C: On intègre vers le fond puis on retire la valeur au fond sur toute la colonne pour avoir v_fond=vo + diff_x = -rho_insitu.roll(x=1) + rho_insitu # - (t,z,y,x) + v_new = ( + 9.81 / ff_f * (diff_x / rho_insitu * e3t_new / e1t).cumsum(dim="nav_lev") + ) # v without V_0 - (t,z,y,x) C: On intègre vers le fond puis on retire la valeur au fond sur toute la colonne pour avoir v_fond=vo v_new = v_new - v_new.isel(nav_lev=ind_prof_v) - vn_new = add_bottom_velocity(vn,v_new,vmask[0]) - -# Modifying the Global Restart file and recording it for analysis - Restart["un"]=un_new[:,:] - Restart["vn"]=vn_new[:,:] - Restart["ub"]=un_new[:,:] - Restart["vb"]=vn_new[:,:] - Restart["sn"]=so[:,:] - Restart["tn"]=thetao[:,:] - Restart["sb"]=so[:,:] - Restart["tb"]=thetao[:,:] - Restart["sshn"]=ssh[:,:] - Restart["sshb"]=ssh[:,:] + vn_new = add_bottom_velocity(vn, v_new, vmask[0]) - Restart["rhop"]=rhop_new[:,:] + # Modifying the Global Restart file and recording it for analysis + Restart["un"] = un_new[:, :] + Restart["vn"] = vn_new[:, :] + Restart["ub"] = un_new[:, :] + Restart["vb"] = vn_new[:, :] + Restart["sn"] = so[:, :] + Restart["tn"] = thetao[:, :] + Restart["sb"] = so[:, :] + Restart["tb"] = thetao[:, :] + Restart["sshn"] = ssh[:, :] + Restart["sshb"] = ssh[:, :] - Restart["ssv_m"]=vn_new[:,0] - Restart["ssu_m"]=un_new[:,0] - Restart["sst_m"]=thetao[:,0] - Restart["sss_m"]=so[:,0] - Restart["ssh_m"]=ssh[:] - Restart["e3t_m"]=e3t_new[:,0] + Restart["rhop"] = rhop_new[:, :] + Restart["ssv_m"] = vn_new[:, 0] + Restart["ssu_m"] = un_new[:, 0] + Restart["sst_m"] = thetao[:, 0] + Restart["sss_m"] = so[:, 0] + Restart["ssh_m"] = ssh[:] + Restart["e3t_m"] = e3t_new[:, 0] - Restart.to_netcdf(radical+"_NEW.nc") + Restart.to_netcdf(radical + "_NEW.nc") - -#Modifying the Local Restart files for use in Accelerated Simulation + # Modifying the Local Restart files for use in Accelerated Simulation for i in range(340): - Restart_NEW=xr.open_dataset(radical+"_%04d.nc"%(i)) + Restart_NEW = xr.open_dataset(radical + "_%04d.nc" % (i)) print(i) - x_slice,y_slice = getXYslice(Restart_NEW) - Restart_NEW["un"]=un_new[:,:,y_slice,x_slice] - Restart_NEW["vn"]=vn_new[:,:,y_slice,x_slice] - Restart_NEW["ub"]=un_new[:,:,y_slice,x_slice] - Restart_NEW["vb"]=vn_new[:,:,y_slice,x_slice] - Restart_NEW["sn"]=so[:,:,y_slice,x_slice] - Restart_NEW["tn"]=thetao[:,:,y_slice,x_slice] - Restart_NEW["sb"]=so[:,:,y_slice,x_slice] - Restart_NEW["tb"]=thetao[:,:,y_slice,x_slice] - Restart_NEW["sshn"]=ssh[:,:,y_slice,x_slice] - Restart_NEW["sshb"]=ssh[:,:,y_slice,x_slice] - - Restart_NEW["rhop"]=rhop_new[:,:,y_slice,x_slice] - - Restart_NEW["ssv_m"]=vn_new[:,0,y_slice,x_slice] - Restart_NEW["ssu_m"]=un_new[:,0,y_slice,x_slice] - Restart_NEW["sst_m"]=thetao[:,0,y_slice,x_slice] - Restart_NEW["sss_m"]=so[:,0,y_slice,x_slice] - Restart_NEW["ssh_m"]=ssh[:,y_slice,x_slice] - Restart_NEW["e3t_m"]=e3t_new[:,0,y_slice,x_slice] - - Restart_NEW.to_netcdf(radical+"_%04d_NEW.nc"%(i)) - - - - - + x_slice, y_slice = getXYslice(Restart_NEW) + Restart_NEW["un"] = un_new[:, :, y_slice, x_slice] + Restart_NEW["vn"] = vn_new[:, :, y_slice, x_slice] + Restart_NEW["ub"] = un_new[:, :, y_slice, x_slice] + Restart_NEW["vb"] = vn_new[:, :, y_slice, x_slice] + Restart_NEW["sn"] = so[:, :, y_slice, x_slice] + Restart_NEW["tn"] = thetao[:, :, y_slice, x_slice] + Restart_NEW["sb"] = so[:, :, y_slice, x_slice] + Restart_NEW["tb"] = thetao[:, :, y_slice, x_slice] + Restart_NEW["sshn"] = ssh[:, :, y_slice, x_slice] + Restart_NEW["sshb"] = ssh[:, :, y_slice, x_slice] + + Restart_NEW["rhop"] = rhop_new[:, :, y_slice, x_slice] + + Restart_NEW["ssv_m"] = vn_new[:, 0, y_slice, x_slice] + Restart_NEW["ssu_m"] = un_new[:, 0, y_slice, x_slice] + Restart_NEW["sst_m"] = thetao[:, 0, y_slice, x_slice] + Restart_NEW["sss_m"] = so[:, 0, y_slice, x_slice] + Restart_NEW["ssh_m"] = ssh[:, y_slice, x_slice] + Restart_NEW["e3t_m"] = e3t_new[:, 0, y_slice, x_slice] + + Restart_NEW.to_netcdf(radical + "_%04d_NEW.nc" % (i)) diff --git a/tools/CMIP_to_xarray.sh b/tools/CMIP_to_xarray.sh index fafee1f..d41a55a 100755 --- a/tools/CMIP_to_xarray.sh +++ b/tools/CMIP_to_xarray.sh @@ -1,10 +1,9 @@ #### # Hopefully you made sure your restart files have been made modifiable ( chmod 666 should be fine) -# +# #### for i in "$@"; do ncrename -v y,yy $i - ncrename -v x,xx $i + ncrename -v x,xx $i done -