1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
|
/*******************************************************************************
*
* McStas, neutron ray-tracing package
* Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* 1D Antiferromagnetic Heisenberg chain
*
* %D
*
* 1D Antiferromagnetic Heisenberg chain
*
* Part of the Union components, a set of components that work together and thus
* sperates geometry and physics within McStas.
* The use of this component requires other components to be used.
*
* 1) One specifies a number of processes using process components like this one
* 2) These are gathered into material definitions using Union_make_material
* 3) Geometries are placed using Union_box / Union_cylinder, assigned a material
* 4) A Union_master component placed after all of the above
*
* Only in step 4 will any simulation happen, and per default all geometries
* defined before the master, but after the previous will be simulated here.
*
* There is a dedicated manual available for the Union_components
*
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* unit_cell_volume: [AA^3] Unit cell volume (set either unit_cell_volume or number density)
* number_density: [1/AA^3] Number of scatteres per volume
* J_interaction: [meV] Exchange constant
* A_constant: [unitless] Constant from Müller paper 1981, probably somewhere between 1 and 1.5
* atom_distance: [AA] Distance between atom's in chain
* packing_factor: [1] How dense is the material compared to optimal 0-1
* interact_fraction: [1] How large a part of the scattering events should use this process 0-1 (sum of all processes in material = 1)
* init: [string] name of Union_init component (typically "init", default)
*
* CALCULATED PARAMETERS:
* Template_storage // Important to update this output paramter
* effective_my_scattering // Variable used in initialize
*
*
*
* %L
*
* %E
******************************************************************************/
DEFINE COMPONENT AF_HB_1D_process // Remember to change the name of process here
SETTING PARAMETERS(atom_distance=1, number_density=0, unit_cell_volume=0, A_constant=1, J_interaction=1, packing_factor=1, interact_fraction=-1, string init="init")
SHARE
%{
#ifndef Union
#error "The Union_init component must be included before this AF_HB_1D_process component"
#endif
struct physical_constants_AF_HB_1D{
// List of constants to be passed between functions
double a; // Scattering length
double J; // Interaction strength
double A; // Constant from Müller conjecture
double Atom_dist; // Distance between atoms
double q2unit_less;
double integral_pre_factor;
//double weight_factor;
};
// Very important to add a pointer to this struct in the union-lib.c file
struct AF_HB_1D_physics_storage_struct{
// Variables that needs to be transfered between any of the following places:
// The initialize in this component
// The function for calculating my
// The function for calculating scattering
double current_total_cross_section;
double number_density_internal;
struct physical_constants_AF_HB_1D physical_constants;
};
double eval_Sqw_AF_HB_1D(double q, double w, struct physical_constants_AF_HB_1D *constants) {
// Evaluate Sqw, here q should be in units of "rad", from normalizing with atom distance
// w is here an energy transfer in meV
//printf("Sqw called with q = %f and w = %e\n",q,w);
if (w > 0.5*PI*constants->J*fabs(sin(q)) ) {
if (w < PI*constants->J*fabs(sin(0.5*q)) ) {
//return constants->A/(sqrt(w*w-0.25*PI*PI*constants->J*constants->J*fabs(sin(q))*fabs(sin(q))));
// The result is currently returned in 1/meV.
double result = constants->A/(sqrt(w*w-0.25*PI*PI*constants->J*constants->J*fabs(sin(q))*fabs(sin(q))));
//printf("Sqw returns = %e\n",result);
return result;
} else return 0;
} else return 0;
}
double numerical_integral_AF_HB_1D(double *k_initial,struct physical_constants_AF_HB_1D *constants) {
// returns cross section for this k_i
double k_i_length;
k_i_length = sqrt(k_initial[0]*k_initial[0]+k_initial[1]*k_initial[1]+k_initial[2]*k_initial[2]);
int theta_step,N_theta_steps=200;
double theta,theta_step_size,q_z;
// Theta from 0 to Pi
theta_step_size = PI/N_theta_steps;
int E_transfer_step,N_E_transfer_steps=200;
double E_transfer,E_transfer_step_size;
// E_transfer [meV]
// k [Å^-1]
// Avoid transfering more energy than the ray have
if (PI*constants->J < k_i_length*k_i_length*K2V*K2V*VS2E)
// Largest omega necessary: PI*J
E_transfer_step_size = PI*constants->J/N_E_transfer_steps;
else E_transfer_step_size = k_i_length*k_i_length*K2V*K2V*VS2E/N_E_transfer_steps;
double t_value; // length of final wavevector for certain theta/E_transfer choice.
double integral_value = 0;
for (theta_step=0;theta_step<N_theta_steps;theta_step++) {
theta = theta_step*theta_step_size;
for (E_transfer_step=0;E_transfer_step<N_E_transfer_steps;E_transfer_step++) {
E_transfer = E_transfer_step*E_transfer_step_size;
t_value = sqrt(k_i_length*k_i_length-E_transfer*SE2V*V2K*SE2V*V2K);
q_z = (k_initial[2] - t_value*cos(theta))*constants->q2unit_less;
// May consider to translate q_z into first Brilluion zone, but not necessary
integral_value += t_value*eval_Sqw_AF_HB_1D(q_z,E_transfer,constants)*sin(theta)*theta_step_size*E_transfer_step_size;
}
}
//printf("integral_value after loop = %e\n",integral_value);
return 2*PI*constants->integral_pre_factor*integral_value/k_i_length;
}
// Function for calculating my, the inverse penetration depth (for only this scattering process).
// The input for this function and its order may not be changed, but the names may be updated.
int AF_HB_1D_physics_my(double *my, double *k_initial, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {
// *k_initial is a pointer to a simple vector with 3 doubles, k[0], k[1], k[2] which describes the wavevector
struct physical_constants_AF_HB_1D *p_constants = &data_transfer.pointer_to_a_AF_HB_1D_physics_storage_struct->physical_constants;
// Need to numerically solve integral to calculate cross section
double cross_section = numerical_integral_AF_HB_1D(k_initial,p_constants);
//printf("Returned cross_section = %e\n",cross_section);
data_transfer.pointer_to_a_AF_HB_1D_physics_storage_struct->current_total_cross_section = cross_section;
// Number density given in units of 1E10*Å^-3, cross section in Å^2, returns 1/m.
*my = data_transfer.pointer_to_a_AF_HB_1D_physics_storage_struct->number_density_internal*cross_section;
//printf("Returned my = %e\n",*my);
return 1;
};
// Function that provides description of a basic scattering event.
// Do not change the
int AF_HB_1D_physics_scattering(double *k_final, double *k_initial, double *weight, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {
// Unpack the physical constants struct
struct physical_constants_AF_HB_1D *p_constants = &data_transfer.pointer_to_a_AF_HB_1D_physics_storage_struct->physical_constants;
// k_final and k_initial are passed as pointers to double vector[3]
double k_length = sqrt(k_initial[0]*k_initial[0]+k_initial[1]*k_initial[1]+k_initial[2]*k_initial[2]);
Coords r_out;
// choose a random direction vector in the specified focusing area
double solid_angle;
focus_data->focusing_function(&r_out,&solid_angle,focus_data);
NORM(r_out.x,r_out.y,r_out.z);
// random energy transfer is now selected between 0 and PI*J
// Need to ensure not transfering more energy than the ray has
// Kim's proposal to use uniform E_transfer sampling, but will be outside of S(q,w) non-zero region often
double E_transfer,E_range,E_i = k_length*k_length*K2V*K2V*VS2E;
if (PI*p_constants->J < E_i)
E_range = PI*p_constants->J;
else
E_range = E_i;
E_transfer = E_range*rand01();
double k_f = sqrt(k_length*k_length - E_transfer*SE2V*V2K*SE2V*V2K);
//printf("---- E in scatter function --- \n");
//printf("E_initial = %e \n",k_length*k_length*hbar*hbar*0.5/m_neutron);
//printf("E_initial = %e \n",k_length*k_length*K2V*K2V*VS2E);
//printf("E_initial = %e \n",E_i);
//printf("E_transfer = %e \n",E_transfer);
//printf("E_final = %e \n",k_f*k_f*K2V*K2V*VS2E);
k_final[0] = r_out.x*k_f; k_final[1] = r_out.y*k_f; k_final[2] = r_out.z*k_f;
//printf("k_final = (%lf,%lf,%lf)\n",k_final[0],k_final[1],k_final[2]);
double q[3];
q[0] = k_initial[0] - k_final[0];
q[1] = k_initial[1] - k_final[1];
q[2] = k_initial[2] - k_final[2];
double sqw_value;
if ((sqw_value = eval_Sqw_AF_HB_1D(p_constants->q2unit_less*q[2],E_transfer,p_constants)) > 0) {
// Weight factor constants done in initialize, solid angle/ wavevector lengths and swq missing.
//printf("Scattering: Sqw(%f,%f) = %E \n",p_constants->q2unit_less*q[2],E_transfer,sqw_value);
*weight *= p_constants->integral_pre_factor*E_range*solid_angle*k_f/k_length*sqw_value/data_transfer.pointer_to_a_AF_HB_1D_physics_storage_struct->current_total_cross_section;
return 1;
} else return 0; // Ray absorbed as Sqw == 0
// A pointer to k_final is returned, and the wavevector will be set to k_final after a scattering event
//return 1; // return 1 is sucess, return 0 is failure, and the ray will be absorbed.
// failure should not happen, as this function will only be called when
// the cross section for the current k_initial is above zero.
// There is access to the data_transfer from within the scattering function
// In this case the only variable is my, but it could be read by:
// double my = data_transfer.pointer_to_a_Template_physics_storage_struct->my_scattering;
// One can assume that if the scattering function is running, the my fuction was
// executed just before and for the same k_initial.
};
// These lines help with future error correction, and tell other Union components
// that at least one process have been defined.
#ifndef PROCESS_DETECTOR
// Obsolete
//struct pointer_to_global_process_list global_process_list = {0,NULL};
#define PROCESS_DETECTOR dummy
#endif
#ifndef PROCESS_AF_HB_1D_DETECTOR
#define PROCESS_AF_HB_1D_DETECTOR dummy
#endif
%}
DECLARE
%{
// Declare for this component, to do calculations on the input / store in the transported data
struct AF_HB_1D_physics_storage_struct AF_HB_1D_storage; // Replace template with your own name here
// Variables needed in initialize of this function.
double effective_my_scattering;
// Needed for transport to the main component, will be the same for all processes
struct global_process_element_struct global_process_element;
struct scattering_process_struct This_process;
%}
INITIALIZE
%{
if ((unit_cell_volume==0) && (number_density==0)) {
printf("ERROR in Union process AF_HB_1D named %s, set either unit_cell_volume or number_density.\n",NAME_CURRENT_COMP);
exit(1);
}
if ((unit_cell_volume>0) && (number_density>0)) {
printf("ERROR in Union process AF_HB_1D named %s, only set one of unit_cell_volume or number_density.\n",NAME_CURRENT_COMP);
exit(1);
}
if (J_interaction < 0) {
printf("ERROR in Union process AF_HB_1D named %s, exchange constant J_interaction needs to be positive (AF).\n",NAME_CURRENT_COMP);
exit(1);
}
if (unit_cell_volume>0) number_density = 1/unit_cell_volume; // Unit of 1/Å^3
// Packing factor taken into account by decreasing number density
number_density = number_density*packing_factor;
AF_HB_1D_storage.physical_constants.q2unit_less = atom_distance;
AF_HB_1D_storage.number_density_internal = number_density*1E10; // Unit conversion so n*sigma gets units of 1/m.
AF_HB_1D_storage.physical_constants.J = J_interaction;
AF_HB_1D_storage.physical_constants.A = A_constant;
AF_HB_1D_storage.physical_constants.Atom_dist = atom_distance;
// prefactor 0.5
// Integral pre factors is (gyromagnetic ratio * r_0 * g)^2
// gyromagnetic ratio = -1.913 [unitless]
// classical electron radius = 2.817940E-15 [m] -> Å = 2.817940E-5 [Å]
// g factor for electron = 2.0023 [unitless]
//AF_HB_1D_storage.physical_constants.integral_pre_factor = 1.832471E8*1.832471E8*2.81794E-15*2.81794E-15*2.0023*2.0023;
AF_HB_1D_storage.physical_constants.integral_pre_factor = 0.5*1.913*1.913*2.81794E-5*2.81794E-5*2.0023*2.0023;
// Factor: 0.5
// Focusing area correction: done in direction choice
// Energy range correction: done in code
// Scattering density: 1/V0 = number_density
// Constant term in diff cross section: integral_pre_factor (calculated in initialize)
// 1E10 to make units of weight factor from Å to m.
//AF_HB_1D_storage.physical_constants.weight_factor = 1E10*number_density*AF_HB_1D_storage.physical_constants.integral_pre_factor;
//AF_HB_1D_storage.physical_constants.weight_factor = AF_HB_1D_storage.physical_constants.integral_pre_factor;
// Need to specify if this process is isotropic
//This_process.non_isotropic_rot_index = -1; // Yes (powder)
This_process.non_isotropic_rot_index = 1; // No (single crystal)
// The type of the process must be saved in the global enum process
This_process.eProcess = AF_HB_1D;
// Packing the data into a structure that is transported to the main component
This_process.data_transfer.pointer_to_a_AF_HB_1D_physics_storage_struct = &AF_HB_1D_storage;
This_process.probability_for_scattering_function = &AF_HB_1D_physics_my;
This_process.scattering_function = &AF_HB_1D_physics_scattering;
// This will be the same for all process's, and can thus be moved to an include.
sprintf(This_process.name,"%s",NAME_CURRENT_COMP);
This_process.process_p_interact = interact_fraction;
rot_copy(This_process.rotation_matrix,ROT_A_CURRENT_COMP);
sprintf(global_process_element.name,"%s",NAME_CURRENT_COMP);
global_process_element.component_index = INDEX_CURRENT_COMP;
global_process_element.p_scattering_process = &This_process;
struct pointer_to_global_process_list *global_process_list = COMP_GETPAR3(Union_init, init, global_process_list);
add_element_to_process_list(global_process_list,global_process_element);
%}
TRACE
%{
// Trace should be empty, the simulation is done in Union_master
%}
END
|