Repository navigation
Conversation
54bd651 to
bba5a78
Compare
bba5a78 to
757dcfe
Compare
|
Hi. I am reviewing the code. It builds, runs, and produces the csv. The timestamp looks a bit odd, for instance, row 569215 looks like this 1704,7.0916666666666668e+00,Genrou,5281_1_genrou,omega,2.2408284627934353e-05 This effectively means that t = 7.09 something. Is this thing going to merge on t if I need to left join? Thank you. |
|
@tsybinae Thanks! Join on Let me know if you have any othr issues! |
|
This functionality is not specific to the csv task, so the merge can go ahead. There may be more precision related questions within a few month. |
tsybinae
left a comment
There was a problem hiding this comment.
Builds and runs cleanly.
superwhiskers
left a comment
There was a problem hiding this comment.
core idea seems like the right approach. just some comments on implementation of formatting that should be addressed before this goes further.
| /// Type used for dispatch | ||
| struct Wide | ||
| { | ||
| }; | ||
|
|
||
| /// Type used for dispatch | ||
| struct Long | ||
| { | ||
| }; | ||
|
|
There was a problem hiding this comment.
instead of using the type for dispatch, i'd recommend using a template parameter instead. you can take in the VariableMonitorLayout as a template parameter and remove the need for this.
| void append(std::string& out, Json json) const override | ||
| void append(std::string& out, Json json) const | ||
| { | ||
| std::ostringstream os; | ||
| os.precision(std::numeric_limits<RealT>::digits10 + 1); | ||
| os << std::scientific; | ||
|
|
||
| std::string indent = " "; | ||
| if (json.after_first) | ||
| { | ||
| os << indent << ",\n"; | ||
| out += " ,\n"; | ||
| } | ||
| os << indent << "{\n"; | ||
| indent.append(2, ' '); | ||
| out += " {\n \"t\": "; | ||
| VariableMonitorDetail::formatReal(out, *time_); | ||
|
|
||
| os << indent << std::quoted("t") << ": " << *time_ << ",\n"; | ||
| for (auto&& var : variables_) | ||
| const Entry* previous = nullptr; | ||
| for (auto&& entry : entries_) | ||
| { | ||
| os << indent << std::quoted(var.label) << ": " << *var.value << ",\n"; | ||
| if (previous && previous->device_class == entry.device_class && previous->id == entry.id) | ||
| { | ||
| out += ",\n \""; | ||
| } | ||
| else | ||
| { | ||
| if (previous) | ||
| { | ||
| out += "\n }"; | ||
| } | ||
| out += ",\n \""; | ||
| out += entry.device_class; | ||
| out += '_'; | ||
| out += entry.id; | ||
| out += "\": {\n \""; | ||
| } | ||
| out += entry.variable; | ||
| out += "\": "; | ||
| VariableMonitorDetail::formatReal(out, entry.value()); | ||
|
|
||
| previous = &entry; | ||
| } | ||
| if (previous) | ||
| { | ||
| out += "\n }"; | ||
| } | ||
| out += "\n }"; | ||
| } |
There was a problem hiding this comment.
since we are already pulling in nlohmann/json, i would recommend using its json serialization capabilities here. that would make this easier to read
| out += " - t: "; | ||
| VariableMonitorDetail::formatReal(out, *time_); | ||
| out += '\n'; | ||
|
|
||
| auto after_first = false; | ||
| for (auto* mon : monitors_) | ||
| const Entry* previous = nullptr; | ||
| for (auto&& entry : entries_) | ||
| { | ||
| if (after_first) | ||
| if (!previous || previous->device_class != entry.device_class || previous->id != entry.id) | ||
| { | ||
| os << ",\n"; | ||
| out += " "; | ||
| out += entry.device_class; | ||
| out += '_'; | ||
| out += entry.id; | ||
| out += ":\n"; | ||
| } | ||
| std::string monitor_out; | ||
| mon->append(monitor_out, Json()); | ||
| os << monitor_out; | ||
| after_first = true; | ||
| } | ||
|
|
||
| indent.erase(indent.size() - 2); | ||
| os << '\n' | ||
| << indent << "}"; | ||
| out += " "; | ||
| out += entry.variable; | ||
| out += ": "; | ||
| VariableMonitorDetail::formatReal(out, entry.value()); | ||
| out += '\n'; | ||
|
|
||
| out += os.str(); | ||
| previous = &entry; | ||
| } |
There was a problem hiding this comment.
we don't have a yaml library so i understand doing this manually, but it could be improved by using <format> to avoid repeatedly concatenating new strings to the output
| /// Type used for dispatch | ||
| struct Csv | ||
| { | ||
| /// Delimiter for CSV line output | ||
| std::string delim{","}; | ||
| }; | ||
|
|
||
| /// Type used for dispatch | ||
| struct Json | ||
| { | ||
| /// Implementation detail used to prevent a comma before the first block | ||
| mutable bool after_first{false}; | ||
| }; | ||
|
|
||
| /// Type used for dispatch | ||
| struct Yaml | ||
| { | ||
| }; |
There was a problem hiding this comment.
while these weren't added here either, i think we should also replace these with a template parameter to dispatch instead. this would be more consistent.
| "monitors": [ | ||
| { | ||
| "file_name": "ACTIVSg2000.long.csv", | ||
| "format": "csv", | ||
| "layout": "long" | ||
| } | ||
| ], |
There was a problem hiding this comment.
I would add an output_layout option to solver.json instead of bringing back monitor sinks in case.json.
| @@ -0,0 +1,24 @@ | |||
| \set ON_ERROR_STOP on | |||
There was a problem hiding this comment.
I haven't looked at SQL in a while. I recommend adding some comments in this file.
| This example runs the reusable | ||
| [ACTIVSg2000 case](../../../../cases/PhasorDynamics/ACTIVSg2000/README.md) | ||
| through a bus fault and loads its long monitor output into PostgreSQL with | ||
| [psql](https://www.postgresql.org/docs/current/app-psql.html). | ||
|
|
||
| ```bash | ||
| DynamicSimulation ACTIVSg2000.solver.json | ||
| psql -f ACTIVSg2000.sql | ||
| ``` |
There was a problem hiding this comment.
This doesn't currently run the example. The user has to run it manually and figure out how to get psql on their own.
| | --- | --- | | ||
| | [ThreeBusBasic](Toy/ThreeBusBasic/README.md) | A three-bus line opened and reclosed. | | ||
| | [ThreeBusConstantSource](Toy/ThreeBusConstantSource/README.md) | A three-bus constant signal source example. | | ||
| | [ACTIVSg2000](ACTIVSg2000/README.md) | A bus fault with long monitor output loaded into PostgreSQL. | |
There was a problem hiding this comment.
Perhaps we should include SQL in the folder name to clarify the purpose. As is, I expected similar setups to the other examples.
| */ | ||
| virtual void appendHeader(std::string&, Csv) const = 0; | ||
| /// Returns the current value of a monitored variable | ||
| using GetterT = std::function<double()>; |
There was a problem hiding this comment.
Everything is now converted to double before printing, including float, int, bool, etc.
Description
Adds an optional
layoutsetting for monitor output. With"layout": "long", each row holds one variable at one sample instead of one column per variable, so results load straight into SQL tables no matter how many variables are monitored. This works for CSV, JSON, and YAML.Closes #597.
Wide output is unchanged, and printing is faster:
developPrint time only, 2000 monitors × 4 variables over 500 samples.
Proposed changes
VariableMonitorLayout(WIDE,LONG) toSinkSpec, set by alayoutkey in the case'smonitorslist. It defaults toWIDE.sample, t, class, id, variable, valuerather than thet, id, valuesketched in the issue:classandidare the device'sclassandidfrom the case file, since anidalone can repeat across classes.variablegets its own column, so queries can filter or group by variable without parsing names.sampleis needed for a unique key, since the samples just before and after an event share the samet.appendEntries), and the controller writes every format and layout from that list. This replaces the per-format virtuals inVariableMonitorBaseand the formatting code inVariableMonitor.Wide,Long) like the format tags, so each sink is aSink<FormatT, LayoutT>resolved at compile time.VariableMonitorController::addVariable.DynamicSimulation/ACTIVSg2000example that runs a bus fault and loads the long output into PostgreSQL with a singlepsqlscript.Checklist
-Wall -Wpedantic -Wconversion -Wextra.Further comments
@pelesh Feedback on PR length/scope
@tsybinae Thank you for the suggestion and guidance.