Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update data collection objects in dynamic simulation #214

Open
bjpalmer opened this issue Jul 17, 2024 · 16 comments · May be fixed by #224
Open

Update data collection objects in dynamic simulation #214

bjpalmer opened this issue Jul 17, 2024 · 16 comments · May be fixed by #224
Assignees

Comments

@bjpalmer
Copy link
Contributor

Add functionality to update values in the data collection objects for buses and branches in dynamic simulation. These values can then be queried and extracted by other applications.

@bjpalmer
Copy link
Contributor Author

bjpalmer commented Jul 18, 2024

I've started adding some code for generators. @jainmilan can you give me some variables that you want current values for?

@tahmid-khan
Copy link

In #176, I asked for a way to get the current value of voltage at each bus, not just generators and loads. Does/will this issue cover that ability?

@bjpalmer
Copy link
Contributor Author

bjpalmer commented Aug 14, 2024

This will probably work for you, but it might be better to add some accessors to the the DSFullBus class so you can access the values directly. Using this method, you will need to update the data collections on all buses and branches (by calling a function in the factory class), then accessing the data collection object for each bus and finally getting the variable you want. We are developing this functionality to create a python interface so the dynamic simulation can work with another code, but it may not be the optimal way for all applications to get this data.

@wperkins
Copy link
Member

wperkins commented Aug 19, 2024

This will probably work for you, but it might be better to add some accessors to the the DSFullBus [...]

This is on my todo list, along with Python methods. It should be done this week.

@wperkins
Copy link
Member

This will probably work for you, but it might be better to add some accessors to the the DSFullBus [...]

This is on my todo list, along with Python methods. It should be done this week.

Both DS and HADREC applications now have an updateData() method, both C++ and Python.

@wperkins
Copy link
Member

@bjpalmer, I'm investigating why CURRENT load data is not available after updateData(). It appears that there are two kinds of loads on the DS bus. One is counted by p_ndyn_load. These are governed by p_loadmodels[] and seem to update CURRENT values when asked (although, I have not run a case where p_ndyn_load > 0 on any bus). The other is counted by p_npowerflow_load. These seem to have their P and Q values stored in p_powerflowload_p and p_powerflowload_q, respectively. These load values are never saved to CURRENT data collection values, even if they change via scaling in the simulation.

I think DSFullBus::updateData() may need another loop over p_powerflowload_p and p_powerflowload_q where those values are put in the DataCollection. I got a little lost trying to figure out when what load applies, so I think you need to take a look.

Also, it appears that other simulation state queries only work on p_powerflowload_*, like DSFullBus::getLoadPower() and DSFullBus::getTotalLoadPower(). This seems inconsistent to me.

@jainmilan, I'm pretty sure this is why we get None when querying load power.

@bjpalmer
Copy link
Contributor Author

We looked at this last week. It seemed to me that the generator objects were not getting created by the calculation and therefore nothing was getting copied to the data collections. The basis for this assumption was that no GENERATOR_MODEL variable was showing up in the data collections after adding a p_factory->dumpData() to the code immediately after calling updateData. We only ran for 1 timestep, but as far as I could tell, it didn't look like the code was setting up generators for the test case we were looking at. The GENERATOR_MODEL variable is used inside the generator factory to create the appropriate generator instance.

@wperkins
Copy link
Member

I've been running the 9-bus case with dsf.py (in current repository), where updateData() is called at the end of the simulation. I get CURRENT generator values for that, so it must work with some kinds of generators.

However, I'm complaining about load values.

The network is queried as follows:

def network_analytics_dump(ds_app):
    nbus = ds_app.totalBuses()
    for bus in range(nbus):
        print(bus,
              ds_app.getBusInfoInt(bus, "BUS_NUMBER"),
              ds_app.getBusInfoString(bus, "BUS_NAME"),
              ds_app.getBusInfoInt(bus, "BUS_TYPE"),
              ds_app.numGenerators(bus),
              ds_app.numLoads(bus),
              ds_app.getBusInfoReal(bus, "BUS_VOLTAGE_MAG"))
        for g in range(ds_app.numGenerators(bus)):
            print(" gen: ", g,
                  ds_app.getBusInfoInt(bus, "GENERATOR_NUMBER", g),
                  ds_app.getBusInfoString(bus, "GENERATOR_ID", g),
                  ds_app.getBusInfoReal(bus, "GENERATOR_PG", g),
                  ds_app.getBusInfoReal(bus, "GENERATOR_QG", g),
                  ds_app.getBusInfoReal(bus, "GENERATOR_PG_CURRENT", g),
                  ds_app.getBusInfoReal(bus, "GENERATOR_QG_CURRENT", g),
                  )
        for l in range(ds_app.numLoads(bus)):
            print("load: ", l,
                  ds_app.getBusInfoInt(bus, "LOAD_NUMBER", l),
                  ds_app.getBusInfoString(bus, "LOAD_ID", l),
                  ds_app.getBusInfoReal(bus, "LOAD_PL", l),
                  ds_app.getBusInfoReal(bus, "LOAD_QL", l),
                  ds_app.getBusInfoReal(bus, "LOAD_PL_CURRENT", l),
                  ds_app.getBusInfoReal(bus, "LOAD_QL_CURRENT", l)
                  )

The results of this query are as follows:

0 1 'bus-1       ' 3 1 0 1.04
 gen:  0 None 1  0.669999999997691 0.7010759010981538 0.669999999997691 0.7010759010981538
1 2 'bus-2       ' 2 1 0 1.02533
 gen:  0 None 1  1.63 0.6028676952146661 1.63 0.6028676952146661
2 3 'bus-3       ' 2 1 0 1.02536
 gen:  0 None 1  0.85 0.4202344495139094 0.85 0.4202344495139094
3 4 'bus-4       ' 1 0 0 1.0018586298562873
4 5 'bus-5       ' 1 0 1 0.9816273801688302
load:  0 None 1  90.0 30.0 None None
5 6 'bus-6       ' 1 0 0 1.002520962766657
6 7 'bus-7       ' 1 0 1 0.9812684602469763
load:  0 None 1  100.0 35.0 None None
7 8 'bus-8       ' 1 0 0 0.9935621039389169
8 9 'bus-9       ' 1 0 1 0.9661929333427239
load:  0 None 1  125.0 50.0 None None

The lines starting with gen all have 2 P/Q values (inital and current). I don't this this would have happened if generator models were not set up.

@bjpalmer
Copy link
Contributor Author

@jainmilan, were we looking at the same input problem that @wperkins is describing? If you dump out the data collections, do you see a LOAD_MODEL variable defined? I'm not completely sure about this, but I think load variables only get printed out if a dynamic load model is defined, so maybe the test you are running does not have one of these, in which case no LOAD_MODEL variable would be defined.

@wperkins
Copy link
Member

So, there can be loads without a LOAD_MODEL? Shouldn't we be able to query those too?

@bjpalmer
Copy link
Contributor Author

Those loads are static. We could query them, although I don't know if there is much point in doing it more than once. @jainmilan, @abhyshr, @yliu250, this is a domain question.

@wperkins
Copy link
Member

Those loads are static. We could query them, although I don't know if there is much point in doing it more than once. @jainmilan, @abhyshr, @yliu250, this is a domain question.

I think it's an API consistency question.

@jainmilan needs to be able query all load P/Q's at arbitrary points in the simulation. He would first need to figure out what loads have a LOAD_MODEL and which don't and then query those loads differently (e.g., different field name), right? I think it would be more consistent, and simple, to have "static" load P/Q's duplicated in CURRENT data collection fields.

Also, are we sure that loads without a LOAD_MODEL (static) do not change during the simulation? DSFullBus::scaleLoadPower() and DSFullBus::resetPower() seem to modify p_powerflowload_*, but I don't know when or if those get called.

Also, based on my reading of DSFullBus::load() handling of load input, things could get pretty messed up if a bus has a mix of static and LOAD_MODEL loads.

@bjpalmer
Copy link
Contributor Author

@jainmilan, @wperkins, @abhyshr, @yliu250, I modified the updateData function so that it will always produce a value.

void gridpack::dynamic_simulation::DSFullBus::updateData(
boost::shared_ptr<gridpack::component::DataCollection> &data)
{
int i;
std::string name;
for (i=0; i<p_ngen; i++) {
if (data->getValue(GENERATOR_MODEL,&name,i)) {
p_generators[i]->updateData(data, i);
} else {
if (!data->setValue(GENERATOR_PG_CURRENT, p_pg[i], i)) {
data->addValue(GENERATOR_PG_CURRENT, p_pg[i], i);
}
if (!data->setValue(GENERATOR_QG_CURRENT, p_qg[i], i)) {
data->addValue(GENERATOR_QG_CURRENT, p_qg[i], i);
}
}
}
int lcnt = 0;
for (i=0; i<p_npowerflow_load; i++) {
if (data->getValue(LOAD_MODEL,&name,i)) {
p_loadmodels[lcnt]->updateData(data, i);
lcnt++;
} else {
if (!data->setValue(LOAD_PL_CURRENT, p_powerflowload_p[i], i)) {
data->addValue(LOAD_PL_CURRENT, p_powerflowload_p[i], i);
}
if (!data->setValue(LOAD_QL_CURRENT, p_powerflowload_q[i], i)) {
data->addValue(LOAD_QL_CURRENT, p_powerflowload_q[i], i);
}
}
}
}

@jainmilan
Copy link
Collaborator

@wperkins @abhyshr @yliu250 @bjpalmer I am trying to access the from and to p and q values using getBranchInfoReal but getting None.

nbranch = ds_app.totalBranches()
for branch in range(0, nbranch):
(f, t) = ds_app.getBranchEndpoints(branch)
print(branch, f, t,
ds_app.getBranchInfoInt(branch, "BRANCH_ELEMENTS"),
ds_app.getBranchInfoInt(branch, "BRANCH_INDEX"),
ds_app.getBranchInfoString(branch, "BRANCH_NAME"),
ds_app.getBranchInfoReal(branch, "BRANCH_LENGTH"),
ds_app.getBranchInfoReal(branch, 'BRANCH_FROM_P_CURRENT'),
ds_app.getBranchInfoReal(branch, 'BRANCH_FROM_Q_CURRENT'),
ds_app.getBranchInfoReal(branch, 'BRANCH_TO_P_CURRENT'),
ds_app.getBranchInfoReal(branch, 'BRANCH_TO_Q_CURRENT')
)

@bjpalmer
Copy link
Contributor Author

bjpalmer commented Sep 4, 2024

I don't think you are going to get anything using that format. The BRANCH_FROM/TO_P/Q_CURRENT variables are all indexed by a specific line within the branch object and it doesn't look like you are including the line index in your calls to getBranchRealInfo. You can verify that the line variables are stored in the data collection by using the dumpData() method.

@bjpalmer
Copy link
Contributor Author

bjpalmer commented Sep 4, 2024

I restructured the code a bit so that the evaluation of the branch flows is in a separate function (evaluateBranchFlow) that is then called inside the branch updateData function before storing data in the data collection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants