4 use scan_input, only : input_field_type, FIELD_TYPE_REAL, FIELD_TYPE_DOUBLE, FIELD_TYPE_INTEGER
7 integer, parameter :: max_queue_length = 2700 ! should be at least (earth circumference / minimum grid distance)
8 integer, parameter :: max_dictionary_size = 82000 ! should be at least (nCells/32)
10 integer :: queue_head = 0
11 integer :: queue_tail = 0
12 integer :: queue_size = 0
13 integer, dimension(0:max_queue_length-1) :: queue_array
15 integer :: int_size = 32
16 integer, dimension(max_dictionary_size) :: dictionary_array
20 integer :: method = -1
21 type (mpas_mesh_type), pointer :: src_mesh
22 type (target_mesh_type), pointer :: dst_mesh
24 ! For nearest-neighbor
25 integer, dimension(:,:), pointer :: nearestCell => null()
26 integer, dimension(:,:), pointer :: nearestVertex => null()
27 integer, dimension(:,:), pointer :: nearestEdge => null()
29 ! For Wachspress interpolation
30 real, dimension(:,:,:), pointer :: cellWeights => null()
31 real, dimension(:,:,:), pointer :: vertexWeights => null()
32 real, dimension(:,:,:), pointer :: edgeWeights => null()
33 integer, dimension(:,:,:), pointer :: sourceCells => null()
34 integer, dimension(:,:,:), pointer :: sourceVertices => null()
35 integer, dimension(:,:,:), pointer :: sourceEdges => null()
37 ! For masked interpolation
38 real, dimension(:,:,:), pointer :: cellMaskedWeights => null()
39 integer, dimension(:,:,:), pointer :: sourceMaskedCells => null()
40 end type remap_info_type
43 type target_field_type
44 character (len=64) :: name
47 logical :: isTimeDependent = .false.
48 integer, dimension(:), pointer :: dimlens => null()
49 character (len=64), dimension(:), pointer :: dimnames => null()
51 ! Members to store field data
53 real, dimension(:), pointer :: array1r => null()
54 real, dimension(:,:), pointer :: array2r => null()
55 real, dimension(:,:,:), pointer :: array3r => null()
56 real, dimension(:,:,:,:), pointer :: array4r => null()
57 double precision :: array0d
58 double precision, dimension(:), pointer :: array1d => null()
59 double precision, dimension(:,:), pointer :: array2d => null()
60 double precision, dimension(:,:,:), pointer :: array3d => null()
61 double precision, dimension(:,:,:,:), pointer :: array4d => null()
63 integer, dimension(:), pointer :: array1i => null()
64 integer, dimension(:,:), pointer :: array2i => null()
65 integer, dimension(:,:,:), pointer :: array3i => null()
66 integer, dimension(:,:,:,:), pointer :: array4i => null()
67 end type target_field_type
70 private :: nearest_cell, &
74 mpas_triangle_signed_area_sphere, &
75 mpas_wachspress_coordinates, &
83 integer function remap_info_setup(src_mesh, dst_mesh, remap_info) result(stat)
87 type (mpas_mesh_type), intent(in), target :: src_mesh
88 type (target_mesh_type), intent(in), target :: dst_mesh
89 type (remap_info_type), intent(out) :: remap_info
97 real, dimension(:,:), allocatable :: vertCoords
98 real, dimension(3) :: pointInterp
102 remap_info % method = 1
103 remap_info % src_mesh => src_mesh
104 remap_info % dst_mesh => dst_mesh
107 ! For nearest neighbor
109 allocate(remap_info % nearestCell(dst_mesh % nlon, dst_mesh % nlat))
110 allocate(remap_info % nearestVertex(dst_mesh % nlon, dst_mesh % nlat))
111 allocate(remap_info % nearestEdge(dst_mesh % nlon, dst_mesh % nlat))
113 irank = dst_mesh % irank
116 do iy=1,dst_mesh % nlat
117 do ix=1,dst_mesh % nlon
118 idx = nearest_cell(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), idx, &
119 src_mesh % nCells, src_mesh % maxEdges, &
120 src_mesh % nEdgesOnCell, src_mesh % cellsOnCell, src_mesh % latCell, src_mesh % lonCell)
121 remap_info % nearestCell(ix, iy) = idx
126 do iy=1,dst_mesh % nlat
127 do ix=1,dst_mesh % nlon
128 idx = nearest_vertex(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), idx, &
129 src_mesh % nCells, src_mesh % nVertices, src_mesh % maxEdges, 3, &
130 src_mesh % nEdgesOnCell, src_mesh % verticesOnCell, &
131 src_mesh % cellsOnVertex, src_mesh % latCell, src_mesh % lonCell, &
132 src_mesh % latVertex, src_mesh % lonVertex )
133 remap_info % nearestVertex(ix, iy) = idx
138 do iy=1,dst_mesh % nlat
139 do ix=1,dst_mesh % nlon
140 idx = nearest_vertex(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), idx, &
141 src_mesh % nCells, src_mesh % nEdges, src_mesh % maxEdges, 2, &
142 src_mesh % nEdgesOnCell, src_mesh % edgesOnCell, &
143 src_mesh % cellsOnEdge, src_mesh % latCell, src_mesh % lonCell, &
144 src_mesh % latEdge, src_mesh % lonEdge )
145 remap_info % nearestEdge(ix, iy) = idx
151 ! For Wachspress interpolation
153 allocate(vertCoords(3,3))
155 allocate(remap_info % cellWeights(3, dst_mesh % nlon, dst_mesh % nlat))
156 allocate(remap_info % sourceCells(3, dst_mesh % nlon, dst_mesh % nlat))
159 do iy=1,dst_mesh % nlat
160 do ix=1,dst_mesh % nlon
161 idx = nearest_vertex(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), idx, &
162 src_mesh % nCells, src_mesh % nVertices, src_mesh % maxEdges, 3, &
163 src_mesh % nEdgesOnCell, src_mesh % verticesOnCell, &
164 src_mesh % cellsOnVertex, src_mesh % latCell, src_mesh % lonCell, &
165 src_mesh % latVertex, src_mesh % lonVertex )
166 remap_info % sourceCells(:,ix,iy) = src_mesh % cellsOnVertex(:,idx)
167 pointInterp(:) = convert_lx(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), 6371229.0)
169 vertCoords(:,j) = convert_lx(src_mesh % latCell(src_mesh % cellsOnVertex(j,idx)), &
170 src_mesh % lonCell(src_mesh % cellsOnVertex(j,idx)), &
173 remap_info % cellWeights(:,ix,iy) = mpas_wachspress_coordinates(3, vertCoords, pointInterp)
177 deallocate(vertCoords)
180 allocate(vertCoords(3,src_mesh % maxEdges))
182 allocate(remap_info % vertexWeights(src_mesh % maxEdges, dst_mesh % nlon, dst_mesh % nlat))
183 allocate(remap_info % sourceVertices(src_mesh % maxEdges, dst_mesh % nlon, dst_mesh % nlat))
186 do iy=1,dst_mesh % nlat
187 do ix=1,dst_mesh % nlon
188 idx = nearest_cell(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), idx, &
189 src_mesh % nCells, src_mesh % maxEdges, &
190 src_mesh % nEdgesOnCell, src_mesh % cellsOnCell, src_mesh % latCell, src_mesh % lonCell)
191 nn = src_mesh % nEdgesOnCell(idx)
192 remap_info % sourceVertices(:,ix,iy) = 1
193 remap_info % sourceVertices(1:nn,ix,iy) = src_mesh % verticesOnCell(1:nn,idx)
194 pointInterp(:) = convert_lx(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), 6371229.0)
196 vertCoords(:,j) = convert_lx(src_mesh % latVertex(src_mesh % verticesOnCell(j,idx)), &
197 src_mesh % lonVertex(src_mesh % verticesOnCell(j,idx)), &
200 remap_info % vertexWeights(:,ix,iy) = 0.0
201 remap_info % vertexWeights(1:nn,ix,iy) = mpas_wachspress_coordinates(3, vertCoords(:,1:nn), pointInterp)
205 deallocate(vertCoords)
208 allocate(vertCoords(3,src_mesh % maxEdges))
210 allocate(remap_info % edgeWeights(src_mesh % maxEdges, dst_mesh % nlon, dst_mesh % nlat))
211 allocate(remap_info % sourceEdges(src_mesh % maxEdges, dst_mesh % nlon, dst_mesh % nlat))
214 do iy=1,dst_mesh % nlat
215 do ix=1,dst_mesh % nlon
216 idx = nearest_cell(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), idx, &
217 src_mesh % nCells, src_mesh % maxEdges, &
218 src_mesh % nEdgesOnCell, src_mesh % cellsOnCell, src_mesh % latCell, src_mesh % lonCell)
219 nn = src_mesh % nEdgesOnCell(idx)
220 remap_info % sourceEdges(:,ix,iy) = 1
221 remap_info % sourceEdges(1:nn,ix,iy) = src_mesh % edgesOnCell(1:nn,idx)
222 pointInterp(:) = convert_lx(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), 6371229.0)
224 vertCoords(:,j) = convert_lx(src_mesh % latEdge(src_mesh % edgesOnCell(j,idx)), &
225 src_mesh % lonEdge(src_mesh % edgesOnCell(j,idx)), &
228 remap_info % edgeWeights(:,ix,iy) = 0.0
229 remap_info % edgeWeights(1:nn,ix,iy) = mpas_wachspress_coordinates(3, vertCoords(:,1:nn), pointInterp)
233 deallocate(vertCoords)
237 ! For masked interpolation
239 allocate(vertCoords(3,3))
241 allocate(remap_info % cellMaskedWeights(3, dst_mesh % nlon, dst_mesh % nlat))
242 allocate(remap_info % sourceMaskedCells(3, dst_mesh % nlon, dst_mesh % nlat))
245 do iy=1,dst_mesh % nlat
246 do ix=1,dst_mesh % nlon
247 idx = nearest_vertex(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), idx, &
248 src_mesh % nCells, src_mesh % nVertices, src_mesh % maxEdges, 3, &
249 src_mesh % nEdgesOnCell, src_mesh % verticesOnCell, &
250 src_mesh % cellsOnVertex, src_mesh % latCell, src_mesh % lonCell, &
251 src_mesh % latVertex, src_mesh % lonVertex )
252 remap_info % sourceMaskedCells(:,ix,iy) = src_mesh % cellsOnVertex(:,idx)
253 pointInterp(:) = convert_lx(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), 6371229.0)
255 vertCoords(:,j) = convert_lx(src_mesh % latCell(src_mesh % cellsOnVertex(j,idx)), &
256 src_mesh % lonCell(src_mesh % cellsOnVertex(j,idx)), &
259 remap_info % cellMaskedWeights(:,ix,iy) = mpas_wachspress_coordinates(3, vertCoords, pointInterp)
262 if (src_mesh % landmask(remap_info % sourceMaskedCells(j,ix,iy)) == 1) then
263 sumWeights = sumWeights + remap_info % cellMaskedWeights(j,ix,iy)
265 remap_info % cellMaskedWeights(j,ix,iy) = 0.0
268 if (sumWeights > 0.0) then
269 remap_info % cellMaskedWeights(:,ix,iy) = remap_info % cellMaskedWeights(:,ix,iy) / sumWeights
271 idx = nearest_cell(dst_mesh % lats(index2d(irank,ix),iy), dst_mesh % lons(ix,index2d(irank,iy)), &
272 remap_info % sourceMaskedCells(1,ix,iy), &
273 src_mesh % nCells, src_mesh % maxEdges, &
274 src_mesh % nEdgesOnCell, src_mesh % cellsOnCell, src_mesh % latCell, src_mesh % lonCell)
275 call search_for_cells(src_mesh % nCells, src_mesh % maxEdges, src_mesh % nEdgesOnCell, src_mesh % cellsOnCell, &
276 src_mesh % landmask, dst_mesh % lats(index2d(irank,ix),iy), &
277 dst_mesh % lons(ix,index2d(irank,iy)), &
278 src_mesh % latCell, src_mesh % lonCell, idx, remap_info % sourceMaskedCells(:,ix,iy), &
279 remap_info % cellMaskedWeights(:,ix,iy))
284 deallocate(vertCoords)
286 end function remap_info_setup
289 integer function remap_info_free(remap_info) result(stat)
293 type (remap_info_type), intent(inout) :: remap_info
298 remap_info % method = -1
299 nullify(remap_info % src_mesh)
300 nullify(remap_info % dst_mesh)
302 if (associated(remap_info % nearestCell)) then
303 deallocate(remap_info % nearestCell)
305 if (associated(remap_info % nearestVertex)) then
306 deallocate(remap_info % nearestVertex)
308 if (associated(remap_info % nearestEdge)) then
309 deallocate(remap_info % nearestEdge)
311 if (associated(remap_info % cellWeights)) then
312 deallocate(remap_info % cellWeights)
314 if (associated(remap_info % vertexWeights)) then
315 deallocate(remap_info % vertexWeights)
317 if (associated(remap_info % edgeWeights)) then
318 deallocate(remap_info % edgeWeights)
320 if (associated(remap_info % sourceCells)) then
321 deallocate(remap_info % sourceCells)
323 if (associated(remap_info % sourceVertices)) then
324 deallocate(remap_info % sourceVertices)
326 if (associated(remap_info % sourceEdges)) then
327 deallocate(remap_info % sourceEdges)
329 if (associated(remap_info % cellMaskedWeights)) then
330 deallocate(remap_info % cellMaskedWeights)
332 if (associated(remap_info % sourceMaskedCells)) then
333 deallocate(remap_info % sourceMaskedCells)
336 end function remap_info_free
339 logical function can_remap_field(field)
343 type (input_field_type), intent(in) :: field
348 can_remap_field = .true.
350 if (field % xtype /= FIELD_TYPE_INTEGER .and. &
351 field % xtype /= FIELD_TYPE_REAL .and. &
352 field % xtype /= FIELD_TYPE_DOUBLE) then
353 can_remap_field = .false.
357 if (field % ndims == 0 .or. &
358 (field % ndims == 1 .and. field % isTimeDependent)) then
359 can_remap_field = .false.
363 decompDim = field % ndims
364 if (field % isTimeDependent) then
365 decompDim = decompDim - 1
368 if (trim(field % dimnames(decompDim)) /= 'nCells' .and. &
369 trim(field % dimnames(decompDim)) /= 'nVertices' .and. &
370 trim(field % dimnames(decompDim)) /= 'nEdges') then
372 can_remap_field = .false.
376 end function can_remap_field
379 integer function remap_field_dryrun(remap_info, src_field, dst_field) result(stat)
383 type (remap_info_type), intent(in) :: remap_info
384 type (input_field_type), intent(in) :: src_field
385 type (target_field_type), intent(out) :: dst_field
391 dst_field % name = src_field % name
392 dst_field % xtype = src_field % xtype
393 if (src_field % isTimeDependent) then
394 ! Single horizontal dimension becomes two horizontal dimensions, nlat and nlon,
395 ! but the time dimension is not counted in the target field
396 dst_field % ndims = src_field % ndims
398 ! Single horizontal dimension becomes two horizontal dimensions, nlat and nlon
399 dst_field % ndims = src_field % ndims + 1
401 allocate(dst_field % dimnames(dst_field % ndims))
402 allocate(dst_field % dimlens(dst_field % ndims))
403 dst_field % isTimeDependent = src_field % isTimeDependent
404 do idim=1,dst_field % ndims-2
405 dst_field % dimlens(idim) = src_field % dimlens(idim)
406 dst_field % dimnames(idim) = src_field % dimnames(idim)
409 dst_field % dimlens(dst_field % ndims-1) = remap_info % dst_mesh % nlon
410 dst_field % dimnames(dst_field % ndims-1) = 'nLons'
411 dst_field % dimlens(dst_field % ndims) = remap_info % dst_mesh % nlat
412 dst_field % dimnames(dst_field % ndims) = 'nLats'
414 end function remap_field_dryrun
417 integer function remap_field(remap_info, src_field, dst_field, masked) result(stat)
421 type (remap_info_type), intent(in) :: remap_info
422 type (input_field_type), intent(in) :: src_field
423 type (target_field_type), intent(out) :: dst_field
424 logical, intent(in), optional :: masked
430 logical :: local_masked
431 integer, dimension(:,:), pointer :: nearestIndex
432 integer, dimension(:,:,:), pointer :: sourceNodes
433 real, dimension(:,:,:), pointer :: nodeWeights
437 decompDim = src_field % ndims
438 if (src_field % isTimeDependent) then
439 decompDim = decompDim - 1
442 local_masked = .false.
443 if (present(masked)) then
444 local_masked = masked
447 if (trim(src_field % dimnames(decompDim)) == 'nCells') then
448 nearestIndex => remap_info % nearestCell
449 if (local_masked) then
450 sourceNodes => remap_info % sourceMaskedCells
451 nodeWeights => remap_info % cellMaskedWeights
453 sourceNodes => remap_info % sourceCells
454 nodeWeights => remap_info % cellWeights
456 else if (trim(src_field % dimnames(decompDim)) == 'nVertices') then
457 nearestIndex => remap_info % nearestVertex
458 sourceNodes => remap_info % sourceVertices
459 nodeWeights => remap_info % vertexWeights
460 else if (trim(src_field % dimnames(decompDim)) == 'nEdges') then
461 nearestIndex => remap_info % nearestEdge
462 sourceNodes => remap_info % sourceEdges
463 nodeWeights => remap_info % edgeWeights
465 write(0,*) 'Remap exception: unhandled decomposed dim'
470 dst_field % name = src_field % name
471 dst_field % xtype = src_field % xtype
472 if (src_field % isTimeDependent) then
473 ! Single horizontal dimension becomes two horizontal dimensions, nlat and nlon,
474 ! but the time dimension is not counted in the target field
475 dst_field % ndims = src_field % ndims
477 ! Single horizontal dimension becomes two horizontal dimensions, nlat and nlon
478 dst_field % ndims = src_field % ndims + 1
480 allocate(dst_field % dimnames(dst_field % ndims))
481 allocate(dst_field % dimlens(dst_field % ndims))
482 dst_field % isTimeDependent = src_field % isTimeDependent
483 do idim=1,dst_field % ndims-2
484 dst_field % dimlens(idim) = src_field % dimlens(idim)
485 dst_field % dimnames(idim) = src_field % dimnames(idim)
487 dst_field % dimlens(dst_field % ndims-1) = remap_info % dst_mesh % nlon
488 dst_field % dimnames(dst_field % ndims-1) = 'nLons'
489 dst_field % dimlens(dst_field % ndims) = remap_info % dst_mesh % nlat
490 dst_field % dimnames(dst_field % ndims) = 'nLats'
492 if (src_field % xtype == FIELD_TYPE_REAL) then
493 if (dst_field % ndims == 2) then
494 allocate(dst_field % array2r(dst_field % dimlens(1), &
495 dst_field % dimlens(2)))
496 #ifdef NEAREST_NEIGHBOR
497 do iy=1,size(nearestIndex, dim=2)
498 do ix=1,size(nearestIndex, dim=1)
499 dst_field % array2r(ix,iy) = src_field % array1r(nearestIndex(ix,iy))
503 do iy=1,size(nearestIndex, dim=2)
504 do ix=1,size(nearestIndex, dim=1)
505 dst_field % array2r(ix,iy) = 0.0
506 do j=1,size(sourceNodes, dim=1)
507 dst_field % array2r(ix,iy) = dst_field % array2r(ix,iy) + &
508 nodeWeights(j,ix,iy) &
509 * src_field % array1r(sourceNodes(j,ix,iy))
514 else if (dst_field % ndims == 3) then
515 allocate(dst_field % array3r(dst_field % dimlens(1), &
516 dst_field % dimlens(2), &
517 dst_field % dimlens(3)))
518 #ifdef NEAREST_NEIGHBOR
519 do iy=1,size(nearestIndex, dim=2)
520 do ix=1,size(nearestIndex, dim=1)
521 dst_field % array3r(:,ix,iy) = src_field % array2r(:,nearestIndex(ix,iy))
525 do iy=1,size(nearestIndex, dim=2)
526 do ix=1,size(nearestIndex, dim=1)
527 dst_field % array3r(:,ix,iy) = 0.0
529 dst_field % array3r(:,ix,iy) = dst_field % array3r(:,ix,iy) + &
530 nodeWeights(j,ix,iy) &
531 * src_field % array2r(:,sourceNodes(j,ix,iy))
536 else if (dst_field % ndims == 4) then
537 allocate(dst_field % array4r(dst_field % dimlens(1), &
538 dst_field % dimlens(2), &
539 dst_field % dimlens(3), &
540 dst_field % dimlens(4)))
541 #ifdef NEAREST_NEIGHBOR
542 do iy=1,size(nearestIndex, dim=2)
543 do ix=1,size(nearestIndex, dim=1)
544 dst_field % array4r(:,:,ix,iy) = src_field % array3r(:,:,nearestIndex(ix,iy))
548 do iy=1,size(nearestIndex, dim=2)
549 do ix=1,size(nearestIndex, dim=1)
550 dst_field % array4r(:,:,ix,iy) = 0.0
552 dst_field % array4r(:,:,ix,iy) = dst_field % array4r(:,:,ix,iy) + &
553 remap_info % cellWeights(j,ix,iy) &
554 * src_field % array3r(:,:,remap_info % sourceCells(j,ix,iy))
559 write(0,*) 'Remap exception: unhandled dimension for real ', dst_field % ndims
561 else if (src_field % xtype == FIELD_TYPE_DOUBLE) then
562 if (dst_field % ndims == 2) then
563 allocate(dst_field % array2d(dst_field % dimlens(1), &
564 dst_field % dimlens(2)))
565 #ifdef NEAREST_NEIGHBOR
566 do iy=1,size(nearestIndex, dim=2)
567 do ix=1,size(nearestIndex, dim=1)
568 dst_field % array2d(ix,iy) = src_field % array1d(nearestIndex(ix,iy))
572 do iy=1,size(nearestIndex, dim=2)
573 do ix=1,size(nearestIndex, dim=1)
574 dst_field % array2d(ix,iy) = 0.0
575 do j=1,size(sourceNodes, dim=1)
576 dst_field % array2d(ix,iy) = dst_field % array2d(ix,iy) + &
577 nodeWeights(j,ix,iy) &
578 * src_field % array1d(sourceNodes(j,ix,iy))
582 else if (dst_field % ndims == 3) then
583 allocate(dst_field % array3d(dst_field % dimlens(1), &
584 dst_field % dimlens(2), &
585 dst_field % dimlens(3)))
586 #ifdef NEAREST_NEIGHBOR
587 do iy=1,size(nearestIndex, dim=2)
588 do ix=1,size(nearestIndex, dim=1)
589 dst_field % array3d(:,ix,iy) = src_field % array2d(:,nearestIndex(ix,iy))
593 do iy=1,size(nearestIndex, dim=2)
594 do ix=1,size(nearestIndex, dim=1)
595 dst_field % array3d(:,ix,iy) = 0.0
597 dst_field % array3d(:,ix,iy) = dst_field % array3d(:,ix,iy) + &
598 remap_info % cellWeights(j,ix,iy) &
599 * src_field % array2d(:,remap_info % sourceCells(j,ix,iy))
603 else if (dst_field % ndims == 4) then
604 allocate(dst_field % array4d(dst_field % dimlens(1), &
605 dst_field % dimlens(2), &
606 dst_field % dimlens(3), &
607 dst_field % dimlens(4)))
608 #ifdef NEAREST_NEIGHBOR
609 do iy=1,size(nearestIndex, dim=2)
610 do ix=1,size(nearestIndex, dim=1)
611 dst_field % array4d(:,:,ix,iy) = src_field % array3d(:,:,nearestIndex(ix,iy))
615 do iy=1,size(nearestIndex, dim=2)
616 do ix=1,size(nearestIndex, dim=1)
617 dst_field % array4d(:,:,ix,iy) = 0.0
619 dst_field % array4d(:,:,ix,iy) = dst_field % array4d(:,:,ix,iy) + &
620 remap_info % cellWeights(j,ix,iy) &
621 * src_field % array3d(:,:,remap_info % sourceCells(j,ix,iy))
626 write(0,*) 'Remap exception: unhandled dimension for dbl ', dst_field % ndims
628 else if (src_field % xtype == FIELD_TYPE_INTEGER) then
629 if (dst_field % ndims == 2) then
630 allocate(dst_field % array2i(dst_field % dimlens(1), &
631 dst_field % dimlens(2)))
632 do iy=1,size(nearestIndex, dim=2)
633 do ix=1,size(nearestIndex, dim=1)
634 dst_field % array2i(ix,iy) = src_field % array1i(nearestIndex(ix,iy))
637 else if (dst_field % ndims == 3) then
638 allocate(dst_field % array3i(dst_field % dimlens(1), &
639 dst_field % dimlens(2), &
640 dst_field % dimlens(3)))
641 do iy=1,size(nearestIndex, dim=2)
642 do ix=1,size(nearestIndex, dim=1)
643 dst_field % array3i(:,ix,iy) = src_field % array2i(:,nearestIndex(ix,iy))
646 else if (dst_field % ndims == 4) then
647 allocate(dst_field % array4i(dst_field % dimlens(1), &
648 dst_field % dimlens(2), &
649 dst_field % dimlens(3), &
650 dst_field % dimlens(4)))
651 do iy=1,size(nearestIndex, dim=2)
652 do ix=1,size(nearestIndex, dim=1)
653 dst_field % array4i(:,:,ix,iy) = src_field % array3i(:,:,nearestIndex(ix,iy))
657 write(0,*) 'Remap exception: unhandled dimension for int ', dst_field % ndims
660 write(0,*) 'Remap exception: unhandled type'
663 end function remap_field
666 integer function remap_get_target_latitudes(remap_info, lat_field) result(stat)
670 type (remap_info_type), intent(in) :: remap_info
671 type (target_field_type), intent(out) :: lat_field
673 real, parameter :: rad2deg = 90.0 / asin(1.0)
678 lat_field % name = 'lat'
679 lat_field % xtype = FIELD_TYPE_REAL
680 lat_field % ndims = 1
681 lat_field % isTimeDependent = .false.
683 allocate(lat_field % dimnames(lat_field % ndims))
684 allocate(lat_field % dimlens(lat_field % ndims))
686 lat_field % dimnames(1) = 'nLats'
687 lat_field % dimlens(1) = remap_info % dst_mesh % nlat
689 allocate(lat_field % array1r(lat_field % dimlens(1)))
690 lat_field % array1r(:) = remap_info % dst_mesh % lats(1,:) * rad2deg
692 end function remap_get_target_latitudes
695 integer function remap_get_target_longitudes(remap_info, lon_field) result(stat)
699 type (remap_info_type), intent(in) :: remap_info
700 type (target_field_type), intent(out) :: lon_field
702 real, parameter :: rad2deg = 90.0 / asin(1.0)
707 lon_field % name = 'lon'
708 lon_field % xtype = FIELD_TYPE_REAL
709 lon_field % ndims = 1
710 lon_field % isTimeDependent = .false.
712 allocate(lon_field % dimnames(lon_field % ndims))
713 allocate(lon_field % dimlens(lon_field % ndims))
715 lon_field % dimnames(1) = 'nLons'
716 lon_field % dimlens(1) = remap_info % dst_mesh % nlon
718 allocate(lon_field % array1r(lon_field % dimlens(1)))
719 lon_field % array1r(:) = remap_info % dst_mesh % lons(:,1) * rad2deg
721 end function remap_get_target_longitudes
724 integer function free_target_field(field) result(stat)
728 type (target_field_type), intent(inout) :: field
732 if (associated(field % dimlens)) then
733 deallocate(field % dimlens)
735 if (associated(field % dimnames)) then
736 deallocate(field % dimnames)
739 if (associated(field % array1r)) then
740 deallocate(field % array1r)
742 if (associated(field % array2r)) then
743 deallocate(field % array2r)
745 if (associated(field % array3r)) then
746 deallocate(field % array3r)
748 if (associated(field % array4r)) then
749 deallocate(field % array4r)
752 if (associated(field % array1d)) then
753 deallocate(field % array1d)
755 if (associated(field % array2d)) then
756 deallocate(field % array2d)
758 if (associated(field % array3d)) then
759 deallocate(field % array3d)
761 if (associated(field % array4d)) then
762 deallocate(field % array4d)
765 if (associated(field % array1i)) then
766 deallocate(field % array1i)
768 if (associated(field % array2i)) then
769 deallocate(field % array2i)
771 if (associated(field % array3i)) then
772 deallocate(field % array3i)
774 if (associated(field % array4i)) then
775 deallocate(field % array4i)
778 end function free_target_field
781 integer function nearest_cell(target_lat, target_lon, start_cell, nCells, maxEdges, &
782 nEdgesOnCell, cellsOnCell, latCell, lonCell)
786 real, intent(in) :: target_lat, target_lon
787 integer, intent(in) :: start_cell
788 integer, intent(in) :: nCells, maxEdges
789 integer, dimension(nCells), intent(in) :: nEdgesOnCell
790 integer, dimension(maxEdges,nCells), intent(in) :: cellsOnCell
791 real, dimension(nCells), intent(in) :: latCell, lonCell
795 integer :: current_cell
796 real :: current_distance, d
797 real :: nearest_distance
799 nearest_cell = start_cell
802 do while (nearest_cell /= current_cell)
803 current_cell = nearest_cell
804 current_distance = sphere_distance(latCell(current_cell), lonCell(current_cell), target_lat, &
806 nearest_cell = current_cell
807 nearest_distance = current_distance
808 do i = 1, nEdgesOnCell(current_cell)
809 iCell = cellsOnCell(i,current_cell)
810 if (iCell <= nCells) then
811 d = sphere_distance(latCell(iCell), lonCell(iCell), target_lat, target_lon, 1.0)
812 if (d < nearest_distance) then
820 end function nearest_cell
823 integer function nearest_vertex( target_lat, target_lon, &
825 nCells, nVertices, maxEdges, vertexDegree, &
826 nEdgesOnCell, verticesOnCell, &
827 cellsOnVertex, latCell, lonCell, &
828 latVertex, lonVertex )
832 real, intent(in) :: target_lat, target_lon
833 integer, intent(in) :: start_vertex
834 integer, intent(in) :: nCells, nVertices, maxEdges, vertexDegree
835 integer, dimension(nCells), intent(in) :: nEdgesOnCell
836 integer, dimension(maxEdges,nCells), intent(in) :: verticesOnCell
837 integer, dimension(vertexDegree,nVertices), intent(in) :: cellsOnVertex
838 real, dimension(nCells), intent(in) :: latCell, lonCell
839 real, dimension(nVertices), intent(in) :: latVertex, lonVertex
842 integer :: i, cell1, cell2, cell3, iCell
844 integer :: current_vertex
845 real :: cell1_dist, cell2_dist, cell3_dist
846 real :: current_distance, d
847 real :: nearest_distance
849 nearest_vertex = start_vertex
852 do while (nearest_vertex /= current_vertex)
853 current_vertex = nearest_vertex
854 current_distance = sphere_distance(latVertex(current_vertex), lonVertex(current_vertex), &
855 target_lat, target_lon, 1.0)
856 nearest_vertex = current_vertex
857 nearest_distance = current_distance
858 cell1 = cellsOnVertex(1,current_vertex)
859 cell1_dist = sphere_distance(latCell(cell1), lonCell(cell1), target_lat, target_lon, 1.0)
860 cell2 = cellsOnVertex(2,current_vertex)
861 cell2_dist = sphere_distance(latCell(cell2), lonCell(cell2), target_lat, target_lon, 1.0)
862 if (vertexDegree == 3) then
863 cell3 = cellsOnVertex(3,current_vertex)
864 cell3_dist = sphere_distance(latCell(cell3), lonCell(cell3), target_lat, target_lon, 1.0)
866 if (vertexDegree == 3) then
867 if (cell1_dist < cell2_dist) then
868 if (cell1_dist < cell3_dist) then
874 if (cell2_dist < cell3_dist) then
881 if (cell1_dist < cell2_dist) then
887 do i = 1, nEdgesOnCell(iCell)
888 iVtx = verticesOnCell(i,iCell)
889 d = sphere_distance(latVertex(iVtx), lonVertex(iVtx), target_lat, target_lon, 1.0)
890 if (d < nearest_distance) then
891 nearest_vertex = iVtx
897 end function nearest_vertex
900 real function sphere_distance(lat1, lon1, lat2, lon2, radius)
901 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
902 ! Compute the great-circle distance between (lat1, lon1) and (lat2, lon2) on a
903 ! sphere with given radius.
904 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
908 real, intent(in) :: lat1, lon1, lat2, lon2, radius
912 arg1 = sqrt( sin(0.5*(lat2-lat1))**2 + &
913 cos(lat1)*cos(lat2)*sin(0.5*(lon2-lon1))**2 )
914 sphere_distance = 2.*radius*asin(arg1)
916 end function sphere_distance
919 !***********************************************************************
921 ! function mpas_wachspress_coordinates
923 !> \brief Compute the barycentric Wachspress coordinates for a polygon
924 !> \author Phillip Wolfram
927 !> Computes the barycentric Wachspress coordinates for a polygon with nVertices
928 !> points in R3, vertCoords for a particular pointInterp with normalized radius.
929 !> Follows Gillette, A., Rand, A., Bajaj, C., 2011.
930 !> Error estimates for generalized barycentric interpolation.
931 !> Advances in computational mathematics 37 (3), 417–439.
932 !> Optimized version of mpas_wachspress_coordinates uses optional cached B_i areas
933 !------------------------------------------------------------------------
934 function mpas_wachspress_coordinates(nVertices, vertCoords, pointInterp, areaBin)
939 integer, intent(in) :: nVertices
940 real, dimension(3, nVertices), intent(in) :: vertCoords
941 real, dimension(3), intent(in) :: pointInterp
942 real, dimension(nVertices), optional, intent(in) :: areaBin
945 real, dimension(nVertices) :: mpas_wachspress_coordinates
947 ! computational intermediates
948 real, dimension(nVertices) :: wach ! The wachpress area-product
949 real :: wach_total ! The wachpress total weight
950 integer :: i, j ! Loop indices
951 integer :: im1, i0, ip1 ! im1 = (i-1), i0 = i, ip1 = (i+1)
953 ! triangle areas to compute wachspress coordinate
954 real, dimension(nVertices) :: areaA
955 real, dimension(nVertices) :: areaB
959 radiusLocal = sqrt(sum(vertCoords(:,1)**2))
961 if (.not. present(areaBin)) then
964 ! compute first area B_i
966 im1 = mod(nVertices + i - 2, nVertices) + 1
967 i0 = mod(nVertices + i - 1, nVertices) + 1
968 ip1 = mod(nVertices + i , nVertices) + 1
970 ! precompute B_i areas
971 ! always the same because B_i independent of xp,yp,zp
972 ! (COULD CACHE AND USE RESULT FROM ARRAY FOR FURTHER OPTIMIZATION)
973 areaB(i) = mpas_triangle_signed_area_sphere(vertCoords(:, im1), vertCoords(:, i0), vertCoords(:, ip1), radiusLocal)
978 areaB(i) = areaBin(i)
984 ! compute first area B_i
986 im1 = mod(nVertices + i - 2, nVertices) + 1
987 i0 = mod(nVertices + i - 1, nVertices) + 1
988 ip1 = mod(nVertices + i , nVertices) + 1
991 ! must be computed each time
992 areaA(i0) = mpas_triangle_signed_area_sphere(pointInterp, vertCoords(:, i0), vertCoords(:, ip1), radiusLocal)
994 ! precomputed B_i areas, cached
998 ! for each vertex compute wachpress coordinate
1001 do j = (i + 1), (i + nVertices - 2)
1002 i0 = mod(nVertices + j - 1, nVertices) + 1
1003 ! accumulate products for A_ij subareas
1004 wach(i) = wach(i) * areaA(i0)
1008 ! get summed weights for normalization
1011 wach_total = wach_total + wach(i)
1015 mpas_wachspress_coordinates= 0.0
1017 mpas_wachspress_coordinates(i) = wach(i)/wach_total
1020 end function mpas_wachspress_coordinates
1023 !***********************************************************************
1025 ! routine mpas_triangle_signed_area_sphere
1027 !> \brief Calculates area of a triangle on a sphere
1028 !> \author Matthew Hoffman
1029 !> \date 13 January 2015
1031 !> This routine calculates the area of a triangle on the surface of a sphere.
1032 !> Uses the spherical analog of Heron's formula.
1033 !> Copied from mesh generator. A CCW winding angle is positive.
1034 !-----------------------------------------------------------------------
1035 real function mpas_triangle_signed_area_sphere(a, b, c, radius)
1039 !-----------------------------------------------------------------
1041 !-----------------------------------------------------------------
1042 real, dimension(3), intent(in) :: a, b, c !< Input: 3d (x,y,z) points forming the triangle in which to calculate the bary weights
1043 real, intent(in) :: radius !< sphere radius
1045 !-----------------------------------------------------------------
1047 !-----------------------------------------------------------------
1048 real :: ab, bc, ca, semiperim, tanqe
1049 real, dimension(3) :: ablen, aclen, Dlen
1051 ab = mpas_arc_length(a(1), a(2), a(3), b(1), b(2), b(3))/radius
1052 bc = mpas_arc_length(b(1), b(2), b(3), c(1), c(2), c(3))/radius
1053 ca = mpas_arc_length(c(1), c(2), c(3), a(1), a(2), a(3))/radius
1054 semiperim = 0.5 * (ab + bc + ca)
1056 tanqe = sqrt(max(0.0,tan(0.5 * semiperim) * tan(0.5 * (semiperim - ab)) &
1057 * tan(0.5 * (semiperim - bc)) * tan(0.5 * (semiperim - ca))))
1059 mpas_triangle_signed_area_sphere = 4.0 * radius * radius * atan(tanqe)
1061 ! computing correct signs (in similar fashion to mpas_sphere_angle)
1062 ablen(1) = b(1) - a(1)
1063 ablen(2) = b(2) - a(2)
1064 ablen(3) = b(3) - a(3)
1066 aclen(1) = c(1) - a(1)
1067 aclen(2) = c(2) - a(2)
1068 aclen(3) = c(3) - a(3)
1070 dlen(1) = (ablen(2) * aclen(3)) - (ablen(3) * aclen(2))
1071 dlen(2) = -((ablen(1) * aclen(3)) - (ablen(3) * aclen(1)))
1072 dlen(3) = (ablen(1) * aclen(2)) - (ablen(2) * aclen(1))
1074 if ((Dlen(1)*a(1) + Dlen(2)*a(2) + Dlen(3)*a(3)) < 0.0) then
1075 mpas_triangle_signed_area_sphere = -mpas_triangle_signed_area_sphere
1078 end function mpas_triangle_signed_area_sphere
1081 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1082 ! FUNCTION MPAS_ARC_LENGTH
1084 ! Returns the length of the great circle arc from A=(ax, ay, az) to
1085 ! B=(bx, by, bz). It is assumed that both A and B lie on the surface of the
1086 ! same sphere centered at the origin.
1087 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1088 real function mpas_arc_length(ax, ay, az, bx, by, bz)
1092 real, intent(in) :: ax, ay, az, bx, by, bz
1101 r = sqrt(ax*ax + ay*ay + az*az)
1102 c = sqrt(cx*cx + cy*cy + cz*cz)
1104 mpas_arc_length = r * 2.0 * asin(c/(2.0*r))
1106 end function mpas_arc_length
1109 function convert_lx(lat, lon, radius) result(vec)
1113 real, intent(in) :: lat, lon, radius
1115 real, dimension(3) :: vec
1117 vec(1) = radius * cos(lon) * cos(lat)
1118 vec(2) = radius * sin(lon) * cos(lat)
1119 vec(3) = radius * sin(lat)
1121 end function convert_lx
1124 subroutine search_for_cells(nCells, maxEdges, nEdgesOnCell, cellsOnCell, cellMask, &
1125 targetLat, targetLon, latCell, lonCell, &
1126 startIdx, sourceMaskedCells, cellMaskedWeights)
1130 integer, intent(in) :: nCells
1131 integer, intent(in) :: maxEdges
1132 integer, dimension(nCells), intent(in) :: nEdgesOnCell
1133 integer, dimension(maxEdges,nCells), intent(in) :: cellsOnCell
1134 integer, dimension(nCells), intent(in) :: cellMask
1135 real, intent(in) :: targetLat
1136 real, intent(in) :: targetLon
1137 real, dimension(nCells), intent(in) :: latCell
1138 real, dimension(nCells), intent(in) :: lonCell
1139 integer, intent(in) :: startIdx
1140 integer, dimension(3), intent(inout) :: sourceMaskedCells
1141 real, dimension(3), intent(inout) :: cellMaskedWeights
1144 integer :: scan_cell
1146 integer :: unscanned
1147 logical :: no_more_queueing
1151 ! Reset data structures
1154 call dictionary_reset()
1156 no_more_queueing = .false.
1161 sourceMaskedCells(:) = 1
1162 cellMaskedWeights(:) = 0.0
1165 ! Insert the origin cell into the queue for processing
1167 call queue_insert(startIdx)
1168 call dictionary_insert(startIdx)
1170 do while (queue_size > 0)
1171 scan_cell = queue_remove()
1174 ! Each cell index removed from the queue represents a unique grid cell
1175 ! that falls within the specified radius of the origin point
1176 ! Here, we can do any processing we like for these cells
1178 if (cellMask(scan_cell) == 1) then
1179 d = sphere_distance(targetLat, targetLon, latCell(scan_cell), lonCell(scan_cell), 1.0)
1182 sourceMaskedCells(1) = scan_cell
1183 cellMaskedWeights(1) = 1.0
1184 if (.not. no_more_queueing) then
1185 unscanned = queue_size
1187 no_more_queueing = .true.
1192 ! Add any neighbors of scan_cell within specified radius to the queue for processing
1194 if (.not. no_more_queueing .or. unscanned > 0) then
1195 do i=1,nEdgesOnCell(scan_cell)
1196 neighbor = cellsOnCell(i,scan_cell)
1197 if (.not. dictionary_search(neighbor)) then
1198 call queue_insert(neighbor)
1199 call dictionary_insert(neighbor)
1204 if (no_more_queueing) then
1205 unscanned = unscanned - 1
1210 end subroutine search_for_cells
1213 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1214 ! Insert a new integer into the queue
1215 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1216 subroutine queue_insert(i)
1220 integer, intent(in) :: i
1222 if (queue_size == max_queue_length) then
1223 write(0,*) 'Error: queue overrun'
1226 queue_size = queue_size + 1
1227 queue_array(queue_head) = i
1228 queue_head = mod(queue_head + 1, max_queue_length)
1230 end subroutine queue_insert
1233 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1234 ! Remove the oldest integer from the queue
1235 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1236 integer function queue_remove()
1240 if (queue_size <= 0) then
1241 write(0,*) 'Error: queue underrun'
1245 queue_size = queue_size - 1
1246 queue_remove = queue_array(queue_tail)
1247 queue_tail = mod(queue_tail + 1, max_queue_length)
1249 end function queue_remove
1252 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1253 ! Reset the queue to an empty state
1254 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1255 subroutine queue_reset()
1263 end subroutine queue_reset
1266 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1267 ! Insert an integer into the dictionary
1268 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1269 subroutine dictionary_insert(i)
1273 integer, intent(in) :: i
1275 integer :: n_integer
1278 n_integer = ((i-1) / int_size) + 1
1279 n_bit = mod((i-1), int_size)
1281 if (n_integer > max_dictionary_size) then
1282 write(0,*) 'Error: dictionary insert out of bounds'
1286 dictionary_array(n_integer) = ibset(dictionary_array(n_integer), n_bit)
1288 end subroutine dictionary_insert
1291 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1292 ! Search for an integer in the dictionary
1293 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1294 logical function dictionary_search(i)
1298 integer, intent(in) :: i
1300 integer :: n_integer
1303 n_integer = ((i-1) / int_size) + 1
1304 n_bit = mod((i-1), int_size)
1306 if (n_integer > max_dictionary_size) then
1307 write(0,*) 'Error: dictionary search out of bounds'
1308 dictionary_search = .false.
1312 dictionary_search = btest(dictionary_array(n_integer), n_bit)
1314 end function dictionary_search
1317 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1318 ! Reset the dictionary to an empty state
1319 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1320 subroutine dictionary_reset()
1324 dictionary_array(:) = 0
1326 end subroutine dictionary_reset
1329 function index2d(irank, idx) result(i)
1333 integer, intent(in) :: irank, idx
1337 i = irank * (idx - 1) + 1
1339 end function index2d