def iterate_lte_ne_eq_pops(self, atmos: Atmosphere): atmos.nondimensionalise() maxIter = 500 prevNe = np.copy(atmos.ne) ne = np.copy(atmos.ne) for it in range(maxIter): atomicPops = [] prevNe[:] = ne ne.fill(0.0) for a in sorted(self.atoms, key=atomic_weight_sort): nTotal = self.atomicTable[a.name].abundance * atmos.nHTot nStar = lte_pops(a, atmos, nTotal, debye=True) atomicPops.append(AtomicState(a, nStar, nTotal)) stages = np.array([l.stage for l in a.levels]) # print(stages) ne += np.sum(nStar * stages[:, None], axis=0) # print(ne) atmos.ne[:] = ne relDiff = np.nanmax(np.abs(1.0 - prevNe / ne)) print(relDiff) maxRelDiff = np.nanmax(relDiff) if maxRelDiff < 1e-3: print("Iterate LTE: %d iterations" % it) break else: print("LTE ne failed to converge") atmos.dimensionalise() table = AtomicStateTable(atmos, self.atomicTable, atomicPops) return table
class Scene2D: """Class of the simple 2-dimensional math model of the action world.""" def __init__(self, xml_rocket: str, xml_traj: str): """ :param xml_rocket: rocket's XML data file's path :param xml_traj: trajectory's XML data file's path """ self.atmo = Atmosphere() self.rocket = data_read_write.create_rocket(self.atmo, xml_rocket, xml_traj) self.is_rocket_calc = False # Public def sim(self, xml_optim=None): """ Starts the simulation. :param xml_optim: optimizer's XML data file's path :type xml_optim: str """ if xml_optim is not None: optim = self.rocket.optimize_traj(data_read_write.init_optim_range(xml_optim)) data_read_write.write_optim(optim) results = self.rocket.start() data_read_write.write_results2d(results) self.is_rocket_calc = True def show_atmosphere(self): """Shows atmosphere's parameters graphs.""" self.atmo.show() def show_rocket_results(self): if self.is_rocket_calc is True: self.rocket.show_results() else: print("Warning: the rocket wasn't been calculated!")
def load_data(self, param_file): with open(param_file, 'r') as inputf: self.parameters = json.load(inputf) # Set imported parameters as properties for parameter in self.parameters: setattr(self, parameter, self.parameters[parameter]) # use for threadsafe commnications with the GUI thread self.mutex = QMutex() self.atmosphere = Atmosphere() # TODO Error analysis vs. ticksize self.ticksize = 0.001 self.time = 0 self.height = self.launch_height self.velocity = 0 self.acceleration = 0 self.max_height = 0 self.max_velocity = 0 self.max_acceleration = 0 self.mass = self.launch_mass self.thruster = Thruster() self.data = {} self.data['time'] = [] self.data['height'] = [] self.data['velocity'] = [] self.data['acceleration'] = []
def __init__(self, xml_rocket: str, xml_traj: str): """ :param xml_rocket: rocket's XML data file's path :param xml_traj: trajectory's XML data file's path """ self.atmo = Atmosphere() self.rocket = data_read_write.create_rocket(self.atmo, xml_rocket, xml_traj) self.is_rocket_calc = False
def update_nTotal(self, atmos : Atmosphere): dim = False if atmos.dimensioned: dim = True atmos.nondimensionalise() self.nTotal[:] = self.model.atomicTable[self.name].abundance * atmos.nHTot if dim: atmos.dimensionalise()
def compute_eq_pops(self, atmos: Atmosphere): dim = False if atmos.dimensioned: dim = True atmos.nondimensionalise() atomicPops = [] for a in sorted(self.atoms, key=atomic_weight_sort): nTotal = self.atomicTable[a.name].abundance * atmos.nHTot nStar = lte_pops(a, atmos, nTotal, debye=True) atomicPops.append(AtomicState(a, nStar, nTotal)) table = AtomicStateTable(atmos, self.atomicTable, atomicPops) if dim: atmos.dimensionalise return table
def setup(self): nn = self.options["num_nodes"] self.add_subsystem("atmosphere", subsys=Atmosphere(num_nodes=nn), promotes_inputs=["h"], promotes_outputs=["rho"]) self.add_subsystem("aerodynamics", subsys=Aerodynamics(num_nodes=nn), promotes_inputs=["alpha", "v", "rho"], promotes_outputs=["lift", "drag"]) self.add_subsystem("heating", subsys=AerodynamicHeating(num_nodes=nn), promotes_inputs=["rho", "v", "alpha"], promotes_outputs=["q"]) self.add_subsystem("eom", subsys=FlightDynamics(num_nodes=nn), promotes_inputs=["beta", "gamma", "h", "psi", "theta", "v", "lift", "drag"], promotes_outputs=["hdot", "gammadot", "phidot", "psidot", "thetadot", "vdot"])
def test_sea_level(self): """Test Sea Level properties """ h = 0 a1 = Atmosphere(h) self.assertAlmostEqual(a1.T, 273 + 15, places=0) self.assertAlmostEqual(a1.P, 101325, places=0) self.assertAlmostEqual(a1.rho, 1.225, places=4)
def get_pv(forcing, output, n1=0, n2=10000, option="timeseries"): """ forcing : nc file assumed to be located in Forcings/ e.g. "SIRTA_all" output : name of output file containing PV production, stored in PV_Outputs directory n1 and n2 : left and right indicess for partial computations along timeseries option : specify the type of forcing to optimize vectorial computations """ print("----------Namelist loading----------------") nam = Namelist( ) # all options should be hardcoded in the Namelist() attributes print("----------Atmosphere loading----------------") # build an Atmosphere object from an nc file. Such object can be built directly from np.arrays atm = Atmosphere(*tools.get_atm_from_ncfile( forcing=forcing, albedo=nam.albedo, n1=n1, n2=n2)) atm.compute_sun() # to compute the full series of sza, saa, sw_toa # define PV panel from information contained in Namelist print("----------Module initialization----------------") panel = Panel( name=nam.panel_name, technology=nam.panel_technology, beta=nam.panel_beta, gamma=nam.panel_gamma, method_FF=nam.method_FF, SR_method=nam.SR_method, namelist=nam ) # get physical characteristics of the panel from a description file - here SIRTA panel used for consistency # call pv_interface to handle large arrays and successive (and possibly multi-thread) calls to pv_production t1 = time.time() if option == "timeseries": power_simul = pv_interface_timeseries(nam, atm, panel) elif option == "atlas": power_simul = pv_interface_atlas(nam, atm, panel) # save PV outputs np.savetxt("PV_Outputs/%s.dat" % output, power_simul) t2 = time.time() print("Time to run the PV code = %s seconds" % (t2 - t1)) return atm.dates, power_simul, atm.lat, atm.lon
def test_under_86km(self): """Tests for altitude values between 35 and 86 km """ z = np.array([50000.0, 70000.0, 86000.0]) expected_T = np.array([270.65, 219.585, 186.87]) expected_p = np.array([79.779, 5.2209, 0.37338]) expected_rho = np.array([0.0010269, 0.000082829, 0.000006958]) for idx, z_current in enumerate(z): a = Atmosphere(z_current) self.assertAlmostEqual(a.T, expected_T[idx], places=0) self.assertAlmostEqual(a.P, expected_p[idx], places=-2) self.assertAlmostEqual(a.rho, expected_rho[idx], places=4)
def test_under_35km(self): """Tests for altitude values between 11 and 35 km """ z = np.array([15000.0, 25000.0, 35000.0]) expected_T = np.array([216.65, 221.552, 236.513]) expected_p = np.array([12111.0, 2549.2, 574.59]) expected_rho = np.array([0.19476, 0.040084, 0.0084634]) for idx, z_current in enumerate(z): a = Atmosphere(z_current) self.assertAlmostEqual(a.T, expected_T[idx], places=3) self.assertAlmostEqual(a.P, expected_p[idx], places=-2) self.assertAlmostEqual(a.rho, expected_rho[idx], places=4)
def test_under_11km(self): """Tests for altitude values between 1 and 11 km """ z = np.array([500.0, 2500.0, 6500.0, 9000.0, 11000.0]) expected_T = np.array([284.900, 271.906, 245.943, 229.733, 216.774]) expected_p = np.array([95461.0, 74691.0, 44075.0, 30800.0, 22699.0]) expected_rho = np.array([1.1673, 0.95695, 0.62431, 0.46706, 0.36480]) for idx, z_current in enumerate(z): a = Atmosphere(z_current) self.assertAlmostEqual(a.T, expected_T[idx], places=3) self.assertAlmostEqual(a.P, expected_p[idx], places=-2) self.assertAlmostEqual(a.rho, expected_rho[idx], places=4)
def test_under_1000m(self): """Tests for altitude values under 1000 meters """ z = np.array([50.0, 550.0, 850.0]) expected_T = np.array([287.825, 284.575, 282.626]) expected_p = np.array([100720.0, 94890.0, 91523.0]) expected_rho = np.array([1.2191, 1.1616, 1.1281]) for idx, z_current in enumerate(z): a = Atmosphere(z_current) self.assertAlmostEqual(a.T, expected_T[idx], places=3) self.assertAlmostEqual(a.P, expected_p[idx], places=-2) self.assertAlmostEqual(a.rho, expected_rho[idx], places=4)
def __main__(): #initDict = readInit(init_file="SunEarth_4m.init") initDict = readInit(init_file="TMT_SuperEarth_N.init") wav_min, wav_max, t_exp = np.float32(initDict["wav_min"]), np.float32( initDict["wav_max"]), np.float32(initDict["t_exp"]) target_pl = Target(distance=np.float32(initDict["distance"]), spec_path=initDict["pl_spec_path"], inclination_deg=np.float32( initDict["pl_inclination_deg"]), rotation_vel=np.float32(initDict["pl_rotation_vel"]), radial_vel=np.float32(initDict["pl_radial_vel"]), spec_reso=np.float32(initDict["spec_reso"])) target_st = Target(distance=np.float32(initDict["distance"]), spec_path=initDict["st_spec_path"], inclination_deg=np.float32( initDict["st_inclination_deg"]), rotation_vel=np.float32(initDict["st_rotation_vel"]), radial_vel=np.float32(initDict["st_radial_vel"]), spec_reso=np.float32(initDict["spec_reso"])) wav_med = (wav_min + wav_max) / 2.0 if initDict["spec_tran_path"] != "None": atmosphere = Atmosphere(spec_tran_path=initDict["spec_tran_path"], spec_radi_path=initDict["spec_radi_path"]) else: atmosphere = None instrument = Instrument( wav_med, telescope_size=np.float32(initDict["telescope_size"]), pl_st_contrast=np.float32(initDict["pl_st_contrast"]), spec_reso=np.float32(initDict["spec_reso"]), read_noise=np.float32(initDict["read_noise"]), dark_current=np.float32(initDict["dark_current"]), fiber_size=np.float32(initDict["fiber_size"]), pixel_sampling=np.float32(initDict["pixel_sampling"]), throughput=np.float32(initDict["throughput"]), wfc_residual=np.float32(initDict["wfc_residual"]), num_surfaces=np.float32(initDict["num_surfaces"]), temperature=np.float32(initDict["temperature"])) thermal_background = ThermTarget(instrument, spec_reso=np.float32( initDict["spec_reso"])) zodi = ZodiTarget(instrument, distance=np.float32(initDict["distance"]), spec_path=initDict["zodi_spec_path"], exozodi_level=np.float32(initDict["exozodi_level"]), spec_reso=np.float32(initDict["spec_reso"])) hci_hrs = HCI_HRS_Observation(wav_min, wav_max, t_exp, target_pl, target_st, instrument, thermal_background, zodi, atmosphere=atmosphere) print( "Star flux: {0} \nLeaked star flux: {1}\nPlanet flux: {2}\nPlanet flux per pixel: {3}\nThermal background flux: {4}\nThermal background flux per pixel: {5}\nSky flux: {6}\nSky flux per pixel: {7}\nSky transmission: {8}\nTotal pixel number: {9}\n" .format( hci_hrs.star_total_flux, hci_hrs.star_total_flux * instrument.pl_st_contrast, hci_hrs.planet_total_flux, hci_hrs.planet_total_flux / (len(hci_hrs.obs_spec_resample.flux) + 0.0), hci_hrs.therm_total_flux, hci_hrs.therm_total_flux / (len(hci_hrs.obs_therm_resample.flux) + 0.0), hci_hrs.sky_total_flux, hci_hrs.sky_total_flux / (len(hci_hrs.obs_therm_resample.flux) + 0.0), hci_hrs.sky_transmission, len(hci_hrs.obs_therm_resample.flux))) spec = pyfits.open(initDict["template_path"]) template = Spectrum(spec[1].data["Wavelength"], spec[1].data["Flux"], spec_reso=np.float32(initDict["spec_reso"])) if initDict["spec_tran_path"] != "None": hci_hrs_red = HCI_HRS_Reduction(hci_hrs, template, save_flag=False, obj_tag=initDict["obj_tag"], template_tag=initDict["template_tag"], speckle_flag=False) else: hci_hrs_red = HCI_HRS_Reduction(hci_hrs, template, save_flag=False, obj_tag=initDict["obj_tag"], template_tag=initDict["template_tag"], speckle_flag=True)
import numpy as np from aircraft import Aircraft from atmosphere import Atmosphere, Wind from constants import Constants import sim_setup from Planes.B737 import B737 from Planes.C172 import C172 from Planes.T38 import T38 # Initialization of objects used in overall sim architecture SIM = sim_setup.Sim_Parameters() CONSTANTS = Constants() Atmosphere = Atmosphere() plane1 = Aircraft() #################################################################################################### # BEGIN INPUT FIELDS #################################################################################################### # Modify simulation parameters SIM.START_TIME = 0.0 SIM.END_TIME = 2000.0 SIM.DELTA_T = 2 # Modify aircraft inital conditions # Choose airplane type - B737, C172, T38 plane1.design = B737()
CD = 0.5 # Approximate drag coefficient of vehicle AREA = 0.79 # Frontal area of rocket GRAV = 32.174 DENSITY = 0.0765 # constant for simplicity for now MASS = 1.24324 # lbs TIME = 0 STEP = 0.01 def acceleration(velocity, density, input): cd = CD + (0.75 * input) return -GRAV - (cd * AREA * density * (velocity**2) / (2 * MASS)) AtmosEngine = Atmosphere() altitude = 1000 velocity = 700 accel = acceleration(velocity, AtmosEngine.density(altitude), 0) pid = PID(0.00025, 0.001, 0.1, 3500, 0, 1) pid_outputs = [] while velocity > 0: altitude += velocity * STEP velocity += accel * STEP p_alt = projected_altitude(accel, velocity, altitude) pid_output = pid.output(p_alt, STEP) accel = acceleration(velocity, AtmosEngine.density(altitude), pid_output) pid_outputs.append(pid_output)
"""Recreate Fig. 14.4 & 14.5 from Anderson Hypersonic & High Temperature Gas Dynamics """ altitudes = np.array([35900, 59800, 82200, 100000, 120300, 154800, 173500, 200100, 230400, 259700, 294800, 322900]) * ft_to_m fig1, ax1 = plt.subplots(figsize=(12, 9)) fig2, ax2 = plt.subplots(figsize=(12, 9)) T1 = 225.0 # Convert altitudes into ambient pressures pressures = np.zeros_like(altitudes) for idx, altitude in enumerate(altitudes): a = Atmosphere(altitude) pressures[idx] = a.P u1 = np.linspace(4000, 40000, num=75) * ft_to_m # Calculate shock properties for each pressure for pidx, p1 in enumerate(pressures): T2_arr = np.zeros_like(u1) rho2_rho1_arr = np.zeros_like(u1) for uidx, u in enumerate(u1): results = normal_shock(air, u, T1, p1) if not results: continue
class RocketSimulator(QtCore.QObject): R_EARTH = 6371000 # meters G_0 = -9.80665 # m/s^2 (at sea level) new_data = QtCore.pyqtSignal(object) def __init__(self, ticksize, param_file='parameters.json'): QtCore.QObject.__init__(self) self.load_data(param_file) def load_data(self, param_file): with open(param_file, 'r') as inputf: self.parameters = json.load(inputf) # Set imported parameters as properties for parameter in self.parameters: setattr(self, parameter, self.parameters[parameter]) # use for threadsafe commnications with the GUI thread self.mutex = QMutex() self.atmosphere = Atmosphere() # TODO Error analysis vs. ticksize self.ticksize = 0.001 self.time = 0 self.height = self.launch_height self.velocity = 0 self.acceleration = 0 self.max_height = 0 self.max_velocity = 0 self.max_acceleration = 0 self.mass = self.launch_mass self.thruster = Thruster() self.data = {} self.data['time'] = [] self.data['height'] = [] self.data['velocity'] = [] self.data['acceleration'] = [] def run_simulation(self): while self.height >= self.launch_height: self.run_tick() print(self.max_height, self.max_velocity, self.max_acceleration) def run_tick(self): self.height += self.velocity * self.ticksize self.velocity += self.acceleration * self.ticksize force = self.thrust_force() + self.drag_force() + self.gravity_force() self.acceleration = force / self.mass locked = False if self.mutex.tryLock(10): self.new_data.emit([self.time, self.height, self.velocity, self.acceleration]) self.mutex.unlock() self.data['time'].append(self.time) self.data['height'].append(self.height) self.data['velocity'].append(self.velocity) self.data['acceleration'].append(self.acceleration) self.update_mass() self.update_max_values() self.time += self.ticksize def drag_force(self): pressure = self.atmosphere.get_density_by_height(self.height) # Rocket is heading up if self.velocity >= 0: drag_coef = self.rocket_drag_coef area = self.cross_sectional_area # Rocket is falling with parachute deployed else: drag_coef = self.parachute_drag_coef area = self.parachute_area # Drag force is the opposite direction of velocity if self.velocity > 0: direction = -1 else: direction = 1 # TODO use increased parachute area return (direction * drag_coef * pressure * self.velocity**2 * area ) / 2 def gravity_force(self): return self.mass * self.get_g_at_alitude(self.height) def get_g_at_alitude(self, height): return self.G_0 * ((height + self.R_EARTH) / self.R_EARTH)**2 def thrust_force(self): if self.time < self.burn_length: return self.thruster.get_thrust_at_time(self.time) else: return 0 def update_mass(self): if self.time > self.burn_length: return else: self.mass -= (self.propellent_mass / self.burn_length) * self.ticksize def update_max_values(self): if self.height > self.max_height: self.max_height = self.height if self.velocity > self.max_velocity: self.max_velocity = self.velocity if self.acceleration > self.max_acceleration: self.max_acceleration = self.acceleration
def main(): # Initial setup pygame.init() running = True clock = pygame.time.Clock() tick_time = 30 # Screen size and screen object creation screen_size = (1920, 1080) # screen = pygame.display.set_mode(screen_size) screen = pygame.display.set_mode(screen_size, pygame.FULLSCREEN) # Window title and icon pygame.display.set_caption("SPACE") pygame.display.set_icon(pygame.image.load("assets/img/ship.png")) # Background color bg = (0, 0, 0) # Set font for text font = pygame.font.SysFont('consolas', 16) # Initialize the actors # Ship initial conditions # Default spawn point is defined in ship class ship_img_path = "assets/img/ship.png" ship_spawn = (25, 75) ship_size = (20, 15) ship = Ship(ship_spawn, ship_img_path, ship_size) # Planet initial conditions planet_spawn = (500, 375) planet_img_path = "assets/img/moon1.png" planet_mass = 8e10 planet_size = (50, 50) planet_influence_height = 250 planet_radius_offset = 1.3 # used below for fine tuning planet collision detection planet = Planet(planet_spawn, planet_img_path, planet_size, planet_mass, planet_influence_height) # Atmosphere initial conditions atm_spawn = (planet_spawn[0] - planet_size[0] / 2, planet_spawn[1] - planet_size[1] / 2) atm_img_path = "assets/img/atm.png" atm_size = (100, 100) atm_press = .002 atm = Atmosphere(atm_spawn, atm_img_path, atm_size, atm_press) min_orbit_radius = 180 min_orbit = pygame.Rect( tuple([ planet.get_center()[0] - min_orbit_radius, planet.get_center()[1] - min_orbit_radius ]), tuple([min_orbit_radius * 2, min_orbit_radius * 2])) max_orbit_radius = 200 max_orbit = pygame.Rect( tuple([ planet.get_center()[0] - max_orbit_radius, planet.get_center()[1] - max_orbit_radius ]), tuple([max_orbit_radius * 2, max_orbit_radius * 2])) success_orbit_padding = 4 success_orbit_radius = max_orbit_radius - (success_orbit_padding // 2) success_orbit_thickness = max_orbit_radius - min_orbit_radius - success_orbit_padding success_orbit = pygame.Rect( tuple([ planet.get_center()[0] - success_orbit_radius, planet.get_center()[1] - success_orbit_radius ]), tuple([success_orbit_radius * 2, success_orbit_radius * 2])) while running: # Paint screen background screen.fill(bg) # Get events for event in pygame.event.get(): if event.type == pygame.QUIT: running = False tick_time = get_keystrokes(event, ship) # Set collision boundaries with walls ship.set_wall_collision(screen_size) # Set collision boundaries with planet ship.set_body_collision(planet, planet_radius_offset) # Gravitational attraction planet.attract_body(ship) # Update atmospheric drag deceleration on ship atm.update_drag(ship) # Update ship velocity and position ship.update_velocity() ship.update_position() ship.check_fuel() # Text objects here: # Fuel display text object fuel_display_string = "Fuel:" fuel_display_loc = (10, 10) fuel_display = font.render(fuel_display_string, True, (0, 255, 0)) score = 0 if min_orbit_radius <= ship.get_apoapsis(planet) <= max_orbit_radius: score = int(50 / ship.get_eccentricity(planet)) if min_orbit_radius <= ship.get_periapsis( planet) <= max_orbit_radius: pygame.draw.ellipse(screen, (0, 63, 0), success_orbit, success_orbit_thickness) # Debug Text debug_display_string = "Score: " + str(score) + " xAccel: " + str( ship.thrust_x) + " yAccel: " + str(ship.thrust_y) debug_display_loc = (10, 120) debug_display = font.render(debug_display_string, True, (0, 255, 0)) # Update ship trail points if ship.get_distance_from_point( ship.trail_points[-1]) >= ship.trail_res: ship.trail_points.append(ship.get_center()) # Trim the ship trail length if it gets too long if len(ship.trail_points) > ship.trail_len: ship.trail_points.pop(0) # Draw a trailing line from the ship if len(ship.trail_points) > 1 and ship.get_apoapsis( planet) <= planet_influence_height: pygame.draw.lines(screen, (0, 127, 0), False, ship.trail_points) pygame.draw.ellipse(screen, (127, 127, 0), min_orbit, width=1) pygame.draw.ellipse(screen, (127, 127, 0), max_orbit, width=1) # Blit sprites to screen ship.blit_sprite(screen) atm.blit_sprite(screen) planet.blit_sprite(screen) fuel_bar_width = 100 fuel_bar_height = 15 fuel_bar_dyn_width = fuel_bar_width * ship.fuel / 100.0 # Avoiding left-side fuel_bar bleed when fuel value is less than 1.0 if ship.fuel <= 1.0: fuel_bar_dyn_width = 1 fuel_bar_container = pygame.Rect((10, 30), (fuel_bar_width, fuel_bar_height)) fuel_bar = pygame.Rect((10, 30), (fuel_bar_dyn_width, fuel_bar_height)) # Blit text screen.blit(fuel_display, fuel_display_loc) screen.blit(debug_display, debug_display_loc) # Dynamic fuel bar color (Default: Green; Half-fuel: Yellow; Low-Fuel: Red) fuel_bar_color = (0, 255, 0) if 50.0 > ship.fuel >= 10.0: fuel_bar_color = (255, 255, 0) elif ship.fuel < 10.0: fuel_bar_color = (255, 0, 0) # Draw fuel bars pygame.draw.rect(screen, (127, 127, 127), fuel_bar_container) # Fuel bar disappears if fuel is depleted if ship.fuel != 0.0: pygame.draw.rect(screen, fuel_bar_color, fuel_bar) clock.tick(tick_time) # Update screen at end of loop pygame.display.update()
def __init__(self,Nmax,Xmax,X0=-150,Lambda=70, zenith=0,azimuth=0,impact_x=0,impact_y=0, ckarray='gg_t_delta_theta.npz'): """Create a shower with the given Gaisser-Hillas parameters and geometry Parameters: Nmax: the maximum size of the shower Xmax: the slant depth a maximum of the shower X0: the "starting point depth" for the shower Lambda: the growth rate of the shower zenith: the zenith angle of the shower azimuth: the azimuthal angle of the shower ckarray: the name of the file containing the CherenkovPhotonArray """ self.Nmax = Nmax self.Xmax = Xmax self.X0 = X0 self.Lambda = Lambda self.zenith = zenith self.azimuth = azimuth self.impact_x = impact_x self.impact_y = impact_y self.atmosphere = Atmosphere() self.ng_t_delta_Omega = CherenkovPhotonArray(ckarray) cq = np.cos(zenith) sq = np.sin(zenith) cp = np.cos(azimuth) sp = np.sin(azimuth) steps = np.append( np.append( np.linspace( 0, 1000,100,endpoint=False,dtype=float), np.linspace(1000,10000,90,endpoint=False,dtype=float)), np.linspace(10000,84000,75,dtype=float)) hs = (steps[1:]+steps[:-1])/2 dhs = steps[1:]-steps[:-1] nh = len(hs) rs = hs*sq/cq c = value('speed of light in vacuum') xs = np.empty_like(hs) xs[-1] = self.atmosphere.depth(hs[-1]) for i in range(nh-2,-1,-1): xs[i] = xs[i+1] + self.atmosphere.depth(hs[i],hs[i+1]) xs /= cq self.location = np.empty((nh,3),dtype=float) self.location[:,0] = self.impact_x + rs*cp self.location[:,1] = self.impact_y + rs*sp self.location[:,2] = hs self.heights = self.location[:,2] self.steps = dhs/cq self.times = hs/cq/c self.slant_depths = xs self.sizes = self.size(xs) self.stages = self.stage(xs) self.deltas = self.atmosphere.delta(hs) xh = zip(xs,hs) self.csizes = np.array([self.cherenkov_size(x,h) for x,h in xh]) xh = zip(xs,hs) self.cyield = np.array([self.cherenkov_yield(x,h) for x,h in xh]) self.cphots = self.steps * self.csizes * self.cyield nq = len(self.ng_t_delta_Omega.theta) self.cangs = np.empty((nh,nq),dtype=float) itd = zip(np.arange(nh),self.stages,self.deltas) for i,t,d in itd: self.cangs[i] = self.ng_t_delta_Omega.angular_distribution(t,d)
class ShowerCherenkov(): """A class to contain a instance of a shower with a Gaisser-Hillas parameterization, a given geometry, an atmosphere, and a table of Cherenkov photon angular distributions. An instance of this class will allow one to calculate the Cherenkov photon flux at a given point on the ground (or at some altitude) and the time distribution of those photons. """ def __init__(self,Nmax,Xmax,X0=-150,Lambda=70, zenith=0,azimuth=0,impact_x=0,impact_y=0, ckarray='gg_t_delta_theta.npz'): """Create a shower with the given Gaisser-Hillas parameters and geometry Parameters: Nmax: the maximum size of the shower Xmax: the slant depth a maximum of the shower X0: the "starting point depth" for the shower Lambda: the growth rate of the shower zenith: the zenith angle of the shower azimuth: the azimuthal angle of the shower ckarray: the name of the file containing the CherenkovPhotonArray """ self.Nmax = Nmax self.Xmax = Xmax self.X0 = X0 self.Lambda = Lambda self.zenith = zenith self.azimuth = azimuth self.impact_x = impact_x self.impact_y = impact_y self.atmosphere = Atmosphere() self.ng_t_delta_Omega = CherenkovPhotonArray(ckarray) cq = np.cos(zenith) sq = np.sin(zenith) cp = np.cos(azimuth) sp = np.sin(azimuth) steps = np.append( np.append( np.linspace( 0, 1000,100,endpoint=False,dtype=float), np.linspace(1000,10000,90,endpoint=False,dtype=float)), np.linspace(10000,84000,75,dtype=float)) hs = (steps[1:]+steps[:-1])/2 dhs = steps[1:]-steps[:-1] nh = len(hs) rs = hs*sq/cq c = value('speed of light in vacuum') xs = np.empty_like(hs) xs[-1] = self.atmosphere.depth(hs[-1]) for i in range(nh-2,-1,-1): xs[i] = xs[i+1] + self.atmosphere.depth(hs[i],hs[i+1]) xs /= cq self.location = np.empty((nh,3),dtype=float) self.location[:,0] = self.impact_x + rs*cp self.location[:,1] = self.impact_y + rs*sp self.location[:,2] = hs self.heights = self.location[:,2] self.steps = dhs/cq self.times = hs/cq/c self.slant_depths = xs self.sizes = self.size(xs) self.stages = self.stage(xs) self.deltas = self.atmosphere.delta(hs) xh = zip(xs,hs) self.csizes = np.array([self.cherenkov_size(x,h) for x,h in xh]) xh = zip(xs,hs) self.cyield = np.array([self.cherenkov_yield(x,h) for x,h in xh]) self.cphots = self.steps * self.csizes * self.cyield nq = len(self.ng_t_delta_Omega.theta) self.cangs = np.empty((nh,nq),dtype=float) itd = zip(np.arange(nh),self.stages,self.deltas) for i,t,d in itd: self.cangs[i] = self.ng_t_delta_Omega.angular_distribution(t,d) def __repr__(self): return "ShowerCherernkov(%.2e,%.0f,%.0f,%.0f,%.4f)"%( self.Nmax,self.Xmax,self.X0,self.Lambda,self.zenith) def size(self,X): """Return the size of the shower at a slant-depth X Parameters: X: the slant depth at which to calculate the shower size [g/cm2] Returns: N: the shower size """ x = (X-self.X0)/self.Lambda m = (self.Xmax-self.X0)/self.Lambda n = np.exp( m*(np.log(x)-np.log(m)) - (x-m) ) return self.Nmax * n def size_height(self,h): """Return the size of the shower at an elevation h Parameters: h: the height about sea-level [m] Returns: N: the shower size """ d1 = h/np.cos(self.zenith) X = self.atmosphere.depth(d1) if self.zenith==0 else \ self.atmosphere.slant_depth(self.zenith,d1) return self.size(X) def stage(self,X,X0=36.62): """Return the shower stage at a given slant-depth X. This is after Lafebre et al. Parameters: X: atmosphering slant-depth [g/cm2] X0: radiation length of air [g/cm2] Returns: t: shower stage """ return (X-self.Xmax)/X0 def cherenkov_size(self,X,h): """Return the size of the shower at a given slant-depth X that can produce Cherenkov radiation. Parameters: X: the slant depth at which to calculate the shower size [g/cm2] h: the height corresponding to the X [m] Returns: N: the size of the shower over the Cherenkov threshold """ delta = self.atmosphere.delta(h) Ec = CherenkovPhoton.cherenkov_threshold(delta) t = self.stage(X) fe = EnergyDistribution('Tot',t) fraction,_ = quad(fe.spectrum,np.log(Ec),35.) return fraction*self.size(X) def cherenkov_yield(self,X,h,lambda1=300.,lambda2=600.): """Return the number of Cherenkov photons produced at X and h. Parameters: X: the slant depth at which to calculate the Cherenkov yield [g/cm2] h: the height corresponding to the X [m] lambda1: the lower limit to the wavelength range [nm] lambda2: the upper limit to the wavelength range [nm] Returns: Nck: the Number of Cherenkov photons produced [1/m] """ alpha_over_hbarc = 370.e2 # per eV per m, from PDG hc = value('Planck constant in eV s') * \ value('speed of light in vacuum') E1 = hc/(lambda1*nano) E2 = hc/(lambda2*nano) dE = np.abs(E1-E2) delta = self.atmosphere.delta(h) q = CherenkovPhoton.cherenkov_angle(1.e12,delta) return alpha_over_hbarc * np.sin(q)**2 * dE
def pv_interface_timeseries(namelist, atm, panel): """Return the spatio-temporal PV power from atmospheric inputs, panel characteristics and PV code options""" ncol, nt = np.shape(atm.ta) # to get the dimensions of the input nthread = namelist.nthread # number of parallel processes power = [] # initialization of the power to be returned for n in range(ncol): # The code is called for each point of the domain, and a time series is returned atm_column = Atmosphere( *atm.get_column(n)) # extract one column from full atmosphere atm_column.set_sun_geometry( atm.sza[n, :], atm.saa[n, :], atm.sw_toa[n, :] ) # sun geometry for the point is taken from full sun geometry if panel.technology == "tracker": panel.set_geometry(atm_column.sza, atm_column.saa, atm_column) # # Multi-porcessing if nthread > 1: #---------------------------- import multiprocessing as mp #---------------------------- print("Multi-processes with %s processes" % nthread) outputs = [ mp.Queue() for x in range(nthread) ] # independent Queue objects to avoid out-of-order return at the end m = int(nt // nthread) # how to equally slice the input atmosphere processes = [] # Preparing all processes for k in range(nthread - 1): n1 = k * m # start of the slice n2 = (k + 1) * m # end of the slice atm_temp = Atmosphere(*atm_column.get_column_time_series( n1, n2 )) # new atmosphere built as temporal slice of full atmosphere atm_temp.set_sun_geometry( atm_column.sza[n1:n2], atm_column.saa[n1:n2], atm_column.sw_toa[n1:n2] ) # sun geometry set for this Atmosphere object processes += [ mp.Process(target=pv_prod, args=(namelist, atm_temp, panel, n2 - n1, outputs[k])) ] # processes call pv_production vi pv_prod interface and output the result to the dedicated Queue # Residual for last thread n1 = (nthread - 1) * m n2 = nt atm_temp = Atmosphere(*atm_column.get_column_time_series(n1, n2)) atm_temp.set_sun_geometry(atm_column.sza[n1:n2], atm_column.saa[n1:n2], atm_column.sw_toa[n1:n2]) processes += [ mp.Process(target=pv_prod, args=(namelist, atm_temp, panel, n2 - n1, outputs[-1])) ] # Start processes for p in processes: p.start() # Exit the completed processes for p in processes: p.join() # Concatenate all Queues power0 = np.array([]) for k, p in enumerate(processes): power0 = np.concatenate((power0, outputs[k].get())) # No multi-processing but splitting input according to maxcol if provided else: try: mt = namelist.maxlen # split full timeseries into slices of length maxlen m = int( nt // mt ) # this corresponds to m + 1 independent computations (nt = m * mt + res) power0 = np.array([]) print("Time series split in", m) for k in range(m): n1 = k * mt # start of the slice n2 = (k + 1) * mt # end of the slice atm_temp = Atmosphere( *atm_column.get_column_time_series(n1, n2) ) # new atmosphere built as temporal slice of full atmosphere atm_temp.set_sun_geometry( atm_column.sza[n1:n2], atm_column.saa[n1:n2], atm_column.sw_toa[n1:n2] ) # sun geometry set for this Atmosphere object power0 = np.concatenate( (power0, pv_power(namelist, atm_temp, panel, n2 - n1))) # Residual for last thread n1 = m * mt n2 = nt atm_temp = Atmosphere( *atm_column.get_column_time_series(n1, n2)) atm_temp.set_sun_geometry(atm_column.sza[n1:n2], atm_column.saa[n1:n2], atm_column.sw_toa[n1:n2]) power0 = np.concatenate( (power0, pv_power(namelist, atm_temp, panel, n2 - n1))) except: power0 = pv_power(namelist, atm_column, panel, nt) power += [power0] return np.array(power)