xref: /petsc/src/sys/ftn-custom/zutils.c (revision 4bcf769ed61900924d14f37844d4b824b520e6e8)
1 #include "zpetsc.h"
2 
3 /*MC
4    PetscFortranAddr - a variable type in Fortran that can hold a
5      regular C pointer.
6 
7    Notes: Used, for example, as the file argument in PetscFOpen()
8 
9    Level: beginner
10 
11 .seealso:  PetscOffset, PetscInt
12 M*/
13 /*MC
14    PetscOffset - a variable type in Fortran used with VecGetArray()
15      and ISGetIndices()
16 
17    Level: beginner
18 
19 .seealso:  PetscFortranAddr, PetscInt
20 M*/
21 
22 /*
23     This is code for translating PETSc memory addresses to integer offsets
24     for Fortran.
25 */
26 char   *PETSC_NULL_CHARACTER_Fortran = 0;
27 void   *PETSC_NULL_INTEGER_Fortran   = 0;
28 void   *PETSC_NULL_OBJECT_Fortran    = 0;
29 void   *PETSC_NULL_Fortran           = 0;
30 void   *PETSC_NULL_SCALAR_Fortran    = 0;
31 void   *PETSC_NULL_DOUBLE_Fortran    = 0;
32 void   *PETSC_NULL_REAL_Fortran      = 0;
33 EXTERN_C_BEGIN
34 void   (*PETSC_NULL_FUNCTION_Fortran)(void) = 0;
35 EXTERN_C_END
36 size_t PetscIntAddressToFortran(PetscInt *base,PetscInt *addr)
37 {
38   size_t tmp1 = (size_t) base,tmp2 = 0;
39   size_t tmp3 = (size_t) addr;
40   size_t itmp2;
41 
42 #if !defined(PETSC_HAVE_CRAY90_POINTER)
43   if (tmp3 > tmp1) {
44     tmp2  = (tmp3 - tmp1)/sizeof(PetscInt);
45     itmp2 = (size_t) tmp2;
46   } else {
47     tmp2  = (tmp1 - tmp3)/sizeof(PetscInt);
48     itmp2 = -((size_t) tmp2);
49   }
50 #else
51   if (tmp3 > tmp1) {
52     tmp2  = (tmp3 - tmp1);
53     itmp2 = (size_t) tmp2;
54   } else {
55     tmp2  = (tmp1 - tmp3);
56     itmp2 = -((size_t) tmp2);
57   }
58 #endif
59 
60   if (base + itmp2 != addr) {
61     (*PetscErrorPrintf)("PetscIntAddressToFortran:C and Fortran arrays are\n");
62     (*PetscErrorPrintf)("not commonly aligned or are too far apart to be indexed \n");
63     (*PetscErrorPrintf)("by an integer. Locations: C %uld Fortran %uld\n",tmp1,tmp3);
64     MPI_Abort(PETSC_COMM_WORLD,1);
65   }
66   return itmp2;
67 }
68 
69 PetscInt *PetscIntAddressFromFortran(PetscInt *base,size_t addr)
70 {
71   return base + addr;
72 }
73 
74 /*
75        obj - PETSc object on which request is made
76        base - Fortran array address
77        addr - C array address
78        res  - will contain offset from C to Fortran
79        shift - number of bytes that prevent base and addr from being commonly aligned
80        N - size of the array
81 
82 */
83 PetscErrorCode PetscScalarAddressToFortran(PetscObject obj,PetscScalar *base,PetscScalar *addr,PetscInt N,size_t *res)
84 {
85   size_t   tmp1 = (size_t) base,tmp2 = tmp1/sizeof(PetscScalar);
86   size_t   tmp3 = (size_t) addr;
87   size_t   itmp2;
88   PetscInt shift;
89 
90 #if !defined(PETSC_HAVE_CRAY90_POINTER)
91   if (tmp3 > tmp1) {  /* C is bigger than Fortran */
92     tmp2  = (tmp3 - tmp1)/sizeof(PetscScalar);
93     itmp2 = (size_t) tmp2;
94     shift = (sizeof(PetscScalar) - (int)((tmp3 - tmp1) % sizeof(PetscScalar))) % sizeof(PetscScalar);
95   } else {
96     tmp2  = (tmp1 - tmp3)/sizeof(PetscScalar);
97     itmp2 = -((size_t) tmp2);
98     shift = (int)((tmp1 - tmp3) % sizeof(PetscScalar));
99   }
100 #else
101   if (tmp3 > tmp1) {  /* C is bigger than Fortran */
102     tmp2  = (tmp3 - tmp1);
103     itmp2 = (size_t) tmp2;
104   } else {
105     tmp2  = (tmp1 - tmp3);
106     itmp2 = -((size_t) tmp2);
107   }
108   shift = 0;
109 #endif
110 
111   if (shift) {
112     /*
113         Fortran and C not PetscScalar aligned,recover by copying values into
114         memory that is aligned with the Fortran
115     */
116     PetscErrorCode       ierr;
117     PetscScalar          *work;
118     PetscObjectContainer container;
119 
120     ierr = PetscMalloc((N+1)*sizeof(PetscScalar),&work);CHKERRQ(ierr);
121 
122     /* shift work by that number of bytes */
123     work = (PetscScalar*)(((char*)work) + shift);
124     ierr = PetscMemcpy(work,addr,N*sizeof(PetscScalar));CHKERRQ(ierr);
125 
126     /* store in the first location in addr how much you shift it */
127     ((PetscInt*)addr)[0] = shift;
128 
129     ierr = PetscObjectContainerCreate(PETSC_COMM_SELF,&container);CHKERRQ(ierr);
130     ierr = PetscObjectContainerSetPointer(container,addr);CHKERRQ(ierr);
131     ierr = PetscObjectCompose(obj,"GetArrayPtr",(PetscObject)container);CHKERRQ(ierr);
132 
133     tmp3 = (size_t) work;
134     if (tmp3 > tmp1) {  /* C is bigger than Fortran */
135       tmp2  = (tmp3 - tmp1)/sizeof(PetscScalar);
136       itmp2 = (size_t) tmp2;
137       shift = (sizeof(PetscScalar) - (int)((tmp3 - tmp1) % sizeof(PetscScalar))) % sizeof(PetscScalar);
138     } else {
139       tmp2  = (tmp1 - tmp3)/sizeof(PetscScalar);
140       itmp2 = -((size_t) tmp2);
141       shift = (int)((tmp1 - tmp3) % sizeof(PetscScalar));
142     }
143     if (shift) {
144       (*PetscErrorPrintf)("PetscScalarAddressToFortran:C and Fortran arrays are\n");
145       (*PetscErrorPrintf)("not commonly aligned.\n");
146       /* double/int doesn't work with ADIC */
147       (*PetscErrorPrintf)("Locations/sizeof(PetscScalar): C %f Fortran %f\n",
148                          ((PetscReal)tmp3)/(PetscReal)sizeof(PetscScalar),((PetscReal)tmp1)/(PetscReal)sizeof(PetscScalar));
149       MPI_Abort(PETSC_COMM_WORLD,1);
150     }
151     ierr = PetscLogInfo(((void*)obj,"PetscScalarAddressToFortran:Efficiency warning, copying array in XXXGetArray() due\n\
152     to alignment differences between C and Fortran\n"));CHKERRQ(ierr);
153   }
154   *res = itmp2;
155   return 0;
156 }
157 
158 /*
159     obj - the PETSc object where the scalar pointer came from
160     base - the Fortran array address
161     addr - the Fortran offset from base
162     N    - the amount of data
163 
164     lx   - the array space that is to be passed to XXXXRestoreArray()
165 */
166 PetscErrorCode PetscScalarAddressFromFortran(PetscObject obj,PetscScalar *base,size_t addr,PetscInt N,PetscScalar **lx)
167 {
168   PetscErrorCode       ierr;
169   PetscInt             shift;
170   PetscObjectContainer container;
171   PetscScalar          *tlx;
172 
173   ierr = PetscObjectQuery(obj,"GetArrayPtr",(PetscObject *)&container);CHKERRQ(ierr);
174   if (container) {
175     ierr  = PetscObjectContainerGetPointer(container,(void**)lx);CHKERRQ(ierr);
176     tlx   = base + addr;
177 
178     shift = *(PetscInt*)*lx;
179     ierr  = PetscMemcpy(*lx,tlx,N*sizeof(PetscScalar));CHKERRQ(ierr);
180     tlx   = (PetscScalar*)(((char *)tlx) - shift);
181     ierr = PetscFree(tlx);CHKERRQ(ierr);
182     ierr = PetscObjectContainerDestroy(container);CHKERRQ(ierr);
183     ierr = PetscObjectCompose(obj,"GetArrayPtr",0);CHKERRQ(ierr);
184   } else {
185     *lx = base + addr;
186   }
187   return 0;
188 }
189 
190 #undef __FUNCT__
191 #define __FUNCT__ "MPICCommToFortranComm"
192 /*@C
193     MPICCommToFortranComm - Converts a MPI_Comm represented
194     in C to one appropriate to pass to a Fortran routine.
195 
196     Not collective
197 
198     Input Parameter:
199 .   cobj - the C MPI_Comm
200 
201     Output Parameter:
202 .   fobj - the Fortran MPI_Comm
203 
204     Level: advanced
205 
206     Notes:
207     MPICCommToFortranComm() must be called in a C/C++ routine.
208     MPI 1 does not provide a standard for mapping between
209     Fortran and C MPI communicators; this routine handles the
210     mapping correctly on all machines.
211 
212 .keywords: Fortran, C, MPI_Comm, convert, interlanguage
213 
214 .seealso: MPIFortranCommToCComm()
215 @*/
216 PetscErrorCode MPICCommToFortranComm(MPI_Comm comm,int *fcomm)
217 {
218   PetscErrorCode ierr;
219   PetscMPIInt    size;
220 
221   PetscFunctionBegin;
222   /* call to MPI_Comm_size() is for error checking on comm */
223   ierr = MPI_Comm_size(comm,&size);
224   if (ierr) SETERRQ(PETSC_ERR_ARG_CORRUPT ,"Invalid MPI communicator");
225 
226   *fcomm = PetscFromPointerComm(comm);
227   PetscFunctionReturn(0);
228 }
229 
230 #undef __FUNCT__
231 #define __FUNCT__ "MPIFortranCommToCComm"
232 /*@C
233     MPIFortranCommToCComm - Converts a MPI_Comm represented
234     int Fortran (as an integer) to a MPI_Comm in C.
235 
236     Not collective
237 
238     Input Parameter:
239 .   fcomm - the Fortran MPI_Comm (an integer)
240 
241     Output Parameter:
242 .   comm - the C MPI_Comm
243 
244     Level: advanced
245 
246     Notes:
247     MPIFortranCommToCComm() must be called in a C/C++ routine.
248     MPI 1 does not provide a standard for mapping between
249     Fortran and C MPI communicators; this routine handles the
250     mapping correctly on all machines.
251 
252 .keywords: Fortran, C, MPI_Comm, convert, interlanguage
253 
254 .seealso: MPICCommToFortranComm()
255 @*/
256 PetscErrorCode MPIFortranCommToCComm(int fcomm,MPI_Comm *comm)
257 {
258   PetscErrorCode ierr;
259   PetscMPIInt    size;
260 
261   PetscFunctionBegin;
262   *comm = (MPI_Comm)PetscToPointerComm(fcomm);
263   /* call to MPI_Comm_size() is for error checking on comm */
264   ierr = MPI_Comm_size(*comm,&size);
265   if (ierr) SETERRQ(PETSC_ERR_ARG_CORRUPT,"Invalid MPI communicator");
266   PetscFunctionReturn(0);
267 }
268 
269 
270 
271