def __init__(self, gzx, gzy, yld, ff, wind, wd, shear, tob=0, dunits='km', wunits='km/h', shearunits='m/s-km', yunits='kT'): self.translation = ~Affine.translation(convert_units(gzx, dunits, 'mi'), convert_units(gzy, dunits, 'mi')) # translate coordinates relative to GZ (st. mi) self.wd = wd # wind direction in degrees (0=N, 90=E, etc.) self.yld = convert_units(yld, yunits, 'MT') # yield (MT) self.ff = ff # fission fraction, 0 < ff <= 1.0 self.wind = convert_units(wind, wunits, 'mph') # wind speed (mi/hr) self.shear = convert_units(shear, shearunits, 'mph/kilofoot') # wind shear in mi/hr-kilofoot self.tob = tob # time of burst (hrs) # FORTRAN is ugly in any language # Store these values in the WSEG10 object to avoid recalculating them d = np.log(self.yld) + 2.42 # physically meaningless, but occurs twice # According to Hanifen, "The cloud is initially formed because the nuclear # fireball vaporizes both the surface of the earth at ground zero and the # weapon itself. The activity contained in the cloud is both neutron induced # and fission. After formation, the fireball rises and begins to cool at its # outer edges faster than the center thereby creating the typical torroidal # currents associated with the nuclear cloud. WSEG arbitrarily assumes that # the cloud will rise to a maximum center height within fifteen minutes and # then stabilize." self.H_c = 44 + 6.1 * np.log(yld) - 0.205 * abs(d) * d # cloud center height lnyield = np.log(self.yld) self.s_0 = np.exp(0.7 + lnyield / 3 - 3.25 / (4.0 + (lnyield + 5.4)**2)) #sigma_0 self.s_02 = self.s_0**2 self.s_h = 0.18 * self.H_c # sigma_h self.T_c = 1.0573203 * (12 * (self.H_c / 60) - 2.5 * (self.H_c / 60)**2) * (1 - 0.5 * np.exp(-1 * (self.H_c / 25)**2)) # time constant self.L_0 = wind * self.T_c # L_0, used by g(x) self.L_02 = self.L_0**2 self.s_x2 = self.s_02 * (self.L_02 + 8 * self.s_02) / (self.L_02 + 2 * self.s_02) self.s_x = np.sqrt(self.s_x2) # sigma_x self.L_2 = self.L_02 + 2 * self.s_x2 self.L = np.sqrt(self.L_2) # L self.n = (ff * self.L_02 + self.s_x2) / (self.L_02 + 0.5 * self.s_x2) # n self.a_1 = 1 / (1 + ((0.001 * self.H_c * wind) / self.s_0)) # alpha_1
def r_soviet_overpressure(y, op, h, thermal_layer=True, yunits='kT', dunits='m', opunits='kg/cm^2'): """Estimate the radius from the epicenter at which peak static overpressure op will be experienced based graphs in the 1987 Soviet military publication _Iadernoe oruzhie: Posbie dlia ofitserov_. The most interesting feature of this model is that it provides for cases in which the Mach stem is suppressed by a thermal layer. This phenomenon was considered a largely theoretical 'second-order effect' among most US NWE researchers, but was observed in extreme forms in the USSR's atmospheric nuclear tests, leading them to conclude it would occur in many real-world military scenarios. To use a Soviet model with a Mach stem present, set the parameter thermal_layer to False.""" yld = convert_units(y, yunits, 'kT') height = convert_units(h, dunits, 'm') sh = scale_height(yld, height) overp = convert_units(op, opunits, 'kg/cm^2') if thermal_layer: return convert_units(_rsovietnomach(sh, overp), 'm', dunits) * y**0.3333333333333333 else: return convert_units(_rsovietmach(sh, overp), 'm', dunits) * y**0.3333333333333333
def brode_overpressure(y, r, h, yunits='kT', dunits='m', opunits='kg/cm^2'): """Estimate peak static overpressure at radius r from the epicenter of a burst with yield y and a burst height of h using the Brode equation.""" yld = convert_units(y, yunits, 'kT') ground_range = convert_units(r, dunits, 'kilofeet') height = convert_units(h, dunits, 'kilofeet') op = _brodeop(yld, ground_range, height) return convert_units(op, 'psi', opunits)
def D_Hplus1(self, x, y, dunits='km', doseunits='Sv'): """Returns dose rate at x, y at 1 hour after burst. This value includes dose rate from all activity that WILL be deposited at that location, not just that that has arrived by H+1 hr.""" rx, ry = self.translation * (convert_units(x, dunits, 'mi'), convert_units(y, dunits, 'mi')) * ~Affine.rotation(-self.wd + 270) f_x = self.yld * 2e6 * self.phi(rx) * self.g(rx) * self.ff s_y = np.sqrt(self.s_02 + ((8 * abs(rx + 2 * self.s_x) * self.s_02) / self.L) + (2 * (self.s_x * self.T_c * self.s_h * self.shear)**2 / self.L_2) + (((rx + 2 * self.s_x) * self.L_0 * self.T_c * self.s_h * self.shear)**2 / self.L**4)) a_2 = 1 / (1 + ((0.001 * self.H_c * self.wind) / self.s_0) * (1 - norm.cdf(2 * x / self.wind))) f_y = np.exp(-0.5 * (ry / (a_2 * s_y))**2) / (2.5066282746310002 * s_y) return convert_units(f_x * f_y, 'Roentgen', doseunits)
def DNA_static_overpressure(y, r, h, yunits='kT', dunits='m', opunits='kg/cm^2'): """Estimate peak static overpressure at range r from a burst of yield y using the the Defense Nuclear Agency 1kT standard free airburst overpressure. This assumes a thermally ideal surface.""" yld = convert_units(y, yunits, 'kT') gr = convert_units(r, dunits, 'm') height = convert_units(h, dunits, 'm') op = _DNAairburstpeakop(gr, yld, height) return convert_units(op, 'Pa', opunits)
def __init__(self, gzx, gzy, yld, ff, wind, wd, shear, tob=0, dunits='km', wunits='km/h', shearunits='m/s-km', yunits='kT'): self.translation = ~Affine.translation(convert_units( gzx, dunits, 'mi'), convert_units( gzy, dunits, 'mi')) # translate coordinates relative to GZ (st. mi) self.wd = wd # wind direction in degrees (0=N, 90=E, etc.) self.yld = convert_units(yld, yunits, 'MT') # yield (MT) self.ff = ff # fission fraction, 0 < ff <= 1.0 self.wind = convert_units(wind, wunits, 'mph') # wind speed (mi/hr) self.shear = convert_units( shear, shearunits, 'mph/kilofoot') # wind shear in mi/hr-kilofoot self.tob = tob # time of burst (hrs) # FORTRAN is ugly in any language # Store these values in the WSEG10 object to avoid recalculating them d = np.log(self.yld) + 2.42 # physically meaningless, but occurs twice # According to Hanifen, "The cloud is initially formed because the nuclear # fireball vaporizes both the surface of the earth at ground zero and the # weapon itself. The activity contained in the cloud is both neutron induced # and fission. After formation, the fireball rises and begins to cool at its # outer edges faster than the center thereby creating the typical torroidal # currents associated with the nuclear cloud. WSEG arbitrarily assumes that # the cloud will rise to a maximum center height within fifteen minutes and # then stabilize." self.H_c = 44 + 6.1 * np.log(yld) - 0.205 * abs( d) * d # cloud center height lnyield = np.log(self.yld) self.s_0 = np.exp(0.7 + lnyield / 3 - 3.25 / (4.0 + (lnyield + 5.4)**2)) #sigma_0 self.s_02 = self.s_0**2 self.s_h = 0.18 * self.H_c # sigma_h self.T_c = 1.0573203 * (12 * (self.H_c / 60) - 2.5 * (self.H_c / 60)**2) * ( 1 - 0.5 * np.exp(-1 * (self.H_c / 25)**2) ) # time constant self.L_0 = wind * self.T_c # L_0, used by g(x) self.L_02 = self.L_0**2 self.s_x2 = self.s_02 * (self.L_02 + 8 * self.s_02) / (self.L_02 + 2 * self.s_02) self.s_x = np.sqrt(self.s_x2) # sigma_x self.L_2 = self.L_02 + 2 * self.s_x2 self.L = np.sqrt(self.L_2) # L self.n = (ff * self.L_02 + self.s_x2) / (self.L_02 + 0.5 * self.s_x2 ) # n self.a_1 = 1 / (1 + ((0.001 * self.H_c * wind) / self.s_0)) # alpha_1
def DNA_dynamic_pressure(y, r, h, yunits='kT', dunits='m', opunits='kg/cm^2'): """Estimate peak pynamic overpressure at range r from a burst of yield y using the the Defense Nuclear Agency 1kT standard free airburst overpressure, assuming an ideal surface. Many real-world surfaces are not ideal (most, in the opinion of Soviet analysts), meaning that this function has only limited predictove capability.""" yld = convert_units(y, yunits, 'kT') gr = convert_units(r, dunits, 'm') height = convert_units(h, dunits, 'm') dyn = _DNAairburstpeakdyn(gr, yld, height) return convert_units(dyn, 'Pa', opunits)
def r_soviet_ground_thermal(y, fluence, h, visibility, yunits='kT', dunits='m'): """Estimate the range from a ground burst of yield y at which fluence will occur using the methodology described in _Iadernoe oruzhie_ 4th ed. (1987). visibility is given as International Visibility Code (IVC) numbers (1=thick fog, 9=clear).""" yld = convert_units(y, yunits, 'kT') v = _ivc_to_model_input(visibility, yld, h) sr = _reverse_soviet_ground_thermal(fluence, yld, v) return convert_units(sr, 'km', dunits)
def soviet_ground_thermal(y, r, h, visibility, yunits='kT', dunits='m'): """Estimate thermal fluence at range r from a ground burst of yield y using the methodology described in _Iadernoe oruzhie_ 4th ed. (1987). visibility is given as International Visibility Code (IVC) numbers (1=thick fog, 9=clear).""" slant_range = convert_units(r, dunits, 'km') yld = convert_units(y, yunits, 'kT') # height only used to determine visibility code v = _ivc_to_model_input(visibility, yld, h) fluence = _soviet_ground_thermal(slant_range, yld, v) return fluence
def D_Hplus1(self, x, y, dunits='km', doseunits='Sv'): """Returns dose rate at x, y at 1 hour after burst. This value includes dose rate from all activity that WILL be deposited at that location, not just that that has arrived by H+1 hr.""" rx, ry = self.translation * (convert_units( x, dunits, 'mi'), convert_units( y, dunits, 'mi')) * ~Affine.rotation(-self.wd + 270) f_x = self.yld * 2e6 * self.phi(rx) * self.g(rx) * self.ff s_y = np.sqrt(self.s_02 + ((8 * abs(rx + 2 * self.s_x) * self.s_02) / self.L) + (2 * (self.s_x * self.T_c * self.s_h * self.shear)**2 / self.L_2) + (((rx + 2 * self.s_x) * self.L_0 * self.T_c * self.s_h * self.shear)**2 / self.L**4)) a_2 = 1 / (1 + ((0.001 * self.H_c * self.wind) / self.s_0) * (1 - norm.cdf(2 * x / self.wind))) f_y = np.exp(-0.5 * (ry / (a_2 * s_y))**2) / (2.5066282746310002 * s_y) return convert_units(f_x * f_y, 'Roentgen', doseunits)
def dose(self, x, y, dunits='km', doseunits='Sv'): """Estimate of total "Equivalent Residual Dose" (ERD) at location x, y from time of fallout arrival to 30 days, including a 90% recovery factor. """ rx, _ = self.translation * (convert_units(x, dunits, 'mi'), convert_units(y, dunits, 'mi')) * ~Affine.rotation(-self.wd + 270) t_a = self.fallouttoa(rx) # To obtain a measure of dose to humans, "Biological Dose" was defined as the # product of the DH+1 and a conversion factor, called Bio. Bio is an # empirical function depending on fallout arrival time and length of # exposure. Ten percent of the dose received is assumed irreparable and # ninety percent is assumed reparable with a thirty day time constant. This # was solved numerically and plotted as Dose vs. Time. Bio was then estimated # as Bio = (t / 19)**0.33, so that the dose at some time after activity # arrival is defined as Dose = DH+1 * Bio. Further refinements in the model # resulted in a second order approximation for Bio used here: bio = np.exp(-(0.287 + 0.52 * np.log(t_a / 31.6) + 0.04475 * np.log((t_a / 31.6)**2))) return self.D_Hplus1(x, y, dunits=dunits, doseunits=doseunits) * bio
def dose(self, x, y, dunits='km', doseunits='Sv'): """Estimate of total "Equivalent Residual Dose" (ERD) at location x, y from time of fallout arrival to 30 days, including a 90% recovery factor. """ rx, _ = self.translation * (convert_units( x, dunits, 'mi'), convert_units( y, dunits, 'mi')) * ~Affine.rotation(-self.wd + 270) t_a = self.fallouttoa(rx) # To obtain a measure of dose to humans, "Biological Dose" was defined as the # product of the DH+1 and a conversion factor, called Bio. Bio is an # empirical function depending on fallout arrival time and length of # exposure. Ten percent of the dose received is assumed irreparable and # ninety percent is assumed reparable with a thirty day time constant. This # was solved numerically and plotted as Dose vs. Time. Bio was then estimated # as Bio = (t / 19)**0.33, so that the dose at some time after activity # arrival is defined as Dose = DH+1 * Bio. Further refinements in the model # resulted in a second order approximation for Bio used here: bio = np.exp(-(0.287 + 0.52 * np.log(t_a / 31.6) + 0.04475 * np.log((t_a / 31.6)**2))) return self.D_Hplus1(x, y, dunits=dunits, doseunits=doseunits) * bio