def p_ion(*, voltage_reference: int, voltage_reduced: int, m_reference: NumberOrArray, m_reduced: NumberOrArray) -> float: """Calculate the ion chamber collection correction. Parameters ---------- voltage_reference : int The "high" voltage; same as the TG51 measurement voltage. voltage_reduced : int The "low" voltage; usually half of the high voltage. m_reference : float, iterable The readings of the ion chamber at the "high" voltage. m_reduced : float, iterable The readings of the ion chamber at the "low" voltage. Raises ------ BoundsError if calculated Pion is outside the range 1.00-1.05. """ ion = (1 - voltage_reference / voltage_reduced) / ( np.mean(m_reference) / np.mean(m_reduced) - voltage_reference / voltage_reduced) argue.verify_bounds( ion, bounds=(1, 1.05), message="Pion out of range (1.00-1.05). Check inputs or chamber") return float(ion)
def k_s(*, voltage_reference: int, voltage_reduced: int, m_reference: NumberOrArray, m_reduced: NumberOrArray) -> float: """Calculate the ion recombination effect using readings at two voltages. The voltages should have a ratio of 2, 2.5, 3, 3.5, 4, or 5. Parameters ---------- voltage_reference : int The voltage at which calibration will be performed (e.g. 300V) voltage_reduced : int The voltage which is lower than reference (e.g. 150V) m_reference : array, float The reading(s) at the reference voltage. m_reduced : array, float The reading(s) at the reduced voltage. Returns ------- k_s : float The ion recombination factor. Raises ------ ValueError If the voltage ratio is not valid. ValueError If the calculated ks value is outside the range (1.0, 1.05). """ v_ratio = voltage_reference / voltage_reduced _verify_voltage_ratio_is_valid(v_ratio) a = V1_V2_FITS[v_ratio] m_ratio = np.mean(m_reference) / np.mean(m_reduced) argue.verify_bounds(m_ratio, bounds=(1.0, 1.05), message="Ks is out of bounds. Verify inputs or check chamber") return float(a['a0'] + a['a1']*m_ratio + a['a2']*(m_ratio**2))
def kp_r50(*, r_50: float) -> float: """Calculate k'R50 for Farmer-like chambers. Parameters ---------- r_50 : float (2-9) The R50 value in cm. """ argue.verify_bounds(r_50, bounds=(2, 9)) return 0.9905 + 0.071 * np.exp(-r_50 / 3.67)
def d_ref(*, i_50: float) -> float: """Calculate the dref of an electron beam based on the I50 depth. Parameters ---------- i_50 : float The value of I50 in cm. """ argue.verify_bounds(i_50, bounds=argue.POSITIVE, message="i50 should be positive") r50 = r_50(i_50=i_50) return 0.6 * r50 - 0.1
def m_corrected(*, m_reference, k_tp, k_elec, k_pol, k_s) -> float: """The fully corrected chamber reading. Parameters ---------- m_reference : array, float The chamber reading(s) at the calibration position. k_tp : float Temperature/Pressure correction. See :func:`~pylinac.calibration.tg51.p_tp`. k_elec : float Electrometer correction; given by the calibration laboratory. k_pol : float Polarity correction. See :func:`~pylinac.calibration.tg51.p_pol`. k_s : float Ion recombination correction. See :func:`~pylinac.calibration.trs398.k_s`. Returns ------- m : float The fully corrected chamber reading. """ argue.verify_bounds(k_tp, bounds=(MIN_PTP, MAX_PTP)) argue.verify_bounds(k_elec, bounds=(MIN_PELEC, MAX_PELEC)) argue.verify_bounds(k_pol, bounds=(MIN_PPOL, MAX_PPOL)) argue.verify_bounds(k_s, bounds=(MIN_PION, MAX_PION)) return float(np.mean(m_reference) * k_tp * k_elec * k_pol * k_s)
def m_corrected(*, p_ion: float, p_tp: float, p_elec: float, p_pol: float, m_reference: NumberOrArray) -> float: """Calculate M_corrected, the ion chamber reading with all corrections applied. Parameters ---------- p_ion : float (1.00-1.05) The ion collection correction. p_tp : float (0.92-1.08) The temperature & pressure correction. p_elec : float (0.98-1.02) The electrometer correction. p_pol : float (0.98-1.02) The polarity correction. m_reference : float, iterable The raw ion chamber reading(s). Returns ------- float """ argue.verify_bounds(p_ion, bounds=(MIN_PION, MAX_PION)) argue.verify_bounds(p_tp, bounds=(MIN_PTP, MAX_PTP)) argue.verify_bounds(p_elec, bounds=(MIN_PELEC, MAX_PELEC)) argue.verify_bounds(p_pol, bounds=(MIN_PPOL, MAX_PPOL)) return float(p_ion * p_tp * p_elec * p_pol * np.mean(m_reference))
def r_50(*, i_50: float) -> float: """Calculate the R50 depth of an electron beam based on the I50 depth. Parameters ---------- i_50 : float The value of I50 in cm. """ argue.verify_bounds(i_50, bounds=argue.POSITIVE, message="i50 should be positive") if i_50 < 10: r50 = 1.029 * i_50 - 0.06 else: r50 = 1.59 * i_50 - 0.37 return r50
def p_pol(*, m_reference: NumberOrArray, m_opposite: NumberOrArray) -> float: """Calculate the polarity correction. Parameters ---------- m_reference : number, array The readings of the ion chamber at the reference polarity and voltage. m_opposite : number, array The readings of the ion chamber at the polarity opposite the reference. The sign does not make a difference. Raises ------ BoundsError if calculated Ppol is >1% from 1.0. """ mref_avg = np.mean(m_reference) mopp_avg = np.mean(m_opposite) polarity = (abs(mref_avg) + abs(mopp_avg))/abs(2*mref_avg) argue.verify_bounds(polarity, bounds=(0.99, 1.01), message="Polarity correction {:2.2f} out of range (+/-2%). Verify inputs") return float(polarity)
def p_ion(*, voltage_reference: int, voltage_reduced: int, m_reference: NumberOrArray, m_reduced: NumberOrArray) -> float: """Calculate the ion chamber collection correction. Parameters ---------- voltage_reference : int The "high" voltage; same as the TG51 measurement voltage. voltage_reduced : int The "low" voltage; usually half of the high voltage. m_reference : float, iterable The readings of the ion chamber at the "high" voltage. m_reduced : float, iterable The readings of the ion chamber at the "low" voltage. Raises ------ BoundsError if calculated Pion is outside the range 1.00-1.05. """ ion = (1 - voltage_reference / voltage_reduced) / (np.mean(m_reference) / np.mean(m_reduced) - voltage_reference / voltage_reduced) argue.verify_bounds(ion, bounds=(1, 1.05), message="Pion out of range (1.00-1.05). Check inputs or chamber") return float(ion)
def p_pol(*, m_reference: NumberOrArray, m_opposite: NumberOrArray) -> float: """Calculate the polarity correction. Parameters ---------- m_reference : number, array The readings of the ion chamber at the reference polarity and voltage. m_opposite : number, array The readings of the ion chamber at the polarity opposite the reference. The sign does not make a difference. Raises ------ BoundsError if calculated Ppol is >1% from 1.0. """ mref_avg = np.mean(m_reference) mopp_avg = np.mean(m_opposite) polarity = (abs(mref_avg) + abs(mopp_avg)) / abs(2 * mref_avg) argue.verify_bounds( polarity, bounds=(0.99, 1.01), message= "Polarity correction {:2.2f} out of range (+/-2%). Verify inputs") return float(polarity)
def p_tp(*, temp: NumberLike, press: NumberLike) -> float: """Calculate the temperature & pressure correction. Parameters ---------- temp : float (17-27) The temperature in degrees Celsius. press : float (91-111) The value of pressure in kPa. Can be converted from mmHg and mbar; see :func:`~pylinac.calibration.tg51.mmHg2kPa` and :func:`~pylinac.calibration.tg51.mbar2kPa`. """ argue.verify_bounds( temp, bounds=(MIN_TEMP, MAX_TEMP), message= "Temperature {:2.2f} out of range. Did you use Fahrenheit? Consider using the utility function fahrenheit2celsius()" ) argue.verify_bounds( press, bounds=(MIN_PRESSURE, MAX_PRESSURE), message= "Pressure {:2.2f} out of range. Did you use kPa? Consider using the utility functions mmHg2kPa() or mbar2kPa()" ) return ((273.2 + temp) / 295.2) * (101.33 / press)