andes icon indicating copy to clipboard operation
andes copied to clipboard

Some enhancements on compatibility with PSS/E and MATPOWER

Open jinningwang opened this issue 8 months ago • 2 comments

Is your feature request related to a problem? Please describe. Interoperation with PSS/E and MATPOWER.

Describe the solution you'd like Functions to export PSS/E dict to DYR file.

  • [x] A function to write MATPOEWR mpc to M file.
  • [x] A function to write Power Flow data to RAW file.
  • [ ] A function to write PSSE dict to DYR file.
  • [ ] A function to replace unsupported models with supported ones

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Additional context

Two example code snippets for reference:

def write_dict_dyr(dyr_dict: Dict[str, pd.DataFrame], outfile: str):
    """
    Write a dictionary of dynamic models to a DYR file.

    This function takes a dictionary where the keys are model names (e.g., 'REGCA1', 'REECA1') 
    and the values are pandas DataFrames containing the model parameters. It writes the data 
    to a DYR file in the correct format, ensuring the following:
    - The first column (IBUS) is an integer representing the bus index.
    - The second column is the model name, enclosed in single quotes.
    - The third column (ID) is an integer representing the device index.
    - Remaining columns are the model parameters, written as space-separated values.
    - Each line ends with a trailing slash ('/').

    Models 'Toggle' are skipped during the writing process.

    Parameters:
    -----------
    dyr_dict : Dict[str, pd.DataFrame]
        A dictionary where each key is a model name and each value is a pandas DataFrame 
        containing the parameters for that model. The DataFrame must have the following structure:
        - Column 0: IBUS (integer)
        - Column 1: ID (integer)
        - Columns 2+: Model parameters (various types).
    
    outfile : str
        The path to the output DYR file where the formatted data will be written.

    Returns:
    --------
    None
        The function writes the data to the specified file and prints a success message.

    Notes:
    ------
    - The function skips models with the name 'Toggle' and prints a message for each skipped model.
    - A blank line is not added between models in the output file.
    - Ensure the input DataFrames are properly formatted to avoid runtime errors.

    Example:
    --------
    >>> dyr_dict = {
    ...     'REGCA1': pd.DataFrame([
    ...         [1004, 1, 1.0, 0.01, 10, 0.9, 0.5],
    ...         [1006, 1, 1.0, 0.02, 10, 0.9, 0.5]
    ...     ]),
    ...     'REECA1': pd.DataFrame([
    ...         [1004, 1, 0, 0, 1, 1, 1],
    ...         [1006, 1, 0, 0, 1, 1, 1]
    ...     ])
    ... }
    >>> write_dict_dyr(dyr_dict, './output.dyr')
    Data successfully written to ./output.dyr
    """
    with open(outfile, 'w') as f:
        for model_name, df in dyr_dict.items():
            if model_name in ['Toggle']:
                print(f"Skipped {model_name}")
                continue
            for _, row in df.iterrows():
                ibus = int(row.iloc[0])  # format IBUS as integer
                model = f"'{model_name}'"  # model name
                device_id = int(row.iloc[1])  # format ID as integer
                # format the rest parameters
                params = ' '.join(map(str, row.iloc[2:].values))
                # Combine all parts into a formatted line
                formatted_line = f"{ibus} {model} {device_id} {params} /"
                # Write the formatted line to the file
                f.write(formatted_line + '\n')

    print(f"Data successfully written to {outfile}")

Replace PSS2A with ST2CUT

# PSS2A -> ST2CUT, there are 22 columns in ST2CUT
ST2CUT = pd.DataFrame()

ST2CUT[0] = dyr['PSS2A'][0]  # IBUS
ST2CUT[1] = dyr['PSS2A'][1]  # ID

ST2CUT[2] = dyr['PSS2A'][2]  # M, IC1 <- M, ICS1
ST2CUT[3] = dyr['PSS2A'][3]  # M+1, IB1 <- M+1, REMBUS1
ST2CUT[4] = dyr['PSS2A'][4]  # M+2, IB2 <- M+2, ICS2
ST2CUT[5] = dyr['PSS2A'][5]  # M+3, IB3 <- M+3, REMBUS2

ST2CUT[6] = 1  # J, K1 <- 1
# J+1, K2 <- J+6, Ks2 * J+7, Ks3
ST2CUT[7] = dyr['PSS2A'][14] * dyr['PSS2A'][15]
ST2CUT[8] = dyr['PSS2A'][10]  # J+2, T1 <- J+2, T6
ST2CUT[9] = dyr['PSS2A'][13]  # J+3, T2 <- J+5, T7
ST2CUT[10] = sa.ST2CUT.T3.default  # J+4, T3 <- default
ST2CUT[11] = sa.ST2CUT.T4.default  # J+5, T4 <- default
ST2CUT[12] = dyr['PSS2A'][19]  # J+6, T5 <- J+11, T1
ST2CUT[13] = dyr['PSS2A'][20]  # J+7, T6 <- J+12, T2
ST2CUT[14] = dyr['PSS2A'][21]  # J+8, T7 <- J+13, T3
ST2CUT[15] = dyr['PSS2A'][22]  # J+9, T8 <- J+14, T4
ST2CUT[16] = sa.ST2CUT.T9.default  # J+10, T9 <- default
ST2CUT[17] = ST2CUT[16]  # J+11, T10 <- copy of T9
ST2CUT[18] = dyr['PSS2A'][23]  # J+12, LSMAX <- J+15, VSTMAX
ST2CUT[19] = dyr['PSS2A'][24]  # J+13, LSMIN <- J+16, VSTMIN
ST2CUT[20] = sa.ST2CUT.VCU.default  # J+14, VCU <- default
ST2CUT[21] = sa.ST2CUT.VCL.default  # J+15, VCL <- default

jinningwang avatar Apr 21 '25 18:04 jinningwang

A quick fix was applied to ams.io.matpower to resolve a bug where multiple PQ loads connected to the same bus were incorrectly converted, resulting in different load values in MATPOWER.

https://github.com/CURENT/ams/blob/2166f4368af3d382720af38408153a70502eb9f5/ams/io/matpower.py#L520-L533

jinningwang avatar Jun 02 '25 14:06 jinningwang

PSS/E v33 RAW file writer and MATPOWER M-file writer are developed in ams v1.0.10, https://ltb.readthedocs.io/projects/ams/en/latest/release-notes.html#v1-0-10-2025-05-23

Please note that the PSSE RAW file writer has not undergone thorough testing.

jinningwang avatar Jun 02 '25 14:06 jinningwang