예제 #1
0
def configure_workspace(verbosity=0):
    """Configures the ARTS application.

    Args:
        verbosity: ARTS verbosity level.

    Returns:
        A Workspace object.
    """
    workspace = Workspace(verbosity=0)
    for name in ["general", "continua", "agendas"]:
        workspace.execute_controlfile(join("general", "{}.arts".format(name)))
    workspace.verbositySetScreen(workspace.verbosity, verbosity)
    workspace.jacobianOff()
    workspace.Copy(workspace.abs_xsec_agenda, workspace.abs_xsec_agenda__noCIA)
    workspace.AtmosphereSet1D()
    return workspace
예제 #2
0
class _ARTS:
    def __init__(self,
                 ws=None,
                 threads=None,
                 nstreams=4,
                 scale_vmr=True,
                 verbosity=0):
        """Initialize a wrapper for an ARTS workspace.

        Parameters:
            ws (pyarts.workspace.Workspace): An ARTS workspace.
            threads (int): Number of threads to use.
                Default is all available threads.
            nstreams (int): Number of viewing angles to base the radiative
                flux calculation on.
            scale_vmr (bool): Control whether dry volume mixing ratios are
                scaled with the water-vapor concentration (default is `False.`)
            verbosity (int): Control the ARTS verbosity from 0 (quiet) to 2.
        """
        from pyarts.workspace import Workspace, arts_agenda

        self.nstreams = nstreams
        self.scale_vmr = scale_vmr

        if ws is None:
            self.ws = Workspace(verbosity=verbosity)

        self.ws.execute_controlfile("general/general.arts")
        self.ws.execute_controlfile("general/continua.arts")
        self.ws.execute_controlfile("general/agendas.arts")
        self.ws.execute_controlfile("general/planet_earth.arts")

        # Agenda settings
        self.ws.Copy(self.ws.abs_xsec_agenda, self.ws.abs_xsec_agenda__noCIA)
        self.ws.Copy(self.ws.iy_main_agenda, self.ws.iy_main_agenda__Emission)
        self.ws.Copy(self.ws.iy_space_agenda,
                     self.ws.iy_space_agenda__CosmicBackground)
        self.ws.Copy(self.ws.iy_surface_agenda,
                     self.ws.iy_surface_agenda__UseSurfaceRtprop)
        self.ws.Copy(
            self.ws.propmat_clearsky_agenda,
            self.ws.propmat_clearsky_agenda__LookUpTable,
        )
        self.ws.Copy(self.ws.ppath_agenda,
                     self.ws.ppath_agenda__FollowSensorLosPath)
        self.ws.Copy(self.ws.ppath_step_agenda,
                     self.ws.ppath_step_agenda__GeometricPath)

        @arts_agenda
        def p_eq_agenda(workspace):
            workspace.water_p_eq_fieldMK05()

        self.ws.Copy(self.ws.water_p_eq_agenda, p_eq_agenda)

        @arts_agenda
        def cloudbox_agenda(workspace):
            workspace.iyInterpCloudboxField()

        self.ws.Copy(self.ws.iy_cloudbox_agenda, cloudbox_agenda)

        # Number of Stokes components to be computed
        self.ws.IndexSet(self.ws.stokes_dim, 1)

        self.ws.jacobianOff()  # No jacobian calculation
        self.ws.cloudboxOff()  # Clearsky = No scattering

        # Set Absorption Species
        self.ws.abs_speciesSet(species=[
            "O2, O2-CIAfunCKDMT100",
            "H2O, H2O-SelfContCKDMT252, H2O-ForeignContCKDMT252",
            "O3",
            "CO2, CO2-CKDMT252",
            "N2, N2-CIAfunCKDMT252, N2-CIArotCKDMT252",
            "N2O",
            "CH4",
            "CO",
        ])

        # Surface handling
        self.ws.VectorSetConstant(self.ws.surface_scalar_reflectivity, 1, 0.0)
        self.ws.Copy(
            self.ws.surface_rtprop_agenda,
            self.ws.
            surface_rtprop_agenda__Specular_NoPol_ReflFix_SurfTFromt_surface,
        )

        # Read lookup table
        abs_lookup = os.getenv("KONRAD_LOOKUP_TABLE",
                               join(dirname(__file__), "data/abs_lookup.xml"))

        if not isfile(abs_lookup):
            raise FileNotFoundError(
                "Could not find ARTS absorption lookup table.\n"
                "To perform ARTS calculations you have to download the lookup "
                "table at:\n\n    https://doi.org/10.5281/zenodo.3885410\n\n"
                "Afterwards, use the following environment variable to tell "
                "konrad where to find it:\n\n"
                "    $ export KONRAD_LOOKUP_TABLE='/path/to/abs_lookup.xml'")

        self.ws.ReadXML(self.ws.abs_lookup, abs_lookup)
        self.ws.f_gridFromGasAbsLookup()
        self.ws.abs_lookupAdapt()

        # Sensor settings
        self.ws.sensorOff()  # No sensor properties

        # Atmosphere
        self.ws.AtmosphereSet1D()

        # Set number of OMP threads
        if threads is not None:
            self.ws.SetNumberOfThreads(threads)

    def calc_lookup_table(self, filename=None, fnum=2**15, wavenumber=None):
        """Calculate an absorption lookup table.

        The lookup table is constructed to cover surface temperatures
        between 200 and 400 K, and water vapor mixing ratio up to 40%.

        The frequency grid covers the whole outgoing longwave spectrum
        from 10 to 3,250 cm^-1.

        References:
            An absorption lookup table can be found at
                https://doi.org/10.5281/zenodo.3885410

        Parameters:
            filename (str): (Optional) path to an ARTS XML file
                to store the lookup table.
            fnum (int): Number of frequencies in frequency grid.
                Ignored if `wavenumber` is set.
            wavenumber (ndarray): Wavenumber grid [m-1].
        """
        # Create a frequency grid
        if wavenumber is None:
            wavenumber = np.linspace(10e2, 3_250e2, fnum)
        self.ws.f_grid = ty.physics.wavenumber2frequency(wavenumber)

        # Read line catagloge and create absorption lines.
        self.ws.ReadSplitARTSCAT(
            abs_lines=self.ws.abs_lines,
            abs_species=self.ws.abs_species,
            basename="hitran_split_artscat5/",
            fmin=0.0,
            fmax=1e99,
            globalquantumnumbers="",
            localquantumnumbers="",
            ignore_missing=0,
        )

        # Set line shape and cut off.
        self.ws.abs_linesSetLineShapeType(self.ws.abs_lines, "VP")
        self.ws.abs_linesSetNormalization(self.ws.abs_lines, "VVH")
        self.ws.abs_linesSetCutoff(self.ws.abs_lines, "ByLine", 750e9)

        self.ws.abs_lines_per_speciesCreateFromLines()
        self.ws.abs_lines_per_speciesCompact()

        # Create a standard atmosphere
        p_grid = get_quadratic_pgrid(1_200e2, 0.5, 80)

        atmosphere = Atmosphere(p_grid)
        atmosphere["T"][
            -1, :] = 300.0 + 5.0 * np.log(atmosphere["plev"] / 1000e2)
        atmosphere.tracegases_rcemip()
        atmosphere["O2"][:] = 0.2095
        atmosphere["CO2"][:] = 1.5 * 348e-6

        h2o = 0.01 * (p_grid / 1000e2)**0.2
        atmosphere["H2O"][:] = h2o[:-1]

        # Convert the konrad atmosphere into an ARTS atm_fields_compact.
        atm_fields_compact = atmosphere.to_atm_fields_compact()
        self.ws.atm_fields_compact = atm_fields_compact

        self.ws.atm_fields_compactAddConstant(
            atm_fields_compact=self.ws.atm_fields_compact,
            name="abs_species-N2",
            value=0.7808,
            condensibles=["abs_species-H2O"],
        )

        # Setup the lookup table calculation
        self.ws.AtmFieldsAndParticleBulkPropFieldFromCompact()
        self.ws.vmr_field.value = self.ws.vmr_field.value.clip(min=0.0)
        self.ws.atmfields_checkedCalc()
        self.ws.abs_lookupSetup(p_step=1.0)  # Do not refine p_grid
        self.ws.abs_t_pert = np.arange(-160, 61, 20)

        nls_idx = [
            i for i, tag in enumerate(self.ws.abs_species.value)
            if "H2O" in tag[0]
        ]
        self.ws.abs_speciesSet(
            abs_species=self.ws.abs_nls,
            species=[", ".join(self.ws.abs_species.value[nls_idx[0]])],
        )

        self.ws.abs_nls_pert = np.array(
            [10**x for x in [-9, -7, -5, -3, -1, 0, 0.5, 1, 1.5, 2]])

        # Run checks
        self.ws.abs_xsec_agenda_checkedCalc()
        self.ws.lbl_checkedCalc()

        # Calculate actual lookup table.
        self.ws.abs_lookupCalc()

        if filename is not None:
            self.ws.WriteXML("binary", self.ws.abs_lookup, filename)

    def set_atmospheric_state(self, atmosphere, t_surface):
        """Set and check the atmospheric fields."""
        import pyarts

        atm_fields_compact = atmosphere.to_atm_fields_compact()

        # Scale dry-air VMRs with H2O and CO2 content.
        if self.scale_vmr:
            variable_vmrs = (atm_fields_compact.get("abs_species-H2O")[0] +
                             atm_fields_compact.get("abs_species-CO2")[0])
        else:
            t3_shape = atm_fields_compact.get("abs_species-H2O")[0].shape
            variable_vmrs = np.zeros(t3_shape)

        for species in atm_fields_compact.grids[0]:
            if (species.startswith("abs_species-") and "H2O" not in species
                    and "CO2" not in species):
                atm_fields_compact.scale(species, 1 - variable_vmrs)

        # Compute the N2 VMR as a residual of the full atmosphere composition.
        n2 = pyarts.types.GriddedField3(
            grids=atm_fields_compact.grids[1:],
            data=0.7808 * (1 - variable_vmrs),
        )

        self.ws.atm_fields_compact = atm_fields_compact
        self.ws.atm_fields_compactAddSpecies(
            atm_fields_compact=self.ws.atm_fields_compact,
            name="abs_species-N2",
            value=n2,
        )
        self.ws.AtmFieldsAndParticleBulkPropFieldFromCompact()
        self.ws.vmr_field = self.ws.vmr_field.value.clip(min=0)

        # Surface & TOA
        # Add pressure layers to the surface and top-of-the-atmosphere to
        # ensure consistent atmosphere boundaries between ARTS and RRTMG.
        self.ws.t_surface = np.array([[t_surface]])
        self.ws.z_surface = np.array([[0.0]])
        self.ws.z_field.value[0, 0, 0] = 0.0

        # Perform configuration and atmosphere checks
        self.ws.atmfields_checkedCalc()
        self.ws.propmat_clearsky_agenda_checkedCalc()
        self.ws.atmgeom_checkedCalc()
        self.ws.cloudbox_checkedCalc()

    def calc_spectral_irradiance_field(self, atmosphere, t_surface):
        """Calculate the spectral irradiance field."""
        self.set_atmospheric_state(atmosphere, t_surface)

        # get the zenith angle grid and the integrations weights
        self.ws.AngularGridsSetFluxCalc(N_za_grid=self.nstreams,
                                        N_aa_grid=1,
                                        za_grid_type="double_gauss")

        # calculate intensity field
        self.ws.Tensor3Create("trans_field")
        self.ws.spectral_radiance_fieldClearskyPlaneParallel(
            trans_field=self.ws.trans_field,
            use_parallel_za=0,
        )
        self.ws.spectral_irradiance_fieldFromSpectralRadianceField()

        return (
            self.ws.f_grid.value.copy(),
            self.ws.p_grid.value.copy(),
            self.ws.spectral_irradiance_field.value.copy(),
            self.ws.trans_field.value[:, 1:, 0].copy().prod(axis=1),
        )

    def calc_optical_thickness(self, atmosphere, t_surface):
        """Calculate the spectral irradiance field."""
        self.set_atmospheric_state(atmosphere, t_surface)

        self.ws.propmat_clearsky_fieldCalc()

        tau = np.trapz(
            y=self.ws.propmat_clearsky_field.value[:, :, 0, 0, :, 0, 0],
            x=self.ws.z_field.value[:, 0, 0],
            axis=-1,
        )

        return self.ws.f_grid.value.copy(), tau

    @staticmethod
    def integrate_spectral_irradiance(frequency, irradiance):
        """Integrate the spectral irradiance field over the frequency.

        Parameters:
            frequency (ndarray): Frequency [Hz].
            irradiance (ndarray): Spectral irradiance [W m^-2 / Hz].

        Returns:
            ndarray, ndarray: Downward flux, upward, flux [W m^-2]
        """
        F = np.trapz(irradiance, frequency, axis=0)[:, 0, 0, :]

        # Fluxes
        lw_down = -F[:, 0]
        lw_up = F[:, 1]

        return lw_down, lw_up

    def calc_spectral_olr(self, atmosphere, surface):
        """Calculate the outgoing longwave radiation as function of wavenumber.

        Parameters:
            atmosphere (konrad.atmosphere.Atmosphere): Atmosphere model.
            surface (konrad.surface.Surface): Surface model.

        Returns:
           ndarray: Outgoing longwave radiation [W m^-2 / cm^-1]
        """
        f, _, irradiance_field, _ = self.calc_spectral_irradiance_field(
            atmosphere=atmosphere, t_surface=surface["temperature"][0])
        return f, irradiance_field[:, -1, 0, 0, 1]
예제 #3
0
class TestAgendas:
    """
    Tests the calling of ARTS workspace methods.
    """
    def setup_method(self):
        """
        This ensures a new Workspace for every test.
        """
        self.ws = Workspace(verbosity = 0)
        self.setup_workspace()

    def setup_workspace(self):
        self.ws.execute_controlfile("artscomponents/clearsky/TestClearSky.arts")

    def test_assignment(self):
        """
        Test assignment of agendas.
        """
        ws = self.ws
        ws.ppath_agenda = ppath_agenda

    def test_include(self):
        ws = self.ws

        @arts_agenda
        def ppath_agenda_inc(ws):
            INCLUDE(ppath_agenda)

        ws.ppath_agenda = ppath_agenda_inc

    def test_execution(self):
        """
        Test definition and execution of agendas.
        """

        self.ws.atmosphere_dim = 1

        @arts_agenda
        def add_1(ws):
            ws.IndexAdd(ws.atmosphere_dim,
                        ws.atmosphere_dim,
                        1)
        add_1.execute(self.ws)

        assert self.ws.atmosphere_dim.value == 2

        add_1.append(add_1)
        add_1.execute(self.ws)

        assert self.ws.atmosphere_dim.value == 4

        args = [self.ws.atmosphere_dim, self.ws.atmosphere_dim, 1]

        @arts_agenda
        def add_2(ws):
            ws.IndexAdd(*args)

        add_2.execute(self.ws)

        assert self.ws.atmosphere_dim.value == 5

    def test_callback(self):
        """
        Test callbacks by re-implementing iy_space_agenda in Python and
        comparing results of yCalc.
        """
        z_ppath = []

        ws = self.ws

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

        import scipy.constants as c

        @arts_agenda(allow_callbacks=True)
        def space_agenda(ws):
            # Since everything happens in Python we need
            # to tell ARTS that we are using all in and outputs.
            ws.Ignore(ws.f_grid)
            ws.Ignore(ws.rtp_pos)
            ws.Ignore(ws.rtp_los)
            ws.Touch(ws.iy)

            # Temperatures and frequency
            t = 2.735
            f = ws.f_grid.value

            # Compute radiances
            c1 = 2.0 * c.h / c.c ** 2
            c2 = c.h / c.k
            b = c1 * f ** 3 / (np.exp(c2 * f / t) - 1.0)

            # Put into iy vector.
            ws.iy = np.zeros((f.size, ws.stokes_dim.value))
            ws.iy.value[:, 0] = b

        # Copy ppath_agenda into workspace.
        ws.iy_space_agenda = space_agenda
        ws.yCalc()

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

        assert(np.allclose(y_new, y_old))


    def test_callback_2(self):
        """
        Test a very complicated Python callback.
        """

        @arts_agenda(allow_callbacks=True)
        def agenda(ws):
            """
            This agenda sets a workspace variable in a very
            obscure way.
            """

            class Foo:
                def __init__(self, ws):
                    self.ws = ws

                def ooo(self):
                    self.ws.IndexSet(ws.stokes_dim, 42)

            foo = Foo(ws)
            ws.IndexSet(ws.stokes_dim, 21)
            foo.ooo()

        agenda.execute(self.ws)

    def test_unknown_wsv(self):
        """
        Ensure that an exception is thrown when an unknown WSV is
        used inside an agenda.

        This covers https://github.com/atmtools/arts/issues/368
        """
        with pytest.raises(ValueError):
            @arts_agenda
            def my_agenda(ws):
                  ws.UnknownMethod()

    def test_starred(self):
        """
        Test expansion of starred expression.
        """
        @arts_agenda
        def agenda(ws):
            """
            This agenda uses a starred expression.
            """
            ws.IndexSet(*[ws.stokes_dim, 42])

        self.ws.stokes_dim = 0
        agenda.execute(self.ws)
        assert self.ws.stokes_dim.value == 42

    def test_double_starred(self):
        """
        Test expansion of starred expression.
        """
        @arts_agenda
        def agenda(ws):
            """
            This agenda uses a starred expression.
            """
            ws.IndexSet(**{"out" : ws.stokes_dim,
                           "value" : 42})

        self.ws.stokes_dim = 0
        agenda.execute(self.ws)
        assert self.ws.stokes_dim.value == 42

    def test_exception(self):
        """
        Ensure that exception is thrown when a agenda
        variable is set to an invalid value.
        """
        @arts_agenda(allow_callbacks=True)
        def abs_xsec_agenda(ws):
              pass
        self.ws = pyarts.workspace.Workspace()
        with pytest.raises(Exception):
              self.ws.abs_xsec_agenda = abs_xsec_agenda
예제 #4
0
class TestWorkspace:
    def setup_method(self):
        """This ensures a new Workspace for every test."""
        self.dir = os.path.dirname(os.path.realpath(__file__))
        self.ws  = Workspace()
        self.setup_workspace()

    def setup_workspace(self):
        ws = self.ws
        ws.atmosphere_dim = 1
        ws.p_grid = np.linspace(1e5, 1e3, 21)
        ws.Touch(ws.lat_grid)
        ws.Touch(ws.lon_grid)

        ws.f_grid = 183.0e9 * np.ones(1)
        ws.stokes_dim = 1

        ws.sensor_los = 180.0 * np.ones((1, 1))
        ws.sensor_pos = 830e3 * np.ones((1, 1))
        ws.sensorOff()

    def test_execute_controlfile(self):

        dir = os.path.dirname(os.path.realpath(__file__))
        test_dir = os.path.join(dir, "test_files")
        self.ws.WriteXML("ascii", np.array([1.0]),
                         os.path.join(test_dir, "vector.xml"))
        os.chdir(test_dir)
        self.ws.execute_controlfile("controlfile.arts")

        os.remove(os.path.join(test_dir, "vector.xml"))


    def test_execute_controlfile(self):

        dir = os.path.dirname(os.path.realpath(__file__))
        test_dir = os.path.join(dir, "test_files")
        self.ws.WriteXML("ascii", np.array([1.0]),
                         os.path.join(test_dir, "vector.xml"))
        os.chdir(test_dir)

        agenda = self.ws.execute_controlfile("controlfile.arts")
        self.ws.foo = "not bar"

        @arts_agenda
        def execute(ws):
            ws.FlagOff(ws.jacobian_do)
            ws.StringSet(ws.foo, "still not bar")
            INCLUDE("controlfile.arts")
            INCLUDE(agenda)

        self.ws.execute_agenda(execute)

        assert self.ws.foo.value == "bar"
        os.remove(os.path.join(test_dir, "vector.xml"))

    def test_wsv_setattr(self):
        wsv = self.ws.atmosphere_dim
        wsv.value = 12
        assert self.ws.atmosphere_dim.value == 12


    def test_callbacks(self):

        @arts_agenda
        def agenda(ws):
            """
            This agenda sets a workspace variable in a very
            obscure way.
            """

            class Foo:
                def __init__(self, ws):
                    self.ws = ws

                def ooo(self):
                    self.ws.IndexSet(ws.stokes_dim, 42)

            foo = Foo(ws)
            ws.IndexSet(ws.stokes_dim, 21)
            foo.ooo()

        agenda.execute(self.ws)

        assert self.ws.stokes_dim.value == 42

    def test_contiguous_arrays(self):
        x = np.linspace(0, 1, 256)

        xf = np.asarray(x, order='F')
        self.ws.f_grid = xf
        assert np.array_equal(self.ws.f_grid.value, xf)

        self.ws.f_grid = x[::2]
        assert np.array_equal(self.ws.f_grid.value, x[::2])

        self.ws.f_grid = np.ascontiguousarray(x[::2])
        assert np.array_equal(self.ws.f_grid.value, x[::2])

    def test_name_collision(self):
        self.ws.VectorSetConstant(self.ws.f_grid, 10, 1.)
        self.ws.VectorCreate("np")
        f_grid = self.ws.f_grid.value
        assert np.all(np.isclose(f_grid, np.ones(10)))
예제 #5
0
class TestMethods:
    """
    Tests the calling of ARTS workspace methods.
    """
    def setup_method(self):
        """
        This ensures a new Workspace for every test.
        """
        self.ws = Workspace(verbosity=0)
        self.setup_workspace()

    def setup_workspace(self):
        self.ws.execute_controlfile(
            "artscomponents/clearsky/TestClearSky.arts")

    def test_mixed_arguments(self):
        """
        Check that this raises a syntax error.
        """
        ws = self.ws
        with pytest.raises(SyntaxError):
            ws.yCalc(ws.yf, y_f=ws.y_f)

    def test_unexpected_argument(self):
        """
        Providing a named argument with a name that is not actually an argument
        of the WSM should raise an error.
        """
        ws = self.ws
        with pytest.raises(Exception):
            ws.yCalc(nonsense=ws.y_f)

    def test_override_output(self):
        """
        Test overriding of output parameters in the two possible
        ways.
        """
        ws = self.ws
        y_ref = np.copy(ws.y.value)

        ws.yf = np.zeros(y_ref.size)
        ws.yCalc(ws.yf)
        assert (np.allclose(ws.yf.value, y_ref))

        ws.yf = np.zeros(y_ref.size)
        ws.yCalc(y=ws.yf)
        assert (np.allclose(ws.yf.value, y_ref))

    def test_override_input(self):
        """
        Test overriding of input parameters in the two possible ways.

        atmgeom_checked WSV is set to zero so that both calculations
        should fail if input is not overridden.
        """
        ws = self.ws
        ws.atmgeom_checked = 0

        ws.rte_pos = np.array([600e3, 0, 0])
        ws.rte_pos2 = np.array([600e3, 0])
        ws.rte_los = np.array([180.0, 0])

        ws.ppathCalc(ws.ppath, ws.ppath_agenda, ws.ppath_lmax,
                     ws.ppath_lraytrace, 1)
        ws.ppathCalc(atmgeom_checked=1)

    def test_generic_input(self):
        """
        Test overriding of generic input in the two possible ways.
        """
        ws = self.ws
        species = ([
            "H2O-SelfContStandardType, H2O-ForeignContStandardType, H2O",
            "N2-SelfContStandardType", "O3"
        ])

        ws.ArrayOfArrayOfSpeciesTagCreate("abs_species_2")
        ws.abs_speciesSet(ws.abs_species_2, ws.abs_xsec_agenda_checked,
                          ws.propmat_clearsky_agenda_checked, species)
        ws.ArrayOfArrayOfSpeciesTagCreate("abs_species_3")
        ws.abs_speciesSet(abs_species=ws.abs_species_3, species=species)
        assert (ws.abs_species_2.value == ws.abs_species_3.value)

    def test_generic_output(self):
        """
        Test overriding of generic input in the two possible ways.
        """
        ws = self.ws

        tempfile = NamedTemporaryFile()

        mat = np.ones((2, 2))
        ws.sensor_los = np.ones((2, 2))
        ws.WriteXML("ascii", ws.sensor_los, tempfile.name)

        ws.sensor_los = np.zeros((2, 2))
        ws.ReadXML(ws.sensor_los, tempfile.name)
        assert (np.allclose(mat, ws.sensor_los.value))

        ws.sensor_los = np.zeros((2, 2))
        ws.ReadXML(out=ws.sensor_los, filename=tempfile.name)
        assert (np.allclose(mat, ws.sensor_los.value))

    def test_supergeneric_overload_resolution(self):
        """
        Test resolution of supergeneric methods.
        """
        self.ws.ArrayOfIndexCreate("array_of_index")
        self.ws.ArrayOfArrayOfIndexCreate("array_of_array_of_index")
        self.ws.array_of_index = [1, 2, 3]
        self.ws.Append(self.ws.array_of_array_of_index, self.ws.array_of_index)
        self.ws.Append(self.ws.array_of_array_of_index, self.ws.array_of_index)

    def test_supergeneric_overload_failure(self):
        """
        Test expected failure of supergeneric overload resolution.
        """
        with pytest.raises(Exception):
            self.ws.NumericCreate("numeric_wsv")
            self.ws.StringCreate("string_wsv")
            self.ws.Copy(self.ws.string_wsv, self.ws.numeric_wsv)

    def test_wsm_error(self):
        """
        Test error handling from ARTS WSMs.
        """
        with pytest.raises(Exception):
            ws.atmgeom_checked = 0
            self.ws.yCalc()