From 1111a55f89b37738e9ddef19735f836798eabf99 Mon Sep 17 00:00:00 2001 From: ocean Date: Tue, 19 Dec 2023 17:38:10 +0000 Subject: [PATCH 1/9] Removed redundant units --- bluemira/base/constants.py | 37 ++++++++++++++++++----- bluemira/plasma_physics/collisions.py | 25 ++++++++++----- bluemira/plasma_physics/reactions.py | 12 +++----- bluemira/plasma_physics/rules_of_thumb.py | 6 ++-- 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/bluemira/base/constants.py b/bluemira/base/constants.py index af4bdfae00..ecd555ac8c 100644 --- a/bluemira/base/constants.py +++ b/bluemira/base/constants.py @@ -62,7 +62,11 @@ def _add_contexts(self, contexts: Optional[List[Context]] = None): Add new contexts to registry """ if not self._contexts_added: - self.contexts = [self._energy_temperature_context(), self._flow_context()] + self.contexts = [ + self._energy_temperature_context(), + self._mass_energy_context(), + self._flow_context(), + ] for c in self.contexts: self.add_context(c) @@ -107,6 +111,31 @@ def _energy_temperature_context(self): lambda _, x: x / conversion, ) + def _mass_energy_context(self): + """ + Converter between mass and energy + + energy = mass * speed-of-light^2 + + Returns + ------- + pint context + """ + m_to_e = Context("Mass_to_Energy") + + m_units = "[mass]" + e_units = "[energy]" + + conversion = self.Quantity("c^2") + + return self._transform( + m_to_e, + m_units, + e_units, + lambda _, x: x * conversion, + lambda _, x: x / conversion, + ) + @property def flow_conversion(self): """Gas flow conversion factor R * T""" @@ -248,9 +277,6 @@ def _transform( # Physical constants # ============================================================================= -# Speed of light -C_LIGHT = ureg.Quantity("c").to_base_units().magnitude # [m/s] - # Vacuum permeability MU_0 = ureg.Quantity("mu_0").to_base_units().magnitude # [T.m/A] or [V.s/(A.m)] @@ -340,9 +366,6 @@ def _transform( # Electron-volts to Joules EV_TO_J = ureg.Quantity(1, ureg.eV).to(ureg.joule).magnitude -# Joules to Electron-volts -J_TO_EV = ureg.Quantity(1, ureg.joule).to(ureg.eV).magnitude - # Atomic mass units to kilograms AMU_TO_KG = ureg.Quantity(1, ureg.amu).to(ureg.kg).magnitude diff --git a/bluemira/plasma_physics/collisions.py b/bluemira/plasma_physics/collisions.py index 0318f08409..37e709cea7 100644 --- a/bluemira/plasma_physics/collisions.py +++ b/bluemira/plasma_physics/collisions.py @@ -12,11 +12,11 @@ from bluemira.base.constants import ( ELECTRON_MASS, + ELEMENTARY_CHARGE, EPS_0, - EV_TO_J, H_PLANCK, - K_BOLTZMANN, PROTON_MASS, + raw_uc, ) @@ -35,7 +35,9 @@ def debye_length(temperature: float, density: float) -> float: ------- Debye length [m] """ - return np.sqrt(EPS_0 * K_BOLTZMANN * temperature / (EV_TO_J**2 * density)) + return np.sqrt( + EPS_0 * raw_uc(temperature, "K", "J") / (ELEMENTARY_CHARGE**2 * density) + ) def reduced_mass(mass_1: float, mass_2: float) -> float: @@ -74,7 +76,10 @@ def thermal_velocity(temperature: float, mass: float) -> float: The sqrt(2) term is for a 3-dimensional system and the most probable velocity in the particle velocity distribution. """ - return np.sqrt(2) * np.sqrt(K_BOLTZMANN * temperature / mass) + return np.sqrt(2) * np.sqrt( + raw_uc(temperature, "K", "J") # = Joule = kg*m^2/s^2 + / mass + ) # sqrt(m^2/s^2) = m/s def de_broglie_length(velocity: float, mu_12: float) -> float: @@ -110,7 +115,7 @@ def impact_parameter_perp(velocity: float, mu_12: float): ------- Perpendicular impact parameter [m] """ - return EV_TO_J**2 / (4 * np.pi * EPS_0 * mu_12 * velocity**2) + return ELEMENTARY_CHARGE**2 / (4 * np.pi * EPS_0 * mu_12 * velocity**2) def coulomb_logarithm(temperature: float, density: float) -> float: @@ -146,7 +151,8 @@ def spitzer_conductivity(Z_eff: float, T_e: float, ln_lambda: float) -> float: Z_eff: Effective charge [a.m.u.] T_e: - Electron temperature on axis [eV] + Electron temperature on axis [keV] + The equation takes in temperature as [eV], so an in-line conversion is used here. ln_lambda: Coulomb logarithm value @@ -160,4 +166,9 @@ def spitzer_conductivity(Z_eff: float, T_e: float, ln_lambda: float) -> float: \t:math:`\\sigma = 1.92e4 (2-Z_{eff}^{-1/3}) \\dfrac{T_{e}^{3/2}}{Z_{eff}ln\\Lambda}` """ - return 1.92e4 * (2 - Z_eff ** (-1 / 3)) * T_e**1.5 / (Z_eff * ln_lambda) + return ( + 1.92e4 + * (2 - Z_eff ** (-1 / 3)) + * raw_uc(T_e, "keV", "eV") ** 1.5 + / (Z_eff * ln_lambda) + ) diff --git a/bluemira/plasma_physics/reactions.py b/bluemira/plasma_physics/reactions.py index ecc425f3da..8e36a39891 100644 --- a/bluemira/plasma_physics/reactions.py +++ b/bluemira/plasma_physics/reactions.py @@ -16,14 +16,10 @@ import numpy.typing as npt from bluemira.base.constants import ( - AMU_TO_KG, - C_LIGHT, D_MOLAR_MASS, ELECTRON_MOLAR_MASS, - EV_TO_J, HE3_MOLAR_MASS, HE_MOLAR_MASS, - J_TO_EV, NEUTRON_MOLAR_MASS, N_AVOGADRO, PROTON_MOLAR_MASS, @@ -59,7 +55,7 @@ def E_DT_fusion() -> float: \\Delta E = \\Delta m c^2 """ delta_m = (D_MOLAR_MASS + T_MOLAR_MASS) - (HE_MOLAR_MASS + NEUTRON_MOLAR_MASS) - return delta_m * C_LIGHT**2 * AMU_TO_KG * J_TO_EV + return raw_uc(delta_m, "amu", "eV") def E_DD_fusion() -> float: @@ -88,7 +84,7 @@ def E_DD_fusion() -> float: ] ) delta_m = np.average(delta_m) - return delta_m * C_LIGHT**2 * AMU_TO_KG * J_TO_EV + return raw_uc(delta_m, "amu", "eV") def n_DT_reactions(p_fus: float) -> float: @@ -108,7 +104,7 @@ def n_DT_reactions(p_fus: float) -> float: Number of D-T reactions per second [1/s] """ e_dt = E_DT_fusion() - return raw_uc(p_fus, "MW", "W") / (e_dt * EV_TO_J) + return raw_uc(p_fus, "MW", "W") / raw_uc(e_dt, "eV", "J") def n_DD_reactions(p_fus: float) -> float: @@ -128,7 +124,7 @@ def n_DD_reactions(p_fus: float) -> float: Number of D-D reactions per second [1/s] """ e_dd = E_DD_fusion() - return p_fus / (e_dd * EV_TO_J) + return p_fus / raw_uc(e_dd, "eV", "J") def r_T_burn(p_fus: float) -> float: # noqa: N802 diff --git a/bluemira/plasma_physics/rules_of_thumb.py b/bluemira/plasma_physics/rules_of_thumb.py index 94d4f44f3f..0e17e44d50 100644 --- a/bluemira/plasma_physics/rules_of_thumb.py +++ b/bluemira/plasma_physics/rules_of_thumb.py @@ -10,7 +10,7 @@ import numpy as np -from bluemira.base.constants import EV_TO_J, K_BOLTZMANN, MU_0 +from bluemira.base.constants import MU_0, raw_uc from bluemira.plasma_physics.collisions import coulomb_logarithm, spitzer_conductivity @@ -29,7 +29,7 @@ def estimate_loop_voltage( Z_eff: Effective charge [a.m.u.] T_e: - Electron temperature on axis [eV] + Electron temperature on axis [keV] n_e: Electron density [1/m^3] q_0: @@ -54,7 +54,7 @@ def estimate_loop_voltage( There is no neo-classical resistivity on axis because there are no trapped particles """ # noqa: W505, E501 - ln_lambda = coulomb_logarithm(T_e * EV_TO_J / K_BOLTZMANN, n_e) + ln_lambda = coulomb_logarithm(raw_uc(T_e, "keV", "K"), n_e) sigma = spitzer_conductivity(Z_eff, T_e, ln_lambda) # Current density on axis From e911242c4b6f5b21cd4637d6042334b340ad26ca Mon Sep 17 00:00:00 2001 From: ocean Date: Tue, 19 Dec 2023 18:03:08 +0000 Subject: [PATCH 2/9] Removed all 'years' variable and YR_TO_S variables. Found two possible errors. --- bluemira/fuel_cycle/tools.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bluemira/fuel_cycle/tools.py b/bluemira/fuel_cycle/tools.py index 85ed25f880..75f7280b6c 100644 --- a/bluemira/fuel_cycle/tools.py +++ b/bluemira/fuel_cycle/tools.py @@ -16,7 +16,7 @@ from scipy.interpolate import griddata from scipy.optimize import curve_fit -from bluemira.base.constants import N_AVOGADRO, S_TO_YR, T_LAMBDA, T_MOLAR_MASS, YR_TO_S +from bluemira.base.constants import N_AVOGADRO, T_LAMBDA, T_MOLAR_MASS, raw_uc from bluemira.base.look_and_feel import bluemira_warn from bluemira.fuel_cycle.error import FuelCycleError from bluemira.plasma_physics.reactions import r_T_burn @@ -270,7 +270,7 @@ def delay_decay(t: np.ndarray, m_t_flow: np.ndarray, tt_delay: float) -> np.ndar ------- The delayed flow vector """ - t_delay = tt_delay * S_TO_YR + t_delay = raw_uc(tt_delay, "s", "yr") shift = np.argmin(np.abs(t - t_delay)) flow = np.zeros(shift) deldec = np.exp(-T_LAMBDA * t_delay) @@ -310,7 +310,7 @@ def fountain(flow: np.ndarray, t: np.ndarray, min_inventory: float) -> np.ndarra for i, _ti in zip(range(1, len(flow)), flow[1:]): dt = t[i] - t[i - 1] - dts = dt * YR_TO_S + dts = raw_uc(dt, "yr", "s") m_in = flow[i] * dts inventory[i] = inventory[i - 1] * np.exp(-T_LAMBDA * dt) overflow = inventory[i] + m_in @@ -349,7 +349,7 @@ def _speed_recycle( """ m_tritium = np.zeros(len(t)) m_tritium[0] = m_start_up - ts = t * YR_TO_S + ts = raw_uc(t, "yr", "s") for i in range(1, len(t)): dt = t[i] - t[i - 1] dts = ts[i] - ts[i - 1] @@ -575,8 +575,8 @@ def _fountain_linear_sink( if dt == 0: return m_flow, inventory, sum_in, decayed - m_in = m_flow * YR_TO_S # kg/yr - dts = dt * YR_TO_S + m_in = raw_uc(m_flow, "yr", "s") # kg/yr # TODO: possible error? Wrong way round + dts = raw_uc(dt, "yr", "s") mass_in = m_flow * dts sum_in += mass_in @@ -731,13 +731,12 @@ def _linear_thresh_sink( decayed: Accountancy parameter to calculate the total value of decayed T in a sink """ - years = 365 * 24 * 3600 dt = t_out - t_in if dt == 0: return m_flow, inventory, sum_in, decayed - m_in = m_flow * years # kg/yr - dts = dt * years + m_in = raw_uc(m_flow, "yr", "s") # kg/yr # TODO: possible error? Wrong way round + dts = raw_uc(dt, "yr", "s") mass_in = m_flow * dts sum_in += mass_in j_inv0 = inventory @@ -825,13 +824,12 @@ def _sqrt_thresh_sink( not accounted for in this function. We have to add decay in the sink and ensure this is handled when calculation the absorbtion and out-flow. """ - years = 365 * 24 * 3600 dt = t_out - t_in if dt == 0: # Nothing can happen if time is zero return m_flow, inventory, sum_in, decayed - dts = dt * years + dts = raw_uc(dt, "yr", "s") mass_in = m_flow * dts sum_in += mass_in From c5c1c5b9e59242e8cfee0bc4cf8a88a6fa0c8ba0 Mon Sep 17 00:00:00 2001 From: ocean Date: Wed, 20 Dec 2023 09:26:04 +0000 Subject: [PATCH 3/9] Made units more understandable --- bluemira/base/constants.py | 6 ------ bluemira/fuel_cycle/tools.py | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/bluemira/base/constants.py b/bluemira/base/constants.py index ecd555ac8c..658a78f566 100644 --- a/bluemira/base/constants.py +++ b/bluemira/base/constants.py @@ -363,12 +363,6 @@ def _transform( # Conversions # ============================================================================= -# Electron-volts to Joules -EV_TO_J = ureg.Quantity(1, ureg.eV).to(ureg.joule).magnitude - -# Atomic mass units to kilograms -AMU_TO_KG = ureg.Quantity(1, ureg.amu).to(ureg.kg).magnitude - # Years to seconds YR_TO_S = ureg.Quantity(1, ureg.year).to(ureg.second).magnitude diff --git a/bluemira/fuel_cycle/tools.py b/bluemira/fuel_cycle/tools.py index 75f7280b6c..1713b899e3 100644 --- a/bluemira/fuel_cycle/tools.py +++ b/bluemira/fuel_cycle/tools.py @@ -575,7 +575,7 @@ def _fountain_linear_sink( if dt == 0: return m_flow, inventory, sum_in, decayed - m_in = raw_uc(m_flow, "yr", "s") # kg/yr # TODO: possible error? Wrong way round + m_in = raw_uc(m_flow, "kg/s", "kg/yr") # kg/yr dts = raw_uc(dt, "yr", "s") mass_in = m_flow * dts sum_in += mass_in @@ -735,7 +735,7 @@ def _linear_thresh_sink( if dt == 0: return m_flow, inventory, sum_in, decayed - m_in = raw_uc(m_flow, "yr", "s") # kg/yr # TODO: possible error? Wrong way round + m_in = raw_uc(m_flow, "kg/s", "kg/yr") # kg/yr dts = raw_uc(dt, "yr", "s") mass_in = m_flow * dts sum_in += mass_in From c8250b74051beef5f40479b4710dd64a5f1307af Mon Sep 17 00:00:00 2001 From: ocean Date: Wed, 20 Dec 2023 09:38:22 +0000 Subject: [PATCH 4/9] Undid all the raw_uc conversions in tools.py as they are not compatible with numba.jit's optimization. Using the constants YR_TO_S and S_TO_YR. --- bluemira/fuel_cycle/tools.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bluemira/fuel_cycle/tools.py b/bluemira/fuel_cycle/tools.py index 1713b899e3..94ec2d5029 100644 --- a/bluemira/fuel_cycle/tools.py +++ b/bluemira/fuel_cycle/tools.py @@ -16,7 +16,7 @@ from scipy.interpolate import griddata from scipy.optimize import curve_fit -from bluemira.base.constants import N_AVOGADRO, T_LAMBDA, T_MOLAR_MASS, raw_uc +from bluemira.base.constants import N_AVOGADRO, S_TO_YR, T_LAMBDA, T_MOLAR_MASS, YR_TO_S from bluemira.base.look_and_feel import bluemira_warn from bluemira.fuel_cycle.error import FuelCycleError from bluemira.plasma_physics.reactions import r_T_burn @@ -263,14 +263,14 @@ def delay_decay(t: np.ndarray, m_t_flow: np.ndarray, tt_delay: float) -> np.ndar The time vector m_t_flow: The mass flow vector - t_delay: - The delay duration [s] + tt_delay: + The delay duration [yr] Returns ------- The delayed flow vector """ - t_delay = raw_uc(tt_delay, "s", "yr") + t_delay = tt_delay * S_TO_YR shift = np.argmin(np.abs(t - t_delay)) flow = np.zeros(shift) deldec = np.exp(-T_LAMBDA * t_delay) @@ -310,7 +310,7 @@ def fountain(flow: np.ndarray, t: np.ndarray, min_inventory: float) -> np.ndarra for i, _ti in zip(range(1, len(flow)), flow[1:]): dt = t[i] - t[i - 1] - dts = raw_uc(dt, "yr", "s") + dts = dt * YR_TO_S m_in = flow[i] * dts inventory[i] = inventory[i - 1] * np.exp(-T_LAMBDA * dt) overflow = inventory[i] + m_in @@ -349,7 +349,7 @@ def _speed_recycle( """ m_tritium = np.zeros(len(t)) m_tritium[0] = m_start_up - ts = raw_uc(t, "yr", "s") + ts = t * YR_TO_S for i in range(1, len(t)): dt = t[i] - t[i - 1] dts = ts[i] - ts[i - 1] @@ -575,8 +575,8 @@ def _fountain_linear_sink( if dt == 0: return m_flow, inventory, sum_in, decayed - m_in = raw_uc(m_flow, "kg/s", "kg/yr") # kg/yr - dts = raw_uc(dt, "yr", "s") + m_in = m_flow * YR_TO_S # converts kg/s to kg/yr + dts = dt * YR_TO_S mass_in = m_flow * dts sum_in += mass_in @@ -735,8 +735,8 @@ def _linear_thresh_sink( if dt == 0: return m_flow, inventory, sum_in, decayed - m_in = raw_uc(m_flow, "kg/s", "kg/yr") # kg/yr - dts = raw_uc(dt, "yr", "s") + m_in = m_flow * YR_TO_S # converts kg/s to kg/yr + dts = dt * YR_TO_S mass_in = m_flow * dts sum_in += mass_in j_inv0 = inventory @@ -829,7 +829,7 @@ def _sqrt_thresh_sink( # Nothing can happen if time is zero return m_flow, inventory, sum_in, decayed - dts = raw_uc(dt, "yr", "s") + dts = dt * YR_TO_S mass_in = m_flow * dts sum_in += mass_in From 7e33c93e7ee5e023f66a1d5087abe12510648cf2 Mon Sep 17 00:00:00 2001 From: ocean Date: Wed, 20 Dec 2023 10:21:45 +0000 Subject: [PATCH 5/9] Removed as many S_TO_YR and YR_TO_S as possible. --- bluemira/fuel_cycle/cycle.py | 13 +++++++------ bluemira/fuel_cycle/lifecycle.py | 26 ++++++++++++++++---------- bluemira/fuel_cycle/timeline.py | 9 ++++----- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/bluemira/fuel_cycle/cycle.py b/bluemira/fuel_cycle/cycle.py index f043813df6..04925bcaa5 100644 --- a/bluemira/fuel_cycle/cycle.py +++ b/bluemira/fuel_cycle/cycle.py @@ -20,7 +20,7 @@ import numpy as np from scipy.interpolate import interp1d -from bluemira.base.constants import N_AVOGADRO, T_LAMBDA, T_MOLAR_MASS, YR_TO_S, raw_uc +from bluemira.base.constants import N_AVOGADRO, T_LAMBDA, T_MOLAR_MASS, raw_uc from bluemira.base.look_and_feel import bluemira_print from bluemira.fuel_cycle.blocks import FuelCycleComponent, FuelCycleFlow from bluemira.fuel_cycle.tools import ( @@ -186,10 +186,11 @@ def tbreed(self, TBR: float, m_T_0: float): m_T = m_T_0 * np.ones(len(self.DEMO_t)) for i in range(1, len(self.DEMO_t)): dt = self.DEMO_t[i] - self.DEMO_t[i - 1] - t_bred = TBR * self.brate[i] * (YR_TO_S * dt) - t_bred += self.prate[i] * (YR_TO_S * dt) - t_burnt = self.brate[i] * (YR_TO_S * dt) - t_DD = self.prate[i] * (YR_TO_S * dt) + dts = raw_uc(dt, "yr", "s") + t_bred = TBR * self.brate[i] * dts + t_bred += self.prate[i] * dts + t_burnt = self.brate[i] * dts + t_DD = self.prate[i] * dts m_T[i] = (m_T[i - 1]) * np.exp(-T_LAMBDA * dt) - t_burnt + t_bred + t_DD return m_T @@ -350,7 +351,7 @@ def recycle(self): m_T_out = self.plasma(self.params.eta_iv, self.params.I_miv, flows=flows) # Resolution - Not used everywhere for speed - n_ts = int(round((YR_TO_S * self.DEMO_t[-1]) / self.timestep)) + n_ts = int(round(raw_uc(self.DEMO_t[-1], "yr", "s") / self.timestep)) self.t, m_pellet_in = discretise_1d(self.DEMO_t, self.m_T_in, n_ts) # Flow out of the vacuum vessel diff --git a/bluemira/fuel_cycle/lifecycle.py b/bluemira/fuel_cycle/lifecycle.py index 90ead74570..29d9c34211 100644 --- a/bluemira/fuel_cycle/lifecycle.py +++ b/bluemira/fuel_cycle/lifecycle.py @@ -17,7 +17,7 @@ import numpy as np from matplotlib.lines import Line2D -from bluemira.base.constants import S_TO_YR, YR_TO_S, raw_uc +from bluemira.base.constants import raw_uc from bluemira.base.look_and_feel import bluemira_print, bluemira_warn from bluemira.fuel_cycle.timeline import Timeline from bluemira.utilities.tools import abs_rel_difference, is_num, json_writer @@ -125,8 +125,8 @@ def life_neutronics(self): self.n_blk_replace = 1 # HLR self.n_div_replace = ndivch_in1blk + ndivch_in2blk - m_short = self.maintenance_s * S_TO_YR - m_long = self.maintenance_l * S_TO_YR + m_short = raw_uc(self.maintenance_s, "s", "yr") + m_long = raw_uc(self.maintenance_l, "s", "yr") phases = [] for i in range(ndivch_in1blk): p_str = "Phase P1." + str(i + 1) @@ -156,10 +156,12 @@ def life_neutronics(self): fpy += phases[i][0] self.fpy = fpy # Irreplaceable components life checks - self.t_on_total = self.fpy * YR_TO_S # [s] total fusion time + self.t_on_total = raw_uc(self.fpy, "yr", "s") # [s] total fusion time tf_ins_life_dose = tf_ins_nflux * self.t_on_total / self.params.tf_fluence if tf_ins_life_dose > 1: - self.tf_lifeend = round(self.params.tf_fluence / tf_ins_nflux / YR_TO_S, 2) + self.tf_lifeend = round( + raw_uc(self.params.tf_fluence / tf_ins_nflux, "s", "yr"), 2 + ) tflifeperc = round(100 * self.tf_lifeend / self.fpy, 1) bluemira_warn( f"TF coil insulation fried after {self.tf_lifeend:.2f} full-power years" @@ -174,7 +176,7 @@ def life_neutronics(self): f" years, or {vvlifeperc:.2f} % of neutron budget." ) # TODO: treat output parameter - self.n_cycles = self.fpy * YR_TO_S / self.t_flattop + self.n_cycles = raw_uc(self.fpy, "yr", "s") / self.t_flattop def set_availabilities(self, load_factor: float): """ @@ -214,7 +216,7 @@ def calc_n_pulses(self, phases: List[List[float]]): Calculate the number of pulses per phase. """ self.n_pulse_p = [ - int(YR_TO_S * phases[i][0] // self.t_flattop) + int(raw_uc(phases[i][0], "yr", "s") // self.t_flattop) for i in range(len(phases)) if phases[i][1].startswith("Phase P") ] @@ -276,12 +278,14 @@ def sanity(self): results that violate the tolerances. """ life = self.fpy / self.params.A_global - actual_life = S_TO_YR * ( + actual_life = raw_uc( self.t_on_total + self.total_ramptime + self.t_interdown + self.total_planned_maintenance - + self.t_unplanned_m + + self.t_unplanned_m, + "s", + "yr", ) actual_lf = self.fpy / actual_life delt = abs_rel_difference(actual_life, life) @@ -318,7 +322,9 @@ def sanity(self): self.inputs, ) # Phoenix - if self.params.A_global > self.fpy / (self.fpy + S_TO_YR * self.min_downtime): + if self.params.A_global > self.fpy / ( + self.fpy + raw_uc(self.min_downtime, "s", "yr") + ): bluemira_warn("FuelCycle::Lifecyle: Input availability is unachievable.") # Re-assign A self.params.A_global = actual_lf diff --git a/bluemira/fuel_cycle/timeline.py b/bluemira/fuel_cycle/timeline.py index 45a7e9a636..75e6aba836 100644 --- a/bluemira/fuel_cycle/timeline.py +++ b/bluemira/fuel_cycle/timeline.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt import numpy as np -from bluemira.base.constants import S_TO_YR, YR_TO_S +from bluemira.base.constants import raw_uc from bluemira.fuel_cycle.timeline_tools import ( LogNormalAvailabilityStrategy, OperationalAvailabilityStrategy, @@ -354,7 +354,7 @@ def __init__( ) j += 1 elif "Phase M" in name: - p = MaintenancePhase(name, duration * YR_TO_S, t_start=t_start) + p = MaintenancePhase(name, raw_uc(duration, "yr", "s"), t_start=t_start) phases.append(p) self.phases = phases self.build_arrays(phases) @@ -385,9 +385,8 @@ def concatenate(p_phases, k_key): self.ft = np.zeros(len(self.t)) for i in fuse_indices[1::2]: self.ft[i] = self.t[i] - self.t[i - 1] - self.ft = np.cumsum(self.ft) - self.ft *= S_TO_YR - self.t *= S_TO_YR + self.ft = raw_uc(np.cumsum(self.ft), "s", "yr") + self.t = raw_uc(self.t, "s", "yr") self.plant_life = self.t[-1] # total plant lifetime [calendar] def to_dict(self) -> Dict[str, Union[np.ndarray, int]]: From 1a9a34e0ad1abadb262ca0f53f5831079b103b74 Mon Sep 17 00:00:00 2001 From: ocean Date: Wed, 20 Dec 2023 14:08:04 +0000 Subject: [PATCH 6/9] Fixed the problem of missing elementary charge (e) constant in base.constants --- bluemira/base/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bluemira/base/constants.py b/bluemira/base/constants.py index 658a78f566..bde534d80d 100644 --- a/bluemira/base/constants.py +++ b/bluemira/base/constants.py @@ -283,6 +283,8 @@ def _transform( # Vacuum permittivity EPS_0 = ureg.Quantity("eps_0").to_base_units().magnitude # [A^2.s^4/kg/m^3] +# absolute charge of an electron +ELEMENTARY_CHARGE = ureg.Quantity("e").to_base_units().magnitude # [e] # Commonly used.. MU_0_2PI = 2e-7 # [T.m/A] or [V.s/(A.m)] From c8a1a24a598bf906e092fcf684dbe2e633ab591c Mon Sep 17 00:00:00 2001 From: ocean Date: Tue, 4 Jun 2024 01:12:21 +0100 Subject: [PATCH 7/9] Undid all S_TO_YR/YR_TO_S changes --- bluemira/fuel_cycle/cycle.py | 6 +++--- bluemira/fuel_cycle/lifecycle.py | 28 +++++++++++----------------- bluemira/fuel_cycle/timeline.py | 8 ++++---- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/bluemira/fuel_cycle/cycle.py b/bluemira/fuel_cycle/cycle.py index 04925bcaa5..3b498bff9a 100644 --- a/bluemira/fuel_cycle/cycle.py +++ b/bluemira/fuel_cycle/cycle.py @@ -20,7 +20,7 @@ import numpy as np from scipy.interpolate import interp1d -from bluemira.base.constants import N_AVOGADRO, T_LAMBDA, T_MOLAR_MASS, raw_uc +from bluemira.base.constants import N_AVOGADRO, T_LAMBDA, T_MOLAR_MASS, YR_TO_S, raw_uc from bluemira.base.look_and_feel import bluemira_print from bluemira.fuel_cycle.blocks import FuelCycleComponent, FuelCycleFlow from bluemira.fuel_cycle.tools import ( @@ -186,7 +186,7 @@ def tbreed(self, TBR: float, m_T_0: float): m_T = m_T_0 * np.ones(len(self.DEMO_t)) for i in range(1, len(self.DEMO_t)): dt = self.DEMO_t[i] - self.DEMO_t[i - 1] - dts = raw_uc(dt, "yr", "s") + dts = dt * YR_TO_S t_bred = TBR * self.brate[i] * dts t_bred += self.prate[i] * dts t_burnt = self.brate[i] * dts @@ -351,7 +351,7 @@ def recycle(self): m_T_out = self.plasma(self.params.eta_iv, self.params.I_miv, flows=flows) # Resolution - Not used everywhere for speed - n_ts = int(round(raw_uc(self.DEMO_t[-1], "yr", "s") / self.timestep)) + n_ts = int(round(self.DEMO_t[-1] * YR_TO_S / self.timestep)) self.t, m_pellet_in = discretise_1d(self.DEMO_t, self.m_T_in, n_ts) # Flow out of the vacuum vessel diff --git a/bluemira/fuel_cycle/lifecycle.py b/bluemira/fuel_cycle/lifecycle.py index 29d9c34211..50b2eb40f3 100644 --- a/bluemira/fuel_cycle/lifecycle.py +++ b/bluemira/fuel_cycle/lifecycle.py @@ -17,7 +17,7 @@ import numpy as np from matplotlib.lines import Line2D -from bluemira.base.constants import raw_uc +from bluemira.base.constants import S_TO_YR, YR_TO_S, raw_uc from bluemira.base.look_and_feel import bluemira_print, bluemira_warn from bluemira.fuel_cycle.timeline import Timeline from bluemira.utilities.tools import abs_rel_difference, is_num, json_writer @@ -125,8 +125,8 @@ def life_neutronics(self): self.n_blk_replace = 1 # HLR self.n_div_replace = ndivch_in1blk + ndivch_in2blk - m_short = raw_uc(self.maintenance_s, "s", "yr") - m_long = raw_uc(self.maintenance_l, "s", "yr") + m_short = self.maintenance_s * S_TO_YR + m_long = self.maintenance_l * S_TO_YR phases = [] for i in range(ndivch_in1blk): p_str = "Phase P1." + str(i + 1) @@ -156,12 +156,10 @@ def life_neutronics(self): fpy += phases[i][0] self.fpy = fpy # Irreplaceable components life checks - self.t_on_total = raw_uc(self.fpy, "yr", "s") # [s] total fusion time + self.t_on_total = self.fpy * YR_TO_S # [s] total fusion time tf_ins_life_dose = tf_ins_nflux * self.t_on_total / self.params.tf_fluence if tf_ins_life_dose > 1: - self.tf_lifeend = round( - raw_uc(self.params.tf_fluence / tf_ins_nflux, "s", "yr"), 2 - ) + self.tf_lifeend = round(self.params.tf_fluence / tf_ins_nflux * S_TO_YR, 2) tflifeperc = round(100 * self.tf_lifeend / self.fpy, 1) bluemira_warn( f"TF coil insulation fried after {self.tf_lifeend:.2f} full-power years" @@ -176,7 +174,7 @@ def life_neutronics(self): f" years, or {vvlifeperc:.2f} % of neutron budget." ) # TODO: treat output parameter - self.n_cycles = raw_uc(self.fpy, "yr", "s") / self.t_flattop + self.n_cycles = self.fpy, YR_TO_S / self.t_flattop def set_availabilities(self, load_factor: float): """ @@ -216,7 +214,7 @@ def calc_n_pulses(self, phases: List[List[float]]): Calculate the number of pulses per phase. """ self.n_pulse_p = [ - int(raw_uc(phases[i][0], "yr", "s") // self.t_flattop) + int((phases[i][0] * YR_TO_S) // self.t_flattop) for i in range(len(phases)) if phases[i][1].startswith("Phase P") ] @@ -278,15 +276,13 @@ def sanity(self): results that violate the tolerances. """ life = self.fpy / self.params.A_global - actual_life = raw_uc( + actual_life = ( self.t_on_total + self.total_ramptime + self.t_interdown + self.total_planned_maintenance - + self.t_unplanned_m, - "s", - "yr", - ) + + self.t_unplanned_m + ) * S_TO_YR actual_lf = self.fpy / actual_life delt = abs_rel_difference(actual_life, life) delta2 = abs_rel_difference(actual_lf, self.params.A_global) @@ -322,9 +318,7 @@ def sanity(self): self.inputs, ) # Phoenix - if self.params.A_global > self.fpy / ( - self.fpy + raw_uc(self.min_downtime, "s", "yr") - ): + if self.params.A_global > self.fpy / (self.fpy + self.min_downtime * S_TO_YR): bluemira_warn("FuelCycle::Lifecyle: Input availability is unachievable.") # Re-assign A self.params.A_global = actual_lf diff --git a/bluemira/fuel_cycle/timeline.py b/bluemira/fuel_cycle/timeline.py index 75e6aba836..d5ff612935 100644 --- a/bluemira/fuel_cycle/timeline.py +++ b/bluemira/fuel_cycle/timeline.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt import numpy as np -from bluemira.base.constants import raw_uc +from bluemira.base.constants import S_TO_YR, YR_TO_S from bluemira.fuel_cycle.timeline_tools import ( LogNormalAvailabilityStrategy, OperationalAvailabilityStrategy, @@ -354,7 +354,7 @@ def __init__( ) j += 1 elif "Phase M" in name: - p = MaintenancePhase(name, raw_uc(duration, "yr", "s"), t_start=t_start) + p = MaintenancePhase(name, duration * YR_TO_S, t_start=t_start) phases.append(p) self.phases = phases self.build_arrays(phases) @@ -385,8 +385,8 @@ def concatenate(p_phases, k_key): self.ft = np.zeros(len(self.t)) for i in fuse_indices[1::2]: self.ft[i] = self.t[i] - self.t[i - 1] - self.ft = raw_uc(np.cumsum(self.ft), "s", "yr") - self.t = raw_uc(self.t, "s", "yr") + self.ft = np.cumsum(self.ft) * S_TO_YR + self.t = self.t * S_TO_YR self.plant_life = self.t[-1] # total plant lifetime [calendar] def to_dict(self) -> Dict[str, Union[np.ndarray, int]]: From c1c9ba420fb69731f8ef5e08db907f1bb92932ea Mon Sep 17 00:00:00 2001 From: ocean Date: Tue, 4 Jun 2024 08:56:17 +0100 Subject: [PATCH 8/9] Minor fixes to the YR_TO_S --- bluemira/fuel_cycle/lifecycle.py | 10 +++++----- bluemira/fuel_cycle/timeline.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bluemira/fuel_cycle/lifecycle.py b/bluemira/fuel_cycle/lifecycle.py index 50b2eb40f3..95fa7d3ed0 100644 --- a/bluemira/fuel_cycle/lifecycle.py +++ b/bluemira/fuel_cycle/lifecycle.py @@ -174,7 +174,7 @@ def life_neutronics(self): f" years, or {vvlifeperc:.2f} % of neutron budget." ) # TODO: treat output parameter - self.n_cycles = self.fpy, YR_TO_S / self.t_flattop + self.n_cycles = self.fpy * YR_TO_S / self.t_flattop def set_availabilities(self, load_factor: float): """ @@ -214,7 +214,7 @@ def calc_n_pulses(self, phases: List[List[float]]): Calculate the number of pulses per phase. """ self.n_pulse_p = [ - int((phases[i][0] * YR_TO_S) // self.t_flattop) + int(YR_TO_S * phases[i][0] // self.t_flattop) for i in range(len(phases)) if phases[i][1].startswith("Phase P") ] @@ -276,13 +276,13 @@ def sanity(self): results that violate the tolerances. """ life = self.fpy / self.params.A_global - actual_life = ( + actual_life = S_TO_YR * ( self.t_on_total + self.total_ramptime + self.t_interdown + self.total_planned_maintenance + self.t_unplanned_m - ) * S_TO_YR + ) actual_lf = self.fpy / actual_life delt = abs_rel_difference(actual_life, life) delta2 = abs_rel_difference(actual_lf, self.params.A_global) @@ -318,7 +318,7 @@ def sanity(self): self.inputs, ) # Phoenix - if self.params.A_global > self.fpy / (self.fpy + self.min_downtime * S_TO_YR): + if self.params.A_global > self.fpy / (self.fpy + S_TO_YR * self.min_downtime): bluemira_warn("FuelCycle::Lifecyle: Input availability is unachievable.") # Re-assign A self.params.A_global = actual_lf diff --git a/bluemira/fuel_cycle/timeline.py b/bluemira/fuel_cycle/timeline.py index d5ff612935..e4bfed3c8d 100644 --- a/bluemira/fuel_cycle/timeline.py +++ b/bluemira/fuel_cycle/timeline.py @@ -386,7 +386,7 @@ def concatenate(p_phases, k_key): for i in fuse_indices[1::2]: self.ft[i] = self.t[i] - self.t[i - 1] self.ft = np.cumsum(self.ft) * S_TO_YR - self.t = self.t * S_TO_YR + self.t *= S_TO_YR self.plant_life = self.t[-1] # total plant lifetime [calendar] def to_dict(self) -> Dict[str, Union[np.ndarray, int]]: From c17dd4ae7f786d71417363d435f70c86e50edf21 Mon Sep 17 00:00:00 2001 From: ocean Date: Tue, 4 Jun 2024 09:10:20 +0100 Subject: [PATCH 9/9] Added back in C_LIGHT --- bluemira/base/constants.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bluemira/base/constants.py b/bluemira/base/constants.py index bde534d80d..e0f37a1f4b 100644 --- a/bluemira/base/constants.py +++ b/bluemira/base/constants.py @@ -277,6 +277,9 @@ def _transform( # Physical constants # ============================================================================= +# Speed of light +C_LIGHT = ureg.Quantity("c").to_base_units().magnitude # [m/s] + # Vacuum permeability MU_0 = ureg.Quantity("mu_0").to_base_units().magnitude # [T.m/A] or [V.s/(A.m)]