From 91bfe24ccc4c27e1294adf68b5e1419aa324cadd Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Sat, 8 Feb 2025 20:17:59 +0000 Subject: [PATCH 01/10] initial version of tools to stage data and create scripts for phase 3 validation (#1487) --- ush/ufoeval/README_phase3 | 36 +++ ush/ufoeval/atmanlvar.yaml | 481 +++++++++++++++++++++++++++++++++++ ush/ufoeval/config_gsi.yaml | 22 ++ ush/ufoeval/config_jedi.yaml | 22 ++ ush/ufoeval/gsiparm.anl | 219 ++++++++++++++++ ush/ufoeval/run_gsi.py | 155 +++++++++++ ush/ufoeval/run_jedi.py | 147 +++++++++++ 7 files changed, 1082 insertions(+) create mode 100644 ush/ufoeval/README_phase3 create mode 100644 ush/ufoeval/atmanlvar.yaml create mode 100644 ush/ufoeval/config_gsi.yaml create mode 100644 ush/ufoeval/config_jedi.yaml create mode 100644 ush/ufoeval/gsiparm.anl create mode 100755 ush/ufoeval/run_gsi.py create mode 100755 ush/ufoeval/run_jedi.py diff --git a/ush/ufoeval/README_phase3 b/ush/ufoeval/README_phase3 new file mode 100644 index 000000000..a249bba2f --- /dev/null +++ b/ush/ufoeval/README_phase3 @@ -0,0 +1,36 @@ +The following files are available to assist developers in JEDI Phase 3 validation. + +run_gsi.py and run_jedi.py + - stage files to run the indicated analysis executable + - create a batch script the user can subit to run executable + +config_gsi.yaml and config_jedi.yaml + - set batch job configuration variables + - specify key directories + - specify the application exeutable and parameter file + +atmanlvar.yaml + - sample yaml file to configure 3DVAR JEDI analysis + +gsiparm.anl + - sample namelist to configure 3DVAR GSI analysis + + +A Phase 3 validation workflow would entail + +1. modify atmanlvar.yaml and gsipmarm.anl to get the desired configuration + +2. modify config_jedi.yaml and config_gsi.yaml to point at the configuration + files from 1 and the executables to be run. Set the RUNDIR in which the + jobs are run + +3. execute run_gsi.py and run_jedi.py. The syntax is "./run_gsi.py -y ./config_gsi.yaml" + and "./run_jedi.py -y ./config_jedi.yaml". The run*py script will + (a) populate the specified RUNDIR with the files to run the given app + (b) create a batch script with the runscript name specified in the input yaml. + The batch script is created RUNDIR + +4. cd to the specified RUNDIR to submit the batch script. The job log file is written + to RUNDIR + +5. examine ouput upon job completion diff --git a/ush/ufoeval/atmanlvar.yaml b/ush/ufoeval/atmanlvar.yaml new file mode 100644 index 000000000..f940f6099 --- /dev/null +++ b/ush/ufoeval/atmanlvar.yaml @@ -0,0 +1,481 @@ +cost function: + cost type: 3D-Var + jb evaluation: false + time window: + begin: '2024-12-24T21:00:00Z' + length: PT6H + bound to include: begin + geometry: + fms initialization: + namelist filename: ./fv3jedi/fmsmpp.nml + field table filename: ./fv3jedi/field_table + akbk: ./fv3jedi/akbk.nc4 + layout: + - 4 + - 4 + npx: 193 + npy: 193 + npz: 127 + field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml + analysis variables: + - ua + - va + - t + - ps + - sphum + - ice_wat + - liq_wat + - o3mr + background: + datapath: ./bkg + filetype: cube sphere history + provider: ufs + datetime: '2024-12-25T00:00:00Z' + filenames: + - gdas.t18z.cubed_sphere_grid_atmf006.nc + - gdas.t18z.cubed_sphere_grid_sfcf006.nc + state variables: + - ua + - va + - t + - delp + - ps + - sphum + - ice_wat + - liq_wat + - o3mr + - hgtsfc + - slmsk + - sheleg + - tmpsfc + - vtype + - stype + - vfrac + - soilt1 + - soilw1 + - snwdphMeters + - u_srf + - v_srf + - f10m + background error: + covariance model: SABER + full inverse: true + saber central block: + saber block name: gsi static covariance + read: + gsi akbk: ./fv3jedi/akbk.nc4 + gsi error covariance file: ./berror/gsi-coeffs-gfs-global.nc4 + gsi berror namelist file: ./berror/gfs_gsi_global.nml + processor layout x direction: 12 + processor layout y direction: 8 + debugging mode: false + saber outer blocks: + - saber block name: gsi interpolation to model grid + gsi akbk: ./fv3jedi/akbk.nc4 + gsi error covariance file: ./berror/gsi-coeffs-gfs-global.nc4 + gsi berror namelist file: ./berror/gfs_gsi_global.nml + processor layout x direction: 12 + processor layout y direction: 8 + debugging mode: false + linear variable change: + linear variable change name: Control2Analysis + input variables: + - eastward_wind + - northward_wind + - air_temperature + - air_pressure_at_surface + - water_vapor_mixing_ratio_wrt_moist_air + - cloud_liquid_ice + - cloud_liquid_water + - mole_fraction_of_ozone_in_air + output variables: + - ua + - va + - t + - ps + - sphum + - ice_wat + - liq_wat + - o3mr + observations: + obs perturbations: false + observers: + - obs space: + name: surface_ps + obsdatain: + engine: + type: H5File + obsfile: ./obs/gdas.t00z.conventional_ps.tm00.nc + obsdataout: + engine: + type: H5File + obsfile: ./diags/diag_conventional_ps_2024122500.nc + io pool: + max pool size: 1 + simulated variables: + - stationPressure + obs operator: + name: SfcPCorrected + variables: + - name: stationPressure + da_psfc_scheme: GSI + station_altitude: height + geovar_sfc_geomz: height_above_mean_sea_level_at_surface + geovar_geomz: geopotential_height + linear obs operator: + name: Identity + variables: + - name: stationPressure + obs prior filters: + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: ObsType/stationPressure + is_in: + - 181 + action: + name: assign error + error function: + name: ObsFunction/ObsErrorModelStepwiseLinear + options: + round_to_the_nearest_integer: true + xvar: + name: ObsValue/stationPressure + xvals: + - 80000 + - 75000 + - 70000 + - 65000 + - 60000 + - 55000 + errors: + - 110 + - 120 + - 120 + - 120 + - 120 + - 100000000000.0 + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: ObsType/stationPressure + is_in: + - 187 + action: + name: assign error + error function: + name: ObsFunction/ObsErrorModelStepwiseLinear + options: + round_to_the_nearest_integer: true + xvar: + name: ObsValue/stationPressure + xvals: + - 85000 + - 80000 + - 75000 + - 70000 + - 65000 + - 60000 + - 55000 + errors: + - 120 + - 140 + - 140 + - 140 + - 140 + - 140 + - 100000000000.0 + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: ObsType/stationPressure + is_in: + - 180 + action: + name: assign error + error function: + name: ObsFunction/ObsErrorModelStepwiseLinear + options: + round_to_the_nearest_integer: true + xvar: + name: ObsValue/stationPressure + xvals: + - 60000 + - 55000 + errors: + - 130 + - 100000000000.0 + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: ObsType/stationPressure + is_in: + - 183 + action: + name: assign error + error parameter: 100000000000.0 + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: ObsType/stationPressure + is_in: + - 120 + action: + name: assign error + error function: + name: ObsFunction/ObsErrorModelStepwiseLinear + options: + round_to_the_nearest_integer: true + xvar: + name: ObsValue/stationPressure + xvals: + - 80000 + - 75000 + - 70000 + - 65000 + - 60000 + - 55000 + errors: + - 110 + - 120 + - 120 + - 120 + - 120 + - 100000000000.0 + - filter: Variable Assignment + assignments: + - name: InputObsError/stationPressure + type: float + source variable: ObsErrorData/stationPressure + - filter: Variable Assignment + assignments: + - name: PreQC/stationPressure + type: int + source variable: QualityMarker/stationPressure + - filter: Variable Assignment + assignments: + - name: PreUseFlag/stationPressure + type: int + source variable: PreQC/stationPressure + - filter: Variable Assignment + where: + - variable: + name: PreUseFlag/stationPressure + is_in: 1-15 + assignments: + - name: PreUseFlag/stationPressure + value: 0 + - filter: Variable Assignment + where: + - variable: + name: ObsType/stationPressure + is_in: 183 + assignments: + - name: PreUseFlag/stationPressure + value: 100 + - filter: Variable Assignment + where: + - variable: + name: ObsValue/stationPressure + is_defined: null + - variable: + name: ObsValue/stationPressure + maxvalue: 50000.0 + where operator: and + assignments: + - name: PreUseFlag/stationPressure + value: 100 + - filter: Variable Assignment + where: + - variable: + name: PreQC/stationPressure + is_in: 9, 12, 15 + assignments: + - name: PreUseFlag/stationPressure + value: 100 + - filter: Variable Assignment + where: + - variable: + name: PreQC/stationPressure + is_in: 4-15 + assignments: + - name: PreUseFlag/stationPressure + value: 101 + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: PreQC/stationPressure + is_in: 3, 7 + action: + name: inflate error + inflation factor: 1.2 + obs post filters: + - filter: Variable Assignment + assignments: + - name: ObsErrorFactorDuplicateCheck/stationPressure + type: float + function: + name: ObsFunction/ObsErrorFactorDuplicateCheck + options: + use_air_pressure: false + variable: stationPressure + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: + name: ObsType/stationPressure + is_in: 180 + - variable: + name: ObsSubType/stationPressure + is_in: 0 + action: + name: inflate error + inflation factor: 0.7 + - filter: Variable Assignment + assignments: + - name: ObsErrorFactorSfcPressure/stationPressure + type: float + function: + name: ObsFunction/ObsErrorFactorSfcPressure + options: + geovar_sfc_geomz: height_above_mean_sea_level_at_surface + geovar_geomz: geopotential_height + station_altitude: height + - filter: Perform Action + filter variables: + - name: stationPressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSfcPressure/stationPressure + - filter: Variable Assignment + assignments: + - name: DerivedMetaData/Innovation + type: float + function: + name: ObsFunction/Arithmetic + options: + variables: + - name: ObsValue/stationPressure + - name: HofX/stationPressure + coefs: + - 1 + - -1 + - filter: Variable Assignment + assignments: + - name: DerivedMetaData/ObsErrorBoundSfcPressure1 + type: float + function: + name: ObsFunction/ObsErrorBoundConventional + options: + obsvar: stationPressure + obserr_bound_min: 100 + obserr_bound_max: 300 + obserr_bound_factor: 5.0 + - filter: Background Check + filter variables: + - name: stationPressure + where: + - variable: PreQC/stationPressure + is_not_in: 3 + function absolute threshold: + - name: DerivedMetaData/ObsErrorBoundSfcPressure1 + action: + name: reject + - filter: Variable Assignment + assignments: + - name: DerivedMetaData/ObsErrorBoundSfcPressure2 + type: float + function: + name: ObsFunction/ObsErrorBoundConventional + options: + obsvar: stationPressure + obserr_bound_min: 100 + obserr_bound_max: 300 + obserr_bound_factor: 3.5 + - filter: Background Check + filter variables: + - name: stationPressure + where: + - variable: PreQC/stationPressure + is_in: 3 + function absolute threshold: + - name: DerivedMetaData/ObsErrorBoundSfcPressure2 + action: + name: reject + - filter: Perform Action + filter variables: + - name: stationPressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorDuplicateCheck/stationPressure + - filter: Perform Action + filter variables: + - name: stationPressure + where: + - variable: PreUseFlag/stationPressure + is_not_in: 0, 1 + action: + name: reject +variational: + minimizer: + algorithm: DRPCG + iterations: + - ninner: 50 + gradient norm reduction: 1e-10 + test: true + geometry: + fms initialization: + namelist filename: ./fv3jedi/fmsmpp.nml + field table filename: ./fv3jedi/field_table + akbk: ./fv3jedi/akbk.nc4 + layout: + - 4 + - 4 + npx: 193 + npy: 193 + npz: 127 + field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml + diagnostics: + departures: bkgmob +final: + diagnostics: + departures: anlmob + prints: + frequency: PT3H + increment: + output: + state component: + filetype: cube sphere history + filename: ./anl/atminc.%yyyy%mm%dd.%hh%MM%ssz.nc4 + provider: ufs + fields to write: + - ugrd + - vgrd + - tmp + - pressfc + - spfh + - icmr + - clwmr + - o3mr + geometry: + fms initialization: + namelist filename: ./fv3jedi/fmsmpp.nml + field table filename: ./fv3jedi/field_table + akbk: ./fv3jedi/akbk.nc4 + layout: + - 4 + - 4 + npx: 193 + npy: 193 + npz: 127 + field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml +final j evaluation: false diff --git a/ush/ufoeval/config_gsi.yaml b/ush/ufoeval/config_gsi.yaml new file mode 100644 index 000000000..68b4326c0 --- /dev/null +++ b/ush/ufoeval/config_gsi.yaml @@ -0,0 +1,22 @@ +machine: hera + +job options: + job-name: gsivar + account: da-cpu + qos: debug + partition: hera + time: '30:00' + nodes: 4 + tasks-per-node: 24 + cpus-per-task: 1 + mem: 96Gb + +directories: + HOMEgfs: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test + STAGEDIR: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR/gsi/gdasanal_00 + RUNDIR: /scratch1/NCEPDEV/stmp2/Russ.Treadon/RUNDIRS/gsi_anl_00 + +app files: + APPEXE: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/exec/gsi.x + APPYML: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/sorc/gdas.cd/ush/ufoeval/gsiparm.anl + runscript: rungsi.sh diff --git a/ush/ufoeval/config_jedi.yaml b/ush/ufoeval/config_jedi.yaml new file mode 100644 index 000000000..bb8987a46 --- /dev/null +++ b/ush/ufoeval/config_jedi.yaml @@ -0,0 +1,22 @@ +machine: hera + +job options: + job-name: jedivar + account: da-cpu + qos: debug + partition: hera + time: '30:00' + nodes: 4 + tasks-per-node: 24 + cpus-per-task: 1 + mem: 96Gb + +directories: + HOMEgfs: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test + STAGEDIR: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR/jedi/gdasatmanl_00 + RUNDIR: /scratch1/NCEPDEV/stmp2/Russ.Treadon/RUNDIRS/jedi_anl_00 + +app files: + APPEXE: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/exec/gdas.x + APPYML: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/sorc/gdas.cd/ush/ufoeval/atmanlvar.yaml + runscript: runjedi.sh diff --git a/ush/ufoeval/gsiparm.anl b/ush/ufoeval/gsiparm.anl new file mode 100644 index 000000000..d73302cde --- /dev/null +++ b/ush/ufoeval/gsiparm.anl @@ -0,0 +1,219 @@ +&SETUP + miter=2, + niter(1)=100,niter(2)=100, + niter_no_qc(1)=50,niter_no_qc(2)=0, + write_diag(1)=.true.,write_diag(2)=.false.,write_diag(3)=.true., + qoption=2, + gencode=0,deltim=189, + factqmin=0.5,factqmax=0.0002, + iguess=-1, + tzr_qc=1, + oneobtest=.false.,retrieval=.false.,l_foto=.false., + use_pbl=.false.,use_compress=.true.,nsig_ext=45,gpstop=50.,commgpstop=45.,commgpserrinf=1.0, + use_gfs_nemsio=.false.,use_gfs_ncio=.true.,sfcnst_comb=.true., + use_readin_anl_sfcmask=.false., + lrun_subdirs=.true., + crtm_coeffs_path='./crtm_coeffs/', + newpc4pred=.true.,adp_anglebc=.true.,angord=4,passive_bc=.true.,use_edges=.false., + diag_precon=.true.,step_start=1.e-3,emiss_bc=.true.,nhr_obsbin=3, + cwoption=3,imp_physics=8,lupp=.true.,cnvw_option=.false.,cao_check=.true., + netcdf_diag=.true.,binary_diag=.false., + lobsdiag_forenkf=.false., + write_fv3_incr=.true., + nhr_anal=6,, + ta2tb=.true., + incvars_to_zero= 'liq_wat_inc','icmr_inc','rwmr_inc','snmr_inc','grle_inc', + incvars_zero_strat= 'sphum_inc','liq_wat_inc','icmr_inc','rwmr_inc','snmr_inc','grle_inc', + incvars_efold= 5, + miter=1,niter(1)=50,niter(2)=50,qoption=1,cwoption=1,factqmin=0.0,factqmax=0.0,sfcnst_comb=.true., +/ +&GRIDOPTS + JCAP_B=382,JCAP=192,NLAT=386,NLON=768,nsig=127, + regional=.false.,nlayers(63)=3,nlayers(64)=6, + nlayers(63)=1,nlayers(64)=1, +/ +&BKGERR + vs=0.7, + hzscl=1.7,0.8,0.5, + hswgt=0.45,0.3,0.25, + bw=0.0,norsp=4, + bkgv_flowdep=.false.,bkgv_rewgtfct=1.5, + bkgv_write=.false., + cwcoveqqcov=.false., + +/ +&ANBKGERR + anisotropic=.false., + +/ +&JCOPTS + ljcdfi=.false.,alphajc=0.0,ljcpdry=.true.,bamp_jcpdry=5.0e7, + ljcpdry=.false., +/ +&STRONGOPTS + tlnmc_option=2,nstrong=1,nvmodes_keep=8,period_max=6.,period_width=1.5, + tlnmc_option=0, +/ +&OBSQC + dfact=0.75,dfact1=3.0,noiqc=.true.,oberrflg=.false.,c_varqc=0.02, + use_poq7=.true.,qc_noirjaco3_pole=.true.,vqc=.false.,nvqc=.true., + aircraft_t_bc=.true.,biaspredt=1.0e5,upd_aircraft=.true.,cleanup_tail=.true., + tcp_width=70.0,tcp_ermax=7.35,airs_cads=.false.,cris_cads=.false., + iasi_cads=.false., + nvqc=.false., +/ +&OBS_INPUT + dmesh(1)=145.0,dmesh(2)=150.0,dmesh(3)=100.0,dmesh(4)=50.0,time_window_max=3.0, + hofx_2m_sfcfile=.false., + +/ +OBS_INPUT:: +! dfile dtype dplat dsis dval dthin dsfcalc + prepbufr ps null ps 0.0 0 0 +! prepbufr t null t 0.0 0 0 +! prepbufr_profl t null t 0.0 0 0 + hdobbufr t null t 0.0 0 0 +! prepbufr q null q 0.0 0 0 +! prepbufr_profl q null q 0.0 0 0 + hdobbufr q null q 0.0 0 0 +! prepbufr pw null pw 0.0 0 0 +! prepbufr uv null uv 0.0 0 0 +! prepbufr_profl uv null uv 0.0 0 0 + satwndbufr uv null uv 0.0 0 0 + hdobbufr uv null uv 0.0 0 0 +! prepbufr spd null spd 0.0 0 0 + hdobbufr spd null spd 0.0 0 0 +! prepbufr dw null dw 0.0 0 0 + radarbufr rw null rw 0.0 0 0 + nsstbufr sst nsst sst 0.0 0 0 + gpsrobufr gps_bnd null gps 0.0 0 0 + ssmirrbufr pcp_ssmi dmsp pcp_ssmi 0.0 -1 0 + tmirrbufr pcp_tmi trmm pcp_tmi 0.0 -1 0 + sbuvbufr sbuv2 n16 sbuv8_n16 0.0 0 0 + sbuvbufr sbuv2 n17 sbuv8_n17 0.0 0 0 + sbuvbufr sbuv2 n18 sbuv8_n18 0.0 0 0 + gimgrbufr goes_img g11 imgr_g11 0.0 1 0 + gimgrbufr goes_img g12 imgr_g12 0.0 1 0 + airsbufr airs aqua airs_aqua 0.0 1 1 + amsuabufr amsua n15 amsua_n15 0.0 1 1 + amsuabufr amsua n18 amsua_n18 0.0 1 1 + amsuabufr amsua metop-a amsua_metop-a 0.0 1 1 + airsbufr amsua aqua amsua_aqua 0.0 1 1 + amsubbufr amsub n17 amsub_n17 0.0 1 1 + mhsbufr mhs n18 mhs_n18 0.0 1 1 + mhsbufr mhs metop-a mhs_metop-a 0.0 1 1 + ssmitbufr ssmi f15 ssmi_f15 0.0 1 0 + amsrebufr amsre_low aqua amsre_aqua 0.0 1 0 + amsrebufr amsre_mid aqua amsre_aqua 0.0 1 0 + amsrebufr amsre_hig aqua amsre_aqua 0.0 1 0 + ssmisbufr ssmis f16 ssmis_f16 0.0 1 0 + ssmisbufr ssmis f17 ssmis_f17 0.0 1 0 + ssmisbufr ssmis f18 ssmis_f18 0.0 1 0 + gsnd1bufr sndrd1 g12 sndrD1_g12 0.0 1 0 + gsnd1bufr sndrd2 g12 sndrD2_g12 0.0 1 0 + gsnd1bufr sndrd3 g12 sndrD3_g12 0.0 1 0 + gsnd1bufr sndrd4 g12 sndrD4_g12 0.0 1 0 + gsnd1bufr sndrd1 g11 sndrD1_g11 0.0 1 0 + gsnd1bufr sndrd2 g11 sndrD2_g11 0.0 1 0 + gsnd1bufr sndrd3 g11 sndrD3_g11 0.0 1 0 + gsnd1bufr sndrd4 g11 sndrD4_g11 0.0 1 0 + gsnd1bufr sndrd1 g13 sndrD1_g13 0.0 1 0 + gsnd1bufr sndrd2 g13 sndrD2_g13 0.0 1 0 + gsnd1bufr sndrd3 g13 sndrD3_g13 0.0 1 0 + gsnd1bufr sndrd4 g13 sndrD4_g13 0.0 1 0 + iasibufr iasi metop-a iasi_metop-a 0.0 1 1 + gomebufr gome metop-a gome_metop-a 0.0 2 0 + omibufr omi aura omi_aura 0.0 2 0 + sbuvbufr sbuv2 n19 sbuv8_n19 0.0 0 0 + amsuabufr amsua n19 amsua_n19 0.0 1 1 + mhsbufr mhs n19 mhs_n19 0.0 1 1 +! tcvitl tcp null tcp 0.0 0 0 + seviribufr seviri m08 seviri_m08 0.0 1 0 + seviribufr seviri m09 seviri_m09 0.0 1 0 + seviribufr seviri m10 seviri_m10 0.0 1 0 + seviribufr seviri m11 seviri_m11 0.0 1 0 + amsuabufr amsua metop-b amsua_metop-b 0.0 1 1 + mhsbufr mhs metop-b mhs_metop-b 0.0 1 1 + iasibufr iasi metop-b iasi_metop-b 0.0 1 1 + gomebufr gome metop-b gome_metop-b 0.0 2 0 + atmsbufr atms npp atms_npp 0.0 1 1 + atmsbufr atms n20 atms_n20 0.0 1 1 + atmsbufr atms n21 atms_n21 0.0 1 1 + crisbufr cris npp cris_npp 0.0 1 0 + crisfsbufr cris-fsr npp cris-fsr_npp 0.0 1 0 + crisfsbufr cris-fsr n20 cris-fsr_n20 0.0 1 0 + crisfsbufr cris-fsr n21 cris-fsr_n21 0.0 1 0 + gsnd1bufr sndrd1 g14 sndrD1_g14 0.0 1 0 + gsnd1bufr sndrd2 g14 sndrD2_g14 0.0 1 0 + gsnd1bufr sndrd3 g14 sndrD3_g14 0.0 1 0 + gsnd1bufr sndrd4 g14 sndrD4_g14 0.0 1 0 + gsnd1bufr sndrd1 g15 sndrD1_g15 0.0 1 0 + gsnd1bufr sndrd2 g15 sndrD2_g15 0.0 1 0 + gsnd1bufr sndrd3 g15 sndrD3_g15 0.0 1 0 + gsnd1bufr sndrd4 g15 sndrD4_g15 0.0 1 0 + oscatbufr uv null uv 0.0 0 0 + mlsbufr mls30 aura mls30_aura 0.0 0 0 + avhambufr avhrr metop-a avhrr3_metop-a 0.0 4 0 + avhpmbufr avhrr n18 avhrr3_n18 0.0 4 0 + avhambufr avhrr metop-b avhrr3_metop-b 0.0 4 0 + avhambufr avhrr metop-c avhrr3_metop-c 0.0 4 0 + avhpmbufr avhrr n19 avhrr3_n19 0.0 4 0 + amsr2bufr amsr2 gcom-w1 amsr2_gcom-w1 0.0 3 0 + gmibufr gmi gpm gmi_gpm 0.0 1 0 + saphirbufr saphir meghat saphir_meghat 0.0 3 0 + ahibufr ahi himawari8 ahi_himawari8 0.0 1 0 + abibufr abi g16 abi_g16 0.0 1 0 + abibufr abi g17 abi_g17 0.0 1 0 + abibufr abi g18 abi_g18 0.0 1 0 + rapidscatbufr uv null uv 0.0 0 0 + ompsnpbufr ompsnp npp ompsnp_npp 0.0 0 0 + ompslpbufr ompslp npp ompslp_npp 0.0 0 0 + ompstcbufr ompstc8 npp ompstc8_npp 0.0 2 0 + ompsnpbufr ompsnp n20 ompsnp_n20 0.0 0 0 + ompstcbufr ompstc8 n20 ompstc8_n20 0.0 2 0 + amsuabufr amsua metop-c amsua_metop-c 0.0 1 1 + mhsbufr mhs metop-c mhs_metop-c 0.0 1 1 + iasibufr iasi metop-c iasi_metop-c 0.0 1 1 + sstviirs viirs-m npp viirs-m_npp 0.0 4 0 + sstviirs viirs-m j1 viirs-m_j1 0.0 4 0 + ahibufr ahi himawari9 ahi_himawari9 0.0 1 0 + sstviirs viirs-m j2 viirs-m_j2 0.0 4 0 + ompsnpbufr ompsnp n21 ompsnp_n21 0.0 0 0 + ompstcbufr ompstc8 n21 ompstc8_n21 0.0 2 0 + gomebufr gome metop-c gome_metop-c 0.0 2 0 +:: +&SUPEROB_RADAR + +/ +&LAG_DATA + +/ +&HYBRID_ENSEMBLE + l_hyb_ens=.false., + generate_ens=.false., + beta_s0=0.125,readin_beta=.false., + s_ens_h=1000.0,300.0,150.0,685.0,219.2,s_ens_v=-0.5,-0.5,-0.5,0.0,0.0, + readin_localization=.false.,global_spectral_filter_sd=.false., + r_ensloccov4scl=1,nsclgrp=3,naensloc=5, + aniso_a_en=.false.,oz_univ_static=.false.,uv_hyb_ens=.true., + ensemble_path='./ensemble_data/', + ens_fast_read=.true., + +/ +&RAPIDREFRESH_CLDSURF + dfi_radar_latent_heat_time_period=30.0, + +/ +&CHEM + +/ +&SINGLEOB_TEST + maginnov=0.1,magoberr=0.1,oneob_type='t', + oblat=45.,oblon=180.,obpres=1000.,obdattim=2024122500, + obhourset=0., + +/ +&NST + nst_gsi=3, + nstinfo=4,fac_dtl=1,fac_tsl=1,zsea1=0,zsea2=0, +/ diff --git a/ush/ufoeval/run_gsi.py b/ush/ufoeval/run_gsi.py new file mode 100755 index 000000000..e4220d31f --- /dev/null +++ b/ush/ufoeval/run_gsi.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +import yaml +import os +import shutil +import subprocess +import argparse +import logging +from pathlib import Path +from datetime import datetime, timedelta + +logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', + level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') + +class JobCard: + + def __init__(self, scriptname, config): + """ + Constructor for the JobCard class. + :param scriptname: name of the output script file + :param config: dictionary containing configuration information + """ + + self.name = scriptname + self.config = config + + self.machine = config['machine'] + self.homegfs = config['directories']['HOMEgfs'] + self.rundir = config['directories']['RUNDIR'] + self.appexe = config['app files']['APPEXE'] + self.appyml = config['app files']['APPYML'] + self.jobname = config['job options']['job-name'] + self.nodes = config['job options']['nodes'] + self.ppn = config['job options']['tasks-per-node'] + self.ntasks = self.nodes * self.ppn + self.threads = config['job options']['cpus-per-task'] + + self.f = open(self.name, "w") + self.f.write("#!/usr/bin/env bash\n") + self.f.write(f"# Running on {self.machine} \n") + + def header(self): + """ + Write machine dependent scheduler header + TODO: generalize to support more than just slurm + """ + self.f.write(f"#SBATCH --output={self.jobname}.o%J\n") + + sbatch = '' + for key, value in self.config['job options'].items(): + sbatch += f"#SBATCH --{key}={value} \n" + + self.f.write(f"{sbatch}\n") + + def load_modules(self): + """ + Load modules + """ + self.f.write("\n") + self.f.write("# Load modules\n") + self.f.write(f"export HOMEgfs={self.homegfs}\n") + self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") + self.f.write(f". {self.homegfs}/ush/load_fv3gfs_modules.sh\n") + self.f.write("set -x\n") + + def aprun(self): + """ + Execute app + """ + + # cd to run directory + self.f.write("\n") + self.f.write(f"# cd to run directory\n") + self.f.write(f"cd {self.rundir}\n") + + # copy app files + self.f.write("\n") + self.f.write(f"# Copy executable and namelist\n") + self.f.write(f"cp -p {self.appexe} ./app.x\n") + self.f.write(f"cp -p {self.appyml} ./\n") + + # execute app + self.f.write("\n") + self.f.write(f"# Execute app\n") + self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") + self.f.write(f"ulimit -s unlimited\n") + + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./app.x" + self.f.write(f"{aprun_command}\n") + + def close(self): + """ + Flush and make the card executable + """ + self.f.close() + subprocess.run(["chmod", "+x", self.name]) + +def main(): + epilog = ["Examples:", + " ./run_jjobs.py -y config.yaml", + " ./run_jjobs.py -h"] + parser = argparse.ArgumentParser(description="Run an ordered list of j-jobs.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=os.linesep.join(epilog)) + parser.add_argument("-y", "--yaml", required=True, help="The YAML file") + args = parser.parse_args() + + # Get the experiment configuration + run_jjobs_yaml = args.yaml + with open(run_jjobs_yaml, 'r') as file: + exp_config = yaml.safe_load(file) + + logging.info(f"exp_config {exp_config}") + + # Set source (stagedir) and destination (rundir) paths + stagedir = exp_config['directories']['STAGEDIR'] + rundir = exp_config['directories']['RUNDIR'] + + # Create and cd to run directory + if os.path.exists(rundir): + shutil.rmtree(rundir) + os.makedirs(rundir) + os.chdir(rundir) + + # Copy files and directories to run directory + files_to_copy = [ + "*info*", "aircftbias_in", "atms_beamwidth.txt", "berror_stats", + "cloudy_radiance_info.txt", "errtable", "prepbufr", "radstat.gdas", "AIRS_CLDDET.NL", + "CRIS_CLDDET.NL", "IASI_CLDDET.NL", "Rcov*", "satbias_angle", "satbias_in", + "satbias_pc", "sfcf06", "sigf06", "vqctp001.dat" + ] + for pattern in files_to_copy: + for file_path in Path(stagedir).glob(pattern): + shutil.copy(file_path, rundir) + + directories_to_copy = ["crtm_coeffs"] + for directory in directories_to_copy: + source_dir = Path(stagedir) / directory + if source_dir.exists(): + shutil.copytree(source_dir, Path(rundir) / directory) + + logging.info(f"Data staged to {rundir}") + + # Create run script. + runscript = exp_config['app files']['runscript'] + run_card = JobCard(runscript, exp_config) + run_card.header() + run_card.load_modules() + run_card.aprun() + run_card.close() + + logging.info(f"Create {runscript} in {rundir}") + + +if __name__ == "__main__": + main() diff --git a/ush/ufoeval/run_jedi.py b/ush/ufoeval/run_jedi.py new file mode 100755 index 000000000..d74970c0b --- /dev/null +++ b/ush/ufoeval/run_jedi.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +import yaml +import os +import shutil +import subprocess +import argparse +import logging +from pathlib import Path +from datetime import datetime, timedelta + +logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', + level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') + +class JobCard: + + def __init__(self, scriptname, config): + """ + Constructor for the JobCard class. + :param scriptname: name of the output script file + :param config: dictionary containing configuration information + """ + + self.name = scriptname + self.config = config + + self.machine = config['machine'] + self.homegfs = config['directories']['HOMEgfs'] + self.rundir = config['directories']['RUNDIR'] + self.appexe = config['app files']['APPEXE'] + self.appyml = config['app files']['APPYML'] + self.jobname = config['job options']['job-name'] + self.nodes = config['job options']['nodes'] + self.ppn = config['job options']['tasks-per-node'] + self.ntasks = self.nodes * self.ppn + self.threads = config['job options']['cpus-per-task'] + + self.f = open(self.name, "w") + self.f.write("#!/usr/bin/env bash\n") + self.f.write(f"# Running on {self.machine} \n") + + def header(self): + """ + Write machine dependent scheduler header + TODO: generalize to support more than just slurm + """ + self.f.write(f"#SBATCH --output={self.jobname}.o%J\n") + + sbatch = '' + for key, value in self.config['job options'].items(): + sbatch += f"#SBATCH --{key}={value} \n" + + self.f.write(f"{sbatch}\n") + + def load_modules(self): + """ + Load modules + """ + self.f.write("\n") + self.f.write("# Load modules\n") + self.f.write(f"export HOMEgfs={self.homegfs}\n") + self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") + self.f.write(f". {self.homegfs}/ush/load_fv3gfs_modules.sh\n") + self.f.write("set -x\n") + + def aprun(self): + """ + Execute app + """ + + # cd to run directory + self.f.write("\n") + self.f.write(f"# cd to run directory\n") + self.f.write(f"cd {self.rundir}\n") + + # link / copy app files + self.f.write("\n") + self.f.write(f"# Copy executable and namelist\n") + self.f.write(f"ln -fs {self.appexe} ./app.x\n") + self.f.write(f"cp -p {self.appyml} ./app.yaml\n") + + # execute app + self.f.write("\n") + self.f.write(f"# Execute app\n") + self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") + self.f.write(f"ulimit -s unlimited\n") + + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./app.x fv3jedi variational ./app.yaml" + self.f.write(f"{aprun_command}\n") + + def close(self): + """ + Flush and make the card executable + """ + self.f.close() + subprocess.run(["chmod", "+x", self.name]) + +def main(): + epilog = ["Examples:", + " ./run_jjobs.py -y config.yaml", + " ./run_jjobs.py -h"] + parser = argparse.ArgumentParser(description="Run an ordered list of j-jobs.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=os.linesep.join(epilog)) + parser.add_argument("-y", "--yaml", required=True, help="The YAML file") + args = parser.parse_args() + + # Get the experiment configuration + run_jjobs_yaml = args.yaml + with open(run_jjobs_yaml, 'r') as file: + exp_config = yaml.safe_load(file) + + logging.info(f"exp_config {exp_config}") + + # Set source (stagedir) and destination (rundir) paths + stagedir = exp_config['directories']['STAGEDIR'] + rundir = exp_config['directories']['RUNDIR'] + + # Create and cd to run directory + if os.path.exists(rundir): + shutil.rmtree(rundir) + os.makedirs(rundir) + os.makedirs(f"{rundir}/anl") + os.makedirs(f"{rundir}/diags") + os.chdir(rundir) + + # Copy directories to run directory + directories_to_copy = ["berror", "bkg", "crtm", "fv3jedi", "obs"] + for directory in directories_to_copy: + source_dir = Path(stagedir) / directory + if source_dir.exists(): + shutil.copytree(source_dir, Path(rundir) / directory) + + logging.info(f"Data staged to {rundir}") + + # Create run script. + runscript = exp_config['app files']['runscript'] + run_card = JobCard(runscript, exp_config) + run_card.header() + run_card.load_modules() + run_card.aprun() + run_card.close() + + logging.info(f"Create {runscript} in {rundir}") + + +if __name__ == "__main__": + main() From a8c07cef61e1ca2960c950da12c8f022e98c26b3 Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Sat, 8 Feb 2025 20:28:10 +0000 Subject: [PATCH 02/10] fix pycodestyle violations (#1487) --- ush/ufoeval/run_gsi.py | 14 ++++++++------ ush/ufoeval/run_jedi.py | 12 +++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ush/ufoeval/run_gsi.py b/ush/ufoeval/run_gsi.py index e4220d31f..fab3d7be3 100755 --- a/ush/ufoeval/run_gsi.py +++ b/ush/ufoeval/run_gsi.py @@ -11,6 +11,7 @@ logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') + class JobCard: def __init__(self, scriptname, config): @@ -33,7 +34,7 @@ def __init__(self, scriptname, config): self.ppn = config['job options']['tasks-per-node'] self.ntasks = self.nodes * self.ppn self.threads = config['job options']['cpus-per-task'] - + self.f = open(self.name, "w") self.f.write("#!/usr/bin/env bash\n") self.f.write(f"# Running on {self.machine} \n") @@ -61,7 +62,7 @@ def load_modules(self): self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") self.f.write(f". {self.homegfs}/ush/load_fv3gfs_modules.sh\n") self.f.write("set -x\n") - + def aprun(self): """ Execute app @@ -83,7 +84,7 @@ def aprun(self): self.f.write(f"# Execute app\n") self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") self.f.write(f"ulimit -s unlimited\n") - + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./app.x" self.f.write(f"{aprun_command}\n") @@ -94,6 +95,7 @@ def close(self): self.f.close() subprocess.run(["chmod", "+x", self.name]) + def main(): epilog = ["Examples:", " ./run_jjobs.py -y config.yaml", @@ -110,7 +112,7 @@ def main(): exp_config = yaml.safe_load(file) logging.info(f"exp_config {exp_config}") - + # Set source (stagedir) and destination (rundir) paths stagedir = exp_config['directories']['STAGEDIR'] rundir = exp_config['directories']['RUNDIR'] @@ -125,7 +127,7 @@ def main(): files_to_copy = [ "*info*", "aircftbias_in", "atms_beamwidth.txt", "berror_stats", "cloudy_radiance_info.txt", "errtable", "prepbufr", "radstat.gdas", "AIRS_CLDDET.NL", - "CRIS_CLDDET.NL", "IASI_CLDDET.NL", "Rcov*", "satbias_angle", "satbias_in", + "CRIS_CLDDET.NL", "IASI_CLDDET.NL", "Rcov*", "satbias_angle", "satbias_in", "satbias_pc", "sfcf06", "sigf06", "vqctp001.dat" ] for pattern in files_to_copy: @@ -139,7 +141,7 @@ def main(): shutil.copytree(source_dir, Path(rundir) / directory) logging.info(f"Data staged to {rundir}") - + # Create run script. runscript = exp_config['app files']['runscript'] run_card = JobCard(runscript, exp_config) diff --git a/ush/ufoeval/run_jedi.py b/ush/ufoeval/run_jedi.py index d74970c0b..896d4d6cf 100755 --- a/ush/ufoeval/run_jedi.py +++ b/ush/ufoeval/run_jedi.py @@ -11,6 +11,7 @@ logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') + class JobCard: def __init__(self, scriptname, config): @@ -33,7 +34,7 @@ def __init__(self, scriptname, config): self.ppn = config['job options']['tasks-per-node'] self.ntasks = self.nodes * self.ppn self.threads = config['job options']['cpus-per-task'] - + self.f = open(self.name, "w") self.f.write("#!/usr/bin/env bash\n") self.f.write(f"# Running on {self.machine} \n") @@ -61,7 +62,7 @@ def load_modules(self): self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") self.f.write(f". {self.homegfs}/ush/load_fv3gfs_modules.sh\n") self.f.write("set -x\n") - + def aprun(self): """ Execute app @@ -83,7 +84,7 @@ def aprun(self): self.f.write(f"# Execute app\n") self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") self.f.write(f"ulimit -s unlimited\n") - + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./app.x fv3jedi variational ./app.yaml" self.f.write(f"{aprun_command}\n") @@ -94,6 +95,7 @@ def close(self): self.f.close() subprocess.run(["chmod", "+x", self.name]) + def main(): epilog = ["Examples:", " ./run_jjobs.py -y config.yaml", @@ -110,7 +112,7 @@ def main(): exp_config = yaml.safe_load(file) logging.info(f"exp_config {exp_config}") - + # Set source (stagedir) and destination (rundir) paths stagedir = exp_config['directories']['STAGEDIR'] rundir = exp_config['directories']['RUNDIR'] @@ -131,7 +133,7 @@ def main(): shutil.copytree(source_dir, Path(rundir) / directory) logging.info(f"Data staged to {rundir}") - + # Create run script. runscript = exp_config['app files']['runscript'] run_card = JobCard(runscript, exp_config) From 3ab0582ff19a5c1e0f83acca05d606f95bcb8d0f Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Tue, 11 Feb 2025 11:59:10 +0000 Subject: [PATCH 03/10] load correct modules for gdas.x (#1487) --- ush/ufoeval/run_jedi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ush/ufoeval/run_jedi.py b/ush/ufoeval/run_jedi.py index 896d4d6cf..c68fc3e0c 100755 --- a/ush/ufoeval/run_jedi.py +++ b/ush/ufoeval/run_jedi.py @@ -60,7 +60,7 @@ def load_modules(self): self.f.write("# Load modules\n") self.f.write(f"export HOMEgfs={self.homegfs}\n") self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") - self.f.write(f". {self.homegfs}/ush/load_fv3gfs_modules.sh\n") + self.f.write(f". {self.homegfs}/ush/load_ufsda_modules.sh\n") self.f.write("set -x\n") def aprun(self): From 0629beb8abc94ea34d2f51af55dc2e6450936f2a Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Tue, 25 Feb 2025 19:24:25 +0000 Subject: [PATCH 04/10] update gsi namelist and jedi yaml to ensure greater 3dvar consistency (#1487) --- ush/ufoeval/atmanlfv3inc.yaml | 108 ++++++++++++++++++++++++++++++++++ ush/ufoeval/atmanlvar.yaml | 23 +++----- ush/ufoeval/config_jedi.yaml | 2 + ush/ufoeval/gsiparm.anl | 18 +++--- ush/ufoeval/run_jedi.py | 18 ++++-- 5 files changed, 138 insertions(+), 31 deletions(-) create mode 100644 ush/ufoeval/atmanlfv3inc.yaml diff --git a/ush/ufoeval/atmanlfv3inc.yaml b/ush/ufoeval/atmanlfv3inc.yaml new file mode 100644 index 000000000..e5b10d10f --- /dev/null +++ b/ush/ufoeval/atmanlfv3inc.yaml @@ -0,0 +1,108 @@ +variable change: + variable change name: Model2GeoVaLs + input variables: + - ua + - va + - t + - ps + - sphum + - ice_wat + - liq_wat + - o3mr + - hgtsfc + output variables: + - ua + - va + - t + - sphum + - ice_wat + - liq_wat + - o3mr + - delp + - delz +jedi increment variables: +- ua +- va +- t +- ps +- sphum +- ice_wat +- liq_wat +- o3mr +fv3 increment variables: +- ua +- va +- t +- sphum +- ice_wat +- liq_wat +- o3mr +- delp +- delz +background geometry: + fms initialization: + namelist filename: ./fv3jedi/fmsmpp.nml + field table filename: ./fv3jedi/field_table + akbk: ./fv3jedi/akbk.nc4 + layout: + - 4 + - 4 + npx: 193 + npy: 193 + npz: 127 + field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml +jedi increment geometry: + fms initialization: + namelist filename: ./fv3jedi/fmsmpp.nml + field table filename: ./fv3jedi/field_table + akbk: ./fv3jedi/akbk.nc4 + layout: + - 4 + - 4 + npx: 97 + npy: 97 + npz: 127 + field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml +fv3 increment geometry: + fms initialization: + namelist filename: ./fv3jedi/fmsmpp.nml + field table filename: ./fv3jedi/field_table + akbk: ./fv3jedi/akbk.nc4 + layout: + - 4 + - 4 + npx: 97 + npy: 97 + npz: 127 + field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_fv3inc.yaml +members: +- background input: + datapath: ./bkg + filetype: cube sphere history + provider: ufs + ufs soil nlev: 4 + ufs fields split by level: + - smc + - stc + datetime: '2024-12-25T00:00:00Z' + filenames: + - gdas.t18z.cubed_sphere_grid_atmf006.nc + - gdas.t18z.cubed_sphere_grid_sfcf006.nc + state variables: + - ua + - va + - t + - ps + - sphum + - ice_wat + - liq_wat + - o3mr + - hgtsfc + jedi increment input: + filetype: cube sphere history + filename: ./anl/atminc.%yyyy%mm%dd.%hh%MM%ssz.nc4 + provider: ufs + fv3 increment output: + filetype: auxgrid + gridtype: gaussian + filename: ./anl/atminc. diff --git a/ush/ufoeval/atmanlvar.yaml b/ush/ufoeval/atmanlvar.yaml index f940f6099..d89a3dfb1 100644 --- a/ush/ufoeval/atmanlvar.yaml +++ b/ush/ufoeval/atmanlvar.yaml @@ -17,7 +17,7 @@ cost function: npy: 193 npz: 127 field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml - analysis variables: + analysis variables: &3dvars - ua - va - t @@ -88,15 +88,7 @@ cost function: - cloud_liquid_ice - cloud_liquid_water - mole_fraction_of_ozone_in_air - output variables: - - ua - - va - - t - - ps - - sphum - - ice_wat - - liq_wat - - o3mr + output variables: *3dvars observations: obs perturbations: false observers: @@ -430,8 +422,7 @@ variational: algorithm: DRPCG iterations: - ninner: 50 - gradient norm reduction: 1e-10 - test: true + gradient norm reduction: 1e-5 geometry: fms initialization: namelist filename: ./fv3jedi/fmsmpp.nml @@ -440,8 +431,8 @@ variational: layout: - 4 - 4 - npx: 193 - npy: 193 + npx: 97 + npy: 97 npz: 127 field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml diagnostics: @@ -474,8 +465,8 @@ final: layout: - 4 - 4 - npx: 193 - npy: 193 + npx: 97 + npy: 97 npz: 127 field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml final j evaluation: false diff --git a/ush/ufoeval/config_jedi.yaml b/ush/ufoeval/config_jedi.yaml index bb8987a46..8c2c53623 100644 --- a/ush/ufoeval/config_jedi.yaml +++ b/ush/ufoeval/config_jedi.yaml @@ -19,4 +19,6 @@ directories: app files: APPEXE: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/exec/gdas.x APPYML: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/sorc/gdas.cd/ush/ufoeval/atmanlvar.yaml + INCEXE: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/exec/fv3jedi_fv3inc.x + INCYML: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/sorc/gdas.cd/ush/ufoeval/atmanlfv3inc.yaml runscript: runjedi.sh diff --git a/ush/ufoeval/gsiparm.anl b/ush/ufoeval/gsiparm.anl index d73302cde..6a8cc1a51 100644 --- a/ush/ufoeval/gsiparm.anl +++ b/ush/ufoeval/gsiparm.anl @@ -1,11 +1,9 @@ &SETUP - miter=2, - niter(1)=100,niter(2)=100, - niter_no_qc(1)=50,niter_no_qc(2)=0, - write_diag(1)=.true.,write_diag(2)=.false.,write_diag(3)=.true., - qoption=2, + miter=1,niter(1)=50, + write_diag(1)=.true.,write_diag(2)=.true., + qoption=1, gencode=0,deltim=189, - factqmin=0.5,factqmax=0.0002, + factqmin=0.0,factqmax=0.0, iguess=-1, tzr_qc=1, oneobtest=.false.,retrieval=.false.,l_foto=.false., @@ -16,7 +14,7 @@ crtm_coeffs_path='./crtm_coeffs/', newpc4pred=.true.,adp_anglebc=.true.,angord=4,passive_bc=.true.,use_edges=.false., diag_precon=.true.,step_start=1.e-3,emiss_bc=.true.,nhr_obsbin=3, - cwoption=3,imp_physics=8,lupp=.true.,cnvw_option=.false.,cao_check=.true., + cwoption=1,imp_physics=8,lupp=.true.,cnvw_option=.false.,cao_check=.false., netcdf_diag=.true.,binary_diag=.false., lobsdiag_forenkf=.false., write_fv3_incr=.true., @@ -25,12 +23,10 @@ incvars_to_zero= 'liq_wat_inc','icmr_inc','rwmr_inc','snmr_inc','grle_inc', incvars_zero_strat= 'sphum_inc','liq_wat_inc','icmr_inc','rwmr_inc','snmr_inc','grle_inc', incvars_efold= 5, - miter=1,niter(1)=50,niter(2)=50,qoption=1,cwoption=1,factqmin=0.0,factqmax=0.0,sfcnst_comb=.true., / &GRIDOPTS - JCAP_B=382,JCAP=192,NLAT=386,NLON=768,nsig=127, - regional=.false.,nlayers(63)=3,nlayers(64)=6, - nlayers(63)=1,nlayers(64)=1, + JCAP_B=126,JCAP=126,NLAT=194,NLON=384,nsig=127, + regional=.false., / &BKGERR vs=0.7, diff --git a/ush/ufoeval/run_jedi.py b/ush/ufoeval/run_jedi.py index c68fc3e0c..dfc780e33 100755 --- a/ush/ufoeval/run_jedi.py +++ b/ush/ufoeval/run_jedi.py @@ -29,6 +29,8 @@ def __init__(self, scriptname, config): self.rundir = config['directories']['RUNDIR'] self.appexe = config['app files']['APPEXE'] self.appyml = config['app files']['APPYML'] + self.incexe = config['app files']['INCEXE'] + self.incyml = config['app files']['INCYML'] self.jobname = config['job options']['job-name'] self.nodes = config['job options']['nodes'] self.ppn = config['job options']['tasks-per-node'] @@ -75,9 +77,11 @@ def aprun(self): # link / copy app files self.f.write("\n") - self.f.write(f"# Copy executable and namelist\n") - self.f.write(f"ln -fs {self.appexe} ./app.x\n") - self.f.write(f"cp -p {self.appyml} ./app.yaml\n") + self.f.write(f"# Copy executables and yamls\n") + self.f.write(f"ln -fs {self.appexe} ./gdas.x\n") + self.f.write(f"cp -p {self.appyml} ./atmanlvar.yaml\n") + self.f.write(f"ln -fs {self.incexe} ./fv3jedi_fv3inc.x\n") + self.f.write(f"cp -p {self.incyml} ./atmanlfv3inc.yaml\n") # execute app self.f.write("\n") @@ -85,7 +89,13 @@ def aprun(self): self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") self.f.write(f"ulimit -s unlimited\n") - aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./app.x fv3jedi variational ./app.yaml" + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./gdas.x fv3jedi variational ./atmanlvar.yaml" + self.f.write(f"{aprun_command}\n") + + # convert cube sphere increments to gaussian grid + self.f.write("\n") + self.f.write(f"# Convert cube sphere increments to gaussian grid\n") + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./fv3jedi_fv3inc.x ./atmanlfv3inc.yaml" self.f.write(f"{aprun_command}\n") def close(self): From 4c1fa5f0aac6a43a21b05e7bc9cfb6af00583b54 Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Wed, 26 Feb 2025 11:32:47 +0000 Subject: [PATCH 05/10] Merge branch 'develop' into feature/phase3_validation --- sorc/fv3-jedi | 2 +- sorc/fv3-jedi-lm | 2 +- sorc/ioda | 2 +- sorc/iodaconv | 2 +- sorc/oops | 2 +- sorc/saber | 2 +- sorc/soca | 2 +- sorc/ufo | 2 +- sorc/vader | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sorc/fv3-jedi b/sorc/fv3-jedi index 9440a15e9..9ca9320b3 160000 --- a/sorc/fv3-jedi +++ b/sorc/fv3-jedi @@ -1 +1 @@ -Subproject commit 9440a15e97eaeb53b46e0b49b6d943c5071803ee +Subproject commit 9ca9320b39bce012afe8d1fff8cd39e06047bc3c diff --git a/sorc/fv3-jedi-lm b/sorc/fv3-jedi-lm index a6e97d76e..5994c385a 160000 --- a/sorc/fv3-jedi-lm +++ b/sorc/fv3-jedi-lm @@ -1 +1 @@ -Subproject commit a6e97d76ed7c0b2a27cf97512893a93d7e2b44bc +Subproject commit 5994c385a01236bb9def57dd37b73bec8b5df487 diff --git a/sorc/ioda b/sorc/ioda index 9644fa134..bc414854e 160000 --- a/sorc/ioda +++ b/sorc/ioda @@ -1 +1 @@ -Subproject commit 9644fa134bd078ad9a674ea581623c49fe3b4b32 +Subproject commit bc414854e27e7cb35cfa9031a4f5c540a6c43ac5 diff --git a/sorc/iodaconv b/sorc/iodaconv index a2f538327..cdab77230 160000 --- a/sorc/iodaconv +++ b/sorc/iodaconv @@ -1 +1 @@ -Subproject commit a2f538327ed37be6f98a21ee6a5acf3ef4dd96b8 +Subproject commit cdab77230032f72165ec738e5fcaa29a1b3d7dfa diff --git a/sorc/oops b/sorc/oops index 3591b63a7..ab181457b 160000 --- a/sorc/oops +++ b/sorc/oops @@ -1 +1 @@ -Subproject commit 3591b63a772d07beaccdc980cc2da87ffa704f14 +Subproject commit ab181457bbca1d4ecb0e57acdaca3137be24906d diff --git a/sorc/saber b/sorc/saber index 160bbf84e..f044a541d 160000 --- a/sorc/saber +++ b/sorc/saber @@ -1 +1 @@ -Subproject commit 160bbf84e90c6e590f5f0fba11123b80aa11b579 +Subproject commit f044a541ddad0a94ea195c8e92272e9552ec00d1 diff --git a/sorc/soca b/sorc/soca index 697e42645..7675ed14a 160000 --- a/sorc/soca +++ b/sorc/soca @@ -1 +1 @@ -Subproject commit 697e42645211854b79e5db2c86f71c58a3de0092 +Subproject commit 7675ed14a2976b13efb311107dab3eca9e32b568 diff --git a/sorc/ufo b/sorc/ufo index e51ced6a4..75be623bd 160000 --- a/sorc/ufo +++ b/sorc/ufo @@ -1 +1 @@ -Subproject commit e51ced6a4e7db01bf99096bb7a603ba5817cd101 +Subproject commit 75be623bdf2f7bb8013afc81eaae24548006b281 diff --git a/sorc/vader b/sorc/vader index 0792f6931..585c86e06 160000 --- a/sorc/vader +++ b/sorc/vader @@ -1 +1 @@ -Subproject commit 0792f693148c3d09a166021e6b8c2758cb8a1251 +Subproject commit 585c86e067dd8549c012b6412a6009bbca08758f From f3d238d859af2b7f7caf4af695d41fb79cd94031 Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Thu, 6 Mar 2025 19:48:55 +0000 Subject: [PATCH 06/10] simplify phase3 validation setup (#1487) --- ush/ufoeval/README_phase3 | 64 ++++++----- ush/ufoeval/config_gsi.yaml | 9 +- ush/ufoeval/config_jedi.yaml | 10 +- ush/ufoeval/run.py | 211 +++++++++++++++++++++++++++++++++++ ush/ufoeval/run_gsi.py | 157 -------------------------- ush/ufoeval/run_jedi.py | 159 -------------------------- 6 files changed, 256 insertions(+), 354 deletions(-) create mode 100755 ush/ufoeval/run.py delete mode 100755 ush/ufoeval/run_gsi.py delete mode 100755 ush/ufoeval/run_jedi.py diff --git a/ush/ufoeval/README_phase3 b/ush/ufoeval/README_phase3 index a249bba2f..2d03e34bc 100644 --- a/ush/ufoeval/README_phase3 +++ b/ush/ufoeval/README_phase3 @@ -1,36 +1,46 @@ The following files are available to assist developers in JEDI Phase 3 validation. -run_gsi.py and run_jedi.py - - stage files to run the indicated analysis executable +config_gsi.yaml - set user options for GSI atmospheric analysis job +config_jedi.yaml - set user option for JEDI atmospheric analysis job +run.py - script - process config, populate run directory, create run script + +config_gsi.yaml and config_jedi.yaml contain the following user settings + - machine: valid machines are hera, hercules, or orion + - job options: may need to adjust nodes, tasks-per-node, or mem appropriate for dataset being processed + - directories + HOMEgfs: global-work installation + STAGEDIR: machine specific path containing staged input + Hera: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR + Hercules, Orion: /work2/noaa/stmp/rtreadon/STAGEDIR + RUNDIR: path to the directory in which job will run. Full path is RUNDIR + DA_CORE + DA_TYPE + - app files + DA_CORE: DA engine to run. Valid options are gsi or jedi + DA_TYPE: type of DA to run. Valid options are + 3dv - 3dvar using gsibec + 3dvfgat - 3dvar using gsibec with fgat + hyb3dvfgat - 3dvar using gsibec & 40 member ensemble with fgat + APPEXEC: GSI or JEDI analysis executable + INCEXEC: executable to convert JEDI cube sphere increment to gaussian grid + +run.py + - stage files to run the case specified in the configuration yaml - create a batch script the user can subit to run executable -config_gsi.yaml and config_jedi.yaml - - set batch job configuration variables - - specify key directories - - specify the application exeutable and parameter file -atmanlvar.yaml - - sample yaml file to configure 3DVAR JEDI analysis +Executing the Phase 3 validation workflow entails -gsiparm.anl - - sample namelist to configure 3DVAR GSI analysis +1. modify config_jedi.yaml and config_gsi.yaml to point at the configuration + you want to use. Point at the executables to be run. Set the RUNDIR in which the + job will run +2. execute run.py. The syntax is "./run.py -c ./config_gsi.yaml" or + "./run.py -c ./config_jedi.yaml". The run.py script will + (a) populate the specified RUNDIR with the files to run the specified configuration + (b) create a batch script, runjob.sh, in RUNDIR to run the specified configuration -A Phase 3 validation workflow would entail + When ./run.py` is executed it echoes to the screen the directory to which data is copied + and the directory in which the job script, runjob.sh, is created. + +3. cd to the specified RUNDIR to submit the batch script. -1. modify atmanlvar.yaml and gsipmarm.anl to get the desired configuration - -2. modify config_jedi.yaml and config_gsi.yaml to point at the configuration - files from 1 and the executables to be run. Set the RUNDIR in which the - jobs are run - -3. execute run_gsi.py and run_jedi.py. The syntax is "./run_gsi.py -y ./config_gsi.yaml" - and "./run_jedi.py -y ./config_jedi.yaml". The run*py script will - (a) populate the specified RUNDIR with the files to run the given app - (b) create a batch script with the runscript name specified in the input yaml. - The batch script is created RUNDIR - -4. cd to the specified RUNDIR to submit the batch script. The job log file is written - to RUNDIR - -5. examine ouput upon job completion +4. examine ouput upon job completion diff --git a/ush/ufoeval/config_gsi.yaml b/ush/ufoeval/config_gsi.yaml index 68b4326c0..5d08bdb93 100644 --- a/ush/ufoeval/config_gsi.yaml +++ b/ush/ufoeval/config_gsi.yaml @@ -1,7 +1,6 @@ machine: hera job options: - job-name: gsivar account: da-cpu qos: debug partition: hera @@ -13,10 +12,10 @@ job options: directories: HOMEgfs: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test - STAGEDIR: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR/gsi/gdasanal_00 - RUNDIR: /scratch1/NCEPDEV/stmp2/Russ.Treadon/RUNDIRS/gsi_anl_00 + STAGEDIR: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR + RUNDIR: /scratch1/NCEPDEV/stmp2/Russ.Treadon/RUNDIRS app files: + DA_CORE: gsi + DA_TYPE: hyb3dvfgat APPEXE: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/exec/gsi.x - APPYML: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/sorc/gdas.cd/ush/ufoeval/gsiparm.anl - runscript: rungsi.sh diff --git a/ush/ufoeval/config_jedi.yaml b/ush/ufoeval/config_jedi.yaml index 8c2c53623..86e1c22a3 100644 --- a/ush/ufoeval/config_jedi.yaml +++ b/ush/ufoeval/config_jedi.yaml @@ -1,7 +1,6 @@ machine: hera job options: - job-name: jedivar account: da-cpu qos: debug partition: hera @@ -13,12 +12,11 @@ job options: directories: HOMEgfs: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test - STAGEDIR: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR/jedi/gdasatmanl_00 - RUNDIR: /scratch1/NCEPDEV/stmp2/Russ.Treadon/RUNDIRS/jedi_anl_00 + STAGEDIR: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR + RUNDIR: /scratch1/NCEPDEV/stmp2/Russ.Treadon/RUNDIRS app files: + DA_CORE: jedi + DA_TYPE: hyb3dvfgat APPEXE: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/exec/gdas.x - APPYML: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/sorc/gdas.cd/ush/ufoeval/atmanlvar.yaml INCEXE: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/exec/fv3jedi_fv3inc.x - INCYML: /scratch1/NCEPDEV/da/Russ.Treadon/git/global-workflow/test/sorc/gdas.cd/ush/ufoeval/atmanlfv3inc.yaml - runscript: runjedi.sh diff --git a/ush/ufoeval/run.py b/ush/ufoeval/run.py new file mode 100755 index 000000000..e252458e4 --- /dev/null +++ b/ush/ufoeval/run.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +import yaml +import os +import shutil +import subprocess +import argparse +import logging +from pathlib import Path +from datetime import datetime, timedelta + +logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', + level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') + +jobname = "runjob" + + +class JobCard: + + def __init__(self, config): + """ + Constructor for the JobCard class. + :param config: dictionary containing configuration information + """ + + self.config = config + + self.appcore = config['app files']['DA_CORE'] + self.apptype = config['app files']['DA_TYPE'] + self.appexe = config['app files']['APPEXE'] + + self.machine = config['machine'] + self.homegfs = config['directories']['HOMEgfs'] + self.rundir = os.path.join(config['directories']['RUNDIR'], self.appcore + '_' + self.apptype) + if self.appcore == 'jedi': + self.incexe = config['app files']['INCEXE'] + self.nodes = config['job options']['nodes'] + self.ppn = config['job options']['tasks-per-node'] + self.ntasks = self.nodes * self.ppn + self.threads = config['job options']['cpus-per-task'] + + self.f = open(jobname + ".sh", "w") + self.f.write("#!/usr/bin/env bash\n") + self.f.write(f"# Running on {self.machine} \n") + + def header(self): + """ + Write machine dependent scheduler header + TODO: generalize to support more than just slurm + """ + self.f.write(f"#SBATCH --output={jobname}.o%J\n") + + sbatch = '' + for key, value in self.config['job options'].items(): + sbatch += f"#SBATCH --{key}={value} \n" + + self.f.write(f"{sbatch}\n") + + def load_modules(self): + """ + Load modules + """ + self.f.write("\n") + self.f.write("# Load modules\n") + self.f.write(f"export HOMEgfs={self.homegfs}\n") + self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") + if self.appcore == 'gsi': + self.f.write(f". {self.homegfs}/ush/load_fv3gfs_modules.sh\n") + else: + self.f.write(f". {self.homegfs}/ush/load_ufsda_modules.sh\n") + self.f.write("set -x\n") + + def aprun(self): + """ + Execute app + """ + + # cd to run directory + self.f.write("\n") + self.f.write(f"# cd to run directory\n") + self.f.write(f"cd {self.rundir}\n") + + # copy or link app executable + self.f.write("\n") + if self.appcore == 'gsi': + self.f.write(f"# Copy executable\n") + self.f.write(f"cp -p {self.appexe} ./gsi.x\n") + else: + self.f.write(f"# Link executables\n") + self.f.write(f"ln -fs {self.appexe} ./gdas.x\n") + self.f.write(f"ln -fs {self.incexe} ./fv3jedi_fv3inc.x\n") + + # execute app + self.f.write("\n") + self.f.write(f"# Execute app\n") + self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") + self.f.write(f"ulimit -s unlimited\n") + + if self.appcore == 'gsi': + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./gsi.x" + else: + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./gdas.x fv3jedi variational ./atmanlvar.yaml" + + self.f.write(f"{aprun_command}\n") + + # JEDI jobs convert cube sphere increments to gausssian grid for comparison + if self.appcore == 'jedi': + self.f.write("\n") + self.f.write(f"# Convert cube sphere increments to gaussian grid\n") + aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./fv3jedi_fv3inc.x ./atmanlfv3inc.yaml" + self.f.write(f"{aprun_command}\n") + + def close(self): + """ + Flush and make the card executable + """ + self.f.close() + subprocess.run(["chmod", "+x", jobname + ".sh"]) + + +def main(): + epilog = ["Examples:", + " ./run.py -c config.yaml", + " ./run.py -h"] + parser = argparse.ArgumentParser(description="Set up atmospheric DA run.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=os.linesep.join(epilog)) + parser.add_argument("-c", "--config", required=True, help="The YAML file") + args = parser.parse_args() + + # Get the experiment configuration + run_jjobs_yaml = args.config + with open(run_jjobs_yaml, 'r') as file: + exp_config = yaml.safe_load(file) + + logging.info(f"exp_config {exp_config}") + + # Set DA core and type + appcore = exp_config['app files']['DA_CORE'] + apptype = exp_config['app files']['DA_TYPE'] + + # Ensure valid app core and type + valid = ['gsi', 'jedi'] + if appcore not in valid: + logging.error(f"DA_CORE {appcore} is invalid. Valid cores are {valid}") + sys.exit() + + valid = ['3dv', '3dvfgat', 'hyb3dvfgat'] + if apptype not in valid: + logging.error(f"DA_TYPE {apptype} is invalid. Valid types are {valid}") + sys.exit() + + # Set source (stagedir) and destination (rundir) paths + stagedir = os.path.join(exp_config['directories']['STAGEDIR'], appcore, apptype) + rundir = os.path.join(exp_config['directories']['RUNDIR'], appcore + '_' + apptype) + + # Create and cd to run directory + if os.path.exists(rundir): + shutil.rmtree(rundir) + os.makedirs(rundir) + os.chdir(rundir) + if appcore == 'jedi': + os.makedirs(f"{rundir}/anl") + os.makedirs(f"{rundir}/diags") + + # Copy files and directories to run directory + if appcore == 'gsi': + files_to_copy = [ + "*info*", "aircftbias_in", "atms_beamwidth.txt", "berror_stats", + "cloudy_radiance_info.txt", "errtable", "gsiparm.anl", "prepbufr", "radstat.gdas", + "AIRS_CLDDET.NL", "CRIS_CLDDET.NL", "IASI_CLDDET.NL", "Rcov*", "satbias_angle", + "satbias_in", "satbias_pc", "sfcf*", "sigf*", "vqctp001.dat" + ] + for pattern in files_to_copy: + for file_path in Path(stagedir).glob(pattern): + shutil.copy(file_path, rundir) + + directories_to_copy = ["crtm_coeffs", "ensemble_data"] + for directory in directories_to_copy: + source_dir = Path(stagedir) / directory + if source_dir.exists(): + shutil.copytree(source_dir, Path(rundir) / directory) + + else: + # Copy files and directories to run directory + files_to_copy = [ + "atmanlvar.yaml", "atmanlfv3inc.yaml" + ] + for pattern in files_to_copy: + for file_path in Path(stagedir).glob(pattern): + shutil.copy(file_path, rundir) + + directories_to_copy = ["berror", "bkg", "crtm", "ens", "fv3jedi", "obs"] + for directory in directories_to_copy: + source_dir = Path(stagedir) / directory + if source_dir.exists(): + shutil.copytree(source_dir, Path(rundir) / directory) + + logging.info(f"Data staged to {rundir}") + + # Create run script. + run_card = JobCard(exp_config) + run_card.header() + run_card.load_modules() + run_card.aprun() + run_card.close() + + logging.info(f"Create {jobname}.sh in {rundir}") + + +if __name__ == "__main__": + main() diff --git a/ush/ufoeval/run_gsi.py b/ush/ufoeval/run_gsi.py deleted file mode 100755 index fab3d7be3..000000000 --- a/ush/ufoeval/run_gsi.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env python3 -import yaml -import os -import shutil -import subprocess -import argparse -import logging -from pathlib import Path -from datetime import datetime, timedelta - -logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', - level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') - - -class JobCard: - - def __init__(self, scriptname, config): - """ - Constructor for the JobCard class. - :param scriptname: name of the output script file - :param config: dictionary containing configuration information - """ - - self.name = scriptname - self.config = config - - self.machine = config['machine'] - self.homegfs = config['directories']['HOMEgfs'] - self.rundir = config['directories']['RUNDIR'] - self.appexe = config['app files']['APPEXE'] - self.appyml = config['app files']['APPYML'] - self.jobname = config['job options']['job-name'] - self.nodes = config['job options']['nodes'] - self.ppn = config['job options']['tasks-per-node'] - self.ntasks = self.nodes * self.ppn - self.threads = config['job options']['cpus-per-task'] - - self.f = open(self.name, "w") - self.f.write("#!/usr/bin/env bash\n") - self.f.write(f"# Running on {self.machine} \n") - - def header(self): - """ - Write machine dependent scheduler header - TODO: generalize to support more than just slurm - """ - self.f.write(f"#SBATCH --output={self.jobname}.o%J\n") - - sbatch = '' - for key, value in self.config['job options'].items(): - sbatch += f"#SBATCH --{key}={value} \n" - - self.f.write(f"{sbatch}\n") - - def load_modules(self): - """ - Load modules - """ - self.f.write("\n") - self.f.write("# Load modules\n") - self.f.write(f"export HOMEgfs={self.homegfs}\n") - self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") - self.f.write(f". {self.homegfs}/ush/load_fv3gfs_modules.sh\n") - self.f.write("set -x\n") - - def aprun(self): - """ - Execute app - """ - - # cd to run directory - self.f.write("\n") - self.f.write(f"# cd to run directory\n") - self.f.write(f"cd {self.rundir}\n") - - # copy app files - self.f.write("\n") - self.f.write(f"# Copy executable and namelist\n") - self.f.write(f"cp -p {self.appexe} ./app.x\n") - self.f.write(f"cp -p {self.appyml} ./\n") - - # execute app - self.f.write("\n") - self.f.write(f"# Execute app\n") - self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") - self.f.write(f"ulimit -s unlimited\n") - - aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./app.x" - self.f.write(f"{aprun_command}\n") - - def close(self): - """ - Flush and make the card executable - """ - self.f.close() - subprocess.run(["chmod", "+x", self.name]) - - -def main(): - epilog = ["Examples:", - " ./run_jjobs.py -y config.yaml", - " ./run_jjobs.py -h"] - parser = argparse.ArgumentParser(description="Run an ordered list of j-jobs.", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=os.linesep.join(epilog)) - parser.add_argument("-y", "--yaml", required=True, help="The YAML file") - args = parser.parse_args() - - # Get the experiment configuration - run_jjobs_yaml = args.yaml - with open(run_jjobs_yaml, 'r') as file: - exp_config = yaml.safe_load(file) - - logging.info(f"exp_config {exp_config}") - - # Set source (stagedir) and destination (rundir) paths - stagedir = exp_config['directories']['STAGEDIR'] - rundir = exp_config['directories']['RUNDIR'] - - # Create and cd to run directory - if os.path.exists(rundir): - shutil.rmtree(rundir) - os.makedirs(rundir) - os.chdir(rundir) - - # Copy files and directories to run directory - files_to_copy = [ - "*info*", "aircftbias_in", "atms_beamwidth.txt", "berror_stats", - "cloudy_radiance_info.txt", "errtable", "prepbufr", "radstat.gdas", "AIRS_CLDDET.NL", - "CRIS_CLDDET.NL", "IASI_CLDDET.NL", "Rcov*", "satbias_angle", "satbias_in", - "satbias_pc", "sfcf06", "sigf06", "vqctp001.dat" - ] - for pattern in files_to_copy: - for file_path in Path(stagedir).glob(pattern): - shutil.copy(file_path, rundir) - - directories_to_copy = ["crtm_coeffs"] - for directory in directories_to_copy: - source_dir = Path(stagedir) / directory - if source_dir.exists(): - shutil.copytree(source_dir, Path(rundir) / directory) - - logging.info(f"Data staged to {rundir}") - - # Create run script. - runscript = exp_config['app files']['runscript'] - run_card = JobCard(runscript, exp_config) - run_card.header() - run_card.load_modules() - run_card.aprun() - run_card.close() - - logging.info(f"Create {runscript} in {rundir}") - - -if __name__ == "__main__": - main() diff --git a/ush/ufoeval/run_jedi.py b/ush/ufoeval/run_jedi.py deleted file mode 100755 index dfc780e33..000000000 --- a/ush/ufoeval/run_jedi.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -import yaml -import os -import shutil -import subprocess -import argparse -import logging -from pathlib import Path -from datetime import datetime, timedelta - -logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', - level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') - - -class JobCard: - - def __init__(self, scriptname, config): - """ - Constructor for the JobCard class. - :param scriptname: name of the output script file - :param config: dictionary containing configuration information - """ - - self.name = scriptname - self.config = config - - self.machine = config['machine'] - self.homegfs = config['directories']['HOMEgfs'] - self.rundir = config['directories']['RUNDIR'] - self.appexe = config['app files']['APPEXE'] - self.appyml = config['app files']['APPYML'] - self.incexe = config['app files']['INCEXE'] - self.incyml = config['app files']['INCYML'] - self.jobname = config['job options']['job-name'] - self.nodes = config['job options']['nodes'] - self.ppn = config['job options']['tasks-per-node'] - self.ntasks = self.nodes * self.ppn - self.threads = config['job options']['cpus-per-task'] - - self.f = open(self.name, "w") - self.f.write("#!/usr/bin/env bash\n") - self.f.write(f"# Running on {self.machine} \n") - - def header(self): - """ - Write machine dependent scheduler header - TODO: generalize to support more than just slurm - """ - self.f.write(f"#SBATCH --output={self.jobname}.o%J\n") - - sbatch = '' - for key, value in self.config['job options'].items(): - sbatch += f"#SBATCH --{key}={value} \n" - - self.f.write(f"{sbatch}\n") - - def load_modules(self): - """ - Load modules - """ - self.f.write("\n") - self.f.write("# Load modules\n") - self.f.write(f"export HOMEgfs={self.homegfs}\n") - self.f.write(f"source {self.homegfs}/ush/preamble.sh\n") - self.f.write(f". {self.homegfs}/ush/load_ufsda_modules.sh\n") - self.f.write("set -x\n") - - def aprun(self): - """ - Execute app - """ - - # cd to run directory - self.f.write("\n") - self.f.write(f"# cd to run directory\n") - self.f.write(f"cd {self.rundir}\n") - - # link / copy app files - self.f.write("\n") - self.f.write(f"# Copy executables and yamls\n") - self.f.write(f"ln -fs {self.appexe} ./gdas.x\n") - self.f.write(f"cp -p {self.appyml} ./atmanlvar.yaml\n") - self.f.write(f"ln -fs {self.incexe} ./fv3jedi_fv3inc.x\n") - self.f.write(f"cp -p {self.incyml} ./atmanlfv3inc.yaml\n") - - # execute app - self.f.write("\n") - self.f.write(f"# Execute app\n") - self.f.write(f"export OMP_NUM_THREADS={self.threads}\n") - self.f.write(f"ulimit -s unlimited\n") - - aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./gdas.x fv3jedi variational ./atmanlvar.yaml" - self.f.write(f"{aprun_command}\n") - - # convert cube sphere increments to gaussian grid - self.f.write("\n") - self.f.write(f"# Convert cube sphere increments to gaussian grid\n") - aprun_command = f"srun -n {self.ntasks} --cpus-per-task={self.threads} ./fv3jedi_fv3inc.x ./atmanlfv3inc.yaml" - self.f.write(f"{aprun_command}\n") - - def close(self): - """ - Flush and make the card executable - """ - self.f.close() - subprocess.run(["chmod", "+x", self.name]) - - -def main(): - epilog = ["Examples:", - " ./run_jjobs.py -y config.yaml", - " ./run_jjobs.py -h"] - parser = argparse.ArgumentParser(description="Run an ordered list of j-jobs.", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=os.linesep.join(epilog)) - parser.add_argument("-y", "--yaml", required=True, help="The YAML file") - args = parser.parse_args() - - # Get the experiment configuration - run_jjobs_yaml = args.yaml - with open(run_jjobs_yaml, 'r') as file: - exp_config = yaml.safe_load(file) - - logging.info(f"exp_config {exp_config}") - - # Set source (stagedir) and destination (rundir) paths - stagedir = exp_config['directories']['STAGEDIR'] - rundir = exp_config['directories']['RUNDIR'] - - # Create and cd to run directory - if os.path.exists(rundir): - shutil.rmtree(rundir) - os.makedirs(rundir) - os.makedirs(f"{rundir}/anl") - os.makedirs(f"{rundir}/diags") - os.chdir(rundir) - - # Copy directories to run directory - directories_to_copy = ["berror", "bkg", "crtm", "fv3jedi", "obs"] - for directory in directories_to_copy: - source_dir = Path(stagedir) / directory - if source_dir.exists(): - shutil.copytree(source_dir, Path(rundir) / directory) - - logging.info(f"Data staged to {rundir}") - - # Create run script. - runscript = exp_config['app files']['runscript'] - run_card = JobCard(runscript, exp_config) - run_card.header() - run_card.load_modules() - run_card.aprun() - run_card.close() - - logging.info(f"Create {runscript} in {rundir}") - - -if __name__ == "__main__": - main() From 34934e9bf2a4e27298ed72e7b2594525ab2f5c5c Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Thu, 6 Mar 2025 20:18:54 +0000 Subject: [PATCH 07/10] remove unused phase3 validation yamls and namelist file (#1487) --- ush/ufoeval/atmanlfv3inc.yaml | 108 -------- ush/ufoeval/atmanlvar.yaml | 472 ---------------------------------- ush/ufoeval/gsiparm.anl | 215 ---------------- 3 files changed, 795 deletions(-) delete mode 100644 ush/ufoeval/atmanlfv3inc.yaml delete mode 100644 ush/ufoeval/atmanlvar.yaml delete mode 100644 ush/ufoeval/gsiparm.anl diff --git a/ush/ufoeval/atmanlfv3inc.yaml b/ush/ufoeval/atmanlfv3inc.yaml deleted file mode 100644 index e5b10d10f..000000000 --- a/ush/ufoeval/atmanlfv3inc.yaml +++ /dev/null @@ -1,108 +0,0 @@ -variable change: - variable change name: Model2GeoVaLs - input variables: - - ua - - va - - t - - ps - - sphum - - ice_wat - - liq_wat - - o3mr - - hgtsfc - output variables: - - ua - - va - - t - - sphum - - ice_wat - - liq_wat - - o3mr - - delp - - delz -jedi increment variables: -- ua -- va -- t -- ps -- sphum -- ice_wat -- liq_wat -- o3mr -fv3 increment variables: -- ua -- va -- t -- sphum -- ice_wat -- liq_wat -- o3mr -- delp -- delz -background geometry: - fms initialization: - namelist filename: ./fv3jedi/fmsmpp.nml - field table filename: ./fv3jedi/field_table - akbk: ./fv3jedi/akbk.nc4 - layout: - - 4 - - 4 - npx: 193 - npy: 193 - npz: 127 - field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml -jedi increment geometry: - fms initialization: - namelist filename: ./fv3jedi/fmsmpp.nml - field table filename: ./fv3jedi/field_table - akbk: ./fv3jedi/akbk.nc4 - layout: - - 4 - - 4 - npx: 97 - npy: 97 - npz: 127 - field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml -fv3 increment geometry: - fms initialization: - namelist filename: ./fv3jedi/fmsmpp.nml - field table filename: ./fv3jedi/field_table - akbk: ./fv3jedi/akbk.nc4 - layout: - - 4 - - 4 - npx: 97 - npy: 97 - npz: 127 - field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_fv3inc.yaml -members: -- background input: - datapath: ./bkg - filetype: cube sphere history - provider: ufs - ufs soil nlev: 4 - ufs fields split by level: - - smc - - stc - datetime: '2024-12-25T00:00:00Z' - filenames: - - gdas.t18z.cubed_sphere_grid_atmf006.nc - - gdas.t18z.cubed_sphere_grid_sfcf006.nc - state variables: - - ua - - va - - t - - ps - - sphum - - ice_wat - - liq_wat - - o3mr - - hgtsfc - jedi increment input: - filetype: cube sphere history - filename: ./anl/atminc.%yyyy%mm%dd.%hh%MM%ssz.nc4 - provider: ufs - fv3 increment output: - filetype: auxgrid - gridtype: gaussian - filename: ./anl/atminc. diff --git a/ush/ufoeval/atmanlvar.yaml b/ush/ufoeval/atmanlvar.yaml deleted file mode 100644 index d89a3dfb1..000000000 --- a/ush/ufoeval/atmanlvar.yaml +++ /dev/null @@ -1,472 +0,0 @@ -cost function: - cost type: 3D-Var - jb evaluation: false - time window: - begin: '2024-12-24T21:00:00Z' - length: PT6H - bound to include: begin - geometry: - fms initialization: - namelist filename: ./fv3jedi/fmsmpp.nml - field table filename: ./fv3jedi/field_table - akbk: ./fv3jedi/akbk.nc4 - layout: - - 4 - - 4 - npx: 193 - npy: 193 - npz: 127 - field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml - analysis variables: &3dvars - - ua - - va - - t - - ps - - sphum - - ice_wat - - liq_wat - - o3mr - background: - datapath: ./bkg - filetype: cube sphere history - provider: ufs - datetime: '2024-12-25T00:00:00Z' - filenames: - - gdas.t18z.cubed_sphere_grid_atmf006.nc - - gdas.t18z.cubed_sphere_grid_sfcf006.nc - state variables: - - ua - - va - - t - - delp - - ps - - sphum - - ice_wat - - liq_wat - - o3mr - - hgtsfc - - slmsk - - sheleg - - tmpsfc - - vtype - - stype - - vfrac - - soilt1 - - soilw1 - - snwdphMeters - - u_srf - - v_srf - - f10m - background error: - covariance model: SABER - full inverse: true - saber central block: - saber block name: gsi static covariance - read: - gsi akbk: ./fv3jedi/akbk.nc4 - gsi error covariance file: ./berror/gsi-coeffs-gfs-global.nc4 - gsi berror namelist file: ./berror/gfs_gsi_global.nml - processor layout x direction: 12 - processor layout y direction: 8 - debugging mode: false - saber outer blocks: - - saber block name: gsi interpolation to model grid - gsi akbk: ./fv3jedi/akbk.nc4 - gsi error covariance file: ./berror/gsi-coeffs-gfs-global.nc4 - gsi berror namelist file: ./berror/gfs_gsi_global.nml - processor layout x direction: 12 - processor layout y direction: 8 - debugging mode: false - linear variable change: - linear variable change name: Control2Analysis - input variables: - - eastward_wind - - northward_wind - - air_temperature - - air_pressure_at_surface - - water_vapor_mixing_ratio_wrt_moist_air - - cloud_liquid_ice - - cloud_liquid_water - - mole_fraction_of_ozone_in_air - output variables: *3dvars - observations: - obs perturbations: false - observers: - - obs space: - name: surface_ps - obsdatain: - engine: - type: H5File - obsfile: ./obs/gdas.t00z.conventional_ps.tm00.nc - obsdataout: - engine: - type: H5File - obsfile: ./diags/diag_conventional_ps_2024122500.nc - io pool: - max pool size: 1 - simulated variables: - - stationPressure - obs operator: - name: SfcPCorrected - variables: - - name: stationPressure - da_psfc_scheme: GSI - station_altitude: height - geovar_sfc_geomz: height_above_mean_sea_level_at_surface - geovar_geomz: geopotential_height - linear obs operator: - name: Identity - variables: - - name: stationPressure - obs prior filters: - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: ObsType/stationPressure - is_in: - - 181 - action: - name: assign error - error function: - name: ObsFunction/ObsErrorModelStepwiseLinear - options: - round_to_the_nearest_integer: true - xvar: - name: ObsValue/stationPressure - xvals: - - 80000 - - 75000 - - 70000 - - 65000 - - 60000 - - 55000 - errors: - - 110 - - 120 - - 120 - - 120 - - 120 - - 100000000000.0 - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: ObsType/stationPressure - is_in: - - 187 - action: - name: assign error - error function: - name: ObsFunction/ObsErrorModelStepwiseLinear - options: - round_to_the_nearest_integer: true - xvar: - name: ObsValue/stationPressure - xvals: - - 85000 - - 80000 - - 75000 - - 70000 - - 65000 - - 60000 - - 55000 - errors: - - 120 - - 140 - - 140 - - 140 - - 140 - - 140 - - 100000000000.0 - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: ObsType/stationPressure - is_in: - - 180 - action: - name: assign error - error function: - name: ObsFunction/ObsErrorModelStepwiseLinear - options: - round_to_the_nearest_integer: true - xvar: - name: ObsValue/stationPressure - xvals: - - 60000 - - 55000 - errors: - - 130 - - 100000000000.0 - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: ObsType/stationPressure - is_in: - - 183 - action: - name: assign error - error parameter: 100000000000.0 - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: ObsType/stationPressure - is_in: - - 120 - action: - name: assign error - error function: - name: ObsFunction/ObsErrorModelStepwiseLinear - options: - round_to_the_nearest_integer: true - xvar: - name: ObsValue/stationPressure - xvals: - - 80000 - - 75000 - - 70000 - - 65000 - - 60000 - - 55000 - errors: - - 110 - - 120 - - 120 - - 120 - - 120 - - 100000000000.0 - - filter: Variable Assignment - assignments: - - name: InputObsError/stationPressure - type: float - source variable: ObsErrorData/stationPressure - - filter: Variable Assignment - assignments: - - name: PreQC/stationPressure - type: int - source variable: QualityMarker/stationPressure - - filter: Variable Assignment - assignments: - - name: PreUseFlag/stationPressure - type: int - source variable: PreQC/stationPressure - - filter: Variable Assignment - where: - - variable: - name: PreUseFlag/stationPressure - is_in: 1-15 - assignments: - - name: PreUseFlag/stationPressure - value: 0 - - filter: Variable Assignment - where: - - variable: - name: ObsType/stationPressure - is_in: 183 - assignments: - - name: PreUseFlag/stationPressure - value: 100 - - filter: Variable Assignment - where: - - variable: - name: ObsValue/stationPressure - is_defined: null - - variable: - name: ObsValue/stationPressure - maxvalue: 50000.0 - where operator: and - assignments: - - name: PreUseFlag/stationPressure - value: 100 - - filter: Variable Assignment - where: - - variable: - name: PreQC/stationPressure - is_in: 9, 12, 15 - assignments: - - name: PreUseFlag/stationPressure - value: 100 - - filter: Variable Assignment - where: - - variable: - name: PreQC/stationPressure - is_in: 4-15 - assignments: - - name: PreUseFlag/stationPressure - value: 101 - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: PreQC/stationPressure - is_in: 3, 7 - action: - name: inflate error - inflation factor: 1.2 - obs post filters: - - filter: Variable Assignment - assignments: - - name: ObsErrorFactorDuplicateCheck/stationPressure - type: float - function: - name: ObsFunction/ObsErrorFactorDuplicateCheck - options: - use_air_pressure: false - variable: stationPressure - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: - name: ObsType/stationPressure - is_in: 180 - - variable: - name: ObsSubType/stationPressure - is_in: 0 - action: - name: inflate error - inflation factor: 0.7 - - filter: Variable Assignment - assignments: - - name: ObsErrorFactorSfcPressure/stationPressure - type: float - function: - name: ObsFunction/ObsErrorFactorSfcPressure - options: - geovar_sfc_geomz: height_above_mean_sea_level_at_surface - geovar_geomz: geopotential_height - station_altitude: height - - filter: Perform Action - filter variables: - - name: stationPressure - action: - name: inflate error - inflation variable: - name: ObsErrorFactorSfcPressure/stationPressure - - filter: Variable Assignment - assignments: - - name: DerivedMetaData/Innovation - type: float - function: - name: ObsFunction/Arithmetic - options: - variables: - - name: ObsValue/stationPressure - - name: HofX/stationPressure - coefs: - - 1 - - -1 - - filter: Variable Assignment - assignments: - - name: DerivedMetaData/ObsErrorBoundSfcPressure1 - type: float - function: - name: ObsFunction/ObsErrorBoundConventional - options: - obsvar: stationPressure - obserr_bound_min: 100 - obserr_bound_max: 300 - obserr_bound_factor: 5.0 - - filter: Background Check - filter variables: - - name: stationPressure - where: - - variable: PreQC/stationPressure - is_not_in: 3 - function absolute threshold: - - name: DerivedMetaData/ObsErrorBoundSfcPressure1 - action: - name: reject - - filter: Variable Assignment - assignments: - - name: DerivedMetaData/ObsErrorBoundSfcPressure2 - type: float - function: - name: ObsFunction/ObsErrorBoundConventional - options: - obsvar: stationPressure - obserr_bound_min: 100 - obserr_bound_max: 300 - obserr_bound_factor: 3.5 - - filter: Background Check - filter variables: - - name: stationPressure - where: - - variable: PreQC/stationPressure - is_in: 3 - function absolute threshold: - - name: DerivedMetaData/ObsErrorBoundSfcPressure2 - action: - name: reject - - filter: Perform Action - filter variables: - - name: stationPressure - action: - name: inflate error - inflation variable: - name: ObsErrorFactorDuplicateCheck/stationPressure - - filter: Perform Action - filter variables: - - name: stationPressure - where: - - variable: PreUseFlag/stationPressure - is_not_in: 0, 1 - action: - name: reject -variational: - minimizer: - algorithm: DRPCG - iterations: - - ninner: 50 - gradient norm reduction: 1e-5 - geometry: - fms initialization: - namelist filename: ./fv3jedi/fmsmpp.nml - field table filename: ./fv3jedi/field_table - akbk: ./fv3jedi/akbk.nc4 - layout: - - 4 - - 4 - npx: 97 - npy: 97 - npz: 127 - field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml - diagnostics: - departures: bkgmob -final: - diagnostics: - departures: anlmob - prints: - frequency: PT3H - increment: - output: - state component: - filetype: cube sphere history - filename: ./anl/atminc.%yyyy%mm%dd.%hh%MM%ssz.nc4 - provider: ufs - fields to write: - - ugrd - - vgrd - - tmp - - pressfc - - spfh - - icmr - - clwmr - - o3mr - geometry: - fms initialization: - namelist filename: ./fv3jedi/fmsmpp.nml - field table filename: ./fv3jedi/field_table - akbk: ./fv3jedi/akbk.nc4 - layout: - - 4 - - 4 - npx: 97 - npy: 97 - npz: 127 - field metadata override: ./fv3jedi/fv3jedi_fieldmetadata_history.yaml -final j evaluation: false diff --git a/ush/ufoeval/gsiparm.anl b/ush/ufoeval/gsiparm.anl deleted file mode 100644 index 6a8cc1a51..000000000 --- a/ush/ufoeval/gsiparm.anl +++ /dev/null @@ -1,215 +0,0 @@ -&SETUP - miter=1,niter(1)=50, - write_diag(1)=.true.,write_diag(2)=.true., - qoption=1, - gencode=0,deltim=189, - factqmin=0.0,factqmax=0.0, - iguess=-1, - tzr_qc=1, - oneobtest=.false.,retrieval=.false.,l_foto=.false., - use_pbl=.false.,use_compress=.true.,nsig_ext=45,gpstop=50.,commgpstop=45.,commgpserrinf=1.0, - use_gfs_nemsio=.false.,use_gfs_ncio=.true.,sfcnst_comb=.true., - use_readin_anl_sfcmask=.false., - lrun_subdirs=.true., - crtm_coeffs_path='./crtm_coeffs/', - newpc4pred=.true.,adp_anglebc=.true.,angord=4,passive_bc=.true.,use_edges=.false., - diag_precon=.true.,step_start=1.e-3,emiss_bc=.true.,nhr_obsbin=3, - cwoption=1,imp_physics=8,lupp=.true.,cnvw_option=.false.,cao_check=.false., - netcdf_diag=.true.,binary_diag=.false., - lobsdiag_forenkf=.false., - write_fv3_incr=.true., - nhr_anal=6,, - ta2tb=.true., - incvars_to_zero= 'liq_wat_inc','icmr_inc','rwmr_inc','snmr_inc','grle_inc', - incvars_zero_strat= 'sphum_inc','liq_wat_inc','icmr_inc','rwmr_inc','snmr_inc','grle_inc', - incvars_efold= 5, -/ -&GRIDOPTS - JCAP_B=126,JCAP=126,NLAT=194,NLON=384,nsig=127, - regional=.false., -/ -&BKGERR - vs=0.7, - hzscl=1.7,0.8,0.5, - hswgt=0.45,0.3,0.25, - bw=0.0,norsp=4, - bkgv_flowdep=.false.,bkgv_rewgtfct=1.5, - bkgv_write=.false., - cwcoveqqcov=.false., - -/ -&ANBKGERR - anisotropic=.false., - -/ -&JCOPTS - ljcdfi=.false.,alphajc=0.0,ljcpdry=.true.,bamp_jcpdry=5.0e7, - ljcpdry=.false., -/ -&STRONGOPTS - tlnmc_option=2,nstrong=1,nvmodes_keep=8,period_max=6.,period_width=1.5, - tlnmc_option=0, -/ -&OBSQC - dfact=0.75,dfact1=3.0,noiqc=.true.,oberrflg=.false.,c_varqc=0.02, - use_poq7=.true.,qc_noirjaco3_pole=.true.,vqc=.false.,nvqc=.true., - aircraft_t_bc=.true.,biaspredt=1.0e5,upd_aircraft=.true.,cleanup_tail=.true., - tcp_width=70.0,tcp_ermax=7.35,airs_cads=.false.,cris_cads=.false., - iasi_cads=.false., - nvqc=.false., -/ -&OBS_INPUT - dmesh(1)=145.0,dmesh(2)=150.0,dmesh(3)=100.0,dmesh(4)=50.0,time_window_max=3.0, - hofx_2m_sfcfile=.false., - -/ -OBS_INPUT:: -! dfile dtype dplat dsis dval dthin dsfcalc - prepbufr ps null ps 0.0 0 0 -! prepbufr t null t 0.0 0 0 -! prepbufr_profl t null t 0.0 0 0 - hdobbufr t null t 0.0 0 0 -! prepbufr q null q 0.0 0 0 -! prepbufr_profl q null q 0.0 0 0 - hdobbufr q null q 0.0 0 0 -! prepbufr pw null pw 0.0 0 0 -! prepbufr uv null uv 0.0 0 0 -! prepbufr_profl uv null uv 0.0 0 0 - satwndbufr uv null uv 0.0 0 0 - hdobbufr uv null uv 0.0 0 0 -! prepbufr spd null spd 0.0 0 0 - hdobbufr spd null spd 0.0 0 0 -! prepbufr dw null dw 0.0 0 0 - radarbufr rw null rw 0.0 0 0 - nsstbufr sst nsst sst 0.0 0 0 - gpsrobufr gps_bnd null gps 0.0 0 0 - ssmirrbufr pcp_ssmi dmsp pcp_ssmi 0.0 -1 0 - tmirrbufr pcp_tmi trmm pcp_tmi 0.0 -1 0 - sbuvbufr sbuv2 n16 sbuv8_n16 0.0 0 0 - sbuvbufr sbuv2 n17 sbuv8_n17 0.0 0 0 - sbuvbufr sbuv2 n18 sbuv8_n18 0.0 0 0 - gimgrbufr goes_img g11 imgr_g11 0.0 1 0 - gimgrbufr goes_img g12 imgr_g12 0.0 1 0 - airsbufr airs aqua airs_aqua 0.0 1 1 - amsuabufr amsua n15 amsua_n15 0.0 1 1 - amsuabufr amsua n18 amsua_n18 0.0 1 1 - amsuabufr amsua metop-a amsua_metop-a 0.0 1 1 - airsbufr amsua aqua amsua_aqua 0.0 1 1 - amsubbufr amsub n17 amsub_n17 0.0 1 1 - mhsbufr mhs n18 mhs_n18 0.0 1 1 - mhsbufr mhs metop-a mhs_metop-a 0.0 1 1 - ssmitbufr ssmi f15 ssmi_f15 0.0 1 0 - amsrebufr amsre_low aqua amsre_aqua 0.0 1 0 - amsrebufr amsre_mid aqua amsre_aqua 0.0 1 0 - amsrebufr amsre_hig aqua amsre_aqua 0.0 1 0 - ssmisbufr ssmis f16 ssmis_f16 0.0 1 0 - ssmisbufr ssmis f17 ssmis_f17 0.0 1 0 - ssmisbufr ssmis f18 ssmis_f18 0.0 1 0 - gsnd1bufr sndrd1 g12 sndrD1_g12 0.0 1 0 - gsnd1bufr sndrd2 g12 sndrD2_g12 0.0 1 0 - gsnd1bufr sndrd3 g12 sndrD3_g12 0.0 1 0 - gsnd1bufr sndrd4 g12 sndrD4_g12 0.0 1 0 - gsnd1bufr sndrd1 g11 sndrD1_g11 0.0 1 0 - gsnd1bufr sndrd2 g11 sndrD2_g11 0.0 1 0 - gsnd1bufr sndrd3 g11 sndrD3_g11 0.0 1 0 - gsnd1bufr sndrd4 g11 sndrD4_g11 0.0 1 0 - gsnd1bufr sndrd1 g13 sndrD1_g13 0.0 1 0 - gsnd1bufr sndrd2 g13 sndrD2_g13 0.0 1 0 - gsnd1bufr sndrd3 g13 sndrD3_g13 0.0 1 0 - gsnd1bufr sndrd4 g13 sndrD4_g13 0.0 1 0 - iasibufr iasi metop-a iasi_metop-a 0.0 1 1 - gomebufr gome metop-a gome_metop-a 0.0 2 0 - omibufr omi aura omi_aura 0.0 2 0 - sbuvbufr sbuv2 n19 sbuv8_n19 0.0 0 0 - amsuabufr amsua n19 amsua_n19 0.0 1 1 - mhsbufr mhs n19 mhs_n19 0.0 1 1 -! tcvitl tcp null tcp 0.0 0 0 - seviribufr seviri m08 seviri_m08 0.0 1 0 - seviribufr seviri m09 seviri_m09 0.0 1 0 - seviribufr seviri m10 seviri_m10 0.0 1 0 - seviribufr seviri m11 seviri_m11 0.0 1 0 - amsuabufr amsua metop-b amsua_metop-b 0.0 1 1 - mhsbufr mhs metop-b mhs_metop-b 0.0 1 1 - iasibufr iasi metop-b iasi_metop-b 0.0 1 1 - gomebufr gome metop-b gome_metop-b 0.0 2 0 - atmsbufr atms npp atms_npp 0.0 1 1 - atmsbufr atms n20 atms_n20 0.0 1 1 - atmsbufr atms n21 atms_n21 0.0 1 1 - crisbufr cris npp cris_npp 0.0 1 0 - crisfsbufr cris-fsr npp cris-fsr_npp 0.0 1 0 - crisfsbufr cris-fsr n20 cris-fsr_n20 0.0 1 0 - crisfsbufr cris-fsr n21 cris-fsr_n21 0.0 1 0 - gsnd1bufr sndrd1 g14 sndrD1_g14 0.0 1 0 - gsnd1bufr sndrd2 g14 sndrD2_g14 0.0 1 0 - gsnd1bufr sndrd3 g14 sndrD3_g14 0.0 1 0 - gsnd1bufr sndrd4 g14 sndrD4_g14 0.0 1 0 - gsnd1bufr sndrd1 g15 sndrD1_g15 0.0 1 0 - gsnd1bufr sndrd2 g15 sndrD2_g15 0.0 1 0 - gsnd1bufr sndrd3 g15 sndrD3_g15 0.0 1 0 - gsnd1bufr sndrd4 g15 sndrD4_g15 0.0 1 0 - oscatbufr uv null uv 0.0 0 0 - mlsbufr mls30 aura mls30_aura 0.0 0 0 - avhambufr avhrr metop-a avhrr3_metop-a 0.0 4 0 - avhpmbufr avhrr n18 avhrr3_n18 0.0 4 0 - avhambufr avhrr metop-b avhrr3_metop-b 0.0 4 0 - avhambufr avhrr metop-c avhrr3_metop-c 0.0 4 0 - avhpmbufr avhrr n19 avhrr3_n19 0.0 4 0 - amsr2bufr amsr2 gcom-w1 amsr2_gcom-w1 0.0 3 0 - gmibufr gmi gpm gmi_gpm 0.0 1 0 - saphirbufr saphir meghat saphir_meghat 0.0 3 0 - ahibufr ahi himawari8 ahi_himawari8 0.0 1 0 - abibufr abi g16 abi_g16 0.0 1 0 - abibufr abi g17 abi_g17 0.0 1 0 - abibufr abi g18 abi_g18 0.0 1 0 - rapidscatbufr uv null uv 0.0 0 0 - ompsnpbufr ompsnp npp ompsnp_npp 0.0 0 0 - ompslpbufr ompslp npp ompslp_npp 0.0 0 0 - ompstcbufr ompstc8 npp ompstc8_npp 0.0 2 0 - ompsnpbufr ompsnp n20 ompsnp_n20 0.0 0 0 - ompstcbufr ompstc8 n20 ompstc8_n20 0.0 2 0 - amsuabufr amsua metop-c amsua_metop-c 0.0 1 1 - mhsbufr mhs metop-c mhs_metop-c 0.0 1 1 - iasibufr iasi metop-c iasi_metop-c 0.0 1 1 - sstviirs viirs-m npp viirs-m_npp 0.0 4 0 - sstviirs viirs-m j1 viirs-m_j1 0.0 4 0 - ahibufr ahi himawari9 ahi_himawari9 0.0 1 0 - sstviirs viirs-m j2 viirs-m_j2 0.0 4 0 - ompsnpbufr ompsnp n21 ompsnp_n21 0.0 0 0 - ompstcbufr ompstc8 n21 ompstc8_n21 0.0 2 0 - gomebufr gome metop-c gome_metop-c 0.0 2 0 -:: -&SUPEROB_RADAR - -/ -&LAG_DATA - -/ -&HYBRID_ENSEMBLE - l_hyb_ens=.false., - generate_ens=.false., - beta_s0=0.125,readin_beta=.false., - s_ens_h=1000.0,300.0,150.0,685.0,219.2,s_ens_v=-0.5,-0.5,-0.5,0.0,0.0, - readin_localization=.false.,global_spectral_filter_sd=.false., - r_ensloccov4scl=1,nsclgrp=3,naensloc=5, - aniso_a_en=.false.,oz_univ_static=.false.,uv_hyb_ens=.true., - ensemble_path='./ensemble_data/', - ens_fast_read=.true., - -/ -&RAPIDREFRESH_CLDSURF - dfi_radar_latent_heat_time_period=30.0, - -/ -&CHEM - -/ -&SINGLEOB_TEST - maginnov=0.1,magoberr=0.1,oneob_type='t', - oblat=45.,oblon=180.,obpres=1000.,obdattim=2024122500, - obhourset=0., - -/ -&NST - nst_gsi=3, - nstinfo=4,fac_dtl=1,fac_tsl=1,zsea1=0,zsea2=0, -/ From 896aa7c3a31d8dab55e93a7a7379ac94feaf8ef6 Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Thu, 6 Mar 2025 20:51:26 +0000 Subject: [PATCH 08/10] clean up README_phase3, rename python setup script (#1487) --- ush/ufoeval/README_phase3 | 49 ++++++++++++++----------- ush/ufoeval/{run.py => setup_phase3.py} | 0 2 files changed, 28 insertions(+), 21 deletions(-) rename ush/ufoeval/{run.py => setup_phase3.py} (100%) diff --git a/ush/ufoeval/README_phase3 b/ush/ufoeval/README_phase3 index 2d03e34bc..f01c88ebc 100644 --- a/ush/ufoeval/README_phase3 +++ b/ush/ufoeval/README_phase3 @@ -1,15 +1,22 @@ +SUMMARY +======= The following files are available to assist developers in JEDI Phase 3 validation. -config_gsi.yaml - set user options for GSI atmospheric analysis job -config_jedi.yaml - set user option for JEDI atmospheric analysis job -run.py - script - process config, populate run directory, create run script + config_gsi.yaml: user configurable options for GSI atmospheric analysis job + config_jedi.yaml: user configurable options for JEDI atmospheric analysis job + setup_phase3.py: script to process config, populate run directory, and create batch script +Machines currently supported are Hera, Hercules, and Orion. + + +DETAILS +======= config_gsi.yaml and config_jedi.yaml contain the following user settings - - machine: valid machines are hera, hercules, or orion + - machine: hera, hercules, or orion - job options: may need to adjust nodes, tasks-per-node, or mem appropriate for dataset being processed - - directories + HOMEgfs: global-work installation - STAGEDIR: machine specific path containing staged input + STAGEDIR: machine specific path containing job input, set to the machine appropriate path below Hera: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR Hercules, Orion: /work2/noaa/stmp/rtreadon/STAGEDIR RUNDIR: path to the directory in which job will run. Full path is RUNDIR + DA_CORE + DA_TYPE @@ -22,25 +29,25 @@ config_gsi.yaml and config_jedi.yaml contain the following user settings APPEXEC: GSI or JEDI analysis executable INCEXEC: executable to convert JEDI cube sphere increment to gaussian grid -run.py - - stage files to run the case specified in the configuration yaml - - create a batch script the user can subit to run executable - +setup_phase3.py + - stage files to a run directory for the specified configuration + - create a batch script the developer can submit -Executing the Phase 3 validation workflow entails -1. modify config_jedi.yaml and config_gsi.yaml to point at the configuration - you want to use. Point at the executables to be run. Set the RUNDIR in which the - job will run +WORKFLOW EXAMPLE +================ +1. edit config_jedi.yaml or config_gsi.yaml to run the desired configuration. + Point at the executable(s) to be used. Set the RUNDIR in which the job will run -2. execute run.py. The syntax is "./run.py -c ./config_gsi.yaml" or - "./run.py -c ./config_jedi.yaml". The run.py script will - (a) populate the specified RUNDIR with the files to run the specified configuration - (b) create a batch script, runjob.sh, in RUNDIR to run the specified configuration +2. execute setup_phase3.py + The syntax is "./setup_phase3.py -c ./config_gsi.yaml" or "./setup_phase3.py -c ./config_jedi.yaml". + The setup_phase3.py script will + - populate the specified RUNDIR with the files for the specified configuration + - create a batch script, runjob.sh, in RUNDIR to run the specified configuration - When ./run.py` is executed it echoes to the screen the directory to which data is copied - and the directory in which the job script, runjob.sh, is created. + When ./setup_phase3.py` is executed it echoes to the screen the directory to which data is copied + and the directory in which the job script (runjob.sh)is created. -3. cd to the specified RUNDIR to submit the batch script. +3. cd to the specified RUNDIR to submit batch script: "sbatch runjob.sh" 4. examine ouput upon job completion diff --git a/ush/ufoeval/run.py b/ush/ufoeval/setup_phase3.py similarity index 100% rename from ush/ufoeval/run.py rename to ush/ufoeval/setup_phase3.py From 72ba1eabff38606fb89a7f52f81a1f3bed294346 Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Fri, 7 Mar 2025 14:05:56 +0000 Subject: [PATCH 09/10] correct README_phase3 typos, rename JobCard class as SlurmJobCard (#1487) --- ush/ufoeval/README_phase3 | 4 ++-- ush/ufoeval/setup_phase3.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ush/ufoeval/README_phase3 b/ush/ufoeval/README_phase3 index f01c88ebc..68eb643a4 100644 --- a/ush/ufoeval/README_phase3 +++ b/ush/ufoeval/README_phase3 @@ -15,7 +15,7 @@ config_gsi.yaml and config_jedi.yaml contain the following user settings - machine: hera, hercules, or orion - job options: may need to adjust nodes, tasks-per-node, or mem appropriate for dataset being processed - HOMEgfs: global-work installation + HOMEgfs: global-workflow installation STAGEDIR: machine specific path containing job input, set to the machine appropriate path below Hera: /scratch2/NCEPDEV/stmp1/Russ.Treadon/STAGEDIR Hercules, Orion: /work2/noaa/stmp/rtreadon/STAGEDIR @@ -50,4 +50,4 @@ WORKFLOW EXAMPLE 3. cd to the specified RUNDIR to submit batch script: "sbatch runjob.sh" -4. examine ouput upon job completion +4. examine output upon job completion diff --git a/ush/ufoeval/setup_phase3.py b/ush/ufoeval/setup_phase3.py index e252458e4..7a07f6168 100755 --- a/ush/ufoeval/setup_phase3.py +++ b/ush/ufoeval/setup_phase3.py @@ -14,11 +14,11 @@ jobname = "runjob" -class JobCard: +class SlurmJobCard: def __init__(self, config): """ - Constructor for the JobCard class. + Constructor for the SlurmJobCard class. :param config: dictionary containing configuration information """ @@ -197,8 +197,8 @@ def main(): logging.info(f"Data staged to {rundir}") - # Create run script. - run_card = JobCard(exp_config) + # Create run script + run_card = SlurmJobCard(exp_config) run_card.header() run_card.load_modules() run_card.aprun() From 1a812350f3de31e0604d93e19e61f010fd691fd6 Mon Sep 17 00:00:00 2001 From: RussTreadon-NOAA Date: Fri, 7 Mar 2025 14:23:07 +0000 Subject: [PATCH 10/10] improve error checking logic (#1487) --- ush/ufoeval/setup_phase3.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ush/ufoeval/setup_phase3.py b/ush/ufoeval/setup_phase3.py index 7a07f6168..bc6c027c7 100755 --- a/ush/ufoeval/setup_phase3.py +++ b/ush/ufoeval/setup_phase3.py @@ -141,13 +141,11 @@ def main(): # Ensure valid app core and type valid = ['gsi', 'jedi'] if appcore not in valid: - logging.error(f"DA_CORE {appcore} is invalid. Valid cores are {valid}") - sys.exit() + raise ValueError(f"DA_CORE {appcore} is invalid. Valid cores are {valid}") valid = ['3dv', '3dvfgat', 'hyb3dvfgat'] if apptype not in valid: - logging.error(f"DA_TYPE {apptype} is invalid. Valid types are {valid}") - sys.exit() + raise ValueError(f"DA_TYPE {apptype} is invalid. Valid types are {valid}") # Set source (stagedir) and destination (rundir) paths stagedir = os.path.join(exp_config['directories']['STAGEDIR'], appcore, apptype)