peng/touchstone.py
def write_touchstone(fname, options, data, noise=None, frac_length=10, exp_length=2):
    r"""
    Write a `Touchstone`_ file.

    Parameter data is first resized to an :code:`points` x :code:`nports` x
    :code:`nports` where :code:`points` represents the number of frequency
    points and :code:`nports` represents the number of ports in the file; then
    parameter data is written to file in scientific notation

    :param fname: Touchstone file name
    :type  fname: `FileNameExists <https://pexdoc.readthedocs.io/en/stable/
                  ptypes.html#filenameexists>`_

    :param options: Touchstone file options
    :type  options: :ref:`TouchstoneOptions`

    :param data: Touchstone file parameter data
    :type  data: :ref:`TouchstoneData`

    :param noise: Touchstone file parameter noise data (only supported in
                  two-port files)
    :type  noise: :ref:`TouchstoneNoiseData`

    :param frac_length: Number of digits to use in fractional part of data
    :type  frac_length: non-negative integer

    :param exp_length: Number of digits to use in exponent
    :type  exp_length: positive integer

    .. [[[cog cog.out(exobj.get_sphinx_autodoc()) ]]]
    .. Auto-generated exceptions documentation for
    .. peng.touchstone.write_touchstone

    :raises:
     * RuntimeError (Argument \`data\` is not valid)

     * RuntimeError (Argument \`exp_length\` is not valid)

     * RuntimeError (Argument \`fname\` is not valid)

     * RuntimeError (Argument \`frac_length\` is not valid)

     * RuntimeError (Argument \`noise\` is not valid)

     * RuntimeError (Argument \`options\` is not valid)

     * RuntimeError (File *[fname]* does not have a valid extension)

     * RuntimeError (Malformed data)

     * RuntimeError (Noise data only supported in two-port files)

    .. [[[end]]]
    """
    # pylint: disable=R0913
    # Exceptions definitions
    exnports = pexdoc.exh.addex(
        RuntimeError, "File *[fname]* does not have a valid extension"
    )
    exnoise = pexdoc.exh.addex(
        RuntimeError, "Noise data only supported in two-port files"
    )
    expoints = pexdoc.exh.addex(RuntimeError, "Malformed data")
    # Data validation
    _, ext = os.path.splitext(fname)
    ext = ext.lower()
    nports_regexp = re.compile(r"\.s(\d+)p")
    match = nports_regexp.match(ext)
    exnports(not match, edata={"field": "fname", "value": fname})
    nports = int(match.groups()[0])
    exnoise(bool((nports != 2) and noise))
    nums_per_freq = nports ** 2
    expoints(data["points"] * nums_per_freq != data["pars"].size)
    #
    npoints = data["points"]
    par_data = np.resize(np.copy(data["pars"]), (npoints, nports, nports))
    if nports == 2:
        par_data = np.transpose(par_data, (0, 2, 1))
    units_dict = {"ghz": "GHz", "mhz": "MHz", "khz": "KHz", "hz": "Hz"}
    options["units"] = units_dict[options["units"].lower()]
    fspace = 2 + frac_length + (exp_length + 2)
    # Format data
    with open(fname, "w") as fobj:
        fobj.write(
            "# {units} {ptype} {pformat} R {z0}\n".format(
                units=options["units"],
                ptype=options["ptype"],
                pformat=options["pformat"],
                z0=options["z0"],
            )
        )
        for row in _chunk_pars(data["freq"], par_data, options["pformat"]):
            row_data = [
                to_scientific_string(item, frac_length, exp_length, bool(num != 0))
                if item is not None
                else fspace * " "
                for num, item in enumerate(row)
            ]
            fobj.write(" ".join(row_data) + "\n")
        if (nports == 2) and noise:
            fobj.write("! Noise data\n")
            for row in _chunk_noise(noise):
                row_data = [
                    to_scientific_string(item, frac_length, exp_length, bool(num != 0))
                    for num, item in enumerate(row)
                ]
                fobj.write(" ".join(row_data) + "\n")
def write_touchstone(fname, options, data, noise=None, frac_length=10, exp_length=2):
    r"""
    Write a `Touchstone`_ file.

    Parameter data is first resized to an :code:`points` x :code:`nports` x
    :code:`nports` where :code:`points` represents the number of frequency
    points and :code:`nports` represents the number of ports in the file; then
    parameter data is written to file in scientific notation

    :param fname: Touchstone file name
    :type  fname: `FileNameExists <https://pexdoc.readthedocs.io/en/stable/
                  ptypes.html#filenameexists>`_

    :param options: Touchstone file options
    :type  options: :ref:`TouchstoneOptions`

    :param data: Touchstone file parameter data
    :type  data: :ref:`TouchstoneData`

    :param noise: Touchstone file parameter noise data (only supported in
                  two-port files)
    :type  noise: :ref:`TouchstoneNoiseData`

    :param frac_length: Number of digits to use in fractional part of data
    :type  frac_length: non-negative integer

    :param exp_length: Number of digits to use in exponent
    :type  exp_length: positive integer

    .. [[[cog cog.out(exobj.get_sphinx_autodoc()) ]]]
    .. Auto-generated exceptions documentation for
    .. peng.touchstone.write_touchstone

    :raises:
     * RuntimeError (Argument \`data\` is not valid)

     * RuntimeError (Argument \`exp_length\` is not valid)

     * RuntimeError (Argument \`fname\` is not valid)

     * RuntimeError (Argument \`frac_length\` is not valid)

     * RuntimeError (Argument \`noise\` is not valid)

     * RuntimeError (Argument \`options\` is not valid)

     * RuntimeError (File *[fname]* does not have a valid extension)

     * RuntimeError (Malformed data)

     * RuntimeError (Noise data only supported in two-port files)

    .. [[[end]]]
    """
    # pylint: disable=R0913
    # Exceptions definitions
    exnports = pexdoc.exh.addex(
        RuntimeError, "File *[fname]* does not have a valid extension"
    )
    exnoise = pexdoc.exh.addex(
        RuntimeError, "Noise data only supported in two-port files"
    )
    expoints = pexdoc.exh.addex(RuntimeError, "Malformed data")
    # Data validation
    _, ext = os.path.splitext(fname)
    ext = ext.lower()
    nports_regexp = re.compile(r"\.s(\d+)p")
    match = nports_regexp.match(ext)
    exnports(not match, edata={"field": "fname", "value": fname})
    nports = int(match.groups()[0])
    exnoise(bool((nports != 2) and noise))
    nums_per_freq = nports ** 2
    expoints(data["points"] * nums_per_freq != data["pars"].size)
    #
    npoints = data["points"]
    par_data = np.resize(np.copy(data["pars"]), (npoints, nports, nports))
    if nports == 2:
        par_data = np.transpose(par_data, (0, 2, 1))
    units_dict = {"ghz": "GHz", "mhz": "MHz", "khz": "KHz", "hz": "Hz"}
    options["units"] = units_dict[options["units"].lower()]
    fspace = 2 + frac_length + (exp_length + 2)
    # Format data
    with open(fname, "w") as fobj:
        fobj.write(
            "# {units} {ptype} {pformat} R {z0}\n".format(
                units=options["units"],
                ptype=options["ptype"],
                pformat=options["pformat"],
                z0=options["z0"],
            )
        )
        for row in _chunk_pars(data["freq"], par_data, options["pformat"]):
            row_data = [
                to_scientific_string(item, frac_length, exp_length, bool(num != 0))
                if item is not None
                else fspace * " "
                for num, item in enumerate(row)
            ]
            fobj.write(" ".join(row_data) + "\n")
        if (nports == 2) and noise:
            fobj.write("! Noise data\n")
            for row in _chunk_noise(noise):
                row_data = [
                    to_scientific_string(item, frac_length, exp_length, bool(num != 0))
                    for num, item in enumerate(row)
                ]
                fobj.write(" ".join(row_data) + "\n")
r"""
    Write a `Touchstone`_ file.

    Parameter data is first resized to an :code:`points` x :code:`nports` x
    :code:`nports` where :code:`points` represents the number of frequency
    points and :code:`nports` represents the number of ports in the file; then
    parameter data is written to file in scientific notation

    :param fname: Touchstone file name
    :type  fname: `FileNameExists <https://pexdoc.readthedocs.io/en/stable/
                  ptypes.html#filenameexists>`_

    :param options: Touchstone file options
    :type  options: :ref:`TouchstoneOptions`

    :param data: Touchstone file parameter data
    :type  data: :ref:`TouchstoneData`

    :param noise: Touchstone file parameter noise data (only supported in
                  two-port files)
    :type  noise: :ref:`TouchstoneNoiseData`

    :param frac_length: Number of digits to use in fractional part of data
    :type  frac_length: non-negative integer

    :param exp_length: Number of digits to use in exponent
    :type  exp_length: positive integer

    .. [[[cog cog.out(exobj.get_sphinx_autodoc()) ]]]
    .. Auto-generated exceptions documentation for
    .. peng.touchstone.write_touchstone

    :raises:
     * RuntimeError (Argument \`data\` is not valid)

     * RuntimeError (Argument \`exp_length\` is not valid)

     * RuntimeError (Argument \`fname\` is not valid)

     * RuntimeError (Argument \`frac_length\` is not valid)

     * RuntimeError (Argument \`noise\` is not valid)

     * RuntimeError (Argument \`options\` is not valid)

     * RuntimeError (File *[fname]* does not have a valid extension)

     * RuntimeError (Malformed data)

     * RuntimeError (Noise data only supported in two-port files)

    .. [[[end]]]