class TimeDependentAccumulationModel(AccumulationModel): """ An accumulation rate model that depends on the time dependent parameter (likely solar insolation or obliquity), A(Var(t)). A is in m/year. Interpolated splines are created for the parameter as a function of time for faster integration. Args: times (np.ndarray): times at which the variable (solar insolation, obliquity) is known (in years) parameter (np.ndarray): values of the time dependent variable (solar insolation (in W/m^2), obliquity (in degrees) ) """ def __init__(self, times: np.ndarray, variable: np.ndarray): dt = np.diff(times) assert (dt > 0).all(), "times must be monotonically increasing" self._times = times self._variable = variable self._var_data_spline = IUS(self._times, self._variable) self._int_var_data_spline = self._var_data_spline.antiderivative() self._var2_data_spline = IUS(self._times, self._variable**2) self._int_var2_data_spline = self._var2_data_spline.antiderivative() def get_accumulation_at_t(self, time: np.ndarray) -> np.ndarray: """ Calculates the accumulation rate at times "time". Args: time (np.ndarray): times at which we want to calculate A, in years. Output: np.ndarray of the same size as time input containing values of accumulation rates A, in m/year """ return self.eval(self._var_data_spline(time)) def get_xt( self, time: np.ndarray, int_retreat_model_t_spline: np.ndarray, cot_angle, csc_angle, ): """ Calculates the horizontal distance x (in m) traveled by a point in the center of the high side of the trough. This distance x is a function of the accumulation rate A(ins(t)) and the retreat rate of ice R(l(t),t) as in dx/dt=(R(l(t),t)+A(ins(t))cos(theta))/sin(theta). Where theta is the slope angle of the trough. Args: time (np.ndarray): times at which we want the path. Output: horizontal distances (np.ndarray) of the same size as time input, in meters. """ yt = self.get_yt(time) return -cot_angle * yt + csc_angle * ( int_retreat_model_t_spline(time) - int_retreat_model_t_spline(0))
def __init__(self, x_points, pdf_points, transform='interval', *args, **kwargs): if transform == 'interval': transform = transforms.interval(x_points[0], x_points[-1]) super(Interpolated, self).__init__(transform=transform, *args, **kwargs) interp = InterpolatedUnivariateSpline(x_points, pdf_points, k=1, ext='zeros') Z = interp.integral(x_points[0], x_points[-1]) self.Z = tt.as_tensor_variable(Z) self.interp_op = DifferentiableSplineWrapper(interp) self.x_points = x_points self.pdf_points = pdf_points / Z self.cdf_points = interp.antiderivative()(x_points) / Z self.median = self._argcdf(0.5)
def test_cubic_spline(): # We sample some irregularly sampled points x = np.logspace(-2, 1, 64) y = _testing_function(x) spl = InterpolatedUnivariateSpline(x, y, k=3) spl_ref = RefSpline(x, y, k=3) # Vector of points at which to interpolate, note that this goes outside of # the interpolation data, so we are also testing extrapolation t = np.linspace(-1, 11, 128) assert_allclose(spl_ref(t), spl(t), rtol=1e-10) # Test the antiderivative, up to integration constant a = spl_ref.antiderivative()(t) - spl_ref.antiderivative()(0.01) b = spl.antiderivative(t) - spl.antiderivative(0.01) assert_allclose(a, b, rtol=1e-10)
def compare_f_dia(shot, time, EQ_exp, EQ_diag, EQ_ed): from Plotting_Configuration import plt EQObj = EQData(shot, EQ_exp=EQ_exp, EQ_diag=EQ_diag, EQ_ed=EQ_ed) EQ_t = EQObj.GetSlice(time) rho = np.linspace(0.0, 1.0, 100) ffp = EQObj.getQuantity(rho, "FFP", time) ffp_spl = InterpolatedUnivariateSpline(rho, ffp) f_sq_spl = ffp_spl.antiderivative(1) magn_field_axis = EQObj.MBI_shot.getSignal("BTFABB", \ tBegin=time - 5.e-5, tEnd=time + 5.e-5) f_spl = InterpolatedUnivariateSpline(rho, np.sign(magn_field_axis) * \ (np.sqrt(2.0 * f_sq_spl(rho) + \ (EQ_t.R_ax * magn_field_axis)**2))) psi_prof = EQObj.rhop_to_Psi(time, rho) plt.plot(psi_prof, f_spl(rho)) gpol = EQObj.getQuantity(rho, "Jpol", time) * cnst.mu_0 / 2.0 / np.pi plt.plot(psi_prof, gpol, "--") plt.show()
class PowerLaw_Insolation(TimeDependentAccumulationModel, PowerLawModel): def __init__( self, ins_times: np.ndarray, insolations: np.ndarray, coeff: float = 0.1, exponent: float = -1, ): PowerLawModel.__init__(self, coeff, exponent) super().__init__(ins_times, insolations) def get_yt(self, time: np.ndarray): """ Calculates the vertical distance y (in m) at traveled by a point in the center of the high side of the trough. This distance is a function of the accumulation rate A as y(t)=integral(A(ins(t)), dt) or dy/dt=A(ins(t)) Args: time (np.ndarray): times at which we want to calculate y, in years. Output: np.ndarray of the same size as time input containing values of the vertical distance y, in meters. """ self._variable_exp = self._variable**self.exponent self._var_exp_data_spline = IUS(self._times, self._variable_exp ) self._int_var_exp_data_spline = self._var_exp_data_spline.antiderivative() return -(self.coeff* (self._int_var_exp_data_spline(time) -self._int_var_exp_data_spline(0) ) )
# draw sphere n=len(first_elem) xline = first_elem_norm_smooth[:n,0] yline = first_elem_norm_smooth[:n,1] zline = first_elem_norm_smooth[:n,2] #spline the accleration, aka alpha' t = np.linspace(0, 2.56, 128) #define time interval a_x_spline = IUS(t, xline) a_y_spline = IUS(t, yline) a_z_spline = IUS(t, zline) #find the velocity, aka alpha v_x_spline = a_x_spline.antiderivative() v_y_spline = a_y_spline.antiderivative() v_z_spline = a_z_spline.antiderivative() #find alpha'' ap_x_spline = a_x_spline.derivative() ap_y_spline = a_y_spline.derivative() ap_z_spline = a_z_spline.derivative() #find alpha''' app_x_spline = ap_x_spline.derivative() app_y_spline = ap_y_spline.derivative() app_z_spline = ap_z_spline.derivative() #make a finer time vector newt = np.linspace(min(t), max(t), num = 20*len(t))
class Trough(object): def __init__( self, acc_params, lag_params, acc_model_number, lag_model_number, errorbar=1.0, ): """Constructor for the trough object. Args: acc_params (array like): model parameters for accumulation acc_model_number (int): index of the accumulation model lag_params (array like): model parameters for lag(t) lag_model_number (int): index of the lag(t) model errorbar (float): errorbar of the datapoints in pixels; default=1 """ # Load in all data with pkg_resources.path(__package__, "Insolation.txt") as path: insolation, ins_times = np.loadtxt(path, skiprows=1).T with pkg_resources.path(__package__, "R_lookuptable.txt") as path: retreats = np.loadtxt(path).T with pkg_resources.path(__package__, "TMP_xz.txt") as path: xdata, ydata = np.loadtxt(path, unpack=True) # TODO: remember what this means... lol # I'm pretty sure one file has temp data and the other # has real data. # xdata, ydata = np.loadtxt(here+"/RealXandZ.txt") # Trough angle self.angle_degrees = 2.9 # degrees self.sin_angle = np.sin(self.angle_degrees * np.pi / 180.0) self.cos_angle = np.cos(self.angle_degrees * np.pi / 180.0) self.csc_angle = 1.0 / self.sin_angle self.sec_angle = 1.0 / self.cos_angle self.tan_angle = self.sin_angle / self.cos_angle self.cot_angle = 1.0 / self.tan_angle # Set up the trough model self.acc_params = np.array(acc_params) self.lag_params = np.array(lag_params) self.acc_model_number = acc_model_number self.lag_model_number = lag_model_number self.errorbar = errorbar self.meters_per_pixel = np.array([500.0, 20.0]) # meters per pixel # Positive times are now in the past ins_times = -ins_times # Attach data to this object self.insolation = insolation self.ins_times = ins_times self.retreats = retreats self.xdata = xdata * 1000 # meters self.ydata = ydata # meters self.Ndata = len(self.xdata) # number of data points # Create splines self.lags = np.arange(16) + 1 self.lags[0] -= 1 self.lags[-1] = 20 self.ins_spline = IUS(ins_times, insolation) self.iins_spline = self.ins_spline.antiderivative() self.ins2_spline = IUS(ins_times, insolation**2) self.iins2_spline = self.ins2_spline.antiderivative() self.ret_spline = RBS(self.lags, self.ins_times, self.retreats) self.re2_spline = RBS(self.lags, self.ins_times, self.retreats**2) # Pre-calculate the lags at all times self.lags_t = self.get_lag_at_t(self.ins_times) self.compute_splines() def set_model(self, acc_params, lag_params, errorbar): """Setup a new model, with new accumulation and lag parameters. """ assert len(acc_params) == len(self.acc_params), ( "New and original accumulation parameters must have the same shape. %d vs %d" % (len(acc_params), len(self.acc_params))) assert len(lag_params) == len(self.lag_params), ( "New and original lag parameters must have the same shape. %d vs %d" % (len(lag_params), len(self.lag_params))) # Set the new errorbar self.errorbar = errorbar # Set the new accumulation and lag parameters self.acc_params = acc_params self.lag_params = lag_params # Compute the lag at all times self.lags_t = self.get_lag_at_t(self.ins_times) return def compute_splines(self): # To be called after set_model self.lag_spline = IUS(self.ins_times, self.lags_t) self.Retreat_model_at_t = self.get_Rt_model(self.lags_t, self.ins_times) self.retreat_model_spline = IUS(self.ins_times, self.Retreat_model_at_t) self.iretreat_model_spline = self.retreat_model_spline.antiderivative() return def get_insolation(self, time): return self.ins_spline(time) def get_retreat(self, lag, time): return self.ret_spline(time, lag) def get_lag_at_t(self, time): # Model dependent num = self.lag_model_number p = self.lag_params if num == 0: a = p[0] # lag = constant return a * np.ones_like(time) if num == 1: a, b = p[0:2] # lag(t) = a + b*t return a + b * time return # error, since no number is returned def get_Rt_model(self, lags, times): ret = self.ret_spline.ev(lags, times) return ret def get_accumulation(self, time): # Model dependent num = self.acc_model_number p = self.acc_params if num == 0: a = p[0] # a*Ins(t) return -1 * (a * self.ins_spline(time)) if num == 1: a, b = p[0:2] # a*Ins(t) + b*Ins(t)^2 return -1 * (a * self.ins_spline(time) + b * self.ins2_spline(time)) return # error, since no number is returned def get_yt(self, time): # Model dependent # This is the depth the trough has traveled num = self.acc_model_number p = self.acc_params if num == 0: a = p[0] # a*Ins(t) return -1 * (a * (self.iins_spline(time) - self.iins_spline(0))) if num == 1: a, b = p[0:2] # a*Ins(t) + b*Ins(t)^2 return -1 * (a * (self.iins_spline(time) - self.iins_spline(0)) + b * (self.iins2_spline(time) - self.iins2_spline(0))) return # error def get_xt(self, time): # This is the horizontal distance the trough has traveled # Model dependent yt = self.get_yt(time) return -self.cot_angle * yt + self.csc_angle * ( self.iretreat_model_spline(time) - self.iretreat_model_spline(0)) def get_trajectory(self): x = self.get_xt(self.ins_times) y = self.get_yt(self.ins_times) return x, y def get_nearest_points(self): x = self.get_xt(self.ins_times) y = self.get_yt(self.ins_times) xd = self.xdata yd = self.ydata xn = np.zeros_like(xd) yn = np.zeros_like(yd) for i in range(len(xd)): ind = np.argmin((x - xd[i])**2 + (y - yd[i])**2) xn[i] = x[ind] yn[i] = y[ind] return xn, yn def lnlikelihood(self): xd = self.xdata yd = self.ydata xn, yn = self.get_nearest_points() # Variance in meters in both directions xvar, yvar = (self.errorbar * self.meters_per_pixel)**2 chi2 = (xd - xn)**2 / xvar + (yd - yn)**2 / yvar return -0.5 * chi2.sum() - 0.5 * self.Ndata * np.log(xvar * yvar)
class Trough(Model): """ This object models trough migration patterns (TMPs). It is composed of a model for the accumulation of ice on the surface of the trough, accessible as the :attr:`accuModel` attribute, as well as a model for the lag that builds up over time, accesible as the :attr:`lagModel` attribute. Args: acc_model (Union[str, Model]): name of the accumulation model (linear, quadratic, etc) or a custom model lag_model_name (Union[str, Model]): name of the lag(t) model (constant, linear, etc) or a custom model acc_params (List[float]): model parameters for accumulation lag_params (List[float]): model parameters for lag(t) errorbar (float, optional): errorbar of the datapoints in pixels; default=1 angle (float, optional): south-facing slope angle in degrees. Default is 2.9. insolation_path (Union[str, Path], optional): path to the file with insolation data. """ def __init__( self, acc_model: Union[str, Model], lag_model: Union[str, Model], acc_params: Optional[List[float]] = None, lag_params: Optional[List[float]] = None, tmp: Optional[int] = None, errorbar: float = 1.0, angle: float = 2.9, ): """Constructor for the trough object. Args: acc_params (array like): model parameters for accumulation acc_model_name (str): name of the accumulation model (linear, quadratic, etc) lag_params (array like): model parameters for lag(t) lag_model_name (str): name of the lag(t) model (constant, linear, etc) errorbar (float, optional): errorbar of the datapoints in pixels; default=1 angle (float, optional): south-facing slope angle in degrees. Default is 2.9. """ # Load all data retreat_times, retreats, lags = load_retreat_data() retreat_times = -retreat_times self.angle = angle self.errorbar = errorbar self.meters_per_pixel = np.array([500.0, 20.0]) # meters per pixel # Create submodels if isinstance(acc_model, str): # name of existing model is given if "obliquity" in acc_model: #load obliquity data and times obliquity, obl_times = load_obliquity_data() obl_times = -obl_times.astype(float) #remove zeros from time array condZero = obl_times == 0 indx = np.array(range(len(condZero))) indxZero = indx[condZero] obl_times[indxZero] = 1e-10 acc_time, acc_y = obl_times, obliquity else: insolation, ins_times = load_insolation_data(tmp) ins_times = -ins_times.astype(float) #remove zeros from time array condZero = ins_times == 0 indx = np.array(range(len(condZero))) indxZero = indx[condZero] ins_times[indxZero] = 1e-10 acc_time, acc_y = ins_times, insolation self.accuModel = ACCUMULATION_MODEL_MAP[acc_model](acc_time, acc_y, *acc_params) else: # custom model is given self.accuModel = acc_model # Lag submodel assert isinstance(lag_model, (str, Model)), "lag_model must be a string or Model" if isinstance(lag_model, str): # name of existing model is given self.lagModel = LAG_MODEL_MAP[lag_model](*lag_params) else: # custom model was given self.lagModel = lag_model # Call super() with the acc and lag models. This # way their parameters are visible here. super().__init__(sub_models=[self.accuModel, self.lagModel]) # Create data splines of retreat of ice (no dependency # on model parameters) self.ret_data_spline = RBS(lags, retreat_times, retreats) self.re2_data_spline = RBS(lags, retreat_times, retreats**2) # Calculate the model of retreat of ice per time self.lag_at_t = self.lagModel.get_lag_at_t(self.accuModel._times) self.retreat_model_t = self.ret_data_spline.ev(self.lag_at_t, self.accuModel._times) # Compute the Retreat(time) spline self.retreat_model_t_spline = IUS(self.accuModel._times, self.retreat_model_t) @property def parameter_names(self) -> List[str]: """Just the errorbar""" return ["errorbar"] def set_model( self, all_parameters: Dict[str, float], ) -> None: """ Updates trough model with new accumulation and lag parameters. Then updates all splines. Args: all_parameter (Dict[str, float]): new parameters to the models """ self.all_parameters = all_parameters # Update the model of retreat of ice per time self.lag_at_t = self.lagModel.get_lag_at_t(self.accuModel._times) self.retreat_model_t = self.ret_data_spline.ev(self.lag_at_t, self.accuModel._times) # Update the Retreat(time) spline self.retreat_model_t_spline = IUS(self.accuModel._times, self.retreat_model_t) return def get_trajectory( self, times: Optional[np.ndarray] = None ) -> Tuple[np.ndarray, np.ndarray]: """ Obtains the x and y coordinates (in m) of the trough model as a function of time. Args: times (Optional[np.ndarray]): if ``None``, default to the times of the observed solar insolation. Output: x and y coordinates (tuple) of size 2 x len(times) (in m). """ y = self.accuModel.get_yt(times) x = self.accuModel.get_xt( times, self.retreat_model_t_spline.antiderivative(), self.cot_angle, self.csc_angle, ) return x, y @staticmethod def _L2_distance(x1, x2, y1, y2) -> Union[float, np.ndarray]: """ The L2 (Eulerean) distance (squared) between two 2D vectors. Args: x1 (Union[float, np.ndarray]): x-coordinate of the first vector x2 (Union[float, np.ndarray]): x-coordinate of the second vector y1 (Union[float, np.ndarray]): y-coordinate of the first vector y2 (Union[float, np.ndarray]): y-coordinate of the second vector Output: L2 distance (int or float) """ return (x1 - x2)**2 + (y1 - y2)**2 def get_nearest_points( self, x_data: np.ndarray, y_data: np.ndarray, times: Optional[np.ndarray] = None, dist_func: Optional[Callable] = None, ) -> Tuple[np.ndarray, np.ndarray]: """ Finds the coordinates of the nearest points between the model TMP and the data TMP. Args: x_data (np.ndarray): x-coordinates of the data y_data (np.ndarray): y-coordinatse of the data dist_func (Optional[Callable]): function to compute distances, defaults to the L2 distance :meth:`mars_troughs.trough.Trough._L2_distance` Output: x and y coordinates of the model TMP that are closer to the data TMP. (Tuple), size 2 x len(x_data) """ dist_func = dist_func or Trough._L2_distance x_model, y_model = self.get_trajectory(times) x_out = np.zeros_like(x_data) y_out = np.zeros_like(y_data) for i, (xdi, ydi) in enumerate(zip(x_data, y_data)): dist = dist_func(x_model, xdi, y_model, ydi) ind = np.argmin(dist) x_out[i] = x_model[ind] y_out[i] = y_model[ind] return x_out, y_out def lnlikelihood( self, x_data: np.ndarray, y_data: np.ndarray, times: Optional[np.ndarray] = None, ) -> float: """ Calculates the log-likelihood of the data given the model. Note that this is the natural log (ln). Args: x_data (np.ndarray): x-coordinates of the trough path y_data (np.ndarray): y-coordinates of the trough path Output: log-likelihood value (float) """ self.xnear, self.ynear = self.get_nearest_points(x_data, y_data, times) # Variance in meters in both directions xvar, yvar = (self.errorbar * self.meters_per_pixel)**2 chi2 = (x_data - self.xnear)**2 / xvar + (y_data - self.ynear)**2 / yvar return -0.5 * chi2.sum() - 0.5 * len(x_data) * np.log(xvar * yvar) @property def angle(self) -> float: """ Slope angle in degrees. """ return self._angle * 180.0 / np.pi @angle.setter def angle(self, value: float) -> float: """Setter for the angle""" self._angle = value * np.pi / 180.0 self._csc = 1.0 / np.sin(self._angle) self._cot = np.cos(self._angle) * self._csc @property def csc_angle(self) -> float: """ Cosecant of the slope angle. """ return self._csc @property def cot_angle(self) -> float: """ Cotangent of the slope angle. """ return self._cot
def make_E_Fit_EQDSK(self, working_dir, shot, time, I_p): EQDSK_file = open(os.path.join(working_dir, "g{0:d}_{1:05d}".format(self.shot, int(time*1.e3))), "w") dummy = 0.0 EQ_t = self.GetSlice(time) today = datetime.date.today() # First line EQDSK_file.write("{0:50s}".format("EFIT " + str(today.month) + "/" + str(today.day) + "/" + str(today.year) + " #" + str(shot) + \ " " + str(int(time*1.e3)) + "ms")) # Second line EQDSK_file.write("{0: 2d}{1: 4d}{2: 4d}\n".format(self.EQ_ed, len(EQ_t.R), len(EQ_t.z))) # Third line EQDSK_file.write("{0: 1.9e}{1: 1.9e}{2: 1.9e}{3: 1.9e}{4: 1.9e}\n".format(EQ_t.R[-1] - EQ_t.R[0], EQ_t.z[-1] - EQ_t.z[0], \ 1.65, EQ_t.R[0], np.mean(EQ_t.z))) B_axis = np.double(self.get_B_on_axis(time)) * np.sign(np.mean(EQ_t.Bt.flatten())) # Fourth line EQDSK_file.write("{0: 1.9e}{1: 1.9e}{2: 1.9e}{3: 1.9e}{4: 1.9e}\n".format(EQ_t.R_ax, EQ_t.z_ax, \ EQ_t.Psi_ax, EQ_t.Psi_sep, np.double(B_axis))) # Fifth line EQDSK_file.write("{0: 1.9e}{1: 1.9e}{2: 1.9e}{3: 1.9e}{4: 1.9e}\n".format(I_p, EQ_t.Psi_ax, 0.0, EQ_t.R_ax, 0.0)) # Sixth line EQDSK_file.write("{0: 1.9e}{1: 1.9e}{2: 1.9e}{3: 1.9e}{4: 1.9e}\n".format(EQ_t.z_ax, 0.0, EQ_t.Psi_sep, 0.0, 0.0)) N = len(EQ_t.R) Psi = np.linspace(EQ_t.Psi_ax, EQ_t.Psi_sep, N) rhop = np.sqrt((Psi - EQ_t.Psi_ax)/(EQ_t.Psi_sep - EQ_t.Psi_ax)) quant_dict = {} quant_dict["q"] = self.getQuantity(rhop, "Qpsi", time) quant_dict["pres"] = self.getQuantity(rhop, "Pres", time) quant_dict["pprime"] = self.getQuantity(rhop, "dPres", time) quant_dict["ffprime"] = self.getQuantity(rhop, "FFP", time) ffp_spl = InterpolatedUnivariateSpline(rhop, quant_dict["ffprime"] ) f_sq_spl = ffp_spl.antiderivative(1) f_spl = InterpolatedUnivariateSpline(rhop, np.sign(B_axis) * \ (np.sqrt(2.0 * f_sq_spl(rhop) + \ (EQ_t.R_ax * B_axis)**2))) # Get the correct sign back since it is not included in ffprime quant_dict["fdia"] = f_spl(rhop) N_max = 5 format_str = " {0: 1.9e}" for key in ["fdia", "pres", "ffprime", "pprime"]: i = 0 while i < N: EQDSK_file.write(format_str.format(quant_dict[key][i])) i += 1 if(i % N_max == 0): EQDSK_file.write("\n") if(i % N_max != 0): EQDSK_file.write("\n") N_new = 0 for i in range(len(EQ_t.R)): for j in range(len(EQ_t.z)): EQDSK_file.write(format_str.format(EQ_t.Psi[i,j])) N_new += 1 if(N_new == N_max): EQDSK_file.write("\n") N_new = 0 if(N_new != 0): EQDSK_file.write("\n") for key in ["q"]: i = 0 while i < N: EQDSK_file.write(format_str.format(quant_dict[key][i])) i += 1 if(i % N_max == 0): EQDSK_file.write("\n") EQDSK_file.flush() EQDSK_file.close()