ws.Copy(ws.iy_main_agenda, ws.iy_main_agenda__Emission)
ws.Copy(ws.propmat_clearsky_agenda, ws.propmat_clearsky_agenda__OnTheFly)

# General Settings
# For the wind retrievals, the forward model calculations are performed on a 3D atmosphere grid.
# Radiation is assumed to be unpolarized.
ws.atmosphere_dim = 3
ws.stokes_dim = 1
ws.iy_unit = "RJBT"

# Absorption
# We only consider absorption from ozone in this example. The lineshape data is available from
# the ARTS testdata available in `controlfiles/testdata`.
ws.abs_speciesSet(["O3", "H2O-PWR98"])
ws.abs_lineshapeDefine("Voigt_Kuntz6", "VVH", 750e9)
ws.ReadXML(ws.abs_lines, "testdata/ozone_line.xml")
ws.abs_lines_per_speciesCreateFromLines()

# Atmosphere (A Priori)
# We create a pressure grid using the `PFromZSimple` function to create a grid of approximate pressure levels
# corresponding to altitudes in the range
# z = 0.0, 2000.0, ..., 94000.0
z_toa = 95e3
z_surf = 1e3
z_grid = np.arange(z_surf - 1e3, z_toa, 2e3)
ws.PFromZSimple(ws.p_grid, z_grid)
ws.lat_grid = np.arange(-40.0, 1.0, 40.0)
ws.lon_grid = np.arange(40.0, 61.0, 20.0)
ws.z_surface = z_surf * np.ones(
    (np.asarray(ws.lat_grid).size, np.asarray(ws.lon_grid).size))
Example #2
0
def main():
    """Main program."""
    args = parse_args()
    outdir = args.outdir
    recalc_lookups = args.recalc
    os.makedirs(outdir, exist_ok=True)

    plt.rc('text', usetex=not args.notex)
    matplotlib.rcParams['text.latex.preamble'] = [
        r'\usepackage{sansmath}', r'\sansmath'
    ]
    plt.style.use(typhon.plots.styles('typhon'))

    ws = Workspace(verbosity=2)
    ws.verbosityInit()

    print('Performing ARTS calculation')
    arts_common_setup(ws, args.nfreq, args.lineshape)

    y_all = {}

    for planet, item in PLANET_SETUP.items():
        arts_calc_atmfields(ws, **item)

        lookup_file = os.path.join(outdir, planet + '_lookup.xml')
        if os.path.isfile(lookup_file) and not recalc_lookups:
            ws.ReadXML(ws.abs_lookup, filename=lookup_file)
            ws.abs_lookupAdapt()
        else:
            ws.ReadXML(ws.abs_cia_data,
                       filename='spectroscopy/cia/hitran2011/'
                       'hitran_cia2012_adapted.xml.gz')
            ws.abs_linesReadFromSplitArtscat(basename='spectroscopy/Perrin/',
                                             fmin=0.,
                                             fmax=1e12)
            ws.abs_lines_per_speciesCreateFromLines()
            arts_calc_lookup_table(ws)
            ws.WriteXML('binary', ws.abs_lookup, lookup_file)

        ws.propmat_clearsky_agenda_checkedCalc()
        ws.yCalc()
        ws.WriteXML("ascii",
                    ws.y,
                    filename=os.path.join(outdir, planet + '.y.xml'))
        y_all[planet] = {
            'f_grid': ws.f_grid.to_typhon(),
            'y': ws.y.to_typhon(),
        }

    print('Plotting')
    fig, ax = plt.subplots()
    plot_spectra(y_all)
    ax.set_xlim(F_MIN, F_MAX)
    annotate_lines(y_all)

    filename = os.path.join(outdir, 'planet_spectra.pdf')
    print(f'Saving {filename}')
    fig.savefig(filename, dpi=300)

    if args.show:
        plt.show()
Example #3
0
ws.Copy(ws.iy_surface_agenda, ws.iy_surface_agenda__UseSurfaceRtprop)
ws.Copy(ws.iy_main_agenda, ws.iy_main_agenda__Emission)
ws.Copy(ws.propmat_clearsky_agenda, ws.propmat_clearsky_agenda__OnTheFly)

# General Settings
# For the wind retrievals, the forward model calculations are performed on a 3D atmosphere grid.
# Radiation is assumed to be unpolarized.
ws.atmosphere_dim = 3
ws.stokes_dim = 1
ws.iy_unit = "RJBT"

# Absorption
# We only consider absorption from ozone in this example.
ws.abs_speciesSet(["O3", "H2O-PWR98"])
ws.abs_lineshapeDefine("Voigt_Kuntz6", "VVH", 750e9)
ws.ReadXML(ws.abs_lines, os.path.join(data_dir, 'Perrin_O3_142.xml'))
ws.abs_lines_per_speciesCreateFromLines()

# Atmosphere (A Priori)
# We create a pressure grid using the `PFromZSimple` function to create a grid of approximate pressure levels
# corresponding to altitudes in the range
# z = 0.0, 2000.0, ..., 94000.0
z_toa = 100e3
z_surf = 1e3
z_grid = np.arange(z_surf - 1e3, z_toa, 2e3)
ws.PFromZSimple(ws.p_grid, z_grid)
ws.lat_grid = np.array([-30, -22.5, -22, -21.5, -14])
ws.lon_grid = np.array([40, 54, 55, 56, 70])
ws.z_surface = z_surf * np.ones(
    (np.asarray(ws.lat_grid).size, np.asarray(ws.lon_grid).size))
Example #4
0
def test_wind_3d_demo():
    ws = Workspace()

    ws.execute_controlfile("general/general.arts")
    ws.verbositySet(0, 0, 0, 0)
    ws.execute_controlfile("general/agendas.arts")
    ws.execute_controlfile("general/continua.arts")
    ws.execute_controlfile("general/planet_earth.arts")

    ws.Copy(ws.abs_xsec_agenda, ws.abs_xsec_agenda__noCIA)
    ws.Copy(ws.ppath_agenda, ws.ppath_agenda__FollowSensorLosPath)
    ws.Copy(ws.ppath_step_agenda, ws.ppath_step_agenda__GeometricPath)
    ws.Copy(ws.iy_space_agenda, ws.iy_space_agenda__CosmicBackground)
    ws.Copy(ws.iy_surface_agenda, ws.iy_surface_agenda__UseSurfaceRtprop)
    ws.Copy(ws.iy_main_agenda, ws.iy_main_agenda__Emission)
    ws.Copy(ws.propmat_clearsky_agenda, ws.propmat_clearsky_agenda__OnTheFly)

    # General Settings
    # For the wind retrievals, the forward model calculations are performed on a 3D atmosphere grid.
    # Radiation is assumed to be unpolarized.
    ws.atmosphere_dim = 3
    ws.stokes_dim = 1
    ws.iy_unit = "RJBT"

    # Absorption
    # We only consider absorption from ozone in this example. The lineshape data is available from
    # the ARTS testdata available in `controlfiles/testdata`.
    ws.abs_speciesSet(["O3", "H2O-PWR98"])
    ws.abs_lineshapeDefine("Voigt_Kuntz6", "VVH", 750e9)
    ws.ReadXML(ws.abs_lines, "testdata/ozone_line.xml")
    ws.abs_lines_per_speciesCreateFromLines()

    # Atmosphere (A Priori)
    # We create a pressure grid using the `PFromZSimple` function to create a grid of approximate pressure levels
    # corresponding to altitudes in the range
    # z = 0.0, 2000.0, ..., 94000.0
    z_toa = 95e3
    z_surf = 1e3
    z_grid = np.arange(z_surf - 1e3, z_toa, 2e3)
    ws.PFromZSimple(ws.p_grid, z_grid)
    ws.lat_grid = np.arange(-40.0, 1.0, 40.0)
    ws.lon_grid = np.arange(40.0, 61.0, 20.0)
    ws.z_surface = z_surf * np.ones(
        (np.asarray(ws.lat_grid).size, np.asarray(ws.lon_grid).size))

    # For the a priori state we read data from the Fascod climatology that is part of the ARTS xml data.
    ws.AtmRawRead(basename="planets/Earth/Fascod/tropical/tropical")
    ws.AtmFieldsCalcExpand1D()

    # Adding Wind
    # Wind in ARTS is represented by the `wind_u_field` and `wind_v_field` WSVs, which hold the horizontal components
    # of the wind at each grid point of the atmosphere model. For this example, a constant wind is assumed.
    u_wind = 60.0
    v_wind = -40.0
    ws.wind_u_field = u_wind * np.ones(
        (ws.p_grid.value.size, ws.lat_grid.value.size, ws.lon_grid.value.size))
    ws.wind_v_field = v_wind * np.ones(
        (ws.p_grid.value.size, ws.lat_grid.value.size, ws.lon_grid.value.size))
    ws.wind_w_field = np.zeros((0, 0, 0))

    # Frequency Grid and Sensor
    # The frequency grid for the simulation consists of 119 grid points between 110.516 and 111.156 GHz.
    # The frequencies are given by a degree-10 polynomial that has been obtained from a fit to the data from
    # the original `qpack` example. This is obscure but also kind of cool.
    coeffs = np.array([
        5.06312189e-08, -2.68851772e-05, 6.20655463e-03, -8.16344090e-01,
        6.75337174e+01, -3.66786505e+03, 1.32578167e+05, -3.14514304e+06,
        4.57491354e+07, 1.10516484e+11
    ])
    ws.f_grid = np.poly1d(coeffs)(np.arange(119))

    # For the sensor we assume a channel width and channel spacing of 50 kHz. We also call AntennaOff to compute
    # only one pencilbeam along the line of sight of the sensor.
    df = 50e3
    f_backend = np.arange(ws.f_grid.value.min() + 2.0 * df,
                          ws.f_grid.value.max() - 2.0 * df, df)
    ws.backend_channel_responseGaussian(np.array([df]), np.array([2.0]))
    ws.AntennaOff()

    ws.sensor_norm = 1
    ws.sensor_time = np.zeros(1)
    ws.sensor_responseInit()

    # Sensor Position and Viewing Geometry
    # 5 Measurements are performed, one straight up, and four with zenith angle  70∘70∘  in directions SW, NW, NE, SE.
    # In ARTS the measurement directions are given by a two-column matrix, where the first column contains the zenith
    # angle and the second column the azimuth angle.
    ws.sensor_los = np.array([[
        0.0,
        0.0,
    ], [70.0, -135.0], [70.0, -45.0], [70.0, 45.0], [70.0, 135.0]])
    ws.sensor_pos = np.array([[2000.0, -21.1, 55.6]] * 5)

    # Reference Measurement
    # Before we can calculate `y`, our setup needs to pass the following tests:
    ws.abs_f_interp_order = 3
    ws.propmat_clearsky_agenda_checkedCalc()
    ws.sensor_checkedCalc()
    ws.atmgeom_checkedCalc()
    ws.atmfields_checkedCalc()
    ws.abs_xsec_agenda_checkedCalc()
    ws.jacobianOff()
    ws.cloudboxOff()
    ws.cloudbox_checkedCalc()

    ws.yCalc()
    y = np.copy(ws.y.value)

    # Setting up the Retrieval
    # In this example, we retrieve ozone and the horizontal and vertical components of the wind velocities.
    # The state space covariance matrix in ARTS is represented by the **covmat_sa** WSV.
    # It belongs to the CovarianceMatrix group, which is used to represent block diagonal matrices.
    # For each retrieval quantity that is added to the retrieval, a corresponding block must be added to **covmat_sa**.
    # This is usually done by the corresponding **retrievalAdd...** call, which looks for this block
    # in the **covmat_block** WSV.
    # In short the general workflow for adding a retrieval quantity is as follows:
    #  - Create the covariance matrix for the retrieval quantity either calling one of the **covmat...** WSV or
    #    by loading your own matrix
    #  - Write the matrix block into **covmat_block**
    #  - Call the **retrievalAdd...** method to add the retrieval quantity and the covariance matrix block
    #    to **covmat_sa**
    lat_ret_grid = np.array([np.mean(ws.lat_grid)])
    lon_ret_grid = np.array([np.mean(ws.lon_grid)])
    n_p = ws.p_grid.value.size

    ws.retrievalDefInit()
    ws.covmat1D(
        ws.covmat_block,
        grid_1=z_grid,
        sigma_1=0.1 * np.ones(n_p),  # Relative uncertainty
        cls_1=10e3 * np.ones(n_p),  # 10km correlation length
        fname="lin")
    ws.retrievalAddAbsSpecies(species="O3",
                              unit="rel",
                              g1=ws.p_grid,
                              g2=lat_ret_grid,
                              g3=lon_ret_grid)
    # Wind u-component
    ws.covmat1D(
        ws.covmat_block,
        grid_1=z_grid[::2],
        sigma_1=100.0 * np.ones(n_p // 2),  # Relative uncertainty
        cls_1=10e3 * np.ones(n_p // 2),  # 10km correlation length
        fname="lin")
    ws.retrievalAddWind(g1=ws.p_grid.value[::2],
                        g2=np.array([np.mean(ws.lat_grid)]),
                        g3=np.array([np.mean(ws.lon_grid)]),
                        component="u")
    # Wind v-component
    ws.covmat1D(
        ws.covmat_block,
        grid_1=z_grid[::2],
        sigma_1=100.0 * np.ones(n_p // 2),  # Relative uncertainty
        cls_1=10e3 * np.ones(n_p // 2),  # 10km correlation length
        fname="lin")
    ws.retrievalAddWind(g1=ws.p_grid.value[::2],
                        g2=np.array([np.mean(ws.lat_grid)]),
                        g3=np.array([np.mean(ws.lon_grid)]),
                        component="v")
    ws.retrievalDefClose()
    ws.covmatDiagonal(ws.covmat_block,
                      ws.covmat_inv_block,
                      vars=0.0001 * np.ones(ws.y.value.shape))
    ws.covmat_seSet(ws.covmat_block)

    @arts_agenda
    def inversion_iterate_agenda(ws):
        ws.x2artsStandard()
        ws.atmfields_checkedCalc()
        ws.atmgeom_checkedCalc()
        ws.yCalc()
        ws.Print(ws.y)
        ws.Print(ws.jacobian)
        ws.VectorAddVector(ws.yf, ws.y, ws.y_baseline)
        ws.IndexAdd(ws.inversion_iteration_counter,
                    ws.inversion_iteration_counter, 1)

    ws.Copy(ws.inversion_iterate_agenda, inversion_iterate_agenda)

    # A Priori State
    # For the a priori state we assume zero wind in any direction. The a priori vector for the OEM is created by
    # the `xaStandard` WSM, which computes $x_a$ from the current atmospheric state.
    ws.wind_u_field.value[:] = 0.0
    ws.wind_v_field.value[:] = 0.0
    ws.xaStandard()

    # The OEM Calculation
    ws.x = np.zeros(0)
    ws.jacobian = np.zeros((0, 0))
    ws.y.value[:] = y
    ws.OEM(method="lm",
           max_iter=20,
           display_progress=1,
           lm_ga_settings=np.array([100.0, 2.0, 2.0, 10.0, 1.0, 1.0]))
    ws.x2artsStandard()

    z = ws.z_field.value[:, 0, 0].ravel()
    wind_u = ws.wind_u_field.value[z > 40e3, 0, 0]
    wind_v = ws.wind_v_field.value[z > 40e3, 0, 0]
    assert np.allclose(wind_u, u_wind, atol=1)
    assert np.allclose(wind_v, v_wind, atol=1)