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