diff --git a/scripts/PPTPmodel-Kyle.py b/scripts/PPTPmodel-Kyle.py index 8aeb2de..4954245 100644 --- a/scripts/PPTPmodel-Kyle.py +++ b/scripts/PPTPmodel-Kyle.py @@ -57,8 +57,8 @@ def polarization_powder(self,Ei, EMin, S2, alpha_p, plot_options="alpha",left=Tr Qx= (-1 if left else 1) * kf2d*np.sqrt((1-cos_theta**2)) cos_ang_PQ = (Qx*Px + Qz*Pz)/Q2d/np.sqrt(Px**2+Pz**2) - cos_ang_PQ[cos_ang_PQ**2 <0.4] = np.nan - cos_ang_PQ[cos_ang_PQ**2 >0.6] = np.nan + # cos_ang_PQ[cos_ang_PQ**2 <0.4] = np.nan + # cos_ang_PQ[cos_ang_PQ**2 >0.6] = np.nan ang_PQ = np.degrees(np.arccos(cos_ang_PQ)) kf=np.sqrt(Ei-E)*SE2K @@ -124,7 +124,7 @@ def polarization_powder(self,Ei, EMin, S2, alpha_p, plot_options="alpha",left=Tr if __name__ == "__main__": obj = Model() - output = obj.polarization_powder(20, None, 60, 30,plot_options="alpha",left=True) + output = obj.polarization_powder(20, None, 40, 0,plot_options="cos^2(a)",left=True) Q_low, Q_hi, E, Q2d, E2d, ang_PQ = output[0], output[1], output[2], output[3], output[4], output[5] fig, ax = plt.subplots() diff --git a/src/hyspecppt/hppt/experiment_settings.py b/src/hyspecppt/hppt/experiment_settings.py index 1fa9cd9..f7ab93c 100644 --- a/src/hyspecppt/hppt/experiment_settings.py +++ b/src/hyspecppt/hppt/experiment_settings.py @@ -21,6 +21,11 @@ DEFAULT_MODE = dict(current_experiment_type="single_crystal") # maximum momentum transfer MAX_MODQ = 15 +# number of points in the plot +N_POINTS = 200 +# tank half-width +TANK_HALF_WIDTH = 30.0 + # invalid style INVALID_QLINEEDIT = """ diff --git a/src/hyspecppt/hppt/hppt_model.py b/src/hyspecppt/hppt/hppt_model.py index d3bd78b..bf64a72 100644 --- a/src/hyspecppt/hppt/hppt_model.py +++ b/src/hyspecppt/hppt/hppt_model.py @@ -11,7 +11,9 @@ DEFAULT_LATTICE, DEFAULT_MODE, MAX_MODQ, + N_POINTS, PLOT_TYPES, + TANK_HALF_WIDTH, ) logger = logging.getLogger("hyspecppt") @@ -20,19 +22,19 @@ class SingleCrystalParameters: """Model for single crystal calculations""" - a: float = DEFAULT_LATTICE["a"] - b: float = DEFAULT_LATTICE["b"] - c: float = DEFAULT_LATTICE["c"] - alpha: float = DEFAULT_LATTICE["alpha"] - beta: float = DEFAULT_LATTICE["beta"] - gamma: float = DEFAULT_LATTICE["gamma"] - h: float = DEFAULT_LATTICE["h"] - k: float = DEFAULT_LATTICE["k"] - l: float = DEFAULT_LATTICE["l"] + a: float + b: float + c: float + alpha: float + beta: float + gamma: float + h: float + k: float + l: float def __init__(self) -> None: """Constructor""" - return + self.set_parameters(DEFAULT_LATTICE) def set_parameters(self, params: dict[str, float]) -> None: """Store single crystal parameters @@ -54,63 +56,58 @@ def set_parameters(self, params: dict[str, float]) -> None: def get_parameters(self) -> dict[str, float]: """Returns all the parameters as a dictionary""" - try: - return dict( - a=self.a, - b=self.b, - c=self.c, - alpha=self.alpha, - beta=self.beta, - gamma=self.gamma, - h=self.h, - k=self.k, - l=self.l, - ) - except AttributeError: - logger.error("The parameters were not initialized-get_parameters-SingleCrystalParameters") + return dict( + a=self.a, + b=self.b, + c=self.c, + alpha=self.alpha, + beta=self.beta, + gamma=self.gamma, + h=self.h, + k=self.k, + l=self.l, + ) def calculate_modQ(self) -> float: """Returns |Q| from lattice parameters and h, k, l""" - try: - ca = np.cos(np.radians(self.alpha)) - sa = np.sin(np.radians(self.alpha)) - cb = np.cos(np.radians(self.beta)) - sb = np.sin(np.radians(self.beta)) - cg = np.cos(np.radians(self.gamma)) - sg = np.sin(np.radians(self.gamma)) - vabg = np.sqrt(1 - ca**2 - cb**2 - cg**2 + 2 * ca * cb * cg) - astar = sa / (self.a * vabg) - bstar = sb / (self.b * vabg) - cstar = sg / (self.c * vabg) - cas = (cb * cg - ca) / (sb * sg) # noqa: F841 - cbs = (cg * ca - cb) / (sg * sa) - cgs = (ca * cb - cg) / (sa * sb) - - # B matrix - B = np.array( - [ - [astar, bstar * cgs, cstar * cbs], - [0, bstar * np.sqrt(1 - cgs**2), -cstar * np.sqrt(1 - cbs**2) * ca], - [0, 0, 1.0 / self.c], - ] - ) - - modQ = 2 * np.pi * np.linalg.norm(B.dot([self.h, self.k, self.l])) - return modQ - except AttributeError: - logger.error("The parameters were not initialized-calculate_modQ") + ca = np.cos(np.radians(self.alpha)) + sa = np.sin(np.radians(self.alpha)) + cb = np.cos(np.radians(self.beta)) + sb = np.sin(np.radians(self.beta)) + cg = np.cos(np.radians(self.gamma)) + sg = np.sin(np.radians(self.gamma)) + vabg = np.sqrt(1 - ca**2 - cb**2 - cg**2 + 2 * ca * cb * cg) + astar = sa / (self.a * vabg) + bstar = sb / (self.b * vabg) + cstar = sg / (self.c * vabg) + cas = (cb * cg - ca) / (sb * sg) # noqa: F841 + cbs = (cg * ca - cb) / (sg * sa) + cgs = (ca * cb - cg) / (sa * sb) + + # B matrix + B = np.array( + [ + [astar, bstar * cgs, cstar * cbs], + [0, bstar * np.sqrt(1 - cgs**2), -cstar * np.sqrt(1 - cbs**2) * ca], + [0, 0, 1.0 / self.c], + ] + ) + + modQ = 2 * np.pi * np.linalg.norm(B.dot([self.h, self.k, self.l])) + return modQ class CrosshairParameters: """Model for the crosshair parameters""" - modQ: float = DEFAULT_CROSSHAIR["modQ"] - DeltaE: float = DEFAULT_CROSSHAIR["DeltaE"] - current_experiment_type: str = DEFAULT_MODE["current_experiment_type"] + modQ: float + DeltaE: float + current_experiment_type: str sc_parameters: SingleCrystalParameters - experiment_types = ["single_crystal", "powder"] - def __init__(self): + def __init__(self) -> None: + """Constructor""" + self.set_crosshair(**DEFAULT_MODE, **DEFAULT_CROSSHAIR) self.sc_parameters = SingleCrystalParameters() def set_crosshair(self, current_experiment_type: str, DeltaE: float = None, modQ: float = None) -> None: @@ -132,18 +129,23 @@ def get_crosshair(self) -> dict[str, float]: return dict(DeltaE=self.DeltaE, modQ=modQ) return dict(DeltaE=self.DeltaE, modQ=self.modQ) + def get_experiment_type(self) -> str: + """Return experiment type""" + return self.current_experiment_type + class HyspecPPTModel: """Main model""" - Ei: float = DEFAULT_EXPERIMENT["Ei"] - S2: float = DEFAULT_EXPERIMENT["S2"] - alpha_p: float = DEFAULT_EXPERIMENT["alpha_p"] - plot_type: str = DEFAULT_EXPERIMENT["plot_type"] + Ei: float + S2: float + alpha_p: float + plot_type: str cp: CrosshairParameters def __init__(self): """Constructor""" + self.set_experiment_data(**DEFAULT_EXPERIMENT) self.cp = CrosshairParameters() def set_single_crystal_data(self, params: dict[str, float]) -> None: @@ -165,70 +167,69 @@ def set_experiment_data(self, Ei: float, S2: float, alpha_p: float, plot_type: s self.plot_type = plot_type def get_experiment_data(self) -> dict[str, float]: - data = dict() - - data["Ei"] = self.Ei - data["S2"] = self.S2 - data["alpha_p"] = self.alpha_p - data["plot_type"] = self.plot_type + data = dict(Ei=self.Ei, S2=self.S2, alpha_p=self.alpha_p, plot_type=self.plot_type) return data - def get_graph_data(self) -> list[float, float, float, list, list, list]: - return self.calculate_graph_data() - - def calculate_graph_data(self) -> list[float]: - """Returns a list of [Q_low, Q_hi, E, Q2d, E2d, data of plot_types]""" - try: - SE2K = np.sqrt(2e-3 * e * m_n) * 1e-10 / hbar - # def Ei, Emin = - Ei to create Qmin, Qmax to generate plot range - if self.cp.DeltaE is not None and self.cp.DeltaE < -self.Ei: - EMin = -self.Ei - E = np.linspace(EMin, self.Ei * 0.9, 200) - - kfmin = np.sqrt(self.Ei - EMin) * SE2K - ki = np.sqrt(self.Ei) * SE2K - - # S2= 60 - # Create Qmin and Qmax - Qmax = np.sqrt(ki**2 + kfmin**2 - 2 * ki * kfmin * np.cos(np.radians(self.S2 + 30))) # Q=ki or Q= - Qmin = 0 - Q = np.linspace(Qmin, Qmax, 200) - - # Create 2D array - E2d, Q2d = np.meshgrid(E, Q) - - Ef2d = self.Ei - E2d - kf2d = np.sqrt(Ef2d) * SE2K - - Px = np.cos(np.radians(self.alpha_p)) - Pz = np.sin(np.radians(self.alpha_p)) - - cos_theta = (ki**2 + kf2d**2 - Q2d**2) / (2 * ki * kf2d) - cos_theta[cos_theta < np.cos(np.radians(self.S2 + 30))] = np.nan - cos_theta[cos_theta > np.cos(np.radians(self.S2 - 30))] = np.nan # cos(30) - - Qz = ki - kf2d * cos_theta - - if self.S2 >= 30: - Qx = (-1) * kf2d * np.sqrt((1 - cos_theta**2)) - elif self.S2 <= -30: - Qx = kf2d * np.sqrt((1 - cos_theta**2)) - - cos_ang_PQ = (Qx * Px + Qz * Pz) / Q2d / np.sqrt(Px**2 + Pz**2) - ang_PQ = np.degrees(np.arccos(cos_ang_PQ)) - - kf = np.sqrt(self.Ei - E) * SE2K - - Q_low = np.sqrt(ki**2 + kf**2 - 2 * ki * kf * np.cos(np.radians(self.S2 - 30))) - Q_hi = np.sqrt(ki**2 + kf**2 - 2 * ki * kf * np.cos(np.radians(self.S2 + 30))) - - if self.plot_type == PLOT_TYPES[0]: # alpha - return [Q_low, Q_hi, E, Q2d, E2d, ang_PQ] - - if self.plot_type == PLOT_TYPES[1]: # cos^2(alpha) - return [Q_low, Q_hi, E, Q2d, E2d, np.cos(np.radians(ang_PQ)) ** 2] - - if self.plot_type == PLOT_TYPES[2]: # "(cos^2(a)+1)/2" - return [Q_low, Q_hi, E, Q2d, E2d, (np.cos(np.radians(ang_PQ)) ** 2 + 1) / 2] - except AttributeError: - logger.error("The parameters were not initialized") + def calculate_graph_data(self) -> dict[str, np.array]: + """Returns a dictionary of arrays [Q_low, Q_hi, E, Q2d, E2d, data of plot_types]""" + # constant to transform from energy in meV to momentum in Angstrom^-1 + SE2K = np.sqrt(2e-3 * e * m_n) * 1e-10 / hbar + + # adjust minimum energy + if self.cp.DeltaE is not None and self.cp.DeltaE <= -self.Ei: + EMin = 1.2 * self.cp.DeltaE + else: + EMin = -self.Ei + + E = np.linspace(EMin, self.Ei * 0.9, N_POINTS) + + # Calculate lines for the edges of the tank + ki = np.sqrt(self.Ei) * SE2K + kf = np.sqrt(self.Ei - E) * SE2K + Q_low = np.sqrt(ki**2 + kf**2 - 2 * ki * kf * np.cos(np.radians(np.abs(self.S2) - TANK_HALF_WIDTH))) + Q_hi = np.sqrt(ki**2 + kf**2 - 2 * ki * kf * np.cos(np.radians(np.abs(self.S2) + TANK_HALF_WIDTH))) + + # Create 2D array + Q = np.linspace(0, np.max(Q_hi), N_POINTS) + E2d, Q2d = np.meshgrid(E, Q) + Ef2d = self.Ei - E2d + kf2d = np.sqrt(Ef2d) * SE2K + + # Calculate the angle between Q and z axis. Set to NAN values outside the detector range + cos_theta = (ki**2 + kf2d**2 - Q2d**2) / (2 * ki * kf2d) + cos_theta[cos_theta < np.cos(np.radians(np.abs(self.S2) + TANK_HALF_WIDTH))] = np.nan + cos_theta[cos_theta > np.cos(np.radians(np.abs(self.S2) - TANK_HALF_WIDTH))] = np.nan + + # Calculate Qz + Qz = ki - kf2d * cos_theta + + # Calculate Qx. Note that is in the opposite direction as detector position + if self.S2 >= TANK_HALF_WIDTH: + Qx = (-1) * kf2d * np.sqrt((1 - cos_theta**2)) + elif self.S2 <= -TANK_HALF_WIDTH: + Qx = kf2d * np.sqrt((1 - cos_theta**2)) + + # Transform polarization angle in the lab frame to vector + Px = np.cos(np.radians(self.alpha_p)) + Pz = np.sin(np.radians(self.alpha_p)) + + # Calculate angle between polarization vector and momentum transfer + cos_ang_PQ = (Qx * Px + Qz * Pz) / Q2d / np.sqrt(Px**2 + Pz**2) + + # Select return value for intensity + if self.plot_type == PLOT_TYPES[0]: # alpha + ang_PQ = np.arccos(cos_ang_PQ) + intensity = np.degrees(ang_PQ) + elif self.plot_type == PLOT_TYPES[1]: # cos^2(alpha) + intensity = cos_ang_PQ**2 + elif self.plot_type == PLOT_TYPES[2]: # "(cos^2(a)+1)/2" + intensity = (cos_ang_PQ**2 + 1) / 2 + + return dict( + Q_low=Q_low, + Q_hi=Q_hi, + E=E, + Q2d=Q2d, + E2d=E2d, + intensity=intensity, + ) diff --git a/src/hyspecppt/hppt/hppt_presenter.py b/src/hyspecppt/hppt/hppt_presenter.py index be530f6..e72dec1 100644 --- a/src/hyspecppt/hppt/hppt_presenter.py +++ b/src/hyspecppt/hppt/hppt_presenter.py @@ -1,6 +1,6 @@ """Presenter for the Main tab""" -from .experiment_settings import DEFAULT_CROSSHAIR, DEFAULT_EXPERIMENT, DEFAULT_LATTICE, DEFAULT_MODE, PLOT_TYPES +from .experiment_settings import PLOT_TYPES class HyspecPPTPresenter: @@ -20,20 +20,13 @@ def __init__(self, view: any, model: any): self.view.connect_sc_mode_switch(self.handle_switch_to_sc) # populate fields - self.view.sc_widget.set_values(DEFAULT_LATTICE) + self.view.sc_widget.set_values(self.model.get_single_crystal_data()) self.view.experiment_widget.initializeCombo(PLOT_TYPES) - self.view.experiment_widget.set_values(DEFAULT_EXPERIMENT) - self.view.crosshair_widget.set_values(DEFAULT_CROSSHAIR) - - # model init - # to be removed needs to happen in the model - self.model.set_experiment_data(**DEFAULT_EXPERIMENT) - self.model.set_crosshair_data(**DEFAULT_CROSSHAIR, **DEFAULT_MODE) - self.model.set_single_crystal_data(params=DEFAULT_LATTICE) + self.view.experiment_widget.set_values(self.model.get_experiment_data()) # set default selection mode experiment_type = self.view.selection_widget.powder_label - if DEFAULT_MODE["current_experiment_type"].startswith("single"): + if self.model.cp.get_experiment_type().startswith("single"): experiment_type = self.view.selection_widget.sc_label self.view.selection_widget.selector_init(experiment_type) # pass the default mode from experiment type diff --git a/tests/hppt_model/test_hyspecpptmodel.py b/tests/hppt_model/test_hyspecpptmodel.py index c10e9b9..2d7c106 100644 --- a/tests/hppt_model/test_hyspecpptmodel.py +++ b/tests/hppt_model/test_hyspecpptmodel.py @@ -1,8 +1,213 @@ -# import numpy as np # noqa: F401 +import numpy as np +from hyspecppt.hppt.experiment_settings import DEFAULT_CROSSHAIR, DEFAULT_EXPERIMENT, DEFAULT_LATTICE, PLOT_TYPES from hyspecppt.hppt.hppt_model import HyspecPPTModel # noqa: F401 -def test_single_crystal_parameter_set_parameters(): - """Test SingleCrystalParameters set_parameters function""" - # saving for next story +def test_defaultvalues(): + """Test setting single crystal data function""" + model = HyspecPPTModel() + assert model.get_experiment_data() == DEFAULT_EXPERIMENT + assert model.get_single_crystal_data() == DEFAULT_LATTICE + assert model.get_crosshair_data() == DEFAULT_CROSSHAIR + + +def test_set_and_get_singlecrystaldata(): + """Test setting and getting single crystal data function""" + model = HyspecPPTModel() + params = dict(a=5.0, b=6.0, c=7.0, alpha=90.0, beta=90.0, gamma=120.0, h=1.0, k=2.0, l=3.0) + model.set_single_crystal_data(params) + assert model.get_single_crystal_data() == params + + +def test_set_and_get_crosshairdata(): + """Test setting and getting crosshair data function""" + model = HyspecPPTModel() + current_experiment_type = "powder" + model.set_crosshair_data(current_experiment_type=current_experiment_type, DeltaE=20.0, modQ=1.0) + assert model.get_crosshair_data()["DeltaE"] == 20.0 + assert model.get_crosshair_data()["modQ"] == 1.0 + + current_experiment_type = "single_crystal" + model.set_single_crystal_data(DEFAULT_LATTICE) + model.set_crosshair_data(current_experiment_type=current_experiment_type, DeltaE=30.0, modQ=1.0) + assert model.get_crosshair_data()["DeltaE"] == 30.0 + assert model.get_crosshair_data()["modQ"] == 0.0 + model.cp.sc_parameters.h = 10 + model.cp.sc_parameters.l = 10 + model.cp.sc_parameters.k = 10 + assert np.isclose(model.get_crosshair_data()["modQ"], 108.827) # modQ greater than maxQ + assert model.cp.modQ == 0.0 # stored modQ is still 0.0 + + +def test_set_and_get_experimentdata(): + """Test setting and getting experiment data function""" + model = HyspecPPTModel() + model.set_experiment_data(Ei=10.0, S2=35.0, alpha_p=45.0, plot_type=PLOT_TYPES[0]) + assert model.get_experiment_data()["Ei"] == 10.0 + assert model.get_experiment_data()["S2"] == 35.0 + assert model.get_experiment_data()["alpha_p"] == 45.0 + assert model.get_experiment_data()["plot_type"] == PLOT_TYPES[0] + + +def test_calculate_graph_data_alpha(): + """Test calculating different graph data""" + model = HyspecPPTModel() + model.set_experiment_data(Ei=20.0, S2=60.0, alpha_p=30.0, plot_type=PLOT_TYPES[0]) + assert np.isclose(min(model.calculate_graph_data()["Q_low"]), 1.55338) + assert np.isclose(max(model.calculate_graph_data()["Q_low"]), 2.30880) + + assert np.isclose(min(model.calculate_graph_data()["Q_hi"]), 3.25839) + assert np.isclose(max(model.calculate_graph_data()["Q_hi"]), 5.3811) + assert model.calculate_graph_data()["E"].all() == np.linspace(-20, 18, 200).all() + + assert model.calculate_graph_data()["Q2d"][0][0] == 0.0 + assert model.calculate_graph_data()["Q2d"][0][1] == 0.0 + + assert np.isclose(model.calculate_graph_data()["Q2d"][199][0], 5.38106) + assert np.isclose(model.calculate_graph_data()["Q2d"][199][1], 5.38106) + + assert np.isclose(model.calculate_graph_data()["E2d"][0][0], -20) + assert np.isclose(model.calculate_graph_data()["E2d"][0][199], 18.0) + + assert np.isnan(model.calculate_graph_data()["intensity"][0][0]) # not allowed (Q, E) positions + assert np.isnan(model.calculate_graph_data()["intensity"][84][4]) # not allowed (Q, E) positions + + assert np.isclose(model.calculate_graph_data()["intensity"][84][5], 166.59943) + assert np.isclose(model.calculate_graph_data()["intensity"][199][0], 114.73561) + assert np.isnan(model.calculate_graph_data()["intensity"][199][1]) # not allowed (Q, E) positions + + +def test_calculate_graph_data_cos2_alpha(): + """Test calculating different graph data""" + model = HyspecPPTModel() + model.set_experiment_data(Ei=20.0, S2=60.0, alpha_p=30.0, plot_type=PLOT_TYPES[1]) + assert np.isclose(min(model.calculate_graph_data()["Q_low"]), 1.55338) + assert np.isclose(max(model.calculate_graph_data()["Q_low"]), 2.30880) + + assert np.isclose(min(model.calculate_graph_data()["Q_hi"]), 3.25839) + assert np.isclose(max(model.calculate_graph_data()["Q_hi"]), 5.3811) + assert model.calculate_graph_data()["E"].all() == np.linspace(-20, 18, 200).all() + + assert model.calculate_graph_data()["Q2d"][0][0] == 0.0 + assert model.calculate_graph_data()["Q2d"][0][1] == 0.0 + + assert np.isclose(model.calculate_graph_data()["Q2d"][199][0], 5.38106) + assert np.isclose(model.calculate_graph_data()["Q2d"][199][1], 5.38106) + + assert np.isclose(model.calculate_graph_data()["E2d"][0][0], -20) + assert np.isclose(model.calculate_graph_data()["E2d"][0][199], 18.0) + + assert np.isnan(model.calculate_graph_data()["intensity"][0][0]) # not allowed (Q, E) positions + assert np.isnan(model.calculate_graph_data()["intensity"][84][4]) # not allowed (Q, E) positions + + assert np.isclose(model.calculate_graph_data()["intensity"][84][5], np.cos(np.radians(166.59943)) ** 2) + assert np.isclose(model.calculate_graph_data()["intensity"][199][0], np.cos(np.radians(114.73561)) ** 2) + assert np.isnan(model.calculate_graph_data()["intensity"][199][1]) # not allowed (Q, E) positions + + +def test_calculate_graph_data_intensity(): + """Test calculating different graph data""" + model = HyspecPPTModel() + model.set_experiment_data(Ei=20.0, S2=60.0, alpha_p=30.0, plot_type=PLOT_TYPES[2]) + assert np.isclose(min(model.calculate_graph_data()["Q_low"]), 1.55338) + assert np.isclose(max(model.calculate_graph_data()["Q_low"]), 2.30880) + + assert np.isclose(min(model.calculate_graph_data()["Q_hi"]), 3.25839) + assert np.isclose(max(model.calculate_graph_data()["Q_hi"]), 5.3811) + assert model.calculate_graph_data()["E"].all() == np.linspace(-20, 18, 200).all() + + assert model.calculate_graph_data()["Q2d"][0][0] == 0.0 + assert model.calculate_graph_data()["Q2d"][0][1] == 0.0 + + assert np.isclose(model.calculate_graph_data()["Q2d"][199][0], 5.38106) + assert np.isclose(model.calculate_graph_data()["Q2d"][199][1], 5.38106) + + assert np.isclose(model.calculate_graph_data()["E2d"][0][0], -20) + assert np.isclose(model.calculate_graph_data()["E2d"][0][199], 18.0) + + assert np.isnan(model.calculate_graph_data()["intensity"][0][0]) # not allowed (Q, E) positions + assert np.isnan(model.calculate_graph_data()["intensity"][84][4]) # not allowed (Q, E) positions + + assert np.isclose(model.calculate_graph_data()["intensity"][84][5], (np.cos(np.radians(166.59943)) ** 2 + 1) / 2) + assert np.isclose(model.calculate_graph_data()["intensity"][199][0], (np.cos(np.radians(114.73561)) ** 2 + 1) / 2) + assert np.isnan(model.calculate_graph_data()["intensity"][199][1]) # not allowed (Q, E) positions + + +def test_DeltaE_less_than_negative_Ei(): + """When DeltaE is < -Ei, Emin should be updated to 1.2Emin""" + model = HyspecPPTModel() + model.set_experiment_data(Ei=20.0, S2=60.0, alpha_p=30.0, plot_type=PLOT_TYPES[2]) + model.cp.DeltaE = -30.0 + + # if DeltaE is < -Ei, set Emin to 1.2 * DeltaE + assert min(model.calculate_graph_data()["E"]) == -36.0 + assert np.isclose(model.calculate_graph_data()["E2d"][0][0], -36.0) + + # now chang DeltaE > -Ei, Emin should go back to -Ei + model.cp.DeltaE = -19.0 + assert min(model.calculate_graph_data()["E"]) == -20.0 + assert np.isclose(model.calculate_graph_data()["E2d"][0][0], -20.0) + + # if we don't initialize DeltaE, Emin by default is -Ei + model = HyspecPPTModel() + model.set_experiment_data(Ei=20.0, S2=60.0, alpha_p=30.0, plot_type=PLOT_TYPES[2]) + assert min(model.calculate_graph_data()["E"]) == -20.0 + assert np.isclose(model.calculate_graph_data()["E2d"][0][0], -20.0) + + +def test_calculate_graph_data_intensity_negative_S2(): + """Test calculating different graph data""" + model = HyspecPPTModel() + model.set_experiment_data(Ei=20.0, S2=-60.0, alpha_p=30.0, plot_type=PLOT_TYPES[2]) + assert np.isclose(min(model.calculate_graph_data()["Q_low"]), 1.55338) + assert np.isclose(max(model.calculate_graph_data()["Q_low"]), 2.30880) + + assert np.isclose(min(model.calculate_graph_data()["Q_hi"]), 3.25839) + assert np.isclose(max(model.calculate_graph_data()["Q_hi"]), 5.3811) + assert model.calculate_graph_data()["E"].all() == np.linspace(-20, 18, 200).all() + + assert model.calculate_graph_data()["Q2d"][0][0] == 0.0 + assert model.calculate_graph_data()["Q2d"][0][1] == 0.0 + + assert np.isclose(model.calculate_graph_data()["Q2d"][199][0], 5.38106) + assert np.isclose(model.calculate_graph_data()["Q2d"][199][1], 5.38106) + + assert np.isclose(model.calculate_graph_data()["E2d"][0][0], -20) + assert np.isclose(model.calculate_graph_data()["E2d"][0][199], 18.0) + + assert np.isnan(model.calculate_graph_data()["intensity"][0][0]) # not allowed (Q, E) positions + assert np.isnan(model.calculate_graph_data()["intensity"][84][4]) # not allowed (Q, E) positions + + assert np.isclose(model.calculate_graph_data()["intensity"][84][5], 0.736049) + assert np.isclose(model.calculate_graph_data()["intensity"][199][0], 0.995790) + assert np.isnan(model.calculate_graph_data()["intensity"][199][1]) # not allowed (Q, E) positions + + +def test_calculate_graph_data_consistency(): + """Test consistency for graph data""" + model = HyspecPPTModel() + model.set_experiment_data(Ei=20, S2=45, alpha_p=45, plot_type=PLOT_TYPES[1]) + d_45_45 = model.calculate_graph_data() + model.set_experiment_data(Ei=20, S2=-45, alpha_p=45, plot_type=PLOT_TYPES[1]) + d_m45_45 = model.calculate_graph_data() + model.set_experiment_data(Ei=20, S2=45, alpha_p=-45, plot_type=PLOT_TYPES[1]) + d_45_m45 = model.calculate_graph_data() + model.set_experiment_data(Ei=20, S2=-45, alpha_p=-45, plot_type=PLOT_TYPES[1]) + d_m45_m45 = model.calculate_graph_data() + + # check all Q and E arrays are equal + assert np.array_equal(d_45_45["Q_low"], d_45_m45["Q_low"]) + assert np.array_equal(d_45_45["Q_hi"], d_m45_m45["Q_hi"]) + assert np.array_equal(d_45_45["E"], d_m45_45["E"]) + assert np.array_equal(d_45_45["Q2d"], d_45_m45["Q2d"]) + assert np.array_equal(d_45_45["E2d"], d_m45_m45["E2d"]) + + # check that cosines are equal in equivalent conditions + assert np.array_equal(d_45_45["intensity"], d_m45_m45["intensity"], equal_nan=True) + assert np.array_equal(d_m45_45["intensity"], d_45_m45["intensity"], equal_nan=True) + + # assert 90 degree difference using cos^2+sin^2=1 + inds = np.isfinite(d_45_45["intensity"]) + assert np.allclose((d_45_45["intensity"] + d_m45_45["intensity"])[inds], 1) + assert np.allclose((d_45_m45["intensity"] + d_m45_m45["intensity"])[inds], 1) diff --git a/tests/hppt_presenter/test_presenter.py b/tests/hppt_presenter/test_presenter.py index ad6fd83..9313fed 100644 --- a/tests/hppt_presenter/test_presenter.py +++ b/tests/hppt_presenter/test_presenter.py @@ -14,9 +14,9 @@ def test_presenter_init(qtbot, hyspec_app): view = hyspec_app.main_window.HPPT_view # check the default values are populated - assert view.experiment_widget.Ei_edit.text() == "20" - assert view.experiment_widget.Pangle_edit.text() == "0" - assert view.experiment_widget.S2_edit.text() == "30" + assert view.experiment_widget.Ei_edit.text() == "20.0" + assert view.experiment_widget.Pangle_edit.text() == "0.0" + assert view.experiment_widget.S2_edit.text() == "30.0" assert view.experiment_widget.Type_combobox.currentText() == "cos" + "\u03b1" + "\u209b" + "\u00b2" @@ -72,7 +72,7 @@ def test_switch_to_powder_ei(hyspec_app, qtbot): experiment_widget = hyspec_view.experiment_widget # set Ei invalid value - qtbot.keyClicks(experiment_widget.Ei_edit, "4") + qtbot.keyClicks(experiment_widget.Ei_edit, "\b\b4") assert experiment_widget.Ei_edit.text() == "204" assert experiment_widget.Ei_edit.styleSheet() == INVALID_QLINEEDIT @@ -80,7 +80,7 @@ def test_switch_to_powder_ei(hyspec_app, qtbot): hyspec_view.switch_to_powder() # Ei value should have the default valid value - assert experiment_widget.Ei_edit.text() == "20" + assert experiment_widget.Ei_edit.text() == "20.0" assert experiment_widget.Ei_edit.styleSheet() != INVALID_QLINEEDIT @@ -103,7 +103,7 @@ def test_switch_to_powder_pangle(hyspec_app, qtbot): hyspec_view.switch_to_powder() # P angle value should have the default valid value - assert experiment_widget.Pangle_edit.text() == "0" + assert experiment_widget.Pangle_edit.text() == "0.0" assert experiment_widget.Pangle_edit.styleSheet() != INVALID_QLINEEDIT @@ -118,7 +118,7 @@ def test_switch_to_powder_s2(hyspec_app, qtbot): experiment_widget = hyspec_view.experiment_widget # set Ei invalid value - qtbot.keyClicks(experiment_widget.S2_edit, "6") + qtbot.keyClicks(experiment_widget.S2_edit, "\b\b6") assert experiment_widget.S2_edit.text() == "306" assert experiment_widget.S2_edit.styleSheet() == INVALID_QLINEEDIT @@ -126,7 +126,7 @@ def test_switch_to_powder_s2(hyspec_app, qtbot): hyspec_view.switch_to_powder() # Ei value should have the default valid value - assert experiment_widget.S2_edit.text() == "30" + assert experiment_widget.S2_edit.text() == "30.0" assert experiment_widget.S2_edit.styleSheet() != INVALID_QLINEEDIT