File: AF_HB_1D_process.comp

package info (click to toggle)
mccode 3.5.19%2Bds5-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,113,256 kB
  • sloc: ansic: 40,697; python: 25,137; yacc: 8,438; sh: 5,405; javascript: 4,596; lex: 1,632; cpp: 742; perl: 296; lisp: 273; makefile: 226; fortran: 132
file content (345 lines) | stat: -rw-r--r-- 14,884 bytes parent folder | download
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