diff --git a/notebooks/swath_generation.ipynb b/notebooks/swath_generation.ipynb index e7778b2..eae45cc 100644 --- a/notebooks/swath_generation.ipynb +++ b/notebooks/swath_generation.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "metadata": {}, "source": [ "## Generate a SWOT swath.\n", "\n", @@ -12,21 +13,22 @@ "The configuration is defined using an associative dictionary between the expected parameters and the values of its parameters. The description of the parameters is available on the [online help](https://swot-simulator.readthedocs.io/en/latest/generated/swot_simulator.settings.Parameters.html#swot_simulator.settings.Parameters).\n", "\n", "This array can be loaded from a Python file using the [eval_config_file](https://swot-simulator.readthedocs.io/en/latest/generated/swot_simulator.settings.eval_config_file.html#swot_simulator.settings.eval_config_file) method. But it can also be declared directly in Python code." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import swot_simulator" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "configuration = dict(\n", " # The swath contains in its centre a central pixel divided in two by the\n", @@ -65,30 +67,29 @@ " # The plug-in handling the SSH interpolation under the satellite swath.\n", " #ssh_plugin = TODO\n", ")" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "We create the parameter object for our simulation." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import swot_simulator.settings\n", "\n", "parameters = swot_simulator.settings.Parameters(configuration)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "---\n", "**Note**\n", @@ -109,55 +110,56 @@ "parameters = swot_simulator.settings.Parameters(configuration)\n", "```\n", "---" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### SSH interpolation\n", "\n", "The written configuration allows us to simulate a swath. However, the interpolation of the SSH under the satellite swath remains undefined. If you don't need this parameter, you can skip this setting.\n", "\n", "For our example, we use the SSH of the CMEMS grids provided on the Pangeo site." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import intake\n", "\n", "cat = intake.open_catalog(\"https://raw.githubusercontent.com/pangeo-data/\"\n", " \"pangeo-datastore/master/intake-catalogs/master.yaml\")" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "ds = cat.ocean.sea_surface_height.to_dask()\n", "ds" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "To interpolate SSH, we need to implement a class that must define a method to\n", "interpolate the data under the swath. This class must be derived from the\n", "[CartesianGridHandler](https://swot-simulator.readthedocs.io/en/latest/generated/swot_simulator.plugins.data_handler.CartesianGridHandler.html)\n", "class to be correctly taken into account by the class managing the parameters." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import pyinterp.backends.xarray\n", "import numpy\n", @@ -207,94 +209,93 @@ " time=time),\n", " interpolator='bilinear')\n", " return ssh" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Now we can update our parameters." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "parameters.ssh_plugin = CMEMS(ds)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Initiating orbit propagator.\n", "\n", "Initialization is simply done by [loading](https://swot-simulator.readthedocs.io/en/latest/generated/swot_simulator.orbit_propagator.load_ephemeris.html#swot_simulator.orbit_propagator.load_ephemeris) the ephemeris file. The satellite's one-day pass is taken into account in this case." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import swot_simulator.orbit_propagator\n", "\n", "\n", "with open(parameters.ephemeris, \"r\") as stream:\n", " orbit = swot_simulator.orbit_propagator.calculate_orbit(parameters, stream)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Iterate on the half-orbits of a period.\n", "\n", "To iterate over all the half-orbits of a period, call the method [iterate](https://swot-simulator.readthedocs.io/en/latest/generated/swot_simulator.orbit_propagator.Orbit.iterate.html#swot_simulator.orbit_propagator.Orbit.iterate). This method returns all cycle numbers, trace numbers, and start dates of the half orbits within the period. If the start date remains not set, the method uses the current date. If the end date remains undefined, the method sets the end date to the start date plus the cycle duration.\n", "\n", "In our case, we generate a cycle from January 1, 2000." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "first_date = numpy.datetime64(\"2000-01-01\")\n", "iterator = orbit.iterate(first_date)\n", "cycle_number, pass_number, date = next(iterator)\n", "cycle_number, pass_number, date" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Initialization of measurement error generators\n", "\n", "Error initialization is done simply by calling the appropriate [class](https://swot-simulator.readthedocs.io/en/latest/generated/swot_simulator.error.generator.Generator.html#swot_simulator.error.generator.Generator). The initialization of the wet troposphere error generator takes a little time (about 40 seconds), which explains the processing time for the next cell." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import swot_simulator.error.generator\n", "\n", "error_generator = swot_simulator.error.generator.Generator(\n", " parameters, first_date, orbit.orbit_duration())" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Generate the positions under the swath.\n", "\n", @@ -302,12 +303,13 @@ "\n", "> If the position of the pass is outside the area of interest (`parameters.area`),\n", "> the generation of the pass can return `None`." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "def generate_one_track(pass_number, date, orbit):\n", " # Compute the spatial/temporal position of the satellite\n", @@ -323,35 +325,35 @@ " track.set_simulated_date(date)\n", "\n", " return track" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Interpolate SSH\n", "\n", "Interpolation of the SSH for the space-time coordinates generated by the simulator." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "def interpolate_ssh(parameters, track):\n", - " swath_time = numpy.repeat(track.time, track.lon.shape[1]).reshape(track.lon.shape)\n", - " ssh = parameters.ssh_plugin.interpolate(track.lon.flatten(),\n", - " track.lat.flatten(),\n", - " swath_time.flatten())\n", + " swath_time = numpy.repeat(track.time,\n", + " track.lon.shape[1]).reshape(track.lon.shape)\n", + " ssh = parameters.ssh_plugin.interpolate(track.lon.ravel(),\n", + " track.lat.ravel(),\n", + " swath_time.ravel())\n", " return ssh.reshape(track.lon.shape)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Calculation of instrumental errors\n", "\n", @@ -360,12 +362,13 @@ "> Karin's instrumental noise can be modulated by wave heights.\n", "> The parameter SWH takes either a constant or a matrix defining\n", "> the SWH for the swath positions. " - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "def generate_instrumental_errors(error_generator, cycle_number, pass_number,\n", " orbit, track):\n", @@ -376,42 +379,42 @@ " track.x_al,\n", " track.x_ac,\n", " swh=2.0)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Calculates the sum of the simulated errors." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "def sum_error(errors, swath=True):\n", " \"\"\"Calculate the sum of errors\"\"\"\n", " dims = 2 if swath else 1\n", " return numpy.add.reduce(\n", " [item for item in errors.values() if len(item.shape) == dims])" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ " ### Create the swath dataset\n", " \n", " Generation of the simulated swath. The function returns an xarray dataset for the half-orbit generated." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import swot_simulator.netcdf\n", "\n", @@ -435,22 +438,22 @@ " error *= mask\n", " product.update_noise_errors(noise_errors)\n", " return product.to_xarray(cycle_number, pass_number, complete_product)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Swath generation.\n", "\n", "Now we can combine the different components to generate the swath." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import dask\n", "import dask.distributed\n", @@ -475,20 +478,20 @@ " return dask.delayed(generate_dataset)(\n", " cycle_number, pass_number, track, ssh, noise_errors,\n", " parameters.complete_product).compute()" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "The simulator calculation can be distributed on a Dask cluster." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import dask.distributed\n", "\n", @@ -496,35 +499,34 @@ "cluster = dask.distributed.LocalCluster()\n", "client = dask.distributed.Client(cluster)\n", "client" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "error_generator_ = client.scatter(error_generator)\n", "parameters_ = client.scatter(parameters)\n", "orbit_ = client.scatter(orbit)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "future = client.submit(generate_swath, cycle_number, pass_number, date,\n", " parameters_, error_generator_, orbit_)\n", "ds = client.gather(future)\n", "ds" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "To calculate a trace set you can use the following code\n", "\n", @@ -540,47 +542,48 @@ " client.gather(futures)\n", " \n", "### Visualization" - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import matplotlib.pyplot\n", "import cartopy.crs\n", "import cartopy.feature\n", "%matplotlib inline" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Selection of a reduced geographical area for visualization." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "selected = ds.where((ds.latitude > -50) & (ds.latitude < -40), drop=True)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Simulated SSH measurements (Interpolated SSH and simulated instrumental errors)." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "fig = matplotlib.pyplot.figure(figsize=(24, 12))\n", "ax = fig.add_subplot(1, 1, 1, projection=cartopy.crs.PlateCarree())\n", @@ -594,20 +597,22 @@ "ax.set_extent([60, 69, -50, -40], crs=cartopy.crs.PlateCarree())\n", "ax.gridlines(draw_labels=True, dms=True, x_inline=False, y_inline=False)\n", "ax.coastlines()" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Simulated KaRIN instrumental noise." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], "source": [ "for item in selected.variables:\n", " if item.startswith(\"simulated_error\"):\n", @@ -625,17 +630,16 @@ " orientation='vertical',\n", " fraction=0.046 * 70 / 250,\n", " pad=0.04)" - ], - "outputs": [], - "metadata": { - "scrolled": false - } + ] } ], "metadata": { + "interpreter": { + "hash": "b5abb4912561cb3944b8ab88aeb89e73503903f2213247e02c73ef25ab3e9321" + }, "kernelspec": { - "name": "python3", - "display_name": "Python 3.8.10 64-bit ('conda-forge': conda)" + "display_name": "Python 3.8.10 64-bit ('conda-forge': conda)", + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -648,11 +652,8 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" - }, - "interpreter": { - "hash": "b5abb4912561cb3944b8ab88aeb89e73503903f2213247e02c73ef25ab3e9321" } }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/swot_simulator/launcher.py b/swot_simulator/launcher.py index dc88cef..d46806e 100644 --- a/swot_simulator/launcher.py +++ b/swot_simulator/launcher.py @@ -287,8 +287,8 @@ def simulate(args: Tuple[int, int, np.datetime64], # Interpolation of the SWH if the user wishes. if parameters.swh_plugin is not None: - swh = parameters.swh_plugin.interpolate(lon.flatten(), lat.flatten(), - swath_time.flatten()) + swh = parameters.swh_plugin.interpolate(lon.ravel(), lat.ravel(), + swath_time.ravel()) swh = swh.reshape(lon.shape) else: swh = None @@ -305,8 +305,8 @@ def simulate(args: Tuple[int, int, np.datetime64], # Interpolation of the SSH if the user wishes. if parameters.ssh_plugin is not None: - ssh = parameters.ssh_plugin.interpolate(lon.flatten(), lat.flatten(), - swath_time.flatten()) + ssh = parameters.ssh_plugin.interpolate(lon.ravel(), lat.ravel(), + swath_time.ravel()) ssh = ssh.reshape(lon.shape) else: ssh = None diff --git a/swot_simulator/plugins/ssh/mitgcm.py b/swot_simulator/plugins/ssh/mitgcm.py index ff6ce65..c570184 100644 --- a/swot_simulator/plugins/ssh/mitgcm.py +++ b/swot_simulator/plugins/ssh/mitgcm.py @@ -87,9 +87,9 @@ def _spatial_interp( # The undefined values are filtered z_face = z_model[face, :].compute() defined = ~np.isnan(z_face) - x += (x_face[defined].flatten(), ) - y += (y_face[defined].flatten(), ) - z += (z_face[defined].flatten(), ) + x += (x_face[defined].ravel(), ) + y += (y_face[defined].ravel(), ) + z += (z_face[defined].ravel(), ) # The tree is built and the interpolation is calculated x = np.concatenate(x) diff --git a/swot_simulator/plugins/ssh/nz.py b/swot_simulator/plugins/ssh/nz.py index 2787dd2..e3a7302 100644 --- a/swot_simulator/plugins/ssh/nz.py +++ b/swot_simulator/plugins/ssh/nz.py @@ -21,7 +21,7 @@ class Base(Interface): """Base class for NZ interface. - + Args: path: Path to the time series. ssh: Name of the variable containing the SSH. @@ -108,9 +108,9 @@ def interpolate(self, lon: np.ndarray, lat: np.ndarray, assert np.all(np.diff(ds.ocean_time.values) == self._dt) interpolator = pyinterp.backends.xarray.RegularGridInterpolator( ds[self.ssh]) - return interpolator(dict(lat_rho=lat.flatten(), - lon_rho=lon.flatten(), - ocean_time=dates.flatten()), + return interpolator(dict(lat_rho=lat.ravel(), + lon_rho=lon.ravel(), + ocean_time=dates.ravel()), method="bilinear", bounds_error=False).reshape(lon.shape) @@ -140,8 +140,8 @@ def interpolate(self, lon: np.ndarray, lat: np.ndarray, ds[self.ssh].values.T) ssh = pyinterp.trivariate(grid3d, - lon.flatten(), - lat.flatten(), - t_axis.safe_cast(dates.flatten()), + lon.ravel(), + lat.ravel(), + t_axis.safe_cast(dates.ravel()), num_threads=1).reshape(lon.shape) return ssh diff --git a/swot_simulator/plugins/ssh/schism.py b/swot_simulator/plugins/ssh/schism.py index ce3a8f3..3c12601 100755 --- a/swot_simulator/plugins/ssh/schism.py +++ b/swot_simulator/plugins/ssh/schism.py @@ -101,7 +101,7 @@ def interpolate(self, lon: np.ndarray, lat: np.ndarray, xp.append(np.asarray(ds.time[index]).astype("datetime64[us]")) mesh = self._rtree(ds, index) ssh, _ = mesh.radial_basis_function( - np.vstack((lon.flatten(), lat.flatten())).T.astype("float32"), + np.vstack((lon.ravel(), lat.ravel())).T.astype("float32"), within=True, k=11, radius=10000, # Spatial resolution in meters.