diff --git a/src/main/python/powerFactory2json/pf2json.py b/src/main/python/powerFactory2json/pf2json.py index 6f7473a3..4fae5f49 100644 --- a/src/main/python/powerFactory2json/pf2json.py +++ b/src/main/python/powerFactory2json/pf2json.py @@ -28,7 +28,7 @@ def get_attribute_dicts(raw_elements, attributes_to_include): elements = [] single_node_connection = ["ElmLod", "ElmLodlv", "ElmLodmv", "ElmPvsys", "ElmSym", "ElmGenstat", "ElmXnet"] edges = ["ElmLne", "ElmCoup"] - typed_models = ["ElmLne", "ElmTr2"] + typed_models = ["ElmLne", "ElmTr2", "ElmLnesec"] for raw_element in raw_elements: element_class = raw_element.GetClassName() element = get_attribute_dict(raw_element, attributes_to_include) diff --git a/src/main/python/powerFactory2json/pf2jsonUtils.py b/src/main/python/powerFactory2json/pf2jsonUtils.py index 7f687463..22577e36 100644 --- a/src/main/python/powerFactory2json/pf2jsonUtils.py +++ b/src/main/python/powerFactory2json/pf2jsonUtils.py @@ -13,11 +13,15 @@ 'extGrid': '*.ElmXnet', 'powerPlants': '*.ElmSym', # renewable power plants 'pvs': '*.ElmPvsys', # additional photovoltaic units - 'switches': '*.ElmCoup' + 'switches': '*.ElmCoup', + 'lineSections': '*.ElmLnesec' } attributes4export = { + 'lineSections': [ + "dline" + ], 'conElms': [], 'nodes': [ "vtarget", @@ -27,7 +31,8 @@ ], 'lines': [ "dline", - "GPScoords" + "GPScoords", + "cubsecs" ], 'lineTypes': [ "rline", diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/LineConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/LineConverter.scala index a6bdb667..e16d1f65 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/LineConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/LineConverter.scala @@ -13,6 +13,7 @@ import edu.ie3.datamodel.models.input.connector.`type`.LineTypeInput import edu.ie3.datamodel.models.input.system.characteristic.OlmCharacteristicInput import edu.ie3.datamodel.utils.GridAndGeoUtils import edu.ie3.powerFactory2psdm.converter.NodeConverter.getNode +import edu.ie3.powerFactory2psdm.converter.types.LineTypeConverter import edu.ie3.powerFactory2psdm.converter.types.LineTypeConverter.getLineType import edu.ie3.powerFactory2psdm.exception.pf.ConversionException import edu.ie3.powerFactory2psdm.model.entity.Line @@ -44,34 +45,51 @@ object LineConverter { lineTypes: Map[String, LineTypeInput] ): List[LineInput] = { lines.map(line => { + /* + Here we check if this is a line with line section and therefore multiple line types in which case we create an + averaged line type. If it isn't we just retrieve the line sections from the converted line types. + */ + val lineType = (line.typeId, line.lineSections) match { + case (_, Some(lineSections)) => + LineTypeConverter.convert( + line.id, + line.length, + lineSections, + lineTypes + ) + case (Some(lineTypeId), None) => + getLineType(lineTypeId, lineTypes).getOrElse( + throw ConversionException( + s"Could not convert line: $line due to failed line type retrieval." + ) + ) + case (None, None) => + throw ConversionException( + s"Could not convert line: ${line.id} since there is no defined type in the model and there are no line section that specify the type" + ) + } ( getNode(line.nodeAId, nodes), - getNode(line.nodeBId, nodes), - getLineType(line.typeId, lineTypes) + getNode(line.nodeBId, nodes) ) match { - case (Success(nodeA), Success(nodeB), Success(lineType)) => - convert( + case (Success(nodeA), Success(nodeB)) => + LineConverter.convert( line, lineLengthPrefix, lineType, nodeA, nodeB ) - case (Failure(exc), _, _) => + case (Failure(exc), _) => throw ConversionException( s"Can't retrieve ${line.nodeAId} for line ${line.id}", exc ) - case (_, Failure(exc), _) => + case (_, Failure(exc)) => throw ConversionException( s"Can't retrieve ${line.nodeBId} for line ${line.id}", exc ) - case (_, _, Failure(exc)) => - throw ConversionException( - s"Could not convert line: $line due to failed line type retrieval.", - exc - ) } }) } diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala index 0f144f88..b4ce511f 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala @@ -5,17 +5,20 @@ */ package edu.ie3.powerFactory2psdm.converter.types +import com.typesafe.scalalogging.LazyLogging import edu.ie3.datamodel.models.input.connector.`type`.LineTypeInput import edu.ie3.powerFactory2psdm.exception.pf.ConversionException +import edu.ie3.powerFactory2psdm.model.entity.{Line, LineSection} import edu.ie3.powerFactory2psdm.model.entity.types.LineType import edu.ie3.powerFactory2psdm.util.QuantityUtils.RichQuantityDouble import java.util.UUID +import scala.math.abs import scala.util.{Failure, Success, Try} /** Functionality to translate a [[LineType]] to a [[LineTypeInput]] */ -object LineTypeConverter { +object LineTypeConverter extends LazyLogging { def convert(input: LineType): LineTypeInput = { @@ -31,6 +34,85 @@ object LineTypeConverter { ) } + /** In PowerFactory lines can be made up of line sections. We convert them to + * a single line by using the aggregated length of the line sections and + * generate a line type by calculating the weighted (by line length) average + * of the parameters. + * + * @param lineId + * name of the line made up of line sections + * @param lineLength + * the noted length of the line + * @param lineSections + * a list of the line sections + * @param lineTypes + * mapping of line types + * @return + * the averaged line type as [[LineTypeInput]] + */ + def convert( + lineId: String, + lineLength: Double, + lineSections: List[LineSection], + lineTypes: Map[String, LineTypeInput] + ): LineTypeInput = { + + val aggregatedLineSectionLength = + lineSections.map(section => section.length).sum + + // sanity check of total line length versus aggregated line length of all corresponding line sections + lineLength - aggregatedLineSectionLength match { + case x if abs(x) < 1e-9 => + case x if x < 0 => + logger.error( + s"The line length of line: $lineId is smaller than the aggregated length of line sections by ${(1 - (lineLength / aggregatedLineSectionLength)) * 100}% which distorts results. This should be prevented by PF and therefore not happen." + ) + case x if x > 0 => + logger.error( + s"The line length of line: $lineId is greater than the aggregated length of line sections by ${((lineLength / aggregatedLineSectionLength) - 1) * 100}% which distorts results. This should be prevented by PF and therefore not happen." + ) + } + + val weightedLineTypes = lineSections.map(section => { + val lineType = getLineType(section.typeId, lineTypes) + .getOrElse( + throw ConversionException( + s"Can't find line type ${section.typeId} of section ${section.id} within the converted line types." + ) + ) + (section.length, lineType) + }) + val emptyLineType = new LineTypeInput( + UUID.randomUUID(), + "Custom_line_type_" + lineId, + 0.asMicroSiemensPerKilometre, + 0.asMicroSiemensPerKilometre, + 0.asOhmPerKilometre, + 0.asOhmPerKilometre, + Double.MaxValue.asKiloAmpere, + Double.MaxValue.asKiloVolt + ) + + weightedLineTypes.foldLeft(emptyLineType)((averageType, current) => { + val currentLine = current._2 + val weightingFactor = current._1 / lineLength + new LineTypeInput( + averageType.getUuid, + averageType.getId, + averageType.getB.add(currentLine.getB.multiply(weightingFactor)), + averageType.getG.add(currentLine.getG.multiply(weightingFactor)), + averageType.getR.add(currentLine.getR.multiply(weightingFactor)), + averageType.getX.add(currentLine.getX.multiply(weightingFactor)), + if (averageType.getiMax().isLessThan(currentLine.getiMax())) + averageType.getiMax() + else currentLine.getiMax(), + if (averageType.getvRated().equals(Double.MaxValue.asKiloVolt)) + currentLine.getvRated() + else averageType.getvRated() + ) + }) + } + def getLineType( id: String, lineTypes: Map[String, LineTypeInput] diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala index dd6a15e4..cd36b3a3 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala @@ -14,6 +14,7 @@ import edu.ie3.powerFactory2psdm.exception.pf.{ MissingParameterException } import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ + LineSections, LineTypes, Lines, Loads, @@ -28,6 +29,7 @@ import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ } import edu.ie3.powerFactory2psdm.model.entity.{ Line, + LineSection, Load, Node, StaticGenerator, @@ -108,6 +110,10 @@ object PreprocessedPfGridModel extends LazyLogging { logger debug "There are no lines in the grid." List.empty[LineTypes] }) + val rawLineSections = rawGrid.lineSections.getOrElse({ + logger debug "There are no line sections in the grid." + List.empty[LineSections] + }) val rawSwitches = rawGrid.switches.getOrElse({ logger debug "There are no switches in the grid." List.empty[Switches] @@ -129,7 +135,7 @@ object PreprocessedPfGridModel extends LazyLogging { }) val models = - rawNodes ++ rawLines ++ rawLineTypes ++ rawSwitches ++ rawTrafos2W ++ rawTrafoTpyes2W ++ rawLoads ++ rawStaticGenerators + rawNodes ++ rawLines ++ rawLineTypes ++ rawLineSections ++ rawSwitches ++ rawTrafos2W ++ rawTrafoTpyes2W ++ rawLoads ++ rawStaticGenerators val modelIds = models.map { case node: Nodes => node.id.getOrElse( @@ -145,6 +151,12 @@ object PreprocessedPfGridModel extends LazyLogging { s"Line type $lineType has no defined id" ) ) + case lineSection: LineSections => + lineSection.id.getOrElse( + throw MissingParameterException( + s"Line section $lineSection has no defined id" + ) + ) case switch: Switches => switch.id.getOrElse( throw MissingParameterException(s"Switch $switch has no defined id") @@ -195,7 +207,8 @@ object PreprocessedPfGridModel extends LazyLogging { } val nodes = rawNodes.map(Node.build) - val lines = rawLines.map(line => Line.build(line)) + val lineSectionsMap = LineSection.buildLineSectionMap(rawLineSections) + val lines = rawLines.map(line => Line.build(line, lineSectionsMap)) val lineTypes = rawLineTypes.map(LineType.build) val switches = rawSwitches.flatMap(Switch.maybeBuild) val transformers2W = rawTrafos2W.map(Transformer2W.build) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala index 43bcfe67..8c68949a 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala @@ -9,6 +9,7 @@ import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ ExtGrid, PowerPlants, Trafos3w, + LineSections, Lines, Pvs, Switches, @@ -29,6 +30,7 @@ final case class RawPfGridModel( loadsMV: Option[List[LoadsMV]], nodes: Option[List[Nodes]], projectSettings: Option[List[ProjectSettings]], + lineSections: Option[List[LineSections]], powerPlants: Option[List[PowerPlants]], trafoTypes3w: Option[List[TrafoTypes3w]], pvs: Option[List[Pvs]], @@ -52,8 +54,6 @@ object RawPfGridModel { bus2Id: Option[String] ) - final case class Pvs() - final case class ConElms(id: Option[String], pfCls: Option[String]) final case class Loads( @@ -102,6 +102,14 @@ object RawPfGridModel { final case class ExtGrid(id: Option[String], busId: Option[String]) + final case class LineSections( + id: Option[String], + dline: Option[Double], + typeId: Option[String] + ) + + final case class Pvs() + final case class TrafoTypes2w( utrnH: Option[Double], nntap0: Option[Double], diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala index 5fdf1a34..c831fb9c 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala @@ -17,19 +17,31 @@ import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines * id of connected node * @param nodeBId * id of connected node + * @param typeId + * id of the corresponding line type + * @param lineSections + * optional list of line sections the line consists of + * @param length + * length of the line + * @param gpsCoords + * optional list of gps coordinates of geo position of the line */ final case class Line( id: String, nodeAId: String, nodeBId: String, - typeId: String, + typeId: Option[String], + lineSections: Option[List[LineSection]], length: Double, - gpsCoords: Option[(List[(Double, Double)])] + gpsCoords: Option[List[(Double, Double)]] ) extends EntityModel with Edge object Line { - def build(rawLine: Lines): Line = { + def build( + rawLine: Lines, + lineSectionsMap: Map[String, List[LineSection]] + ): Line = { val id = rawLine.id.getOrElse( throw MissingParameterException(s"There is no id for line $rawLine") ) @@ -39,12 +51,8 @@ object Line { val nodeBId = rawLine.bus2Id.getOrElse( throw MissingParameterException(s"Line: $id has no defined node b") ) - val typId = rawLine.typeId.getOrElse( - throw MissingParameterException( - s"Line: $id has no defined type - line conversion without defined type" + - s" is not supported " - ) - ) + val typId = rawLine.typeId + val lineSections = lineSectionsMap.get(id) val length = rawLine.dline.getOrElse( throw MissingParameterException( s"Line: $id has no defined length" @@ -65,6 +73,7 @@ object Line { nodeAId, nodeBId, typId, + lineSections, length, gpsCoords ) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/LineSection.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/LineSection.scala new file mode 100644 index 00000000..ef4f1aa7 --- /dev/null +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/LineSection.scala @@ -0,0 +1,69 @@ +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.model.entity + +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + MissingParameterException +} +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineSections +import org.apache.commons.lang3.Conversion + +/** A section of an electrical [[Line]] + * + * @param id + * its identifier + * @param length + * its length + * @param typeId + * the identifier of its type + */ +final case class LineSection( + id: String, + length: Double, + typeId: String +) extends EntityModel + +object LineSection { + + def build(rawLineSection: LineSections): LineSection = { + val id = rawLineSection.id.getOrElse( + throw MissingParameterException( + s"There is no id for line section $rawLineSection" + ) + ) + val length = rawLineSection.dline.getOrElse( + throw MissingParameterException( + s"There is no defined length for line section $id" + ) + ) + val typeId = rawLineSection.typeId.getOrElse( + throw MissingParameterException( + s"There is no defined type for line section $id" + ) + ) + LineSection(id, length, typeId) + } + + def buildLineSectionMap( + rawLineSections: List[LineSections] + ): Map[String, List[LineSection]] = { + val lineSections = rawLineSections.map(build) + lineSections.groupBy(lineSection => getLineId(lineSection.id)) + } + + private def getLineId(lineSectionId: String): String = { + val lineIdRegEx = raw".+?(?=\\[\w\s]*\.ElmLnesec)".r + lineIdRegEx.findFirstIn(lineSectionId) match { + case Some(id) => id + case None => + throw ConversionException( + s"Can't extract line id from line section: $lineSectionId" + ) + } + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/QuantityUtils.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/QuantityUtils.scala index 74977d64..d2b37518 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/QuantityUtils.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/QuantityUtils.scala @@ -71,7 +71,7 @@ object QuantityUtils { def asMicroSiemensPerKilometre: ComparableQuantity[SpecificConductance] = Quantities.getQuantity( value, - MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) + MICRO_SIEMENS_PER_KILOMETRE ) def asOhm: ComparableQuantity[ElectricResistance] = Quantities.getQuantity( @@ -98,6 +98,12 @@ object QuantityUtils { MetricPrefix.MEGA(VOLTAMPERE) ) + def asAmpere: ComparableQuantity[ElectricCurrent] = + Quantities.getQuantity( + value, + AMPERE + ) + def asKiloAmpere: ComparableQuantity[ElectricCurrent] = Quantities.getQuantity( value, diff --git a/src/test/resources/pfGrids/exampleGrid.json b/src/test/resources/pfGrids/exampleGrid.json index bf59880c..05539667 100644 --- a/src/test/resources/pfGrids/exampleGrid.json +++ b/src/test/resources/pfGrids/exampleGrid.json @@ -439,11 +439,11 @@ "vtarget": 1.0, "conElms": [ { - "id": "Grid.ElmNet\\LS/TR Schalter(1).ElmCoup", + "id": "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\S1.ElmCoup", "pfCls": "ElmCoup" }, { - "id": "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\S1.ElmCoup", + "id": "Grid.ElmNet\\LS/TR Schalter(1).ElmCoup", "pfCls": "ElmCoup" } ] @@ -481,6 +481,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0002.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0005.ElmTerm", @@ -491,6 +492,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0010.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0011.ElmTerm", @@ -501,6 +503,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0013.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0014.ElmTerm", @@ -511,6 +514,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0002.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0003.ElmTerm", @@ -521,6 +525,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0013.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0006.ElmTerm", @@ -531,6 +536,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0005.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0004.ElmTerm", @@ -541,6 +547,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0011.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0006.ElmTerm", @@ -551,6 +558,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0006.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0012.ElmTerm", @@ -561,6 +569,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0009.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0010.ElmTerm", @@ -571,6 +580,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm", @@ -592,6 +602,7 @@ 13.8273 ] ], + "cubsecs": [], "dline": 1.7364000082015991, "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm", @@ -613,6 +624,7 @@ 3.0 ] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm", @@ -634,6 +646,7 @@ 3.0 ] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0002.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0004.ElmTerm", @@ -655,6 +668,7 @@ 3.0 ] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0003.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0004.ElmTerm", @@ -676,6 +690,7 @@ 3.0 ] ], + "cubsecs": [], "dline": 1.0, "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0005.ElmTerm", @@ -686,6 +701,7 @@ "GPScoords": [ [] ], + "cubsecs": [], "dline": 0.30000001192092896, "bus1Id": "Grid.ElmNet\\Bus_0012.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0013.ElmTerm", @@ -1192,5 +1208,22 @@ "bus1Id": "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\1.ElmTerm", "bus2Id": "Grid.ElmNet\\Bus_0015.ElmTerm" } + ], + "lineSections": [ + { + "id": "Grid.ElmNet\\Line_0001_0005.ElmLne\\Leitungssektion 1.ElmLnesec", + "dline": 0.5, + "typeId": "TypLne 0001 to 0005.TypLne" + }, + { + "id": "Grid.ElmNet\\Line_0001_0005.ElmLne\\Leitungssektion 2.ElmLnesec", + "dline": 0.5, + "typeId": "TypLne 0002 to 0004.TypLne" + }, + { + "id": "Grid.ElmNet\\Line_0012_0013.ElmLne\\Leitungssektion.ElmLnesec", + "dline": 0.30000001192092896, + "typeId": "TypLne 0009 to 0010.TypLne" + } ] } \ No newline at end of file diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala b/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala index ecc61af7..a62f206f 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala @@ -493,7 +493,8 @@ object ConverterTestData extends LazyLogging { "someLine", "someNode", "someSlackNode", - "someLineType", + Some("someLineType"), + None, 1.5, Some(List((11.1123, 52.1425), (11.1153, 52.1445))) ), diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala index 68355149..f44b1d12 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala @@ -6,10 +6,16 @@ package edu.ie3.powerFactory2psdm.converter.types +import edu.ie3.datamodel.models.input.connector.`type`.LineTypeInput import edu.ie3.powerFactory2psdm.common.ConverterTestData.getLineTypePair +import edu.ie3.powerFactory2psdm.model.entity.LineSection +import edu.ie3.powerFactory2psdm.util.QuantityUtils.RichQuantityDouble +import edu.ie3.scalatest.QuantityMatchers.equalWithTolerance import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike +import java.util.UUID + class LineTypeConverterSpec extends Matchers with AnyWordSpecLike { "The line type converter" should { @@ -26,5 +32,53 @@ class LineTypeConverterSpec extends Matchers with AnyWordSpecLike { actual.getiMax shouldBe expected.getiMax actual.getvRated shouldBe expected.getvRated } + + "generate a line type from line sections" in { + val lineTypes = Map( + "lineTypeA" -> new LineTypeInput( + UUID.randomUUID(), + "lineTypeA", + 1.asMicroSiemensPerKilometre, + 1.asMicroSiemensPerKilometre, + 1.asOhmPerKilometre, + 1.asOhmPerKilometre, + 1.asKiloAmpere, + 20.asKiloVolt + ), + "lineTypeB" -> new LineTypeInput( + UUID.randomUUID(), + "lineTypeB", + 2.asMicroSiemensPerKilometre, + 2.asMicroSiemensPerKilometre, + 2.asOhmPerKilometre, + 2.asOhmPerKilometre, + 2.asKiloAmpere, + 20.asKiloVolt + ) + ) + val lineSections = List( + LineSection( + id = "lineSectionA", + length = 1, + typeId = "lineTypeA" + ), + LineSection( + id = "lineSectionB", + length = 1, + typeId = "lineTypeB" + ) + ) + + implicit val quantityTolerance: Double = 1e-6 + + val actual = + LineTypeConverter.convert("testLine", 2, lineSections, lineTypes) + actual.getB should equalWithTolerance(1.5.asMicroSiemensPerKilometre) + actual.getG should equalWithTolerance(1.5.asMicroSiemensPerKilometre) + actual.getR should equalWithTolerance(1.5.asOhmPerKilometre) + actual.getX should equalWithTolerance(1.5.asOhmPerKilometre) + actual.getiMax should equalWithTolerance(1000.asAmpere) + actual.getvRated should equalWithTolerance(20.asKiloVolt) + } } } diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala index 196bc605..8b8702e4 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala @@ -32,26 +32,26 @@ class LineSpec extends Matchers with AnyWordSpecLike { "throw an exception if the id is missing" in { val line = input.copy(id = None) - val exc = intercept[MissingParameterException](Line.build(line)) + val exc = intercept[MissingParameterException](Line.build(line, Map())) exc.getMessage shouldBe s"There is no id for line $line" } "throw an exception if the bus1Id is missing" in { val id = "BrokenLine1.ElmLne" val line = input.copy(id = Some(id), bus1Id = None) - val exc = intercept[MissingParameterException](Line.build(line)) + val exc = intercept[MissingParameterException](Line.build(line, Map())) exc.getMessage shouldBe s"Line: $id has no defined node a" } "throw an exception if the bus2Id is missing" in { val id = "BrokenLine2.ElmLne" val line = input.copy(id = Some(id), bus2Id = None) - val exc = intercept[MissingParameterException](Line.build(line)) + val exc = intercept[MissingParameterException](Line.build(line, Map())) exc.getMessage shouldBe s"Line: $id has no defined node b" } "build a fully configured line correctly" in { - val line = Line.build(input) + val line = Line.build(input, Map()) line.id shouldBe "SomeLine.ElmLne" line.nodeAId shouldBe "SomeBusA" line.nodeBId shouldBe "SomeBusB" diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/util/QuantityUtilsSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/util/QuantityUtilsSpec.scala index 71bff958..767aaef8 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/util/QuantityUtilsSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/util/QuantityUtilsSpec.scala @@ -16,6 +16,7 @@ import edu.ie3.util.quantities.PowerSystemUnits.{ KILOVOLT, KILOWATT, KILOWATTHOUR, + MICRO_SIEMENS_PER_KILOMETRE, OHM_PER_KILOMETRE, PERCENT_PER_HOUR, PU, @@ -74,7 +75,7 @@ class QuantityUtilsSpec extends Matchers with AnyWordSpecLike { "convert a double to micro siemens per kilometre" in { value.asMicroSiemensPerKilometre should equalWithTolerance( - Quantities.getQuantity(value, MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE)) + Quantities.getQuantity(value, MICRO_SIEMENS_PER_KILOMETRE) ) } @@ -120,6 +121,15 @@ class QuantityUtilsSpec extends Matchers with AnyWordSpecLike { ) } + "convert a double to ampere" in { + value.asAmpere should equalWithTolerance( + Quantities.getQuantity( + value, + AMPERE + ) + ) + } + "convert a double to kilo ampere" in { value.asKiloAmpere should equalWithTolerance( Quantities.getQuantity(