Source code for hysteresis.resample

import numpy as np
from .baseClass import Hysteresis, SimpleCycle, MonotonicCurve
from .baseFuncs import (concatenateHys, concatenate, _linInterp, 
                        _linInterpSample, _getNsamples)
       
# =============================================================================
# concatenate and resample
# =============================================================================
           
[docs]def resample(curve, Nsamples, **kwargs): """ Creates a new curve object that has a different number of sample points between the start and end of each Cycle object. The curve resampled will be one level down, i.e. Hystereses will their simpleCycles resampled, and SimpleCycles will have their Monotonic Curves resampled The number of points is specified by the user, and intermediate points will be evenly spaced between the cycles start and end points. Linear interpolation is used to find the y value of intermediate points. In the case of a Hysteresis, every SimpleCycle curve will be resampled with an amount of points equal to Nsamples. In the case of a SimpleCycle, Monotonic curves are resampled if they are set. Parameters ---------- curve : Hysteresis, Monotonic Cycle, or numpy array The curve to be resampled. If a numpy array is provided, it must be increasing to be resampled properly. Nsamples : The number of samples for the new curve. Returns ------- curve : Hysteresis, SimpleCycle, An object of the input type with a number of points equal to Nsamples """ # We recursively resample by calling the function again for sub-cycle objects # if the curve is a SimpleCycle if isinstance(curve, SimpleCycle): x = curve.xy[:,0] y = curve.xy[:,1] # If the simple cycle has subcycles, resample at the level of those. if curve.subCycles: outputSubcycles = [None]*curve.NsubCycles for ii, subcycle in enumerate(curve.subCycles): outputSubcycles[ii] = resample(subcycle, Nsamples) tmpOut = concatenate(outputSubcycles, outputClass = SimpleCycle) tmpProps = curve._getStatePropreties() Output = SimpleCycle(tmpOut.xy, *tmpProps) # Otherwise, resample the curve directly else: Output = SimpleCycle(_linInterp(x,y, Nsamples)) # if the curve is a hysteresis, we recursively create a series of Cycles elif isinstance(curve, Hysteresis): outputCycles = [None]*curve.NCycles for ii, cycle in enumerate(curve.cycles): outputCycles[ii] = resample(cycle, Nsamples) Output = concatenateHys(outputCycles) # If curve tmpOut = concatenateHys(outputCycles) # If curve tmpProps = curve._getStatePropreties() Output = Hysteresis(tmpOut.xy,*tmpProps) # if the curve is a Monotonic Cycle elif isinstance(curve, MonotonicCurve): x = curve.xy[:,0] y = curve.xy[:,1] Output = MonotonicCurve(_linInterp(x,y, Nsamples)) # if it is a np array elif isinstance(curve, np.ndarray): x = curve[:,0] y = curve[:,1] Output = _linInterp(x,y, Nsamples) else: Output = None return Output
[docs]def resampleDx(curve, Targetdx): """ Creates a new curve object where the distance between sample points is approximately dx. The number of samples betweent the start and end point of each cycle is calculated so that the is as close to dx as possible. Note that distance isn't precisely dx. For each curve, a number of points will be chosen based on some target displacement value in the x direction. If the value overshoots, the last point will instead be used. For example, if the start = 2.5, end = 7.5 and dx = 2, points will be placed at 2.5, 4.5, 6.5, 7.5 Linear interpolation is used to find the y value of intermediate points. In the case of a Hysteresis, every SimpleCycle curve will be resampled with an amount of points equal to Nsamples. In the case of a SimpleCycle, each monotonic curve will be resampled. Parameters ---------- curve : Hysteresis, Monotonic Cycle, or numpy array The curve to be resampled. Targetdx : float The target distance to use for sample points. Returns ------- curve : An object of the input type, with points approximately at Targetdx. """ # the logic in these functions is getting a little nasty, in the future # consider pulling these out into class methods. if isinstance(curve, SimpleCycle): if curve.subCycles: # if subsycles are set, resample those outputSubcycles = [None]*curve.NsubCycles for ii, subcycle in enumerate(curve.subCycles): outputSubcycles[ii] = resampleDx(subcycle, Targetdx) # Output = concatenate(*outputSubcycles, outputClass = SimpleCycle) # If curve tmpOut = concatenate(outputSubcycles, outputClass = SimpleCycle) tmpProps = curve._getStatePropreties() Output = SimpleCycle(tmpOut.xy, *tmpProps) else: # if subsycles aren't set, resample those x = curve.xy[:,0] dxNet = x[-1] - x[0] Nsamples = _getNsamples(Targetdx, dxNet) Output = resample(curve, Nsamples) elif isinstance(curve, Hysteresis): outputCycles = [None]*curve.NCycles for ii, cycle in enumerate(curve.cycles): outputCycles[ii] = resampleDx(cycle, Targetdx) tmpOut = concatenateHys(outputCycles) # If curve tmpProps = curve._getStatePropreties() Output = Hysteresis(tmpOut.xy,*tmpProps) # if the curve is a MonotonicCurve Cycle elif isinstance(curve, MonotonicCurve): x = curve.xy[:,0] dxNet = x[-1] - x[0] Nsamples = _getNsamples(Targetdx, dxNet) Output = resample(curve, Nsamples) # if it is a np array elif isinstance(curve, np.ndarray): x = curve[:,0] dxNet = x[-1] - x[0] Nsamples = _getNsamples(Targetdx, dxNet) Output = resample(curve, Nsamples) return Output
def _parseRegions(regions): """ Ensures each region either has only two items, and is specified in a list of lists. """ if len(regions) == 2 and isinstance(regions[0], float): regions = [regions] for item in regions: if len(item) !=2: raise Exception return regions def _getBreakPoints(regions): breakPoints = set() for region in regions: breakPoints.update(set(region)) return list(breakPoints) def _getOutsideRegions(regions): """ Gets the regions outside of the specified region """ xmin = 0 xmax = 1 breakPoints = _getBreakPoints(regions) outsideRegions = [] isInside = [] if xmin < breakPoints[0]: outsideRegions.append([xmin, breakPoints[0]]) isInside.append(False) Nregions = len(regions) for ii in range(Nregions): outsideRegions.append(regions[ii]) isInside.append(True) if ii != Nregions - 1: # check if we aren't out of bounds. outsideRegions.append([regions[ii][1], regions[ii+1][0]]) isInside.append(False) if breakPoints[-1] < xmax: outsideRegions.append([breakPoints[-1], xmax]) isInside.append(False) return outsideRegions, isInside
[docs]def resampleRegion(curve, Nsamples, regions = [[0, 0.1], [0.9, 1]]): """ Resamples only parts of an input a curve. The part of the curve that is resampled is defined using the regions variable. The regions is a list of lists, defining a start and end point of the curve in terms of a parameter varying between 0 and 1. Each region is resampled using Nsamples Parameters ---------- curve : Hysteresis Curve, or numpy array The input curve to be resampled. Can be either a hystersis curve, or a numpy array. If a numpy array is provided, it must not change direction to be resampled properly. region : list of lists, or list A lists of regions, written in terms of a parameter that ranges from 0 at the start of the curve to 1 at the end of the curve. Regions are either lists of lists, or a single list if the number of regions is equal to one. Regions must be sorted from left to right and have no overlaps, i.e. [[0, 0.2], [0.1, 0.2]] is invalid [[0, 0.1], [0.8, 0.9], [0.4, 0.5]] is invalid Nsample : int The number of sample to use for each region. Returns ------- None. """ regions = _parseRegions(regions) # these get interpolated allRegions, isInside = _getOutsideRegions(regions) # these get left alone if isinstance(curve, SimpleCycle): if curve.subCycles: # if subsycles are set, resample those outputSubcycles = [None]*curve.NsubCycles for ii, subcycle in enumerate(curve.subCycles): outputSubcycles[ii] = resampleRegion(subcycle, Nsamples, regions) tmpOut = concatenate(outputSubcycles, outputClass = SimpleCycle) tmpProps = curve._getStatePropreties() output = SimpleCycle(tmpOut.xy, *tmpProps) else: # if subsycles aren't set, resample at level of xy x = curve.xy[:,0] y = curve.xy[:,1] xy = np.column_stack([x, y]) output = SimpleCycle(resampleRegion(xy, Nsamples, regions)) elif isinstance(curve, Hysteresis): outputCycles = [None]*curve.NCycles for ii, cycle in enumerate(curve.cycles): outputCycles[ii] = resampleRegion(cycle, Nsamples, regions) tmpOut = concatenateHys(outputCycles) tmpProps = curve._getStatePropreties() output = Hysteresis(tmpOut.xy,*tmpProps) elif isinstance(curve, MonotonicCurve): x = curve.xy[:,0] y = curve.xy[:,1] xy = np.column_stack([x, y]) output = MonotonicCurve(resampleRegion(xy, Nsamples, regions)) # if it is a np array elif isinstance(curve, np.ndarray): x = curve[:,0] y = curve[:,1] xSamples = [] ySamples = [] allRegions = np.array(allRegions)*(x[-1] - x[0]) + x[0] if x[0] <= x[-1]: isLeftRight = True else: isLeftRight = False for ii, region in enumerate(allRegions): if isInside[ii]: xtmp = np.linspace(region[0], region[1], Nsamples) ytmp = _linInterpSample(x, y, xtmp)[:,1] else: if isLeftRight: inds = np.where((region[0] < x) * (x < region[1])) else: inds = np.where((x < region[0]) * (region[1] < x)) xtmp = x[inds] ytmp = y[inds] xSamples.append(xtmp) ySamples.append(ytmp) xSamples = np.concatenate(xSamples) ySamples = np.concatenate(ySamples) output = np.column_stack([xSamples, ySamples]) return output