xref: /petsc/src/sys/ftn-custom/zutils.c (revision 09cb0f53fc4ca334bb6939695b65b036666b7dbb)
1 #include <petsc/private/fortranimpl.h>
2 
3 /*MC
4    PetscFortranAddr - a variable type in Fortran that can hold a
5      regular C pointer.
6 
7    Note:
8     Used, for example, as the file argument in `PetscFOpen()`
9 
10    Level: beginner
11 
12 .seealso:  `PetscOffset`, `PetscInt`
13 M*/
14 /*MC
15    PetscOffset - a variable type in Fortran used with `VecGetArray()`
16      and `ISGetIndices()`
17 
18    Level: beginner
19 
20 .seealso:  `PetscFortranAddr`, `PetscInt`
21 M*/
22 
23 /*
24     This is code for translating PETSc memory addresses to integer offsets
25     for Fortran.
26 */
27 char *PETSC_NULL_CHARACTER_Fortran     = NULL;
28 void *PETSC_NULL_INTEGER_Fortran       = NULL;
29 void *PETSC_NULL_SCALAR_Fortran        = NULL;
30 void *PETSC_NULL_DOUBLE_Fortran        = NULL;
31 void *PETSC_NULL_REAL_Fortran          = NULL;
32 void *PETSC_NULL_BOOL_Fortran          = NULL;
33 void *PETSC_NULL_ENUM_Fortran          = NULL;
34 void *PETSC_NULL_INTEGER_ARRAY_Fortran = NULL;
35 void *PETSC_NULL_SCALAR_ARRAY_Fortran  = NULL;
36 void *PETSC_NULL_REAL_ARRAY_Fortran    = NULL;
37 
38 EXTERN_C_BEGIN
39 void (*PETSC_NULL_FUNCTION_Fortran)(void) = NULL;
40 EXTERN_C_END
41 void *PETSC_NULL_MPI_COMM_Fortran = NULL;
42 
43 size_t PetscIntAddressToFortran(const PetscInt *base, const PetscInt *addr)
44 {
45   size_t tmp1 = (size_t)base, tmp2 = 0;
46   size_t tmp3 = (size_t)addr;
47   size_t itmp2;
48 
49 #if !defined(PETSC_HAVE_CRAY90_POINTER)
50   if (tmp3 > tmp1) {
51     tmp2  = (tmp3 - tmp1) / sizeof(PetscInt);
52     itmp2 = (size_t)tmp2;
53   } else {
54     tmp2  = (tmp1 - tmp3) / sizeof(PetscInt);
55     itmp2 = -((size_t)tmp2);
56   }
57 #else
58   if (tmp3 > tmp1) {
59     tmp2  = (tmp3 - tmp1);
60     itmp2 = (size_t)tmp2;
61   } else {
62     tmp2  = (tmp1 - tmp3);
63     itmp2 = -((size_t)tmp2);
64   }
65 #endif
66 
67   if (base + itmp2 != addr) {
68     PetscCallAbort(PETSC_COMM_SELF, (*PetscErrorPrintf)("PetscIntAddressToFortran:C and Fortran arrays are\n"));
69     PetscCallAbort(PETSC_COMM_SELF, (*PetscErrorPrintf)("not commonly aligned or are too far apart to be indexed \n"));
70     PetscCallAbort(PETSC_COMM_SELF, (*PetscErrorPrintf)("by an integer. Locations: C %zu Fortran %zu\n", tmp1, tmp3));
71     PETSCABORT(PETSC_COMM_WORLD, PETSC_ERR_PLIB);
72   }
73   return itmp2;
74 }
75 
76 PetscInt *PetscIntAddressFromFortran(const PetscInt *base, size_t addr)
77 {
78   return (PetscInt *)(base + addr);
79 }
80 
81 /*
82        obj - PETSc object on which request is made
83        base - Fortran array address
84        addr - C array address
85        res  - will contain offset from C to Fortran
86        shift - number of bytes that prevent base and addr from being commonly aligned
87        N - size of the array
88 
89        align indicates alignment relative to PetscScalar, 1 means aligned on PetscScalar, 2 means aligned on 2 PetscScalar etc
90 */
91 PetscErrorCode PetscScalarAddressToFortran(PetscObject obj, PetscInt align, PetscScalar *base, PetscScalar *addr, PetscInt N, size_t *res)
92 {
93   size_t   tmp1 = (size_t)base, tmp2;
94   size_t   tmp3 = (size_t)addr;
95   size_t   itmp2;
96   PetscInt shift;
97 
98   PetscFunctionBegin;
99 #if !defined(PETSC_HAVE_CRAY90_POINTER)
100   if (tmp3 > tmp1) { /* C is bigger than Fortran */
101     tmp2  = (tmp3 - tmp1) / sizeof(PetscScalar);
102     itmp2 = (size_t)tmp2;
103     shift = (align * sizeof(PetscScalar) - (PetscInt)((tmp3 - tmp1) % (align * sizeof(PetscScalar)))) % (align * sizeof(PetscScalar));
104   } else {
105     tmp2  = (tmp1 - tmp3) / sizeof(PetscScalar);
106     itmp2 = -((size_t)tmp2);
107     shift = (PetscInt)((tmp1 - tmp3) % (align * sizeof(PetscScalar)));
108   }
109 #else
110   if (tmp3 > tmp1) { /* C is bigger than Fortran */
111     tmp2  = (tmp3 - tmp1);
112     itmp2 = (size_t)tmp2;
113   } else {
114     tmp2  = (tmp1 - tmp3);
115     itmp2 = -((size_t)tmp2);
116   }
117   shift = 0;
118 #endif
119 
120   if (shift) {
121     /*
122         Fortran and C not PetscScalar aligned,recover by copying values into
123         memory that is aligned with the Fortran
124     */
125     PetscScalar   *work;
126     PetscContainer container;
127 
128     PetscCall(PetscMalloc1(N + align, &work));
129 
130     /* recompute shift for newly allocated space */
131     tmp3 = (size_t)work;
132     if (tmp3 > tmp1) { /* C is bigger than Fortran */
133       shift = (align * sizeof(PetscScalar) - (PetscInt)((tmp3 - tmp1) % (align * sizeof(PetscScalar)))) % (align * sizeof(PetscScalar));
134     } else {
135       shift = (PetscInt)((tmp1 - tmp3) % (align * sizeof(PetscScalar)));
136     }
137 
138     /* shift work by that number of bytes */
139     work = (PetscScalar *)(((char *)work) + shift);
140     PetscCall(PetscArraycpy(work, addr, N));
141 
142     /* store in the first location in addr how much you shift it */
143     ((PetscInt *)addr)[0] = shift;
144 
145     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
146     PetscCall(PetscContainerSetPointer(container, addr));
147     PetscCall(PetscObjectCompose(obj, "GetArrayPtr", (PetscObject)container));
148 
149     tmp3 = (size_t)work;
150     if (tmp3 > tmp1) { /* C is bigger than Fortran */
151       tmp2  = (tmp3 - tmp1) / sizeof(PetscScalar);
152       itmp2 = (size_t)tmp2;
153       shift = (align * sizeof(PetscScalar) - (PetscInt)((tmp3 - tmp1) % (align * sizeof(PetscScalar)))) % (align * sizeof(PetscScalar));
154     } else {
155       tmp2  = (tmp1 - tmp3) / sizeof(PetscScalar);
156       itmp2 = -((size_t)tmp2);
157       shift = (PetscInt)((tmp1 - tmp3) % (align * sizeof(PetscScalar)));
158     }
159     if (shift) {
160       PetscCall((*PetscErrorPrintf)("PetscScalarAddressToFortran:C and Fortran arrays are\n"));
161       PetscCall((*PetscErrorPrintf)("not commonly aligned.\n"));
162       PetscCall((*PetscErrorPrintf)("Locations/sizeof(PetscScalar): C %g Fortran %g\n", (double)(((PetscReal)tmp3) / (PetscReal)sizeof(PetscScalar)), (double)(((PetscReal)tmp1) / (PetscReal)sizeof(PetscScalar))));
163       PETSCABORT(PETSC_COMM_WORLD, PETSC_ERR_PLIB);
164     }
165     PetscCall(PetscInfo(obj, "Efficiency warning, copying array in XXXGetArray() due\n\
166     to alignment differences between C and Fortran\n"));
167   }
168   *res = itmp2;
169   PetscFunctionReturn(PETSC_SUCCESS);
170 }
171 
172 /*
173     obj - the PETSc object where the scalar pointer came from
174     base - the Fortran array address
175     addr - the Fortran offset from base
176     N    - the amount of data
177 
178     lx   - the array space that is to be passed to XXXXRestoreArray()
179 */
180 PetscErrorCode PetscScalarAddressFromFortran(PetscObject obj, PetscScalar *base, size_t addr, PetscInt N, PetscScalar **lx)
181 {
182   PetscInt       shift;
183   PetscContainer container;
184   PetscScalar   *tlx;
185 
186   PetscFunctionBegin;
187   PetscCall(PetscObjectQuery(obj, "GetArrayPtr", (PetscObject *)&container));
188   if (container) {
189     PetscCall(PetscContainerGetPointer(container, (void **)lx));
190     tlx = base + addr;
191 
192     shift = *(PetscInt *)*lx;
193     PetscCall(PetscArraycpy(*lx, tlx, N));
194     tlx = (PetscScalar *)(((char *)tlx) - shift);
195 
196     PetscCall(PetscFree(tlx));
197     PetscCall(PetscContainerDestroy(&container));
198     PetscCall(PetscObjectCompose(obj, "GetArrayPtr", NULL));
199   } else {
200     *lx = base + addr;
201   }
202   PetscFunctionReturn(PETSC_SUCCESS);
203 }
204 
205 #if defined(PETSC_HAVE_FORTRAN_CAPS)
206   #define petscisinfornanscalar_ PETSCISINFORNANSCALAR
207   #define petscisinfornanreal_   PETSCISINFORNANREAL
208 #elif !defined(PETSC_HAVE_FORTRAN_UNDERSCORE)
209   #define petscisinfornanscalar_ petscisinfornanscalar
210   #define petscisinfornanreal_   petscisinfornanreal
211 #endif
212 
213 PETSC_EXTERN PetscBool petscisinfornanscalar_(PetscScalar *v)
214 {
215   return (PetscBool)PetscIsInfOrNanScalar(*v);
216 }
217 
218 PETSC_EXTERN PetscBool petscisinfornanreal_(PetscReal *v)
219 {
220   return (PetscBool)PetscIsInfOrNanReal(*v);
221 }
222