PROGRAM XML_2_GRD ! Utility program to read a CSEP-format seismicity forecast file ! (3.7 GB, with 31 magnitude bins from 6.00+-0.05 to 8.95+), ! and convert it to a single-magnitude-bin (all above threshold) ! forecast in Peter Bird's GRD format. ! This allows plotting with NeoKineMap, and also allows ! testing with Kagan_2009_GJI_I_scores and/or PseudoCSEP. ! Note that the spatial grid is unchanged; however, ! the forecast is also transformed from specific earthquake counts ! in a specific time window to earthquake rates densities ! (/m**2 /s) for an indefinite or open window. ! By Peter Bird, UCLA, 2014.05.12. IMPLICIT NONE CHARACTER*12 :: duration_days_c12 CHARACTER*79 :: GRD_file, line, XML_file INTEGER, PARAMETER :: nMagnitudeBins = 31 INTEGER :: i, ios, iRow, j, jCol, k1, k2, n, nColumns, nRows, whole_steps REAL, PARAMETER :: Earth_radius_m = 6371000. REAL :: bottom_lat, & & d_lat, d_lon, duration_days_real, duration_s, & & EQ_count, EQ_rate_density, & & highest_threshold, & & lat, lat_max, lat_min, lon, lon_max, lon_min, lowest_threshold, & & magnitudeBinDimension, magnitudeMinValue, & & R2, & & s_per_year, strip_area_steradian, & & threshold_magnitude, top_lat REAL, DIMENSION(nMagnitudeBins) :: magnitude_lower_limit REAL, DIMENSION(:), ALLOCATABLE :: cell_area_m2 ! with one entry per row of seismicity matrix (N to S) DOUBLE PRECISION, PARAMETER :: unity = 1.0D0 DOUBLE PRECISION, PARAMETER :: Pi = 3.141592654D0 DOUBLE PRECISION :: dx, dy, exponent, fk1, fk2, fraction, integral, integral_converted, radians_per_degree, total_area_m2 DOUBLE PRECISION, DIMENSION(nMagnitudeBins) :: earthquake_count ! for one cell, over duration_s, in magnitude bin #i = 1, ... , nMagnitudeBins DOUBLE PRECISION, DIMENSION(nMagnitudeBins) :: log10_earthquake_rate_density ! in one cell, for indefinite duration, in magnitude bin #i = 1, ... , nMagnitudeBins DOUBLE PRECISION, DIMENSION(:, :), ALLOCATABLE :: seismicity radians_per_degree = Pi / 180.0D0 s_per_year = 365.25 * 24. * 60. * 60. WRITE (*, "(' PROGRAM XML_2_GRD')") WRITE (*, "(' ')") WRITE (*, "(' Utility program to read a CSEP-format seismicity forecast file')") WRITE (*, "(' (3.7 GB, with ',I2,' magnitude bins from 6.00+-0.05 to 8.95+),')") nMagnitudeBins WRITE (*, "(' and convert it to a single-magnitude-bin (all above threshold)')") WRITE (*, "(' forecast in Peter Bird''s GRD format.')") WRITE (*, "(' This allows plotting with NeoKineMap, and also allows')") WRITE (*, "(' testing with Kagan_2009_GJI_I_scores and/or PseudoCSEP.')") WRITE (*, "(' Note that the spatial grid is unchanged; however,')") WRITE (*, "(' the forecast is also transformed from specific earthquake counts')") WRITE (*, "(' in a specific time window to earthquake rates densities')") WRITE (*, "(' (/m**2 /s) for an indefinite or open window.')") WRITE (*, "(' By Peter Bird, UCLA, 2014.05.12.')") 10 WRITE (*, *) WRITE (*, "(' Enter name of existing XML file: '\)") READ (*, "(A)") XML_file WRITE (*, "(' Note that each OPEN takes a minute or two, for buffering...')") OPEN (UNIT = 1, FILE = TRIM(XML_file), STATUS = "OLD", PAD = "YES", IOSTAT = ios) IF (ios /= 0) THEN WRITE (*, "(' ERROR: This file could not be found/opened (in current directory).')") CALL Pause() GO TO 10 END IF WRITE (*, *) WRITE (*, "(' HEADER LINES in this XML file:')") WRITE (*, "(' -----------------------------------------------------------------------')") DO i = 1, 16 ! expecting 16 lines of header information... READ (1, "(A)") line WRITE (*, "(' ',A)") TRIM(line) IF (i == 11) THEN ! try to get duration_days_c12 IF (line(1:22) == " ") THEN duration_days_c12 = line(23:34) n = SCAN(duration_days_c12, '<') ! check for intrusion of IF (n > 0) duration_days_c12(n:n) = ' ' ! create a space to permit READ(duration_days_c12, *) READ (duration_days_c12, *, IOSTAT = ios) duration_days_real IF (ios /= 0) THEN WRITE (*, *) WRITE (*, "(' ERROR: Could not get duration_days_real from: ',A)") duration_days_c12 CALL Pause() STOP END IF duration_s = duration_days_real * 24. * 60. * 60. ELSE WRITE (*, *) WRITE (*, "(' ERROR in file format: Expecting , but found:')") WRITE (*, "(A)") TRIM(line) CALL Pause() STOP END IF END IF ! line #11: duration_day_c1 IF (i == 12) THEN ! try to get d_lat, d_lon IF (line(1:28) == " ") THEN line = line(28:79) ! cut out the prefix n = SCAN(line, '<') !find start of line(n:n) = ' ' !convert it to a space READ (line, *, IOSTAT = ios) magnitudeBinDimension IF (ios /= 0) THEN WRITE (*, *) WRITE (*, "(' ERROR: Could not get magnitudeBinDimension from start of string: '/' ', A)") TRIM(line) CALL Pause() STOP END IF ELSE WRITE (*, *) WRITE (*, "(' ERROR: Expecting but found: '/' ', A)") TRIM(line) CALL Pause() STOP END IF END IF ! line #13: magnitudeBinDimension IF (i == 14) THEN ! line #14: magnitudeminValue IF (line(1:23) == " ") THEN line = line(24:79) ! cut out the prefix n = SCAN(line, '<') !find start of line(n:n) = ' ' !convert it to a space READ (line, *, IOSTAT = ios) magnitudeMinValue IF (ios /= 0) THEN WRITE (*, *) WRITE (*, "(' ERROR: Could not get magnitudeMinValue from start of string: '/' ', A)") TRIM(line) CALL Pause() STOP END IF ELSE WRITE (*, *) WRITE (*, "(' ERROR: Expecting but found: '/' ', A)") TRIM(line) CALL Pause() STOP END IF END IF ! line #14: magnitudeMinValue END DO WRITE (*, "(' -----------------------------------------------------------------------')") WRITE (*, "(' Inferred forecast duration = ',ES12.5,' seconds.')") duration_s WRITE (*, "(' Inferred d_lat = ',ES12.5,', d_lon = ',ES12.5)") d_lat, d_lon WRITE (*, "(' Inferred magnitudeBinDimension = ',F6.3,', magnitudeMinValue = ',F6.3)") magnitudeBinDimension, magnitudeMinValue magnitude_lower_limit(1) = magnitudeMinValue DO i = 2, nMagnitudeBins magnitude_lower_limit(i) = magnitude_lower_limit(i-1) + magnitudeBinDimension END DO CALL Pause() !scan file for ranges of lon and lat: WRITE (*, *) WRITE (*, "(' Scanning XML file for extreme values of lon and lat (SLOW)...')") lat_max = -9999. lat_min = +9999. lon_max = -9999. lon_min = +9999. scanning: DO READ (1, "(A)", IOSTAT = ios) line IF (ios /= 0) EXIT scanning IF (line(1:16) == " highest_threshold)) THEN WRITE (*, *) WRITE (*, "(' ERROR: Please enter a threshold_magnitude value from ',F6.3,' to ',F6.3,';')") lowest_threshold, highest_threshold WRITE (*, "(' because off-end extrapolation is limited to 2 bin-widths.')") CALL Pause() GO TO 90 END IF whole_steps = (threshold_magnitude - magnitudeMinValue) / magnitudeBinDimension ! truncates to: whole-steps (ignoring suplus fraction) to right from lower-limit of lowest bin k1 = whole_steps + 1 ! may be negative, at this point.... k1 = MAX(1, k1) k1 = MIN((nMagnitudeBins-1), k1) ! within range from 1 ~ (nMagnitudeBins-1) k2 = k1 + 1 ! within range from 2 ~ nMagnitudeBins fraction = ((threshold_magnitude - magnitudeMinValue) / magnitudeBinDimension) - (k1 - 1.0) ! usually in 0.0 ~ 1.0, but not always! fk1 = (1.0D0 - fraction) ! usually in 0.0 ~ 1.0, but not always! fk2 = fraction ! usually in 0.0 ~ 1.0, but not always! WRITE (*, *) WRITE (*, "(' Log_10(seismicity) at threshold ',F6.3,' will get weight of ',F7.4)") magnitude_lower_limit(k1), fk1 WRITE (*, "(' Log_10(seismicity) at threshold ',F6.3,' will get weight of ',F7.4)") magnitude_lower_limit(k2), fk2 !select name for output GRD file: WRITE (*, *) WRITE (*, "(' Enter filename for new, output GRD file: '\)") READ (*, "(A)") GRD_file OPEN (UNIT = 2, FILE = TRIM(GRD_file)) ! unconditional OPEN; overwrites any existing file! WRITE (*, *) WRITE (*, "(' Starting primary working loop; SLOW ...')") rescanning: DO READ (1, "(A)", IOSTAT = ios) line IF (ios /= 0) EXIT rescanning IF (line(1:16) == "