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

[DEV-1994] Phase/wire level impedances #197

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
## [0.24.0] - UNRELEASED
### Breaking Changes
* Database readers and writes for each `BaseService` no longer accept a `MetadataCollection`, and will instead use the collection of the provided service.
* `AcLineSegment.perLengthSequenceImpedance` has been corrected to `perLengthImpedance`. This has been done in a non-breaking way, however the public resolver
`Resolvers.perLengthSequenceImpedance` is now `Resolvers.perLengthImpedance`, correctly reflecting the CIM relationship.

### New Features
* Network state services for updating and querying network state events via gRPC.
Expand All @@ -14,6 +16,8 @@
* Added `BatteryControl`, a new class which describes behaviour specific to controlling a `BatteryUnit`.
* Added `StaticVarCompensator` a new class representing a facility for providing variable and controllable shunt reactive power.
* Added `ControlledAppliance` a new class representing the identity of the appliance controlled by a specific `EndDeviceFunction`.
* Added `PerLengthPhaseImpedance` a new class used for representing the impedance of individual wires on an AcLineSegment.
* Added `PhaseImpedanceData` a data class with a link to `PerLengthPhaseImpedance`, for capturing the phase impedance data of an individual wire.
* Added new enums:
* `BatteryControlMode`
* `EndDeviceFunctionKind`
Expand All @@ -23,9 +27,9 @@
* Added `ctPrimary` and `minTargetDeadband` to `RegulatingContrl`.
* Added collection of `BatteryControl` to `BatteryUnit`
* Added collection of `EndDeviceFunctionKind` to `EndDevice`
* Added an unordered collection comparator.

### Fixes
* None.

### Notes
* None.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.zepben.maven</groupId>
<artifactId>evolve-super-pom</artifactId>
<version>0.36.4</version>
<version>0.36.7</version>
</parent>

<groupId>com.zepben</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,24 @@ package com.zepben.evolve.cim.iec61970.base.wires
* The BaseVoltage at the two ends of ACLineSegments in a Line shall have the same BaseVoltage.nominalVoltage. However, boundary lines may have
* slightly different BaseVoltage.nominalVoltages and variation is allowed. Larger voltage difference in general requires use of an equivalent branch.
*
* @property perLengthSequenceImpedance Per-length impedance of this line segment.
* @property perLengthImpedance Per-length impedance of this line segment.
* @property perLengthPhaseImpedance Per-length phase impedance of this line segment.
* @property perLengthSequenceImpedance Per-length sequence impedance of this line segment.
*/
class AcLineSegment @JvmOverloads constructor(mRID: String = "") : Conductor(mRID) {

var perLengthSequenceImpedance: PerLengthSequenceImpedance? = null
var perLengthImpedance: PerLengthImpedance? = null

var perLengthSequenceImpedance: PerLengthSequenceImpedance?
get() = perLengthImpedance as? PerLengthSequenceImpedance
set(it) {
perLengthImpedance = it
}

var perLengthPhaseImpedance: PerLengthPhaseImpedance?
get() = perLengthImpedance as? PerLengthPhaseImpedance
set(it) {
perLengthImpedance = it
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2024 Zeppelin Bend Pty Ltd
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package com.zepben.evolve.cim.iec61970.base.wires

import com.zepben.evolve.services.common.extensions.asUnmodifiable
import com.zepben.evolve.services.common.extensions.typeNameAndMRID

/**
* Impedance and admittance parameters per unit length for n-wire unbalanced lines, in matrix form.
*/
class PerLengthPhaseImpedance @JvmOverloads constructor(mRID: String = "") : PerLengthImpedance(mRID) {

private var _data: MutableList<PhaseImpedanceData>? = null

val data: List<PhaseImpedanceData> get() = _data.asUnmodifiable()

/**
* Get the number of entries in the [PhaseImpedanceData] collection.
*/
fun numData(): Int = _data?.size ?: 0

/**
* Get only the diagonal elements of the matrix, i.e toPhase == fromPhase.
*/
fun diagonal(): List<PhaseImpedanceData>? = _data?.filter { it.toPhase == it.fromPhase }

/**
* Get the matrix entry for the corresponding to and from phases.
* @param fromPhase The from phase to lookup.
* @param toPhase The to phase to lookup.
*/
fun getData(fromPhase: SinglePhaseKind, toPhase: SinglePhaseKind): PhaseImpedanceData? =
_data?.find { it.fromPhase == fromPhase && it.toPhase == toPhase }

/**
* Add a [PhaseImpedanceData] to this [PerLengthPhaseImpedance]
* @param phaseImpedanceData The [PhaseImpedanceData] to add
*/
fun addData(phaseImpedanceData: PhaseImpedanceData): PerLengthPhaseImpedance {
require(
_data.isNullOrEmpty()
|| _data?.none { pid -> pid.fromPhase == phaseImpedanceData.fromPhase && pid.toPhase == phaseImpedanceData.toPhase } == true) {
"Unable to add PhaseImpedanceData to ${typeNameAndMRID()}. " +
"A PhaseImpedanceData with fromPhase ${phaseImpedanceData.fromPhase} and toPhase ${phaseImpedanceData.toPhase} already exists in " +
"this PerLengthPhaseImpedance."
}

_data = _data ?: mutableListOf()
_data!!.add(phaseImpedanceData)

return this
}

/**
* Remove a [PhaseImpedanceData] from this [PerLengthPhaseImpedance]
* @param phaseImpedanceData The [PhaseImpedanceData] to remove.
* @return true if the [phaseImpedanceData] was removed.
*/
fun removeData(phaseImpedanceData: PhaseImpedanceData): Boolean {
val ret = _data?.remove(phaseImpedanceData) == true
if (_data.isNullOrEmpty()) _data = null
return ret
}

/**
* Clear all [PhaseImpedanceData] from this [PerLengthPhaseImpedance]
*/
fun clearData(): PerLengthPhaseImpedance {
_data = null
return this
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 Zeppelin Bend Pty Ltd
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package com.zepben.evolve.cim.iec61970.base.wires

/**
* Impedance and conductance matrix element values. The diagonal elements are described by the elements having the same toPhase and fromPhase value and the off
* diagonal elements have different toPhase and fromPhase values.
*
* @property fromPhase Refer to the class description.
* @property toPhase Refer to the class description.
* @property b Susceptance matrix element value, per length of unit.
* @property g Conductance matrix element value, per length of unit.
* @property r Resistance matrix element value, per length of unit.
* @property x Reactance matrix element value, per length of unit.
*/
data class PhaseImpedanceData(
val fromPhase: SinglePhaseKind,
val toPhase: SinglePhaseKind,
val b: Double? = null,
val g: Double? = null,
val r: Double? = null,
val x: Double? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.scada.TableRem
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.scada.TableRemotePoints
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.scada.TableRemoteSources
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.*
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.*
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TableBatteryUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TablePhotoVoltaicUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TablePowerElectronicsUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TablePowerElectronicsWindUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.infiec61970.feeder.TableCircuits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.infiec61970.feeder.TableLoops
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.infiec61970.feeder.TableLvFeeders
Expand All @@ -72,6 +75,7 @@ import com.zepben.evolve.services.common.extensions.*
import com.zepben.evolve.services.network.NetworkService
import java.sql.ResultSet
import java.sql.SQLException
import kotlin.Throws

/**
* A class for reading the [NetworkService] tables from the database.
Expand Down Expand Up @@ -1584,11 +1588,7 @@ class NetworkCimReader(
@Throws(SQLException::class)
fun load(table: TableAcLineSegments, resultSet: ResultSet, setIdentifier: (String) -> String): Boolean {
val acLineSegment = AcLineSegment(setIdentifier(resultSet.getString(table.MRID.queryIndex))).apply {
perLengthSequenceImpedance =
service.ensureGet(
resultSet.getNullableString(table.PER_LENGTH_SEQUENCE_IMPEDANCE_MRID.queryIndex),
typeNameAndMRID()
)
perLengthImpedance = service.ensureGet(resultSet.getNullableString(table.PER_LENGTH_IMPEDANCE_MRID.queryIndex), typeNameAndMRID())
}

return loadConductor(acLineSegment, table, resultSet) && service.addOrThrow(acLineSegment)
Expand Down Expand Up @@ -1969,6 +1969,55 @@ class NetworkCimReader(
private fun loadPerLengthLineParameter(perLengthLineParameter: PerLengthLineParameter, table: TablePerLengthLineParameters, resultSet: ResultSet): Boolean =
loadIdentifiedObject(perLengthLineParameter, table, resultSet)

/**
* Create a [PerLengthPhaseImpedance] and populate its fields from [TablePerLengthPhaseImpedances].
*
* @param table The database table to read the [PerLengthPhaseImpedance] fields from.
* @param resultSet The record in the database table containing the fields for this [PerLengthPhaseImpedance].
* @param setIdentifier A callback to register the mRID of this [PerLengthPhaseImpedance] for logging purposes.
*
* @return true if the [PerLengthPhaseImpedance] was successfully read from the database and added to the service.
* @throws SQLException For any errors encountered reading from the database.
*/
@Throws(SQLException::class)
fun load(table: TablePerLengthPhaseImpedances, resultSet: ResultSet, setIdentifier: (String) -> String): Boolean {
val perLengthPhaseImpedance = PerLengthPhaseImpedance(setIdentifier(resultSet.getString(table.MRID.queryIndex)))

return loadPerLengthImpedance(perLengthPhaseImpedance, table, resultSet) && service.addOrThrow(perLengthPhaseImpedance)
}

/**
* Create a [PhaseImpedanceData] and populate its fields from [TablePhaseImpedanceData] then add it to associated [PerLengthPhaseImpedance].
*
* @param table The database table to read the [PhaseImpedanceData] fields from.
* @param resultSet The record in the database table containing the fields for this [PhaseImpedanceData].
* @param setIdentifier A callback to register the mRID of this [PhaseImpedanceData] and its associated [PerLengthPhaseImpedance] for logging purposes.
*
* @return true if the [PhaseImpedanceData] was successfully read from the database and added to associated [PerLengthPhaseImpedance].
* @throws SQLException For any errors encountered reading from the database.
*/
@Throws(SQLException::class)
fun load(table: TablePhaseImpedanceData, resultSet: ResultSet, setIdentifier: (String) -> String): Boolean {
val perLengthPhaseImpedanceMRID = setIdentifier(resultSet.getString(table.PER_LENGTH_PHASE_IMPEDANCE_MRID.queryIndex))
val id = setIdentifier(perLengthPhaseImpedanceMRID)

val perLengthPhaseImpedance =
service.getOrThrow<PerLengthPhaseImpedance>(perLengthPhaseImpedanceMRID, "PerLengthPhaseImpedance to PhaseImpedanceData association $id")

perLengthPhaseImpedance.addData(
PhaseImpedanceData(
SinglePhaseKind.valueOf(resultSet.getString(table.FROM_PHASE.queryIndex)),
SinglePhaseKind.valueOf(resultSet.getString(table.TO_PHASE.queryIndex)),
resultSet.getNullableDouble(table.B.queryIndex),
resultSet.getNullableDouble(table.G.queryIndex),
resultSet.getNullableDouble(table.R.queryIndex),
resultSet.getNullableDouble(table.X.queryIndex),
)
)

return true
}

/**
* Create a [PerLengthSequenceImpedance] and populate its fields from [TablePerLengthSequenceImpedances].
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.scada.TableRem
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.scada.TableRemotePoints
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.scada.TableRemoteSources
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.*
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.*
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TableBatteryUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TablePhotoVoltaicUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TablePowerElectronicsUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires.generation.production.TablePowerElectronicsWindUnits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.infiec61970.feeder.TableCircuits
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.infiec61970.feeder.TableLoops
import com.zepben.evolve.database.sqlite.cim.tables.iec61970.infiec61970.feeder.TableLvFeeders
Expand All @@ -74,6 +77,7 @@ import com.zepben.evolve.database.sqlite.extensions.*
import com.zepben.evolve.services.network.NetworkService
import java.sql.PreparedStatement
import java.sql.SQLException
import kotlin.Throws

/**
* A class for writing the [NetworkService] tables to the database.
Expand Down Expand Up @@ -1474,10 +1478,7 @@ class NetworkCimWriter(
val table = databaseTables.getTable<TableAcLineSegments>()
val insert = databaseTables.getInsert<TableAcLineSegments>()

insert.setNullableString(
table.PER_LENGTH_SEQUENCE_IMPEDANCE_MRID.queryIndex,
acLineSegment.perLengthSequenceImpedance?.mRID
)
insert.setNullableString(table.PER_LENGTH_IMPEDANCE_MRID.queryIndex, acLineSegment.perLengthImpedance?.mRID)

return saveConductor(table, insert, acLineSegment, "AC line segment")
}
Expand Down Expand Up @@ -1839,6 +1840,43 @@ class NetworkCimWriter(
return saveIdentifiedObject(table, insert, perLengthLineParameter, description)
}

/**
* Save the [PerLengthPhaseImpedance] fields to [TablePerLengthPhaseImpedances].
*
* @param perLengthPhaseImpedance The [PerLengthPhaseImpedance] instance to write to the database.
*
* @return true if the [PerLengthPhaseImpedance] was successfully written to the database, otherwise false.
* @throws SQLException For any errors encountered writing to the database.
*/
@Throws(SQLException::class)
fun save(perLengthPhaseImpedance: PerLengthPhaseImpedance): Boolean {
val table = databaseTables.getTable<TablePerLengthPhaseImpedances>()
val insert = databaseTables.getInsert<TablePerLengthPhaseImpedances>()

var status = true
perLengthPhaseImpedance.data.forEach { phaseImpedanceData ->
status = status and savePhaseImpedanceData(perLengthPhaseImpedance, phaseImpedanceData)
}

return status and savePerLengthImpedance(table, insert, perLengthPhaseImpedance, "per length phase impedance")
}

@Throws(SQLException::class)
private fun savePhaseImpedanceData(perLengthPhaseImpedance: PerLengthPhaseImpedance, phaseImpedanceData: PhaseImpedanceData): Boolean {
val table = databaseTables.getTable<TablePhaseImpedanceData>()
val insert = databaseTables.getInsert<TablePhaseImpedanceData>()

insert.setNullableString(table.PER_LENGTH_PHASE_IMPEDANCE_MRID.queryIndex, perLengthPhaseImpedance.mRID)
insert.setString(table.FROM_PHASE.queryIndex, phaseImpedanceData.fromPhase.name)
insert.setString(table.TO_PHASE.queryIndex, phaseImpedanceData.toPhase.name)
insert.setNullableDouble(table.B.queryIndex, phaseImpedanceData.b)
insert.setNullableDouble(table.G.queryIndex, phaseImpedanceData.g)
insert.setNullableDouble(table.R.queryIndex, phaseImpedanceData.r)
insert.setNullableDouble(table.X.queryIndex, phaseImpedanceData.x)

return insert.tryExecuteSingleUpdate("phase impedance data")
}

/**
* Save the [PerLengthSequenceImpedance] fields to [TablePerLengthSequenceImpedances].
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ class NetworkDatabaseTables : CimDatabaseTables() {
TableOrganisations(),
TableOverheadWireInfo(),
TablePanDemandResponseFunctions(),
TablePerLengthPhaseImpedances(),
TablePerLengthSequenceImpedances(),
TablePhaseImpedanceData(),
TablePetersenCoils(),
TablePhotoVoltaicUnits(),
TablePoles(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class NetworkServiceReader @JvmOverloads constructor(
.andLoadEach<TableSubGeographicalRegions>(reader::load)
.andLoadEach<TableSubstations>(reader::load)
.andLoadEach<TableSites>(reader::load)
.andLoadEach<TablePerLengthPhaseImpedances>(reader::load)
.andLoadEach<TablePhaseImpedanceData>(reader::load)
.andLoadEach<TablePerLengthSequenceImpedances>(reader::load)
.andLoadEach<TableEquivalentBranches>(reader::load)
.andLoadEach<TableAcLineSegments>(reader::load)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class NetworkServiceWriter @JvmOverloads constructor(
.andSaveEach<Jumper>(writer::save)
.andSaveEach<Junction>(writer::save)
.andSaveEach<LinearShuntCompensator>(writer::save)
.andSaveEach<PerLengthPhaseImpedance>(writer::save)
.andSaveEach<PerLengthSequenceImpedance>(writer::save)
.andSaveEach<PowerElectronicsConnection>(writer::save)
.andSaveEach<PowerElectronicsConnectionPhase>(writer::save)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import com.zepben.evolve.database.sqlite.cim.tables.Column.Nullable.NULL
@Suppress("PropertyName")
class TableAcLineSegments : TableConductors() {

val PER_LENGTH_SEQUENCE_IMPEDANCE_MRID: Column =
Column(++columnIndex, "per_length_sequence_impedance_mrid", "TEXT", NULL)
val PER_LENGTH_IMPEDANCE_MRID: Column =
Column(++columnIndex, "per_length_impedance_mrid", "TEXT", NULL)

override val name: String = "ac_line_segments"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2024 Zeppelin Bend Pty Ltd
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package com.zepben.evolve.database.sqlite.cim.tables.iec61970.base.wires

class TablePerLengthPhaseImpedances : TablePerLengthImpedances() {

override val name: String = "per_length_phase_impedances"

}
Loading
Loading