def smoothed_linear_interpolation_between_tiepoints(l2, ssh_tiepoint_indices, filter_width): """ The main SLA computation method in this class :param l2: Level-2 data container :param ssh_tiepoint_indices: :param filter_width: :return: None """ # Step 1: Get the observed (noisy) sea surface elevations sla_raw = np.full(l2.n_records, np.nan) sla_raw[ssh_tiepoint_indices] = l2.elev[ssh_tiepoint_indices] - l2.mss[ssh_tiepoint_indices] non_tiepoints = np.isnan(sla_raw) # Step 2: Create a filtered version of the raw SLA, but don't interpolate yet # Use python implementation of IDL SMOOTH: # idl_smooth(x, w) equivalent to SMOOTH(x, w, /edge_truncate, /nan) sla_filter1 = idl_smooth(sla_raw, filter_width) sla_filter1[non_tiepoints] = np.nan # Step 3: Fill nans with linear interpolation and constant values at borders # python: fill_nan(x) = IDL: FILL_NAN(x, /NEIGHBOUR) sla_filter2 = fill_nan(sla_filter1) # Step 4: The sea level anomaly is the smoothed version # of the gap filled sla sla = idl_smooth(sla_filter2, filter_width) # All done, return value return sla
def _linear_smoothed_interpolation_between_tiepoints(self, l2): """ Based in cs2awi code from Robert Ricker """ # Use ocean and lead elevations self._ssh_tiepoints = l2.surface_type.lead.indices if self._options.use_ocean_wfm: self._ssh_tiepoints.append(l2.surface_type.ocean.indices) self._ssh_tiepoints = np.sort(self._ssh_tiepoints) # Get initial elevation at tiepoint locations mss_frb = l2.elev - l2.mss # Remove ssh tiepoints from the list if their elevation # corrected by the median offset of all tiepoints from the mss # exceeds a certain threshold if self._options.pre_filtering: # Startup index_dict = np.arange(l2.surface_type.lead.num) threshold = self._options.pre_filter_maximum_mss_median_offset # Compute the mean distance to the mss tiepoint_mss_distance = mss_frb[self._ssh_tiepoints] valid_points = np.where(np.isfinite(tiepoint_mss_distance))[0] tiepoint_mss_distance = tiepoint_mss_distance[valid_points] median_mss_offset = np.median(tiepoint_mss_distance) # Filter points for outliers offset = np.abs(mss_frb[self._ssh_tiepoints] - median_mss_offset) valid = np.where(offset < threshold) self._ssh_tiepoints = self._ssh_tiepoints[index_dict[valid]] self.ssa_raw = np.ndarray(shape=(l2.n_records)) * np.nan self.ssa_raw[self._ssh_tiepoints] = mss_frb[self._ssh_tiepoints] non_tiepoints = np.where(np.isnan(self.ssa_raw)) # Filtered raw values # Use custom implementation of IDL SMOOTH: # idl_smooth(x, w) equivalent to SMOOTH(x, w, /edge_truncate, /nan) ssa_filter1 = idl_smooth(self.ssa_raw, self.filter_width) # Leave only the original ssh tie points ssa_filter1[non_tiepoints] = np.nan # Fill nans with linear interpolation and contant values at borders # python: fill_nan(x) = IDL: FILL_NAN(x, /NEIGHBOUR) ssa_filter2 = fill_nan(ssa_filter1) # Final smoothing ssa = idl_smooth(ssa_filter2, self.filter_width) self._value = ssa
def get_l2_track_vars(self, l2): # Set the requested data self.set_requested_date_from_l2(l2) # CAVEAT: The method `update_external_data()` will fail # if the requested date is February 29, since no # corresponding source file exists. As a fix, the data in # in this case is set back to the February 28. if self.month == "02" and self.day == "29": self.set_requested_date(int(self.year), int(self.month), 28) # Update the external data self.update_external_data() # Check if error with file I/O if self.error.status or self._data is None: # This will return an empty container snow = SnowParameterContainer() snow.set_dummy(l2.n_records) else: # Extract along track snow depth and density sd, sd_unc = self._get_snow_track(l2) # Apply along-track smoothing if required smooth_snowdepth = self.cfg.options.get("self.cfg.options", False) if smooth_snowdepth: filter_width = self.cfg.options.smooth_filter_width_m # Convert filter width to index filter_width /= l2.footprint_spacing # Round to odd number filter_width = np.floor(filter_width) // 2 * 2 + 1 sd = idl_smooth(sd, filter_width) sd_unc = idl_smooth(sd_unc, filter_width) # Collect Parameters and return # (density and density uncertainty fixed from l2 settings) snow = SnowParameterContainer() snow.depth = sd snow.depth_uncertainty = sd_unc snow.density = np.full(sd.shape, self.cfg.options.snow_density) snow.density_uncertainty = np.full( sd.shape, self.cfg.options.snow_density_uncertainty) # Register Variables self.register_auxvar("sd", "snow_depth", snow.depth, snow.depth_uncertainty) self.register_auxvar("sdens", "snow_density", snow.density, snow.density_uncertainty)
def get_l2_track_vars(self, l2): # Set the requested data self.set_requested_date_from_l2(l2) # Update the external data self.update_external_data() # Check if error with file I/O if self.error.status or self._data is None: # This will return an empty container snow = SnowParameterContainer() snow.set_dummy(l2.n_records) else: # Extract along track snow depth and density sd, sd_unc = self._get_snow_track(l2) # Apply along-track smoothing if required if self.cfg.options.smooth_snowdepth: filter_width = self.cfg.options.smooth_filter_width_m # Convert filter width to index filter_width /= l2.footprint_spacing # Round to odd number filter_width = np.floor(filter_width) // 2 * 2 + 1 sd = idl_smooth(sd, filter_width) sd_unc = idl_smooth(sd_unc, filter_width) # Collect Parameters and return # (density and density uncertainty fixed from l2 settings) snow = SnowParameterContainer() snow.depth = sd snow.depth_uncertainty = sd_unc snow.density = np.full(sd.shape, self.cfg.options.snow_density) snow.density_uncertainty = np.full( sd.shape, self.cfg.options.snow_density_uncertainty) # Register Variables self.register_auxvar("sd", "snow_depth", snow.depth, snow.depth_uncertainty) self.register_auxvar("sdens", "snow_density", snow.density, snow.density_uncertainty)
def get_l2_track_vars(self, l2): """ Get the snow depth, density and their uncertainties for the track in the l2 data object including the potential modification of the original climatology and filters """ # Validate hemisphere if l2.hemisphere == "south": snow = SnowParameterContainer() snow.set_dummy(l2.n_records) msg = "Warren99 not valid for southern hemisphere, returning 0" self.error.add_error("warren99-invalid-hemisphere", msg) return snow # Get the original warren climatology values # NOTE: snow is a class with properties depth, depth_uncertainty, density & density_uncertainty snow = self._get_warren99_fit_from_l2(l2) # Filter invalid values valid_min, valid_max = self.cfg.options.valid_snow_depth_range invalid = np.logical_or(snow.depth < valid_min, snow.depth > valid_max) invalid_records = np.where(invalid)[0] snow.set_invalid(invalid_records) # Apply ice_type (myi_fraction correction) scale_factor = (1.0 - l2.sitype) * self.cfg.options.fyi_correction_factor # The scaling factor affects the snow depth ... snow.depth = snow.depth - scale_factor * snow.depth # ... and the uncertainty. Here it is assumed that the uncertainty # is similar affected by the scaling factor. snow.depth_uncertainty = snow.depth_uncertainty - scale_factor * snow.depth_uncertainty # the uncertainty of the myi fraction is acknowledged by adding # an additional term that depends on snow depth, the magnitude of # scaling and the sea ice type uncertainty scaling_uncertainty = snow.depth * scale_factor * l2.sitype.uncertainty snow.depth_uncertainty = snow.depth_uncertainty + scaling_uncertainty # Smooth snow depth (if applicable) if self.cfg.options.smooth_snow_depth: filter_width = self.cfg.options.smooth_filter_width_m # Convert filter width to index filter_width /= l2.footprint_spacing # Round to odd number filter_width = np.floor(filter_width) // 2 * 2 + 1 snow.depth = idl_smooth(snow.depth, filter_width) # Register Variables self.register_auxvar("sd", "snow_depth", snow.depth, snow.depth_uncertainty) self.register_auxvar("sdens", "snow_density", snow.density, snow.density_uncertainty)