!> @file output_2d.f90 !> @brief Structured 2D field writer: columns x, y, rho, u, v, p. module output_2d use precision, only: wp, safe_vel use solver_state_2d, only: solver_state_2d_t use option_registry, only: method_fdm implicit none private public :: write_result_2d contains !> Write this rank's interior cells as "x y rho u v p" (6ES24.12E3), i !! fastest, with a blank line after each constant-j row (gnuplot/ParaView !! structured grid). This is the per-rank/test-only writer; the production !! gathered-field writer is `solution_gather_2d.write_global_field`. The two !! share the same ASCII layout and column format (6ES24.12E3) — this routine !! writes per-rank local cells using each cell's global-index offset, while !! `write_global_field` writes the assembled global field on rank 0. subroutine write_result_2d(state, filename, is_ok, message) type(solver_state_2d_t), intent(in) :: state character(len=*), intent(in) :: filename logical, intent(out) :: is_ok character(len=*), intent(out) :: message integer :: u, ios, i, j real(wp) :: x, y, rho, vu, vv, p, gam is_ok = .false. message = '' gam = state % cfg % gam open (newunit=u, file=filename, status='replace', action='write', iostat=ios) if (ios /= 0) then message = 'output_2d: cannot open "'//trim(filename)//'"' return end if do j = 1, state % ny_local do i = 1, state % nx_local if (trim(state % blocks(1) % method) == method_fdm) then ! FDM: x_node/y_node are GLOBAL arrays (build_mesh_2d_local is a no-op for ! uniform meshes), so index with the GLOBAL node index, mirroring the FDM ! path in initial_conditions_2d.f90. Node g = ix_first_global + i - 1 lies ! at x_left + (g-1)*dx. At np=1 (ix_first_global=1) this is bit-identical ! to x_node(i,j). x = state % cfg % x_left + real(state % decomp_2d % ix_first_global + i - 2, wp) * state % dx y = state % cfg % y_left + real(state % decomp_2d % iy_first_global + j - 2, wp) * state % dy else if (state % mesh % uniform) then x = state % cfg % x_left + (real(state % decomp_2d % ix_first_global + i - 1, wp) - 0.5_wp) * state % dx y = state % cfg % y_left + (real(state % decomp_2d % iy_first_global + j - 1, wp) - 0.5_wp) * state % dy else x = state % mesh % xc(i, j) y = state % mesh % yc(i, j) end if rho = state % ub(1, i, j) ! safe_vel emits 0 velocity (no division) for a vacuum/non-physical cell ! (rho <= 0) so output cannot raise a SIGFPE; a no-op for valid rho > 0. vu = safe_vel(state % ub(2, i, j), rho) vv = safe_vel(state % ub(3, i, j), rho) p = (gam - 1.0_wp) * (state % ub(4, i, j) - 0.5_wp * rho * (vu * vu + vv * vv)) ! ES24.12E3: explicit 3-digit exponent so gfortran always emits 'E±NNN' ! (even for |exp| >= 100), matching the production write_global_field ! format so np.loadtxt parses both writers' output identically. write (u, '(6ES24.12E3)', iostat=ios) x, y, rho, vu, vv, p if (ios /= 0) then message = 'output_2d: write error' close (u, iostat=ios) return end if end do write (u, '(a)', iostat=ios) '' ! blank line between rows if (ios /= 0) then message = 'output_2d: write error' close (u, iostat=ios) return end if end do close (u, iostat=ios) if (ios /= 0) then message = 'output_2d: close failed for "'//trim(filename)//'"' return end if is_ok = .true. end subroutine write_result_2d end module output_2d