Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1958da6
Simplify Volume is only an absorber but not vacuum path in union master
Lomholy Jul 14, 2026
59fac42
Gather focus preprocessing into its own function
Lomholy Jul 14, 2026
be52fe5
transfer non isotropic wavevector rotation into its own function
Lomholy Jul 14, 2026
8d0c557
Make inhomogenous sampling loop more transparent by moving most logic…
Lomholy Jul 14, 2026
e45e91b
Move inhomogenous distance sampling to a function
Lomholy Jul 14, 2026
e322f23
Move p_interact behaviour and inhomogenous scattering point sampling …
Lomholy Jul 14, 2026
3128fbb
Move process choice after scattering into functions for p_interact an…
Lomholy Jul 14, 2026
78189d5
Move using forced length to scatter from to a separate function
Lomholy Jul 14, 2026
6550419
fix p being used instead of weight in dir length function
Lomholy Jul 14, 2026
4265eef
Fix pointer magic that resulted in only writing to the first value of…
Lomholy Jul 15, 2026
17a1be5
Assign scattering length for inhomogenous process
Lomholy Jul 15, 2026
4b687fc
Apply mccode-clangformat to Union_master.comp
Lomholy Jul 15, 2026
12041c4
Rename choose scattering point and direction, to reflect that it is o…
Lomholy Aug 2, 2026
abc7514
fix erroneus if statements inside process_needs_inhomogenous sampling
Lomholy Aug 2, 2026
c25937c
Add pointer dereference to scattering_event inside safeguard function
Lomholy Aug 2, 2026
0e155de
Remove erroneously placed symbolic link to Union master
Lomholy Aug 2, 2026
3f8a4f0
Add pointer dereference to scattering_event inside safeguard function…
Lomholy Aug 2, 2026
e45afc6
Change debug statement from using Volumes array to using correct Volu…
Lomholy Aug 2, 2026
c29d82f
regularize naming conventions across the functions for the logic path…
Lomholy Aug 2, 2026
f5dd909
Merge branch 'main' into union_refactor_scattering_while_loop
willend Aug 29, 2026
b430a0b
Merge branch 'main' into union_refactor_scattering_while_loop
willend Sep 7, 2026
8f0a606
Move the helper functions from Union master to union-lib.c
Lomholy Sep 16, 2026
25f8659
Use copying of values instead of pointer passing for doubles and ints…
Lomholy Sep 16, 2026
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
283 changes: 283 additions & 0 deletions mcstas-comps/share/union-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -9221,3 +9221,286 @@ void overwrite_if_empty(char *input_string, char *overwrite) {
}
}

//==============================================================================
//=========== Helper functions to Union_master =================================
//==============================================================================
int
volume_is_only_absorber (struct Volume_struct* Volume) {
// This function returns true if a volume does not have any physical processes
// and if the volume is not a vacuum.
if (!Volume->p_physics->number_of_processes && !Volume->p_physics->is_vacuum)
return 1;
return 0;
}

int
process_needs_inhomogenous_sampling (struct physics_struct* current_p_physics, struct scattering_process_struct* process) {
if (current_p_physics->sampling_points != 0) {
if (process->needs_cross_section_focus || process->sampling_points != -1)
return 1;
}
return 0;
}

void
adjust_abs_weight_factor (struct Volume_struct* Volume, double* my_sum_plus_abs,
double* length_to_boundary, double v_length, double time_to_boundary,
double* abs_weight_factor, int* abs_weight_factor_set) {
*my_sum_plus_abs = Volume->p_physics->my_a * (2200 / v_length);
*length_to_boundary = time_to_boundary * v_length;

*abs_weight_factor = exp (-Volume->p_physics->my_a * 2200 * time_to_boundary);
*abs_weight_factor_set = 1;

#ifdef Union_trace_verbal_setting
printf ("name of material: %s \n", Volume->name);
printf ("length to boundery = %f\n", length_to_boundary);
printf ("absorption cross section = %f\n", Volume->p_physics->my_a);
printf ("chance to get through this length of absorber: %f %%\n", 100 * exp (-Volume->p_physics->my_a * length_to_boundary));
#endif
}


void
transform_wavevector_into_local_coord_system (struct Volume_struct* Volume, Coords* wavevector_rotated, double (*k_rotated)[3], int p_index,
Coords* wavevector, Coords* ray_position_geometry) {

int non_isotropic_rot_index = Volume->p_physics->p_scattering_array[p_index].non_isotropic_rot_index;
*wavevector_rotated = rot_apply (Volume->geometry.process_rot_matrix_array[non_isotropic_rot_index], *wavevector);
coords_get (*wavevector_rotated, &(*k_rotated)[0], &(*k_rotated)[1], &(*k_rotated)[2]);

if (Volume->p_physics->p_scattering_array[p_index].needs_cross_section_focus == 1) {
// Prepare focus data using ray_position_geometry of forced scattering point which will be prepared if any process needs cross_section time
// focusing
Coords ray_position_geometry_rotated = rot_apply (Volume->geometry.process_rot_matrix_array[non_isotropic_rot_index], *ray_position_geometry);

int focus_data_index = Volume->geometry.focus_array_indices.elements[p_index];
struct focus_data_struct* this_focus_data = &Volume->geometry.focus_data_array.elements[focus_data_index];
this_focus_data->RayAim = coords_sub (this_focus_data->Aim, ray_position_geometry_rotated); // Aim vector for this ray

#ifdef Union_trace_verbal_setting
printf ("Checking process number : %d, it was not isotropic, so RayAim updated \n", p_index);
print_position (*ray_position_geometry, "ray_position_geometry");
print_position (ray_position_geometry_rotated, "ray_position_geometry_rotated");
print_position (this_focus_data->RayAim, "this_focus_data->RayAim");
#endif
}
}



void
move_and_aim_neutron (struct physics_struct* current_p_physics, int i, struct scattering_process_struct* process, _class_particle* _particle,
struct Volume_struct* Volume, int p_index, Coords* ray_velocity, Coords* ray_position, struct focus_data_struct* this_focus_data) {
// Transport neutron to place inside geometry
*ray_velocity = coords_set (_particle->vx, _particle->vy, _particle->vz);
// Find location of scattering point in master coordinate system without changing main position / velocity variables
Coords direction = coords_scalar_mult (*ray_velocity, 1.0 / length_of_position_vector (*ray_velocity));
Coords sampling_displacement = coords_scalar_mult (direction, current_p_physics->cumul_dists[i]);
Coords sampling_point = coords_add (*ray_position, sampling_displacement);
Coords sampling_point_geometry = coords_sub (sampling_point, Volume->geometry.center);
// Also focus the ray at this point, if the component needs focusing
if (process->needs_cross_section_focus) {
if (Volume->p_physics->p_scattering_array[p_index].non_isotropic_rot_index != -1) {
int non_isotropic_rot_index = Volume->p_physics->p_scattering_array[p_index].non_isotropic_rot_index;
sampling_point_geometry = rot_apply (Volume->geometry.process_rot_matrix_array[non_isotropic_rot_index], sampling_point_geometry);
}
this_focus_data->RayAim = coords_sub (this_focus_data->Aim, sampling_point_geometry);
}
// Calculate mu and probability
coords_get (sampling_point_geometry, &_particle->x, &_particle->y, &_particle->z);
}

int
mu_and_intersect_dist_safeguard (double mu_sum, double length_to_boundary, double safety_distance2, int* scattering_event) {
if (mu_sum < 1E-18) {
*scattering_event = 0;
return 0;
}
if (length_to_boundary < safety_distance2) {
*scattering_event = 0;
return 0;
}
return 1;
}


void
focus_in_cross_section_set_forced_point_and_dir (double* forced_length_to_scattering, double safety_distance, double safety_distance2, double length_to_boundary,
_class_particle* _particle, Coords* ray_velocity, Coords* ray_position_geometry, Coords* ray_position,
struct Volume_struct* Volume, struct focus_data_struct* this_focus_data) {
// Sample length_to_scattering in linear manner
*forced_length_to_scattering = safety_distance + rand01 () * (length_to_boundary - safety_distance2);

*ray_velocity = coords_set (_particle->vx, _particle->vy, _particle->vz); // Test for root cause
// Find location of scattering point in master coordinate system without changing main position / velocity variables
Coords direction = coords_scalar_mult (*ray_velocity, 1.0 / length_of_position_vector (*ray_velocity));
Coords scattering_displacement = coords_scalar_mult (direction, *forced_length_to_scattering);
Coords forced_ray_scattering_point = coords_add (*ray_position, scattering_displacement);
*ray_position_geometry = coords_sub (forced_ray_scattering_point, Volume->geometry.center); // ray_position relative to geometry center

// Calculate the aim for non isotropic processes
this_focus_data = &Volume->geometry.focus_data_array.elements[0];
this_focus_data->RayAim = coords_sub (this_focus_data->Aim, *ray_position_geometry); // Aim vector for this ray

#ifdef Union_trace_verbal_setting
printf ("Prepared for focus in cross section calculation in volume: %s \n", Volume->name);
printf ("forced_length_to_scattering =%lf \n", forced_length_to_scattering);
print_position (*ray_position, "ray_position");
print_position (direction, "direction");
print_position (scattering_displacement, "scattering_displacement");
print_position (forced_ray_scattering_point, "forced_ray_scattering_point");
print_position (*ray_position_geometry, "ray_position_geometry");
printf ("for isotropic processes this RayAim is used \n");
print_position (this_focus_data->RayAim, "this_focus_data->RayAim");
#endif
}

void
focus_in_cross_section_set_scat_length (double* length_to_scattering, double forced_length_to_scattering, double* weight, double length_to_boundary,
double my_sum_plus_abs) {
// Respect forced length to scattering chosen by process
*length_to_scattering = forced_length_to_scattering;
// Drawing between 0 and L from constant s = 1/L and should have been q = A*exp(-kz).
// Normalizing A*exp(-kz) over 0 to L: A = k/(1-exp(-k*L))
// Weight correction is ratio between s and q, L*A*exp(-kz) = L*k*exp(-kz)/(1-exp(-Lk))
*weight *= length_to_boundary * my_sum_plus_abs * exp (-*length_to_scattering * my_sum_plus_abs) / (1.0 - exp (-length_to_boundary * my_sum_plus_abs));
#ifdef Union_trace_verbal_setting
printf ("Used forced length to scattering, %lf \n", length_to_scattering);
#endif
}


void
inhomogenous_set_cumul_dist_array (struct physics_struct* current_p_physics, int i) {
current_p_physics->cumul_dists[i] = (i > 0) ? current_p_physics->cumul_dists[i - 1] + current_p_physics->dist : current_p_physics->dist / 2;
}

void
inhomogenous_sample_transmission_probability (struct physics_struct* current_p_physics, struct Volume_struct* Volume, double* real_transmission_probability,
double v_length) {
// Calculate the probabilities and then add them cumulatively
memset (current_p_physics->total_mus, 0, sizeof (double) * current_p_physics->sampling_points);

for (int i = 0; i < Volume->p_physics->number_of_processes; i++) {
struct scattering_process_struct* process_i = &Volume->p_physics->p_scattering_array[i];
if (process_i->needs_numerical_integration != 1)
for (int j = 0; j < current_p_physics->sampling_points; j++) {
current_p_physics->total_mus[j] += current_p_physics->mus[i][0] * current_p_physics->dist;
}
else
for (int j = 0; j < current_p_physics->sampling_points; j++) {
current_p_physics->total_mus[j] += current_p_physics->mus[i][j] * current_p_physics->dist;
}
}
// for (int i =0;i<current_p_physics->sampling_points;i++){
// printf("\nTotalmu=%g\tinteger=%d\n", current_p_physics->total_mus[i], i);
// }
double mu_at_speed = Volume->p_physics->my_a * (2200 / v_length);
for (int j = 0; j < current_p_physics->sampling_points; j++) {
current_p_physics->total_mus[j] += mu_at_speed * current_p_physics->dist;
}
double trans_prob;
for (int i = 0; i < current_p_physics->sampling_points; i++) {
trans_prob = exp (-current_p_physics->total_mus[i]);
if (i == 0)
current_p_physics->cumul_transmission_prob[i] = trans_prob;
else
current_p_physics->cumul_transmission_prob[i] = current_p_physics->cumul_transmission_prob[i - 1] * trans_prob;
}

*real_transmission_probability = current_p_physics->cumul_transmission_prob[current_p_physics->sampling_points - 1];
}

double
inhomogenous_sample_scattering_point (struct Volume_struct* Volume, struct physics_struct* current_p_physics, double* abs_weight_factor, double v_length,
double safety_distance, int* selected_sampling) {

// Numerical integration happens, and therefore we must choose between the different samples
// We do this by drawing a random number between 0 and max cumul prob,
// and then seeing which cumul prob is the first to include it.
*abs_weight_factor = 1;
double mu_at_speed = Volume->p_physics->my_a * (2200 / v_length);
double pseudo_rand = rand01 () * (1 - current_p_physics->cumul_transmission_prob[current_p_physics->sampling_points - 1]);
for (int i = 0; i < current_p_physics->sampling_points; i++) {
// printf("\nCumul trans prob = %g\t pseudo rand = %g\n", current_p_physics->cumul_transmission_prob[i], pseudo_rand);
if (pseudo_rand >= 1 - current_p_physics->cumul_transmission_prob[i])
continue;
*selected_sampling = i;
break;
}
*abs_weight_factor
*= (current_p_physics->total_mus[*selected_sampling] - mu_at_speed * current_p_physics->dist) / current_p_physics->total_mus[*selected_sampling];

// printf("\nSelected_sampling = %d\n", selected_sampling);

// printf("dist i = %g\tdist=%g\n", dist_i, dist);
double sampled_dist = safety_distance
- log (1.0 - rand01 () * (1.0 - exp (-current_p_physics->total_mus[*selected_sampling])))
/ current_p_physics->total_mus[*selected_sampling] * current_p_physics->dist;
return current_p_physics->cumul_dists[*selected_sampling] - current_p_physics->dist / 2 + sampled_dist;
}


void
inhomogenous_choose_process (struct physics_struct* current_p_physics, struct Volume_struct* Volume, double* culmative_probability, double mc_prop,
double my_sum, int selected_sampling, int* selected_process) {
for (int i = 0; i < Volume->p_physics->number_of_processes; i++) {
*culmative_probability += current_p_physics->mus[i][selected_sampling] / my_sum;
if (*culmative_probability > mc_prop) {
*selected_process = i;
break;
}
}
}


int
p_interact_is_set (struct Volume_struct* Volume) {
if (Volume->geometry.geometry_p_interact != 0) {
return 1;
}
return 0;
}

void
p_interact_check_scattering_event (struct Volume_struct* Volume, int* scattering_event, double* weight, double real_transmission_prob) {
double mc_transmission_prob = 1 - Volume->geometry.geometry_p_interact;
*scattering_event = rand01 () > mc_transmission_prob;
if (*scattering_event) {
// Scattering event happens, this is the correction for the weight
*weight *= (1.0 - real_transmission_prob) / (1.0 - mc_transmission_prob);
} else {
// Scattering event does not happen, this is the appropriate correction
*weight *= real_transmission_prob / mc_transmission_prob;
}
}

void
p_interact_select_process (struct Volume_struct* Volume, double* my_trace_fraction_control, double* my_trace, double* total_process_interact,
double* culmative_probability, double* mc_prop, double* weight, double my_sum, int* selected_process) {
// Interact_fraction is used to influence the choice of process in this material
*mc_prop = rand01 ();
*culmative_probability = 0;
*total_process_interact = 1.0;

// If any of the processes have probability 0, they are excluded from the selection
for (int i = 0; i < Volume->p_physics->number_of_processes; i++) {
if (my_trace[i] < 1E-18) {
// When this happens, the total force probability is corrected and the probability for this particular instance is set to 0
*total_process_interact -= Volume->p_physics->p_scattering_array[i].process_p_interact;
my_trace_fraction_control[i] = 0;
// In cases where my_trace is not zero, the forced fraction is still used.
} else
my_trace_fraction_control[i] = Volume->p_physics->p_scattering_array[i].process_p_interact;
}
// Randomly select a process using the weights stored in my_trace_fraction_control divided by total_process_interact
for (int i = 0; i < Volume->p_physics->number_of_processes; i++) {
*culmative_probability += my_trace_fraction_control[i] / *total_process_interact;
if (*culmative_probability > *mc_prop) {
*selected_process = i;
*weight *= (my_trace[i] / my_sum) * (*total_process_interact / my_trace_fraction_control[i]);
break;
}
}
}
Loading
Loading