xobjects 0.5.9__tar.gz → 0.5.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. xobjects-0.5.11/Architecture.md +202 -0
  2. xobjects-0.5.11/MANIFEST.in +9 -0
  3. {xobjects-0.5.9/xobjects.egg-info → xobjects-0.5.11}/PKG-INFO +1 -1
  4. xobjects-0.5.11/xobjects/_version.py +1 -0
  5. xobjects-0.5.11/xobjects/headers/atomicadd.h +350 -0
  6. xobjects-0.5.11/xobjects/headers/common.h +107 -0
  7. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/hybrid_class.py +4 -1
  8. {xobjects-0.5.9 → xobjects-0.5.11/xobjects.egg-info}/PKG-INFO +1 -1
  9. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects.egg-info/SOURCES.txt +6 -1
  10. xobjects-0.5.9/xobjects/_version.py +0 -1
  11. {xobjects-0.5.9 → xobjects-0.5.11}/LICENSE +0 -0
  12. /xobjects-0.5.9/README.md → /xobjects-0.5.11/Readme.md +0 -0
  13. {xobjects-0.5.9 → xobjects-0.5.11}/pyproject.toml +0 -0
  14. {xobjects-0.5.9 → xobjects-0.5.11}/setup.cfg +0 -0
  15. {xobjects-0.5.9 → xobjects-0.5.11}/setup.py +0 -0
  16. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_align.py +0 -0
  17. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_array.py +0 -0
  18. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_buffer.py +0 -0
  19. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_capi.py +0 -0
  20. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_chunk.py +0 -0
  21. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_common.py +0 -0
  22. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_context_opencl.py +0 -0
  23. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_hybrid_class.py +0 -0
  24. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_kernel.py +0 -0
  25. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_linked_array.py +0 -0
  26. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_nplike_arrays.py +0 -0
  27. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_ref.py +0 -0
  28. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_scalars.py +0 -0
  29. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_shared_memory.py +0 -0
  30. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_strides.py +0 -0
  31. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_string.py +0 -0
  32. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_struct.py +0 -0
  33. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_to_dict.py +0 -0
  34. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_typeutils.py +0 -0
  35. {xobjects-0.5.9 → xobjects-0.5.11}/tests/test_unionref.py +0 -0
  36. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/__init__.py +0 -0
  37. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/_patch_pyopencl_array.py +0 -0
  38. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/array.py +0 -0
  39. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/capi.py +0 -0
  40. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/context.py +0 -0
  41. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/context_cpu.py +0 -0
  42. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/context_cupy.py +0 -0
  43. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/context_pyopencl.py +0 -0
  44. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/general.py +0 -0
  45. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/linkedarray.py +0 -0
  46. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/ref.py +0 -0
  47. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/scalar.py +0 -0
  48. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/specialize_source.py +0 -0
  49. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/string.py +0 -0
  50. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/struct.py +0 -0
  51. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/test_helpers.py +0 -0
  52. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/typeutils.py +0 -0
  53. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects/union.py +0 -0
  54. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects.egg-info/dependency_links.txt +0 -0
  55. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects.egg-info/entry_points.txt +0 -0
  56. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects.egg-info/requires.txt +0 -0
  57. {xobjects-0.5.9 → xobjects-0.5.11}/xobjects.egg-info/top_level.txt +0 -0
@@ -0,0 +1,202 @@
1
+ # Architecture
2
+
3
+ ## Introduction
4
+
5
+ The library is based on:
6
+ - Buffers: manage chunck of bytes on CPU and GPU (Cuda and OpenCL) memory using different backends
7
+ - Contexts: keep track of buffers, launch functions operating on buffers
8
+ - Basic types: a set of basic types that allow to describe structured data. Basic types are scalars, strings, structs, arrays and unions. User defined classes, composed with basic types, allow to interact with data on buffers and generate C functions to operate with data.
9
+ - C-API generation: The type information allows to write C functions that can be interact with data. The C functions are specialized for CPU, OpenCL and Cuda. Also loops described by annotation in comments are automatically generated for CPU, OpenCL and Cuda.
10
+
11
+ When a user defined class is created, Python methods and additional metadeta is attached to the class.
12
+ When an instance of a user defined class is instantiated, memory is written to a buffer and a Python object is created to interact with the data.
13
+ The size of an instance may not be known at class defintion time, but it cannot be changed after initialization.
14
+
15
+ ## Contexts and Buffers
16
+
17
+ A context contain:
18
+
19
+ - `new_buffer(capacity)`: create new buffer of initial capacity
20
+ - `buffers`: list of weakref to buffers
21
+ - kernels : Object containing compiled code from sources/
22
+
23
+ Buffer:
24
+ - `buffer`: low level buffer
25
+ - `allocate(self,size)`: return the offset of the first free memory chunk of given size and grows the buffer if necessary
26
+ - `free(self,offset,size)`: free a chuck of memory of a given size
27
+ - `grow(self,capacity)`: increase the size of a buffer
28
+ - `_new_buffer(capacity)`: creae lowlevel buffer
29
+ - `update_from_native(...)`: Copy data from native buffer into self.buffer starting from offset
30
+ - `update_from_buffer(...)`: Copy data from python buffer such as bytearray, bytes, memoryview, numpy array.data
31
+ - `update_from_nplike(...)`: Copy data from nplike matching dest_dtype
32
+ - `update_from_xbuffer(...)`: Copy data from any xbuffer, don't pass through cpu if possible
33
+ - `copy_to_native(...)`: Copy data from self.buffer into dest
34
+ - `to_native(...)`: Return native data with content at from offset and nbytes
35
+ - `to_nplike(...)`: Return a view in of an nplike
36
+ - `to_bytearray(...)`: Return a byte array: used in update_from_xbuffer
37
+ - `to_pointer_arg(...)`: Return data that can be used as argument in kernel
38
+
39
+ ## Types
40
+
41
+ Types can be composed of:
42
+ - scalar: numbers, String
43
+ - compound: Struct, Array, Ref, UnionRef
44
+
45
+ ### Scalars
46
+ - examples: Float64, Int64, ...
47
+ - create: Float64(3.14)
48
+ - memory layout
49
+ - data
50
+
51
+ ### String:
52
+ - create: String(string_or_int)
53
+ - memory layout
54
+ - size
55
+ - data
56
+
57
+
58
+ ### Struct
59
+ - example: struct MyStruct field1 field2 ...
60
+ - create: Struct(dict), Struct(**args)
61
+ - memory layout:
62
+ - [ instance size ]
63
+ - static-field1
64
+ - ..
65
+ - static-fieldn
66
+ - [ offset field 2 ]
67
+ - [ offset ... ]
68
+ - [ offset field n ]
69
+ - [ dynamic-field1 ]
70
+ - [ ... ]
71
+ - [ dynamic-fieldn ]
72
+
73
+ ### Array:
74
+ - example: 'array' 'f64' d1 d2 d3 'C' ; 'array' 'i64' d1 ':' d3 'F' ;
75
+ - where:
76
+ - 'C'/'F' is C-contiguos or F-contiguous
77
+ - d1, d2, .. are the dimensions for each axis and ':' means not specified
78
+ - create: Array(d1,d2,...) or Array([...]) or Array(np-array)
79
+ - memory layout:
80
+ - [size] if not _is_static_shape or not _is_static_type
81
+ - [dims ... ] len(_dynamic_shape)
82
+ - [strides...] if nd>1 and dynamic shapes
83
+ - [offsets] if not _is_static_type
84
+ - data
85
+
86
+ ### Union:
87
+ - create: Union(obj), Union( (typename,args) )
88
+ - memory layout
89
+ - typeid
90
+ - data
91
+
92
+ ### UnionRef:
93
+ - create: UnionRef(obj), UnionRef( (typename,args) )
94
+ - memory layout
95
+ - typeid
96
+ - offset
97
+
98
+
99
+ ### Ref:
100
+ - memory layout
101
+ - offset
102
+
103
+ ## Implementation details
104
+
105
+ Assumptions:
106
+ - Python user API cannot change the internal structure of the object but only the content
107
+ - Python object caches the structure information to avoid round trips to access data
108
+ - C user API does not manipulate structures
109
+
110
+
111
+ Operation:
112
+
113
+ - _inspect_args(*args, **args) -> info: Info
114
+ - check value and calculate size, offsets and dimensions from arguments, recursively
115
+ - returns at least `size` for `_get_a_buffer` and `value` for `_to_buffer`
116
+
117
+ - _get_a_buffer(size, _context, _buffer, _offset):
118
+ - make sure a valid buffer and offset is created
119
+
120
+ - _to_buffer(buffer, offset, value, info, size?):
121
+ - set data on buffer with offset from pyhon value, using info and implicitely respecting size
122
+ - if value same class -> binary copy (passing from context if needed)
123
+
124
+ - _from_buffer(buffer, offset) -> value:
125
+ - create python object from data on buffer. Can be a native type (scalar or string) or another XObject (struct, array)
126
+
127
+
128
+ - __init__(self,*args,_context=None, _buffer=None, _offset=None,*nargs)
129
+ - initialize object data on buffer from args and create python object:
130
+ - use _pre_init(*args,**nargs) -> return args, nargs standard initialization value
131
+ - check values and calculate size using `_inspect_args`, return info object containing size, offsets and values to be later used by _to_buffer
132
+ - allocate using `_get_a_buffer`
133
+ - write data using `_to_buffer`
134
+ - use `_post_init` to complete object
135
+ `
136
+ - _update(self, value, size=None):
137
+ - Optional update object content using value, in case respecting size for string
138
+
139
+ - __get__(field,instance) or __getitem__(self,index...)
140
+ - if return instance._cache[field.index] else #should implement item caching for struct and array
141
+ - else return _from_buffer
142
+
143
+ - __set__(..., value) or __setitem__(...,value)
144
+ - if hasattr(self.ftype._update) get object and update in place
145
+ else call _to_buffer
146
+
147
+ - json enconding
148
+ nested object can be initialized using a json enconding
149
+ d:dict -> type(**d)
150
+ t:tuple -> type(*t)
151
+ a:other -> type(a)
152
+
153
+
154
+
155
+ Class content:
156
+
157
+ - _inspect_args(cls, *args, **nargs) -> size, offsets
158
+ compute size and sizes and offsets from arguments
159
+
160
+ - _to_buffer(cls, buffer, offset, value, offsets=None):
161
+ initialize object on buffer
162
+
163
+ - _from_buffer(cls, buffer,offset):
164
+ create python object from initlialized object on buffer,offset
165
+
166
+ - _size: object size
167
+ None if dynamic
168
+
169
+ Instance content:
170
+
171
+ - _get_size(self) -> size
172
+
173
+ ## C-API
174
+
175
+ Access
176
+ - `<Class>_get<_attrs>(obj, <indexes>)` -> scalar or object pointer
177
+ - `<Class>_set<_attrs>(obj, <indexes>, scalar)`
178
+
179
+ e.g. Multipole_get_field_normal(3)
180
+
181
+ Kernel convention:
182
+
183
+ ```
184
+ <fun>(void **refs){
185
+ Type0 arg0 = (Type0) refs[0];
186
+ ....
187
+ Type1 argN = (TypeN) refs[N];
188
+ ...
189
+ }
190
+ ```
191
+
192
+ ``` <fun>(buffer1, offset, ..., offset3){
193
+ Type0 arg0 = (Type0) refs[offset0];
194
+ ,,,
195
+ TypeN argN = (TypeN) refs[offsetN];
196
+ }
197
+ ```
198
+
199
+ ## TODO
200
+
201
+ - add free methods to delete data from buffer
202
+ - implement SOA types
@@ -0,0 +1,9 @@
1
+ include pyproject.toml
2
+
3
+ # Include the README
4
+ include *.md
5
+
6
+ # Include the license file
7
+ include LICENSE
8
+
9
+ recursive-include xobjects *.h
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xobjects
3
- Version: 0.5.9
3
+ Version: 0.5.11
4
4
  Summary: In-memory serialization and code generator for CPU and GPU
5
5
  Home-page: https://xsuite.readthedocs.io/
6
6
  Download-URL: https://pypi.python.org/pypi/xobjects
@@ -0,0 +1 @@
1
+ __version__ = "0.5.11"
@@ -0,0 +1,350 @@
1
+ // copyright ################################# //
2
+ // This file is part of the Xtrack Package. //
3
+ // Copyright (c) CERN, 2025. //
4
+ // ########################################### //
5
+
6
+ #ifndef _ATOMICADD_H_
7
+ #define _ATOMICADD_H_
8
+
9
+ #include "xobjects/headers/common.h"
10
+
11
+
12
+ // Ensure no conflicting atomicAdd macro is defined earlier for CPU or OpenCL
13
+ #ifndef XO_CONTEXT_CUDA
14
+ #ifdef atomicAdd
15
+ #warning "Warning: atomicAdd macro already defined, undefining it."
16
+ #undef atomicAdd
17
+ #endif // atomicAdd
18
+ #endif // XO_CONTEXT_CUDA
19
+
20
+
21
+ // ################################# //
22
+ // ### CPU Contexts ### //
23
+ // ################################# //
24
+
25
+ #ifdef XO_CONTEXT_CPU
26
+ #include <stdint.h>
27
+
28
+ #ifdef XO_CONTEXT_CPU_OPENMP
29
+ #ifndef _OPENMP
30
+ #error "XO_CONTEXT_CPU_OPENMP set, but compiled without -fopenmp"
31
+ #endif
32
+ // OpenMP atomic capture gives us read+write atomically
33
+ #define OMP_ATOMIC_CAPTURE _Pragma("omp atomic capture")
34
+ #else
35
+ #define OMP_ATOMIC_CAPTURE // No OpenMP: non-atomic fallback
36
+ #endif // XO_CONTEXT_CPU_OPENMP
37
+
38
+ // Macro to define atomicAdd for different types, will be overloaded via _Generic.
39
+ #define DEF_ATOMIC_ADD(T, SUF) \
40
+ GPUFUN T atomicAdd_##SUF(GPUGLMEM T *addr, T val) { \
41
+ T old; \
42
+ const T inc = (T)val; \
43
+ OMP_ATOMIC_CAPTURE \
44
+ { old = *addr; *addr = *addr + inc; } \
45
+ return old; \
46
+ }
47
+
48
+ DEF_ATOMIC_ADD(int8_t , i8)
49
+ DEF_ATOMIC_ADD(int16_t, i16)
50
+ DEF_ATOMIC_ADD(int32_t, i32)
51
+ DEF_ATOMIC_ADD(int64_t, i64)
52
+ DEF_ATOMIC_ADD(uint8_t , u8)
53
+ DEF_ATOMIC_ADD(uint16_t, u16)
54
+ DEF_ATOMIC_ADD(uint32_t, u32)
55
+ DEF_ATOMIC_ADD(uint64_t, u64)
56
+ DEF_ATOMIC_ADD(float , f32)
57
+ DEF_ATOMIC_ADD(double , f64)
58
+
59
+ // Using _Generic to select the right function based on type (since C11).
60
+ // See https://en.cppreference.com/w/c/language/generic.html
61
+ #define atomicAdd(addr, val) _Generic((addr), \
62
+ int8_t*: atomicAdd_i8, \
63
+ int16_t*: atomicAdd_i16, \
64
+ int32_t*: atomicAdd_i32, \
65
+ int64_t*: atomicAdd_i64, \
66
+ uint8_t*: atomicAdd_u8, \
67
+ uint16_t*: atomicAdd_u16, \
68
+ uint32_t*: atomicAdd_u32, \
69
+ uint64_t*: atomicAdd_u64, \
70
+ float*: atomicAdd_f32, \
71
+ double*: atomicAdd_f64 \
72
+ )(addr, (val))
73
+ #endif // XO_CONTEXT_CPU
74
+
75
+
76
+ // ################################# //
77
+ // ### GPU Contexts ### //
78
+ // ################################# //
79
+
80
+ /* -------------- Design notes for GPU atomics ---------------------------------
81
+ * We will go for a unified approach for CUDA and OpenCL, with macros where needed.
82
+ * We implement 8/16-bit atomic add by atomically CAS'ing the *containing*
83
+ * 32-bit word. Only the target byte/halfword lane is modified; the neighbor
84
+ * lane is preserved. This is linearisable: each successful CAS is one atomic
85
+ * RMW on the 32-bit word.
86
+ * Assumptions: little-endian lane layout (true on NVIDIA) and natural alignment
87
+ * of the 16-bit addresses (addr % 2 == 0). 8-bit has no alignment requirement.
88
+ * Return value matches CUDA semantics: the **old** value at *addr* (fetch-add).
89
+ * ---------------------------------------------------------------------------*/
90
+
91
+ // Info: a CAS function (Compare-And-Swap) takes three arguments: a pointer to
92
+ // the value to be modified, the expected old value, and the new value. The
93
+ // second argument helps to recognise if another thread has modified the value
94
+ // in the meantime. The function compares the value at the address with the
95
+ // expected values, and if they are the same, it writes the new value at the
96
+ // address. In any case, the function returns the actual old value at the
97
+ // address. If the returned value is different from the expected value, it
98
+ // means that another thread has modified the value in the meantime.
99
+
100
+ // Define types and macros for CUDA and OpenCL
101
+ // -------------------------------------------
102
+ #if defined(XO_CONTEXT_CUDA)
103
+ // CUDA compiler may not have <stdint.h>, so define the types if needed.
104
+ #ifdef __CUDACC_RTC__
105
+ // NVRTC (CuPy RawModule default) can’t see <stdint.h>, so detect it via __CUDACC_RTC__
106
+ typedef signed char int8_t;
107
+ typedef short int16_t;
108
+ typedef int int32_t;
109
+ typedef long long int64_t;
110
+ typedef unsigned char uint8_t;
111
+ typedef unsigned short uint16_t;
112
+ typedef unsigned int uint32_t;
113
+ typedef unsigned long long uint64_t;
114
+ #else
115
+ // Alternatively, NVCC path is fine with host headers
116
+ #include <stdint.h>
117
+ #endif // __CUDACC_RTC__
118
+ #define GPUVOLATILE GPUGLMEM
119
+ #define __XT_CAS_U32(ptr, exp, val) atomicCAS((GPUVOLATILE uint32_t*)(ptr), (uint32_t)(exp), (uint32_t)(val))
120
+ #define __XT_CAS_U64(ptr, exp, val) atomicCAS((GPUVOLATILE uint64_t*)(ptr), (uint64_t)(exp), (uint64_t)(val))
121
+ #define __XT_AS_U32_FROM_F32(x) __float_as_uint(x)
122
+ #define __XT_AS_F32_FROM_U32(x) __uint_as_float(x)
123
+ #define __XT_AS_U64_FROM_F64(x) __double_as_longlong(x)
124
+ #define __XT_AS_F64_FROM_U64(x) __longlong_as_double(x)
125
+ #elif defined(XO_CONTEXT_CL)
126
+ // It seems OpenCL already has the types from <stdint.h> defined.
127
+ // typedef char int8_t;
128
+ // typedef short int16_t;
129
+ // typedef int int32_t;
130
+ // typedef long int64_t;
131
+ // typedef unsigned char uint8_t;
132
+ // typedef unsigned short uint16_t;
133
+ // typedef unsigned int uint32_t;
134
+ // typedef unsigned long uint64_t;
135
+ #define GPUVOLATILE GPUGLMEM volatile
136
+ #if __OPENCL_C_VERSION__ < 110
137
+ // Map 1.0 "atom_*" names to 1.1+ "atomic_*"
138
+ #pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
139
+ #define atomic_add atom_add
140
+ #define atomic_cmpxchg atom_cmpxchg
141
+ #endif
142
+ #pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
143
+ #pragma OPENCL EXTENSION cl_khr_fp64 : enable
144
+ #define __XT_CAS_U32(ptr, exp, val) atomic_cmpxchg((GPUVOLATILE uint32_t*)(ptr), (uint32_t)(exp), (uint32_t)(val))
145
+ #define __XT_CAS_U64(ptr, exp, val) atom_cmpxchg((GPUVOLATILE uint64_t*)(ptr), (uint64_t)(exp), (uint64_t)(val))
146
+ #define __XT_AS_U32_FROM_F32(x) as_uint(x)
147
+ #define __XT_AS_F32_FROM_U32(x) as_float(x)
148
+ #define __XT_AS_U64_FROM_F64(x) as_ulong(x)
149
+ #define __XT_AS_F64_FROM_U64(x) as_double(x)
150
+ #endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
151
+
152
+ // Define atomic functions per type
153
+ // --------------------------------
154
+ #if defined(XO_CONTEXT_CUDA) || defined(XO_CONTEXT_CL)
155
+ // Helper: compute (base 32-bit word pointer, shift, mask) for a byte in that word.
156
+ GPUFUN inline void __xt_lane8(GPUVOLATILE void* addr, GPUVOLATILE uint32_t** w, uint32_t* sh, uint32_t* mask){
157
+ size_t a = (size_t)addr;
158
+ *w = (GPUVOLATILE uint32_t*)(a & ~(size_t)3); // word: align down to 4-byte boundary
159
+ *sh = (uint32_t)((a & (size_t)3) * 8U); // shift 0,8,16,24 depending on byte lane
160
+ *mask = (uint32_t)(0xFFu << *sh);
161
+ }
162
+ // Helper: same for a halfword (16-bit) in the containing 32-bit word.
163
+ GPUFUN inline void __xt_lane16(GPUVOLATILE void* addr, GPUVOLATILE uint32_t** w, uint32_t* sh, uint32_t* mask){
164
+ size_t a = (size_t)addr;
165
+ *w = (GPUVOLATILE uint32_t*)(a & ~(size_t)3); // word: align down to 4-byte boundary
166
+ *sh = (uint32_t)((a & (size_t)2) ? 16U : 0U); // shift0 or 16 depending on halfword
167
+ *mask = (uint32_t)(0xFFFFU << *sh);
168
+ }
169
+
170
+ // ---------------- 8-bit: int8_t / uint8_t (CAS on 32-bit word) --------------
171
+ GPUFUN int8_t atomicAdd_i8(GPUVOLATILE int8_t* addr, int8_t val){
172
+ GPUVOLATILE uint32_t *w;
173
+ uint32_t sh, mask;
174
+ __xt_lane8(addr, &w, &sh, &mask);
175
+ uint32_t old = *w, assumed, b, nb, nw; // byte, newbyte, newword
176
+ do {
177
+ assumed = old;
178
+ b = (assumed & mask) >> sh; // Extract current 8-bit lane
179
+ nb = (uint32_t)((uint8_t)b + (uint8_t)val); // Add in modulo-256 (two's complement)
180
+ nw = (assumed & ~mask) | ((nb & 0xFFU) << sh); // Merge back updated lane; leave neighbor lanes intact
181
+ // Try to publish; if someone raced us, retry with their value
182
+ old = __XT_CAS_U32(w, assumed, nw);
183
+ } while (old != assumed);
184
+ return (int8_t)((assumed & mask) >> sh);
185
+ }
186
+ GPUFUN uint8_t atomicAdd_u8(GPUVOLATILE uint8_t* addr, uint8_t val){
187
+ GPUVOLATILE uint32_t *w;
188
+ uint32_t sh, mask;
189
+ __xt_lane8(addr, &w, &sh, &mask);
190
+ uint32_t old = *w, assumed, b, nb, nw;
191
+ do {
192
+ assumed = old;
193
+ b = (assumed & mask) >> sh;
194
+ nb = (uint32_t)(b + val); /* mod 256 */
195
+ nw = (assumed & ~mask) | ((nb & 0xFFU) << sh);
196
+ old = __XT_CAS_U32(w, assumed, nw);
197
+ } while (old != assumed);
198
+ return (uint8_t)((assumed & mask) >> sh);
199
+ }
200
+
201
+ // ---------------- 16-bit: int16_t / uint16_t (CAS on 32-bit word) -----------
202
+ GPUFUN int16_t atomicAdd_i16(GPUVOLATILE int16_t* addr, int16_t val){
203
+ GPUVOLATILE uint32_t* w;
204
+ uint32_t sh, mask;
205
+ __xt_lane16(addr, &w, &sh, &mask);
206
+ uint32_t old = *w, assumed, b, nb, nw;
207
+ do {
208
+ assumed = old;
209
+ b = (assumed & mask) >> sh;
210
+ nb = (uint32_t)((uint16_t)b + (uint16_t)val);
211
+ nw = (assumed & ~mask) | ((nb & 0xFFFFU) << sh);
212
+ old = __XT_CAS_U32(w, assumed, nw);
213
+ } while (old != assumed);
214
+ return (int16_t)((assumed & mask) >> sh);
215
+ }
216
+ GPUFUN uint16_t atomicAdd_u16(GPUVOLATILE uint16_t* addr, uint16_t val){
217
+ GPUVOLATILE uint32_t* w;
218
+ uint32_t sh, mask;
219
+ __xt_lane16(addr, &w, &sh, &mask);
220
+ uint32_t old = *w, assumed, b, nb, nw;
221
+ do {
222
+ assumed = old;
223
+ b = (assumed & mask) >> sh;
224
+ nb = (uint32_t)(b + val);
225
+ nw = (assumed & ~mask) | ((nb & 0xFFFFU) << sh);
226
+ old = __XT_CAS_U32(w, assumed, nw);
227
+ } while (old != assumed);
228
+ return (uint16_t)((assumed & mask) >> sh);
229
+ }
230
+
231
+ // ---------------- 32-bit: int32_t / uint32_t (built-in) -----------
232
+ GPUFUN int32_t atomicAdd_i32(GPUVOLATILE int32_t* addr, int32_t val){
233
+ #ifdef XO_CONTEXT_CUDA
234
+ return atomicAdd(addr, val);
235
+ #else // XO_CONTEXT_CL
236
+ return atomic_add(addr, val);
237
+ #endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
238
+ }
239
+ GPUFUN uint32_t atomicAdd_u32(GPUVOLATILE uint32_t* addr, uint32_t val){
240
+ #ifdef XO_CONTEXT_CUDA
241
+ return atomicAdd(addr, val);
242
+ #else // XO_CONTEXT_CL
243
+ return atomic_add(addr, val);
244
+ #endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
245
+ }
246
+
247
+ // ---------------- 64-bit: int64_t / uint64_t (built-in or CAS on 64-bit word) -----------
248
+ GPUFUN int64_t atomicAdd_i64(GPUVOLATILE int64_t* addr, int64_t val){
249
+ uint64_t old, nw;
250
+ do {
251
+ old = *addr;
252
+ nw = old + val;
253
+ } while (__XT_CAS_U64((GPUVOLATILE uint64_t*)addr, old, nw) != old);
254
+ return old;
255
+ }
256
+ GPUFUN uint64_t atomicAdd_u64(GPUVOLATILE uint64_t* addr, uint64_t val){
257
+ #ifdef XO_CONTEXT_CUDA
258
+ return atomicAdd(addr, val);
259
+ #else // XO_CONTEXT_CL
260
+ return atom_add(addr, val);
261
+ #endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
262
+ }
263
+
264
+ // ---------------- 32-bit: float (built-in or CAS on 32-bit word) -----------
265
+ GPUFUN float atomicAdd_f32(GPUVOLATILE float* addr, float val){
266
+ #ifdef XO_CONTEXT_CUDA
267
+ return atomicAdd(addr, val);
268
+ #else // XO_CONTEXT_CL
269
+ uint32_t old, nw;
270
+ do {
271
+ old = __XT_AS_U32_FROM_F32(*addr);
272
+ nw = __XT_AS_U32_FROM_F32(__XT_AS_F32_FROM_U32(old) + val);
273
+ } while (__XT_CAS_U32((GPUVOLATILE uint32_t*)addr, old, nw) != old);
274
+ return __XT_AS_F32_FROM_U32(old);
275
+ #endif // XO_CONTEXT_CUDA / XO_CONTEXT_CL
276
+ }
277
+
278
+ // ---------------- 64-bit: float (built-in or CAS on 64-bit word) -----------
279
+ GPUFUN double atomicAdd_f64(GPUVOLATILE double* addr, double val){
280
+ #if __CUDA_ARCH__ >= 600
281
+ return atomicAdd(addr, val);
282
+ #else // XO_CONTEXT_CL || __CUDA_ARCH__ < 600
283
+ uint64_t old, nw;
284
+ do {
285
+ old = __XT_AS_U64_FROM_F64(*addr);
286
+ nw = __XT_AS_U64_FROM_F64(__XT_AS_F64_FROM_U64(old) + val);
287
+ } while (__XT_CAS_U64((GPUVOLATILE uint64_t*)addr, old, nw) != old);
288
+ return __XT_AS_F64_FROM_U64(old);
289
+ #endif // __CUDA_ARCH__ >= 600 / XO_CONTEXT_CL
290
+ }
291
+ #endif // defined(XO_CONTEXT_CUDA) || defined(XO_CONTEXT_CL)
292
+
293
+ // Define the overloaded function
294
+ // ------------------------------
295
+ #ifdef XO_CONTEXT_CUDA
296
+ // NVRTC (CuPy RawModule) usually compiles under extern "C".
297
+ // In C, function overloading is not possible, but we can cheat by doing it in
298
+ // C++ (with a different name to avoid clashes with the built-in atomicAdd).
299
+ // This function will then be remapped to atomicAdd() via a macro in C.
300
+ #ifdef __cplusplus
301
+ extern "C++" {
302
+
303
+ GPUFUN int8_t xt_atomicAdd(GPUVOLATILE int8_t* p, int8_t v) { return atomicAdd_i8 (p, v); }
304
+ GPUFUN uint8_t xt_atomicAdd(GPUVOLATILE uint8_t* p, uint8_t v) { return atomicAdd_u8 (p, v); }
305
+ GPUFUN int16_t xt_atomicAdd(GPUVOLATILE int16_t* p, int16_t v) { return atomicAdd_i16(p, v); }
306
+ GPUFUN uint16_t xt_atomicAdd(GPUVOLATILE uint16_t*p, uint16_t v) { return atomicAdd_u16(p, v); }
307
+ GPUFUN int64_t xt_atomicAdd(GPUVOLATILE int64_t*p, int64_t v) { return atomicAdd_i64(p, v); }
308
+
309
+ // Existing type definitions: forward to CUDA built-ins
310
+ GPUFUN int32_t xt_atomicAdd(GPUVOLATILE int32_t* p, int32_t v) { return ::atomicAdd(p, v); }
311
+ GPUFUN uint32_t xt_atomicAdd(GPUVOLATILE uint32_t* p, uint32_t v) { return ::atomicAdd(p, v); }
312
+ GPUFUN uint64_t xt_atomicAdd(GPUVOLATILE uint64_t* p, uint64_t v) { return ::atomicAdd(p, v); }
313
+ GPUFUN float xt_atomicAdd(GPUVOLATILE float* p, float v) { return ::atomicAdd(p, v); }
314
+ #if __CUDA_ARCH__ >= 600
315
+ GPUFUN double xt_atomicAdd(GPUVOLATILE double* p, double v) { return ::atomicAdd(p, v); }
316
+ #else
317
+ GPUFUN double xt_atomicAdd(GPUVOLATILE double* p, double v) { return atomicAdd_f64(p, v); }
318
+ #endif
319
+
320
+ }
321
+ #endif // __cplusplus
322
+
323
+ // ---------- Global remap of the public name on device code ----------
324
+ // Define AFTER the wrappers so we don't macro-rewrite our own calls.
325
+ #ifdef atomicAdd
326
+ #undef atomicAdd
327
+ #endif
328
+ #define atomicAdd(ptr, val) xt_atomicAdd((ptr), (val))
329
+ #endif /* XO_CONTEXT_CUDA */
330
+
331
+ #ifdef XO_CONTEXT_CL
332
+ #if !__has_attribute(overloadable)
333
+ #error "The current OpenCL compiler/architecture does not support __attribute__((overloadable))"
334
+ #endif
335
+ #define OCL_OVERLOAD __attribute__((overloadable))
336
+ GPUFUN int8_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int8_t* p, int8_t v) { return atomicAdd_i8 (p, v); }
337
+ GPUFUN uint8_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint8_t* p, uint8_t v) { return atomicAdd_u8 (p, v); }
338
+ GPUFUN int16_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int16_t* p, int16_t v) { return atomicAdd_i16(p, v); }
339
+ GPUFUN uint16_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint16_t*p, uint16_t v) { return atomicAdd_u16(p, v); }
340
+ GPUFUN int64_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int64_t*p, int64_t v) { return atomicAdd_i64(p, v); }
341
+ GPUFUN float OCL_OVERLOAD atomicAdd(GPUVOLATILE float* p, float v) { return atomicAdd_f32(p, v); }
342
+ GPUFUN double OCL_OVERLOAD atomicAdd(GPUVOLATILE double* p, double v) { return atomicAdd_f64(p, v); }
343
+
344
+ // Existing type definitions: forward to OpenCL built-ins
345
+ GPUFUN int32_t OCL_OVERLOAD atomicAdd(GPUVOLATILE int32_t* p, int32_t v) { return atomic_add(p, v); }
346
+ GPUFUN uint32_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint32_t* p, uint32_t v) { return atomic_add(p, v); }
347
+ GPUFUN uint64_t OCL_OVERLOAD atomicAdd(GPUVOLATILE uint64_t* p, uint64_t v) { return atom_add(p, v); }
348
+ #endif // XO_CONTEXT_CL
349
+
350
+ #endif //_ATOMICADD_H_
@@ -0,0 +1,107 @@
1
+ // copyright ################################# //
2
+ // This file is part of the Xtrack Package. //
3
+ // Copyright (c) CERN, 2025. //
4
+ // ########################################### //
5
+
6
+
7
+ #ifndef XOBJECTS_COMMON_H
8
+ #define XOBJECTS_COMMON_H
9
+
10
+ /*
11
+ Common macros for vectorization and parallelization, as well as common
12
+ arithmetic operations.
13
+ */
14
+
15
+ #ifdef XO_CONTEXT_CPU_SERIAL
16
+ // We are on CPU, without OpenMP
17
+
18
+ #define VECTORIZE_OVER(INDEX_NAME, COUNT) \
19
+ for (int64_t INDEX_NAME = 0; INDEX_NAME < (COUNT); INDEX_NAME++) {
20
+
21
+ #define END_VECTORIZE \
22
+ }
23
+ #endif // XO_CONTEXT_CPU_SERIAL
24
+
25
+ #ifdef XO_CONTEXT_CPU_OPENMP
26
+ // We are on CPU with the OpenMP context switched on
27
+
28
+ #define VECTORIZE_OVER(INDEX_NAME, COUNT) \
29
+ _Pragma("omp parallel for") \
30
+ for (int64_t INDEX_NAME = 0; INDEX_NAME < (COUNT); INDEX_NAME++) {
31
+
32
+ #define END_VECTORIZE \
33
+ }
34
+ #endif // XO_CONTEXT_CPU_OPENMP
35
+
36
+
37
+ #ifdef XO_CONTEXT_CUDA
38
+ // We are on a CUDA GPU
39
+
40
+ #define VECTORIZE_OVER(INDEX_NAME, COUNT) { \
41
+ int64_t INDEX_NAME = blockDim.x * blockIdx.x + threadIdx.x; \
42
+ if (INDEX_NAME < (COUNT)) {
43
+
44
+ #define END_VECTORIZE \
45
+ } \
46
+ }
47
+ #endif // XO_CONTEXT_CUDA
48
+
49
+
50
+ #ifdef XO_CONTEXT_CL
51
+ // We are on an OpenCL GPU
52
+
53
+ #define VECTORIZE_OVER(INDEX_NAME, COUNT) \
54
+ { \
55
+ int64_t INDEX_NAME = get_global_id(0); \
56
+ if (INDEX_NAME < (COUNT)) { \
57
+
58
+ #define END_VECTORIZE \
59
+ } \
60
+ }
61
+ #endif // XO_CONTEXT_CL
62
+
63
+
64
+ /*
65
+ Qualifier keywords for GPU and optimisation
66
+ */
67
+
68
+ #ifdef XO_CONTEXT_CPU // for both serial and OpenMP
69
+ #define GPUKERN
70
+ #define GPUFUN static inline
71
+ #define GPUGLMEM
72
+ #define RESTRICT restrict
73
+ #endif
74
+
75
+
76
+ #ifdef XO_CONTEXT_CUDA
77
+ #define GPUKERN __global__
78
+ #define GPUFUN __device__
79
+ #define GPUGLMEM
80
+ #define RESTRICT
81
+ #endif // XO_CONTEXT_CUDA
82
+
83
+
84
+ #ifdef XO_CONTEXT_CL
85
+ #define GPUKERN __kernel
86
+ #define GPUFUN
87
+ #define GPUGLMEM __global
88
+ #define RESTRICT
89
+ #endif // XO_CONTEXT_CL
90
+
91
+
92
+ /*
93
+ Common maths-related macros
94
+ */
95
+
96
+ #define POW2(X) ((X)*(X))
97
+ #define POW3(X) ((X)*(X)*(X))
98
+ #define POW4(X) ((X)*(X)*(X)*(X))
99
+ #define NONZERO(X) ((X) != 0.0)
100
+ #define NONZERO_TOL(X, TOL) (fabs((X)) > (TOL))
101
+
102
+
103
+ #ifndef VECTORIZE_OVER
104
+ #error "Unknown context, or the expected context (XO_CONTEXT_*) flag undefined. Try updating Xobjects?"
105
+ #endif
106
+
107
+ #endif // XOBJECTS_COMMON_H
@@ -169,7 +169,7 @@ class MetaHybridClass(type):
169
169
 
170
170
  setattr(new_class, pyname, _FieldOfDressed(fname, _XoStruct))
171
171
 
172
- new_class._fields = pynames_list
172
+ new_class._fields = [ff.name for ff in _XoStruct._fields]
173
173
 
174
174
  _XoStruct._DressingClass = new_class
175
175
 
@@ -295,6 +295,9 @@ class HybridClass(metaclass=MetaHybridClass):
295
295
 
296
296
  defaults = {}
297
297
  for field in obj._XoStruct._fields:
298
+ if dft := getattr(self, f"_default_{field.name}", None):
299
+ defaults[field.name] = dft
300
+ continue
298
301
  name = obj._rename.get(field.name, field.name)
299
302
  try:
300
303
  defaults[name] = field.get_default()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xobjects
3
- Version: 0.5.9
3
+ Version: 0.5.11
4
4
  Summary: In-memory serialization and code generator for CPU and GPU
5
5
  Home-page: https://xsuite.readthedocs.io/
6
6
  Download-URL: https://pypi.python.org/pypi/xobjects
@@ -1,5 +1,8 @@
1
+ Architecture.md
1
2
  LICENSE
3
+ MANIFEST.in
2
4
  README.md
5
+ Readme.md
3
6
  pyproject.toml
4
7
  setup.cfg
5
8
  setup.py
@@ -48,4 +51,6 @@ xobjects.egg-info/SOURCES.txt
48
51
  xobjects.egg-info/dependency_links.txt
49
52
  xobjects.egg-info/entry_points.txt
50
53
  xobjects.egg-info/requires.txt
51
- xobjects.egg-info/top_level.txt
54
+ xobjects.egg-info/top_level.txt
55
+ xobjects/headers/atomicadd.h
56
+ xobjects/headers/common.h
@@ -1 +0,0 @@
1
- __version__ = "0.5.9"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes