Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature light lut #247

Merged
merged 7 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 39 additions & 28 deletions cli/simulate_pixels.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def run_simulation(input_filename,
store memory snapshot information
"""
# Define a nested function to save the results
def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_only=False):
def save_results(event_times, results, i_trig, i_mod=-1, light_only=False):
'''
results is a dictionary with the following keys

Expand All @@ -156,8 +156,6 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
- light_waveforms_true_track_id: true track ids for each tick in each waveform
- light_waveforms_true_photons: equivalent pe for each track at each tick in each waveform

returns is_first_batch = False

Note: can't handle empty inputs
'''
for key in list(results.keys()):
Expand Down Expand Up @@ -190,7 +188,6 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
results['traj_pixel_map'],
output_filename, # defined earlier in script
uniq_event_times,
is_first_batch=is_first_batch,
light_trigger_times=light_trigger_times,
light_trigger_event_id=light_trigger_event_ids,
light_trigger_modules=light_trigger_modules,
Expand Down Expand Up @@ -218,9 +215,6 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
results['light_waveforms_true_photons'],
i_trig,
i_mod)
if is_first_batch:
is_first_batch = False
return is_first_batch
###########################################################################################

print(LOGO)
Expand Down Expand Up @@ -716,8 +710,15 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
# Set up light simulation data objects and calculate the optical responses
if light.LIGHT_SIMULATED:
n_light_channel = int(light.N_OP_CHANNEL/len(mod_ids)) if mod2mod_variation else light.N_OP_CHANNEL
# if light.LIGHT_TRIG_MODE == 0:
# light_sim_dat = np.zeros([len(tracks), n_light_channel],
# dtype=[('segment_id', 'u4'), ('n_photons_det','f4'),('t0_det','f4')])
# else: # light.LIGHT_TRIG_MODE == 1
# light_sim_dat = np.zeros([len(tracks), n_light_channel],
# dtype=[('segment_id', 'u4'), ('n_photons_det','f4')])
#
light_sim_dat = np.zeros([len(tracks), n_light_channel],
dtype=[('segment_id', 'u4'), ('n_photons_det','f4'),('t0_det','f4')])
dtype=[('segment_id', 'u4'), ('n_photons_det','f4'),('t0_det','f4')])
light_sim_dat['segment_id'] = segment_ids[..., np.newaxis]
track_light_voxel = np.zeros([len(tracks), 3], dtype='i4')

Expand All @@ -742,6 +743,9 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
mask = lut['vis'] > 0
lut['vis'][~mask] = lut['vis'][mask].min()

# get length of the t0 time profile
t0_profile_length = lut['time_dist'].shape[-1]

lut = to_device(lut)

if mod2mod_variation:
Expand Down Expand Up @@ -809,7 +813,8 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
trajectory_ids_arr = cp.asarray(trajectory_ids)

# We divide the sample in portions that can be processed by the GPU
is_first_batch = True
is_new_event = True
event_id_buffer = -1
logger.start()
logger.take_snapshot([0])
i_batch = 0
Expand All @@ -827,25 +832,31 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
evt_tracks = track_subset
#first_trk_id = np.argmax(batch_mask) # first track in batch

# this relies on that batching is done in the order of events
if ievd > event_id_buffer:
is_new_event = True
else:
is_new_event = False
this_event_time = [event_times[ievd % sim.MAX_EVENTS_PER_FILE]]
# forward sync packets
if this_event_time[0] - sync_start >= 0:
sync_times = cp.arange(sync_start, this_event_time[0]+1, fee.CLOCK_RESET_PERIOD * fee.CLOCK_CYCLE) #us
#PSS Sync also resets the timestamp in the PACMAN controller, so all of the timestamps in the packs should read 1e7 (for PPS)
sync_times_export = cp.full( sync_times.shape, fee.CLOCK_RESET_PERIOD * fee.CLOCK_CYCLE)
if len(sync_times) > 0:
fee.export_sync_to_hdf5(output_filename, sync_times_export, i_mod)
sync_start = sync_times[-1] + fee.CLOCK_RESET_PERIOD * fee.CLOCK_CYCLE
# beam trigger is only forwarded to one specific pacman (defined in fee)
if (light.LIGHT_TRIG_MODE == 0 or light.LIGHT_TRIG_MODE == 1) and (i_mod == trig_module or i_mod == -1):
fee.export_timestamp_trigger_to_hdf5(output_filename, this_event_time, i_mod)
if is_new_event:
# forward sync packets
if this_event_time[0] - sync_start >= 0: # this is duplicate to "is_new_event"
sync_times = cp.arange(sync_start, this_event_time[0]+1, fee.CLOCK_RESET_PERIOD * fee.CLOCK_CYCLE) #us
#PSS Sync also resets the timestamp in the PACMAN controller, so all of the timestamps in the packs should read 1e7 (for PPS)
sync_times_export = cp.full( sync_times.shape, fee.CLOCK_RESET_PERIOD * fee.CLOCK_CYCLE)
if len(sync_times) > 0:
fee.export_sync_to_hdf5(output_filename, sync_times_export, i_mod)
sync_start = sync_times[-1] + fee.CLOCK_RESET_PERIOD * fee.CLOCK_CYCLE
# beam trigger is only forwarded to one specific pacman (defined in fee)
if (light.LIGHT_TRIG_MODE == 0 or light.LIGHT_TRIG_MODE == 1) and (i_mod == trig_module or i_mod == -1):
fee.export_timestamp_trigger_to_hdf5(output_filename, this_event_time, i_mod)

# generate light waveforms for null signal in the module
# so we can have light waveforms in this case (if the whole detector is triggered together)
if len(track_subset) == 0:
if light.LIGHT_SIMULATED and (light.LIGHT_TRIG_MODE == 0 or light.LIGHT_TRIG_MODE == 1):
null_light_results_acc['light_event_id'].append(cp.full(1, ievd)) # one event
save_results(event_times, is_first_batch, null_light_results_acc, i_trig, i_mod, light_only=True)
save_results(event_times, null_light_results_acc, i_trig, i_mod, light_only=True)
i_trig += 1 # add to the trigger counter
del null_light_results_acc['light_event_id']
# Nothing to simulate for charge readout?
Expand Down Expand Up @@ -886,7 +897,7 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
if not active_pixels.shape[1] or not neighboring_pixels.shape[1]:
if light.LIGHT_SIMULATED and (light.LIGHT_TRIG_MODE == 0 or light.LIGHT_TRIG_MODE == 1):
null_light_results_acc['light_event_id'].append(cp.full(1, ievd)) # one event
save_results(event_times, is_first_batch, null_light_results_acc, i_trig, i_mod, light_only=True)
save_results(event_times, null_light_results_acc, i_trig, i_mod, light_only=True)
i_trig += 1 # add to the trigger counter n_max_pixels
del null_light_results_acc['light_event_id']
continue
Expand Down Expand Up @@ -941,7 +952,7 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
if not unique_pix.shape[0]:
if light.LIGHT_SIMULATED and (light.LIGHT_TRIG_MODE == 0 or light.LIGHT_TRIG_MODE == 1):
null_light_results_acc['light_event_id'].append(cp.full(1, ievd)) # one event
save_results(event_times, is_first_batch, null_light_results_acc, i_trig, i_mod, light_only=True)
save_results(event_times, null_light_results_acc, i_trig, i_mod, light_only=True)
i_trig += 1 # add to the trigger counter
del null_light_results_acc['light_event_id']
continue
Expand Down Expand Up @@ -1097,7 +1108,7 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
light_sim.sum_light_signals[BPG, TPB](
selected_tracks, track_light_voxel[batch_mask][itrk:itrk+sim.BATCH_SIZE], selected_track_id,
light_inc, op_channel, lut, light_t_start, light_sample_inc, light_sample_inc_true_track_id,
light_sample_inc_true_photons, sorted_indices)
light_sample_inc_true_photons, sorted_indices, t0_profile_length)
RangePop()
if light_sample_inc_true_track_id.shape[-1] > 0 and cp.any(light_sample_inc_true_track_id[...,-1] != -1):
warnings.warn(f"Maximum number of true segments ({light.MAX_MC_TRUTH_IDS}) reached in backtracking info, consider increasing MAX_MC_TRUTH_IDS (larndsim/consts/light.py)")
Expand Down Expand Up @@ -1153,10 +1164,10 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o

if len(results_acc['event_id']) >= sim.WRITE_BATCH_SIZE:
if len(results_acc['event_id']) > 0 and len(np.concatenate(results_acc['event_id'], axis=0)) > 0:
is_first_batch = save_results(event_times, is_first_batch, results_acc, i_trig, i_mod, light_only=False)
save_results(event_times, results_acc, i_trig, i_mod, light_only=False)
i_trig += 1 # add to the trigger counter
elif len(results_acc['light_event_id']) > 0 and len(np.concatenate(results_acc['light_event_id'], axis=0)) > 0:
is_first_batch = save_results(event_times, is_first_batch, results_acc, i_trig, i_mod, light_only=True)
save_results(event_times, results_acc, i_trig, i_mod, light_only=True)
i_trig += 1 # add to the trigger counter
results_acc = defaultdict(list) # reinitialize after each save_results

Expand All @@ -1166,10 +1177,10 @@ def save_results(event_times, is_first_batch, results, i_trig, i_mod=-1, light_o
RangePush('save_results')
# Always save results after last iteration
if len(results_acc['event_id']) > 0 and len(np.concatenate(results_acc['event_id'], axis=0)) > 0:
is_first_batch = save_results(event_times, is_first_batch, results_acc, i_trig, i_mod, light_only=False)
save_results(event_times, results_acc, i_trig, i_mod, light_only=False)
i_trig += 1 # add to the trigger counter
elif len(results_acc['light_event_id']) > 0 and len(np.concatenate(results_acc['light_event_id'], axis=0)) > 0:
is_first_batch = save_results(event_times, is_first_batch, results_acc, i_trig, i_mod, light_only=True)
save_results(event_times, results_acc, i_trig, i_mod, light_only=True)
i_trig += 1 # add to the trigger counter
results_acc = defaultdict(list) # reinitialize after each save_results
RangePop()
Expand Down
11 changes: 7 additions & 4 deletions larndsim/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ module0:
# Path to light LUT at NERSC
# Web portal: https://portal.nersc.gov/project/dune/data/2x2/simulation/larndsim_data/light_LUT/
# Alternatively low granularity Mod0 based light LUT in larndsim, lightLUT.npz
LIGHT_LUT: /global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod0.npz
LIGHT_LUT: /global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod0_06052024_time_norm.npz
LIGHT_DET_NOISE: light_noise-module0.npy
LIGHT_SIMULATED: True

# 2x2 (same module configuration), beam
2x2_no_modvar: &2x2main
Expand All @@ -19,8 +20,9 @@ module0:
# Path to light LUT at NERSC
# Web portal: https://portal.nersc.gov/project/dune/data/2x2/simulation/larndsim_data/light_LUT/
# Alternatively low granularity Mod0 based light LUT in larndsim, lightLUT.npz
LIGHT_LUT: /global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod123.npz
LIGHT_LUT: /global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod123_06052024_time_norm.npz
LIGHT_DET_NOISE: 4Mod_LNoise_Mod1_2fftx192_MR5-ish.npy
LIGHT_SIMULATED: True
MOD2MOD_VARIATION: False

# 2x2 (same module configuration), mpvmpr
Expand All @@ -46,7 +48,8 @@ module0:
# Path to light LUT at NERSC
# Web portal: https://portal.nersc.gov/project/dune/data/2x2/simulation/larndsim_data/light_LUT/
# Alternatively low granularity Mod0 based light LUT in larndsim, lightLUT.npz
LIGHT_LUT: [/global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod0_06052024.npz, /global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod123_06052024.npz]
# LIGHT_LUT: [/sdf/data/neutrino/2x2/light_lut/lightLUT_Mod0_06052024_time_norm.npz, /sdf/data/neutrino/2x2/light_lut/lightLUT_Mod123_06052024_time_norm.npz]
LIGHT_LUT: [/global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod0_06052024_time_norm.npz, /global/cfs/cdirs/dune/www/data/2x2/simulation/larndsim_data/light_LUT/lightLUT_Mod123_06052024_time_norm.npz]
LIGHT_LUT_ID: [0, 1, 1, 1]
MOD2MOD_VARIATION: True

Expand All @@ -73,6 +76,6 @@ ndlar:
PIXEL_LAYOUT: multi_tile_layout-3.0.40.yaml
DET_PROPERTIES: ndlar-module.yaml
RESPONSE: response_38.npy
LIGHT_SIMULATED: 0
LIGHT_SIMULATED: True
LIGHT_LUT: ''
LIGHT_DET_NOISE: ''
4 changes: 2 additions & 2 deletions larndsim/consts/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ def set_light_properties(detprop_file):
LIGHT_OSCILLATION_PERIOD = float(detprop.get('light_oscillation_period', LIGHT_OSCILLATION_PERIOD))
impulse_model_filename = str(detprop.get('impulse_model', ''))
if impulse_model_filename and SIPM_RESPONSE_MODEL == 1:
print('Light impulse model:', impulse_model_filename)
try:
# first try to load from current directory
IMPULSE_MODEL = np.load(impulse_model_filename)
Expand All @@ -151,7 +150,8 @@ def set_light_properties(detprop_file):
try:
IMPULSE_MODEL = np.load(os.path.join(os.path.dirname(__file__), '../../') + impulse_model_filename)
except FileNotFoundError:
print("Impulse model file not found:", impulse_model_filename)
SIPM_RESPONSE_MODEL = 0
print("Impulse model file not found:", impulse_model_filename, ", and setting SIPM_RESPONSE_MODEL to 0 (RLC model).")
IMPULSE_TICK_SIZE = float(detprop.get('impulse_tick_size', IMPULSE_TICK_SIZE))

OP_CHANNEL_PER_TRIG = int(detprop.get('op_channel_per_det', OP_CHANNEL_PER_TRIG))
Expand Down
3 changes: 3 additions & 0 deletions larndsim/detector_properties/2x2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ tpc_to_op_channel:
singlet_fraction: 0.3
tau_s: 0.001 # us
tau_t: 1.530 # us
enable_lut_smearing: True # sample from the time profile for photon arrival time (True) or use average photon arrival time (False)
op_channel_efficiency: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
light_gain: [-7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0, -7.0] # ADC us / PE
light_det_noise_sample_spacing: 0.016 # us
Expand All @@ -61,5 +62,7 @@ light_window: [0, 16] # us
light_trig_window: [1.6, 14.4] # us
light_digit_sample_spacing: 0.016 # us
light_nbit: 14
sipm_response_model: 1 # 0: RLC model; 1: measured
impulse_model: 'larndsim/bin/sipm_impulse.npy'
max_light_truth_ids: 50 # set to zero to disable light truth backtracking
mc_truth_threshold: 0.1 # pe/us
Loading