From d0efd549475ee8a431cc0088b82ea3f530751ca9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 04:15:52 +0000 Subject: [PATCH 1/8] Bump scalafmt-interfaces from 3.0.1 to 3.0.2 Bumps [scalafmt-interfaces](https://github.com/scalameta/scalafmt) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/scalameta/scalafmt/releases) - [Changelog](https://github.com/scalameta/scalafmt/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/scalameta/scalafmt/compare/v3.0.1...v3.0.2) --- updated-dependencies: - dependency-name: org.scalameta:scalafmt-interfaces dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 25581887..ce2ee79e 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,7 @@ dependencies { implementation "io.circe:circe-parser_${scalaVersion}:${circeVersion}" // https://mvnrepository.com/artifact/org.scalameta/scalafmt-interfaces - implementation group: 'org.scalameta', name: 'scalafmt-interfaces', version: '3.0.1' + implementation group: 'org.scalameta', name: 'scalafmt-interfaces', version: '3.0.2' // https://mvnrepository.com/artifact/org.scalameta/scalafmt-dynamic implementation group: 'org.scalameta', name: "scalafmt-dynamic_${scalaVersion}", version: '3.0.1' From ce98db0c4e0dacb22520d42f257d1f0f11500343 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 04:16:04 +0000 Subject: [PATCH 2/8] Bump scalafmt-dynamic_2.13 from 3.0.1 to 3.0.2 Bumps [scalafmt-dynamic_2.13](https://github.com/scalameta/scalafmt) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/scalameta/scalafmt/releases) - [Changelog](https://github.com/scalameta/scalafmt/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/scalameta/scalafmt/compare/v3.0.1...v3.0.2) --- updated-dependencies: - dependency-name: org.scalameta:scalafmt-dynamic_2.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 25581887..04a6f597 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ dependencies { // https://mvnrepository.com/artifact/org.scalameta/scalafmt-interfaces implementation group: 'org.scalameta', name: 'scalafmt-interfaces', version: '3.0.1' // https://mvnrepository.com/artifact/org.scalameta/scalafmt-dynamic - implementation group: 'org.scalameta', name: "scalafmt-dynamic_${scalaVersion}", version: '3.0.1' + implementation group: 'org.scalameta', name: "scalafmt-dynamic_${scalaVersion}", version: '3.0.2' implementation('com.github.ie3-institute:PowerSystemDataModel:2.0.1') { From 55ca9feb5cddb5f4e1ec9523cc0adf8160635396 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Sep 2021 05:21:01 +0000 Subject: [PATCH 3/8] Bump com.diffplug.spotless from 5.14.3 to 5.15.0 Bumps com.diffplug.spotless from 5.14.3 to 5.15.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ce2ee79e..26244a80 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'java' // java support id 'scala' // scala support id 'pmd' //code check, working on source code - id 'com.diffplug.spotless' version '5.14.3'//code format + id 'com.diffplug.spotless' version '5.15.0'//code format id "org.sonarqube" version "3.3" // sonarqube id 'org.scoverage' version '7.0.0' // Code coverage plugin for scala id "com.github.maiflai.scalatest" version "0.31" // run scalatest without specific spec task From bc367e07c66fcd6fb673e248ce5b73a9c39e3e19 Mon Sep 17 00:00:00 2001 From: t-ober <63147366+t-ober@users.noreply.github.com> Date: Tue, 24 Aug 2021 11:13:11 +0200 Subject: [PATCH 4/8] fix windows path issue --- .../scala/edu/ie3/powerFactory2psdm/util/Formatter.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala index 0ae5d0dd..a29db96d 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala @@ -9,13 +9,16 @@ package edu.ie3.powerFactory2psdm.util import java.nio.file.{Files, Paths} import org.scalafmt.interfaces.Scalafmt +import java.io.File + object Formatter { def format(str: String, fmtPath: Option[String]): String = { val classLoader = this.getClass.getClassLoader val scalafmt = Scalafmt.create(classLoader) + val configFile = new File(classLoader.getResource(".scalafmt.conf").getFile) val defaultConfigPath = - Paths.get(classLoader.getResource(".scalafmt.conf").getPath) + Paths.get(configFile.getPath) val defaultConfig = if (Files.exists(defaultConfigPath)) defaultConfigPath else Paths.get("") val config = fmtPath.fold(defaultConfig)(Paths.get(_)) From 4a9cffb62435bea2853776fb8a5ca6d4077a2bc0 Mon Sep 17 00:00:00 2001 From: t-ober <63147366+t-ober@users.noreply.github.com> Date: Wed, 8 Sep 2021 13:21:56 +0200 Subject: [PATCH 5/8] fmt --- CHANGELOG.md | 28 +- CONTRIBUTING.md | 146 +-- README.md | 10 +- build.gradle | 286 ++--- gradle/scripts/checkJavaVersion.gradle | 28 +- gradle/scripts/pmd.gradle | 14 +- gradle/scripts/scoverage.gradle | 24 +- gradle/scripts/sonarqube.gradle | 70 +- gradle/scripts/spotless.gradle | 90 +- settings.gradle | 20 +- .../config/ConfigValidator.scala | 300 ++--- .../config/ConversionConfig.scala | 114 +- .../converter/CoordinateConverter.scala | 72 +- .../converter/GridConverter.scala | 102 +- .../converter/GridGraphBuilder.scala | 128 +- .../converter/NodeConverter.scala | 142 +-- .../converter/SubnetBuilder.scala | 190 +-- .../converter/types/LineTypeConverter.scala | 126 +- .../types/TransformerType2WConverter.scala | 174 +-- .../io/ConversionConfigException.scala | 24 +- .../exception/io/GridParsingException.scala | 24 +- .../exception/io/IoException.scala | 28 +- .../exception/pf/ConversionException.scala | 24 +- .../pf/ElementConfigurationException.scala | 24 +- .../pf/GridConfigurationException.scala | 24 +- .../pf/MissingGridElementException.scala | 24 +- .../pf/MissingParameterException.scala | 24 +- .../exception/pf/PfException.scala | 28 +- .../exception/pf/TestException.scala | 24 +- .../powerFactory2psdm/io/PfGridParser.scala | 70 +- .../main/RunConversion.scala | 82 +- .../model/PreprocessedPfGridModel.scala | 406 +++---- .../model/RawPfGridModel.scala | 256 ++-- .../model/entity/ConnectedElement.scala | 76 +- .../powerFactory2psdm/model/entity/Edge.scala | 28 +- .../model/entity/EntityModel.scala | 32 +- .../powerFactory2psdm/model/entity/Line.scala | 92 +- .../powerFactory2psdm/model/entity/Node.scala | 150 +-- .../model/entity/Subnet.scala | 40 +- .../model/entity/Switch.scala | 134 +- .../model/entity/types/LineType.scala | 178 +-- .../entity/types/TransformerType2W.scala | 332 ++--- .../model/setting/ConversionPrefixes.scala | 106 +- .../model/setting/UnitSystem.scala | 32 +- .../powerFactory2psdm/util/Formatter.scala | 63 +- .../util/GridPreparator.scala | 100 +- .../util/SchemaGenerator.scala | 840 ++++++------- .../common/ConverterTestData.scala | 642 +++++----- .../config/ConfigValidatorSpec.scala | 234 ++-- .../converter/CoordinateConverterSpec.scala | 86 +- .../converter/GridGraphBuilderSpec.scala | 150 +-- .../converter/NodeConverterSpec.scala | 104 +- .../converter/SubnetBuilderSpec.scala | 130 +- .../types/LineTypeConverterSpec.scala | 64 +- .../TransformerType2WConverterSpec.scala | 134 +- .../model/PreprocessedPfGridModelSpec.scala | 188 +-- .../model/entity/LineSpec.scala | 108 +- .../model/entity/NodeSpec.scala | 150 +-- .../model/entity/SwitchSpec.scala | 114 +- .../model/types/LineTypeSpec.scala | 150 +-- .../model/types/TransformerType2WSpec.scala | 340 +++--- .../util/GridPreparatorSpec.scala | 90 +- .../util/SchemaGeneratorIT.scala | 134 +- .../util/SchemaGeneratorSpec.scala | 1076 ++++++++--------- .../edu/ie3/scalatest/QuantityMatchers.scala | 86 +- 65 files changed, 4654 insertions(+), 4655 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9014d2f..63020fa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,14 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased / SNAPSHOT] - -### Added - -### Changed - -[Unreleased / SNAPSHOT]: https://github.com/ie3-institute/powerFactory2psdm/compare/v1.0...HEAD -[v1.0]: https://github.com/ie3-institute/powerFactory2psdm/releases/tag/v1.0 +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased / SNAPSHOT] + +### Added + +### Changed + +[Unreleased / SNAPSHOT]: https://github.com/ie3-institute/powerFactory2psdm/compare/v1.0...HEAD +[v1.0]: https://github.com/ie3-institute/powerFactory2psdm/releases/tag/v1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff987f35..6cb8ebec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,73 +1,73 @@ -# Contributing to powerFactory2psdm -Welcome dear fellow of sophisticated power system modelling! :wave: -And thank you for considering your contribution to this project! -With this document we would like to give you some orientation on how you can contribute. - -## Table of contents -* [Testing and reporting bugs](#testing-and-reporting-bugs) -* [Suggest extensions](#suggest-extensions) -* [Contributing code](#contributing-code) -* [Branching and handing in pull requests](#branching-and-handing-in-pull-requests) -* [General (software) design guidelines](#general-software-design-guidelines) -* [Testing](#testing) -* [Finalising your pull request](#finalising-your-pull-request) -* [For any doubts](#for-any-doubts) - -## Testing and reporting bugs -We really appreciate your usage of this project. -Whenever you find a bug, it would be nice to check, if this isn't a feature to us. :wink: -Currently, we are lacking sophisticated documentation, but we will work on it. -If you still think it's a bug, please raise an [issue](https://guides.github.com/features/issues/) for us. -Considering the following aspects in your inquiry, assists us in helping you: - -* **Is there already an issue addressing your problem?** -* Try to **locate the error** as precise as possible. -* What has to be done to **reproduce the error**? -* **Provide stack trace, logs etc.** and further helpful information -* **What would do you expect to happen?** -* Mark the issue with the **label _bug_**. - -## Suggest extensions -We use issues as well to keep track of enhancement suggestions. -Considering the following aspects, assists us in understanding your needs properly: - -* **Is there already an issue addressing your request?** -* **What would do you desire for?** -* If possible provide an **example or sketch**. -* Show a **use case**, that should be as versatile as possible. -* Mark the issue with the **label _enhancement_**. - -## Contributing code -If you intend to produce some lines of code, pick an issue and get some hands on! - -### Branching and handing in pull requests -We try to follow a branch naming strategy of the form `/#-`. -If for example [Prof. Dr. rer. hort. Klaus-Dieter Brokkoli](https://www.instagram.com/prof_broccoli/) would like to add some work on node models reported in issue 4711, he would open a branch `kb/#4711-extendingNodeModels`. -Please hand in a _draft_ pull request as early as possible to allow other to keep track on your changes. -Before opening it for review, please [finalise your pull request](#finalising-your-pull-request). - -### General (software) design guidelines -In order to maintain a consistent project, we thought of some general design guidlines, we kindly ask you to take care of: - -* We :heart: **immutability**. Therefore, please don't provide setters and use proper instantiation instead. -* `double a = b * pow(x, j)`? :hand: Please **be expressive** in what you code! -* Document your code with **javadoc**. - -### Testing -Ensure the proper function of your code by [test driven development (TDD)](https://www.guru99.com/test-driven-development.html). - -### Finalising your pull request -Some automated checks assist us in delivering a pretty fair quality of software. -Before marking the pull request as 'ready to review', take these precautionary actions: - -* Are all tests passing? -* Is your code properly formatted? - -## For any doubts -... please contact -* Chris (@ckittl), -* Johannes (@johanneshiry), -* Thomas (@t-ober) or -* Debopama (@sensarmad) - -We are happy to help! :smiley: +# Contributing to powerFactory2psdm +Welcome dear fellow of sophisticated power system modelling! :wave: +And thank you for considering your contribution to this project! +With this document we would like to give you some orientation on how you can contribute. + +## Table of contents +* [Testing and reporting bugs](#testing-and-reporting-bugs) +* [Suggest extensions](#suggest-extensions) +* [Contributing code](#contributing-code) +* [Branching and handing in pull requests](#branching-and-handing-in-pull-requests) +* [General (software) design guidelines](#general-software-design-guidelines) +* [Testing](#testing) +* [Finalising your pull request](#finalising-your-pull-request) +* [For any doubts](#for-any-doubts) + +## Testing and reporting bugs +We really appreciate your usage of this project. +Whenever you find a bug, it would be nice to check, if this isn't a feature to us. :wink: +Currently, we are lacking sophisticated documentation, but we will work on it. +If you still think it's a bug, please raise an [issue](https://guides.github.com/features/issues/) for us. +Considering the following aspects in your inquiry, assists us in helping you: + +* **Is there already an issue addressing your problem?** +* Try to **locate the error** as precise as possible. +* What has to be done to **reproduce the error**? +* **Provide stack trace, logs etc.** and further helpful information +* **What would do you expect to happen?** +* Mark the issue with the **label _bug_**. + +## Suggest extensions +We use issues as well to keep track of enhancement suggestions. +Considering the following aspects, assists us in understanding your needs properly: + +* **Is there already an issue addressing your request?** +* **What would do you desire for?** +* If possible provide an **example or sketch**. +* Show a **use case**, that should be as versatile as possible. +* Mark the issue with the **label _enhancement_**. + +## Contributing code +If you intend to produce some lines of code, pick an issue and get some hands on! + +### Branching and handing in pull requests +We try to follow a branch naming strategy of the form `/#-`. +If for example [Prof. Dr. rer. hort. Klaus-Dieter Brokkoli](https://www.instagram.com/prof_broccoli/) would like to add some work on node models reported in issue 4711, he would open a branch `kb/#4711-extendingNodeModels`. +Please hand in a _draft_ pull request as early as possible to allow other to keep track on your changes. +Before opening it for review, please [finalise your pull request](#finalising-your-pull-request). + +### General (software) design guidelines +In order to maintain a consistent project, we thought of some general design guidlines, we kindly ask you to take care of: + +* We :heart: **immutability**. Therefore, please don't provide setters and use proper instantiation instead. +* `double a = b * pow(x, j)`? :hand: Please **be expressive** in what you code! +* Document your code with **javadoc**. + +### Testing +Ensure the proper function of your code by [test driven development (TDD)](https://www.guru99.com/test-driven-development.html). + +### Finalising your pull request +Some automated checks assist us in delivering a pretty fair quality of software. +Before marking the pull request as 'ready to review', take these precautionary actions: + +* Are all tests passing? +* Is your code properly formatted? + +## For any doubts +... please contact +* Chris (@ckittl), +* Johannes (@johanneshiry), +* Thomas (@t-ober) or +* Debopama (@sensarmad) + +We are happy to help! :smiley: diff --git a/README.md b/README.md index 10ca5eb5..7de92d47 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![License](https://img.shields.io/github/license/ie3-institute/powerFactory2psdm)](https://github.com/ie3-institute/powerFactory2psdm/blob/main/LICENSE) - -# powerFactory2psdm - -Convert PowerFactory projects to [PowerSystemDataModel](https://github.com/ie3-institute/PowerSystemDataModel). +[![License](https://img.shields.io/github/license/ie3-institute/powerFactory2psdm)](https://github.com/ie3-institute/powerFactory2psdm/blob/main/LICENSE) + +# powerFactory2psdm + +Convert PowerFactory projects to [PowerSystemDataModel](https://github.com/ie3-institute/PowerSystemDataModel). diff --git a/build.gradle b/build.gradle index ce2ee79e..1737f9c4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,143 +1,143 @@ -plugins { - id 'groovy' // groovy support - id 'java' // java support - id 'scala' // scala support - id 'pmd' //code check, working on source code - id 'com.diffplug.spotless' version '5.14.3'//code format - id "org.sonarqube" version "3.3" // sonarqube - id 'org.scoverage' version '7.0.0' // Code coverage plugin for scala - id "com.github.maiflai.scalatest" version "0.31" // run scalatest without specific spec task -} - -ext { - javaVersion = JavaVersion.VERSION_11 - scalaVersion = '2.13' - scalaBinaryVersion = '2.13.6' - slf4jVersion = '1.7.32' - circeVersion = '0.14.1' - scriptsLocation = 'gradle' + File.separator + 'scripts' + File.separator //location of script plugins -} - -apply from: scriptsLocation + 'pmd.gradle' -apply from: scriptsLocation + 'spotless.gradle' -apply from: scriptsLocation + 'checkJavaVersion.gradle' -apply from: scriptsLocation + 'scoverage.gradle' -apply from: scriptsLocation + 'sonarqube.gradle' - -configurations { - scalaCompilerPlugin -} - -repositories { - //searches in Maven Central - mavenCentral() - - // allows github repos as dependencies - maven { url 'https://www.jitpack.io' } - - // sonatype snapshot repo - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } -} - -dependencies { - - // json parsing - implementation "io.circe:circe-generic_${scalaVersion}:${circeVersion}" - implementation "io.circe:circe-parser_${scalaVersion}:${circeVersion}" - - // https://mvnrepository.com/artifact/org.scalameta/scalafmt-interfaces - implementation group: 'org.scalameta', name: 'scalafmt-interfaces', version: '3.0.2' - // https://mvnrepository.com/artifact/org.scalameta/scalafmt-dynamic - implementation group: 'org.scalameta', name: "scalafmt-dynamic_${scalaVersion}", version: '3.0.1' - - - implementation('com.github.ie3-institute:PowerSystemDataModel:2.0.1') { - exclude group: 'org.slf4j', module: 'slf4j-api' - /* Exclude our own nested dependencies */ - exclude group: 'com.github.ie3-institute' - } - - // Graphs - implementation group: 'org.jgrapht', name: 'jgrapht-core', version: '1.5.1' - - // config // - //implementation 'com.typesafe:config:+' - implementation "com.github.pureconfig:pureconfig_${scalaVersion}:0.16.0" - - // cmd args parser // - implementation "com.github.scopt:scopt_${scalaVersion}:+" - - // ie³ internal repository - implementation 'com.github.ie3-institute:PowerSystemUtils:1.5.3' - - // logging - implementation "org.slf4j:slf4j-api:${slf4jVersion}" // slf4j wrapper - implementation 'com.lmax:disruptor:3.4.4' // async logging - implementation 'org.apache.logging.log4j:log4j-api:+' // log4j - implementation 'org.apache.logging.log4j:log4j-core:+' // log4j - implementation 'org.apache.logging.log4j:log4j-slf4j-impl:+' // log4j -> slf4j - - implementation "com.typesafe.scala-logging:scala-logging_${scalaVersion}:3.9.4" // akka scala logging - implementation 'com.typesafe.scala-logging:scala-logging-slf4j_2.11:2.1.2'// scala logging - implementation "org.slf4j:log4j-over-slf4j:${slf4jVersion}" // slf4j -> log4j - - // CORE Scala // - implementation "org.scala-lang:scala-library:${scalaBinaryVersion}" - - // TEST Scala // - testImplementation "org.scalatest:scalatest_${scalaVersion}:3.2.9" - testImplementation 'com.vladsch.flexmark:flexmark-all:0.35.10' - - // Linter Scala // - implementation "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat - scalaCompilerPlugin "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat -} - -/* scapegoat hook configuration - * https://github.com/sksamuel/scapegoat - * using compileScala instead of tasks.withType(ScalaCompile) prevents applying scapegoat to scala test classes - * see https://docs.gradle.org/current/userguide/scala_plugin.html#sec:configure_scala_classpath for details - */ -compileScala { - scalaCompileOptions.additionalParameters = [ - "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, - "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/src/", - "-P:scapegoat:disabledInspections:VariableShadowing", - "-P:scapegoat:ignoredFiles:.*/PowerFactoryGrid.scala" // see scapegoat-sbt page for this param - ] -} - -// separate scapegoat report for test classes -compileTestScala { - scalaCompileOptions.additionalParameters = [ - "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, - "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/testsrc/", - "-P:scapegoat:disabledInspections:VariableShadowing" - ] -} -group = 'edu.ie3' -version = '0.1-SNAPSHOT' -description = 'powerFactory2psdm' -sourceCompatibility = javaVersion -targetCompatibility = javaVersion - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -tasks.withType(ScalaCompile) { - options.forkOptions.jvmArgs += [ - '-Xmx4096m', - '-Xss4m', - '-Xms2048m', - '-XX:+UseParallelGC', - '-XX:MaxInlineLevel=20' - ] - options.compilerArgs += [ - '-Xmx4096m', - '-Xss4m', - '-Xms2048m', - '-XX:+UseParallelGC', - '-XX:MaxInlineLevel=20' - ] -} +plugins { + id 'groovy' // groovy support + id 'java' // java support + id 'scala' // scala support + id 'pmd' //code check, working on source code + id 'com.diffplug.spotless' version '5.14.3'//code format + id "org.sonarqube" version "3.3" // sonarqube + id 'org.scoverage' version '7.0.0' // Code coverage plugin for scala + id "com.github.maiflai.scalatest" version "0.31" // run scalatest without specific spec task +} + +ext { + javaVersion = JavaVersion.VERSION_11 + scalaVersion = '2.13' + scalaBinaryVersion = '2.13.6' + slf4jVersion = '1.7.32' + circeVersion = '0.14.1' + scriptsLocation = 'gradle' + File.separator + 'scripts' + File.separator //location of script plugins +} + +apply from: scriptsLocation + 'pmd.gradle' +apply from: scriptsLocation + 'spotless.gradle' +apply from: scriptsLocation + 'checkJavaVersion.gradle' +apply from: scriptsLocation + 'scoverage.gradle' +apply from: scriptsLocation + 'sonarqube.gradle' + +configurations { + scalaCompilerPlugin +} + +repositories { + //searches in Maven Central + mavenCentral() + + // allows github repos as dependencies + maven { url 'https://www.jitpack.io' } + + // sonatype snapshot repo + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } +} + +dependencies { + + // json parsing + implementation "io.circe:circe-generic_${scalaVersion}:${circeVersion}" + implementation "io.circe:circe-parser_${scalaVersion}:${circeVersion}" + + // https://mvnrepository.com/artifact/org.scalameta/scalafmt-interfaces + implementation group: 'org.scalameta', name: 'scalafmt-interfaces', version: '3.0.2' + // https://mvnrepository.com/artifact/org.scalameta/scalafmt-dynamic + implementation group: 'org.scalameta', name: "scalafmt-dynamic_${scalaVersion}", version: '3.0.1' + + + implementation('com.github.ie3-institute:PowerSystemDataModel:2.0.1') { + exclude group: 'org.slf4j', module: 'slf4j-api' + /* Exclude our own nested dependencies */ + exclude group: 'com.github.ie3-institute' + } + + // Graphs + implementation group: 'org.jgrapht', name: 'jgrapht-core', version: '1.5.1' + + // config // + //implementation 'com.typesafe:config:+' + implementation "com.github.pureconfig:pureconfig_${scalaVersion}:0.16.0" + + // cmd args parser // + implementation "com.github.scopt:scopt_${scalaVersion}:+" + + // ie³ internal repository + implementation 'com.github.ie3-institute:PowerSystemUtils:1.5.3' + + // logging + implementation "org.slf4j:slf4j-api:${slf4jVersion}" // slf4j wrapper + implementation 'com.lmax:disruptor:3.4.4' // async logging + implementation 'org.apache.logging.log4j:log4j-api:+' // log4j + implementation 'org.apache.logging.log4j:log4j-core:+' // log4j + implementation 'org.apache.logging.log4j:log4j-slf4j-impl:+' // log4j -> slf4j + + implementation "com.typesafe.scala-logging:scala-logging_${scalaVersion}:3.9.4" // akka scala logging + implementation 'com.typesafe.scala-logging:scala-logging-slf4j_2.11:2.1.2'// scala logging + implementation "org.slf4j:log4j-over-slf4j:${slf4jVersion}" // slf4j -> log4j + + // CORE Scala // + implementation "org.scala-lang:scala-library:${scalaBinaryVersion}" + + // TEST Scala // + testImplementation "org.scalatest:scalatest_${scalaVersion}:3.2.9" + testImplementation 'com.vladsch.flexmark:flexmark-all:0.35.10' + + // Linter Scala // + implementation "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat + scalaCompilerPlugin "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat +} + +/* scapegoat hook configuration + * https://github.com/sksamuel/scapegoat + * using compileScala instead of tasks.withType(ScalaCompile) prevents applying scapegoat to scala test classes + * see https://docs.gradle.org/current/userguide/scala_plugin.html#sec:configure_scala_classpath for details + */ +compileScala { + scalaCompileOptions.additionalParameters = [ + "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, + "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/src/", + "-P:scapegoat:disabledInspections:VariableShadowing", + "-P:scapegoat:ignoredFiles:.*/PowerFactoryGrid.scala" // see scapegoat-sbt page for this param + ] +} + +// separate scapegoat report for test classes +compileTestScala { + scalaCompileOptions.additionalParameters = [ + "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, + "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/testsrc/", + "-P:scapegoat:disabledInspections:VariableShadowing" + ] +} +group = 'edu.ie3' +version = '0.1-SNAPSHOT' +description = 'powerFactory2psdm' +sourceCompatibility = javaVersion +targetCompatibility = javaVersion + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +tasks.withType(ScalaCompile) { + options.forkOptions.jvmArgs += [ + '-Xmx4096m', + '-Xss4m', + '-Xms2048m', + '-XX:+UseParallelGC', + '-XX:MaxInlineLevel=20' + ] + options.compilerArgs += [ + '-Xmx4096m', + '-Xss4m', + '-Xms2048m', + '-XX:+UseParallelGC', + '-XX:MaxInlineLevel=20' + ] +} diff --git a/gradle/scripts/checkJavaVersion.gradle b/gradle/scripts/checkJavaVersion.gradle index 665708ad..68cf0674 100644 --- a/gradle/scripts/checkJavaVersion.gradle +++ b/gradle/scripts/checkJavaVersion.gradle @@ -1,14 +1,14 @@ -// Enforces the correct Java version, as some parts of the project may malfunction under a wrong version -// If this task fails, try changing your JAVA_HOME to the required version -tasks.register("checkJavaVersion") { - group = 'Verification' - description = 'Enforces correct Java version' - - doLast { - def foundVersion = JavaVersion.current() - if (foundVersion != javaVersion) - throw new IllegalStateException("Wrong Java version: required is " - + javaVersion + ", but found " + foundVersion) - } -} -compileJava.dependsOn(checkJavaVersion) +// Enforces the correct Java version, as some parts of the project may malfunction under a wrong version +// If this task fails, try changing your JAVA_HOME to the required version +tasks.register("checkJavaVersion") { + group = 'Verification' + description = 'Enforces correct Java version' + + doLast { + def foundVersion = JavaVersion.current() + if (foundVersion != javaVersion) + throw new IllegalStateException("Wrong Java version: required is " + + javaVersion + ", but found " + foundVersion) + } +} +compileJava.dependsOn(checkJavaVersion) diff --git a/gradle/scripts/pmd.gradle b/gradle/scripts/pmd.gradle index 46bb0c56..aa3c1478 100644 --- a/gradle/scripts/pmd.gradle +++ b/gradle/scripts/pmd.gradle @@ -1,7 +1,7 @@ -// pmd is a code check tool, working on source code - -pmd { - consoleOutput = true - toolVersion = "6.21.0" - rulesMinimumPriority = 2 -} +// pmd is a code check tool, working on source code + +pmd { + consoleOutput = true + toolVersion = "6.21.0" + rulesMinimumPriority = 2 +} diff --git a/gradle/scripts/scoverage.gradle b/gradle/scripts/scoverage.gradle index 9317b61d..ceef437b 100644 --- a/gradle/scripts/scoverage.gradle +++ b/gradle/scripts/scoverage.gradle @@ -1,12 +1,12 @@ -/* - * Scala code coverage gradle plugin see https://github.com/scoverage/gradle-scoverage#configuration and - * https://github.com/scoverage/gradle-scoverage/issues/109 for details - */ -scoverage { - scoverageScalaVersion = scalaBinaryVersion - coverageOutputHTML = false - coverageOutputXML = true - coverageOutputCobertura = false - minimumRate = 0.6 // minimum code coverage - coverageDebug = false -} +/* + * Scala code coverage gradle plugin see https://github.com/scoverage/gradle-scoverage#configuration and + * https://github.com/scoverage/gradle-scoverage/issues/109 for details + */ +scoverage { + scoverageScalaVersion = scalaBinaryVersion + coverageOutputHTML = false + coverageOutputXML = true + coverageOutputCobertura = false + minimumRate = 0.6 // minimum code coverage + coverageDebug = false +} diff --git a/gradle/scripts/sonarqube.gradle b/gradle/scripts/sonarqube.gradle index 1857368d..591e72c8 100644 --- a/gradle/scripts/sonarqube.gradle +++ b/gradle/scripts/sonarqube.gradle @@ -1,35 +1,35 @@ -sonarqube { - properties { - // general stuff - property 'sonar.projectName', 'powerfactory2psdm' // project name - property 'sonar.verbose', 'true' // verbose mode - property 'sonar.sourceEncoding', 'UTF-8' // encoding - property 'sonar.sources', [ - 'src/main/resources', - 'src/main/scala'] // src dirs - property "sonar.tests", [ - 'src/test/resources', - 'src/test/scala'] // test src dirs - // reports stuff (for all languages) - property 'sonar.junit.reportPaths', [ - 'build/test-results/test'] // Comma-delimited list of paths to Surefire XML-format reports. - // scapegoat report dir - property "sonar.scala.scapegoat.reportPaths", [ - "build/reports/scapegoat/src/scapegoat-scalastyle.xml", - "build/reports/scapegoat/testsrc/scapegoat-scalastyle.xml"] // Comma-delimited list of paths to Scapegoat reports in the Scalastyle format - // scala specific stuff - property 'sonar.scala.coverage.reportPaths', 'build/reports/scoverage/scoverage.xml' - // remove auto generated RawGridModel file from sonarqube analysis - property 'sonar.exclusions', [ - "src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" - ] - } -} - -// forces sonarqube to execute integration tests -project.tasks["sonarqube"].dependsOn "pmdMain" -project.tasks["sonarqube"].dependsOn "pmdTest" -project.tasks["sonarqube"].dependsOn "check" - -project.tasks["sonarqube"].dependsOn "reportScoverage" -project.tasks["sonarqube"].dependsOn "checkScoverage" +sonarqube { + properties { + // general stuff + property 'sonar.projectName', 'powerfactory2psdm' // project name + property 'sonar.verbose', 'true' // verbose mode + property 'sonar.sourceEncoding', 'UTF-8' // encoding + property 'sonar.sources', [ + 'src/main/resources', + 'src/main/scala'] // src dirs + property "sonar.tests", [ + 'src/test/resources', + 'src/test/scala'] // test src dirs + // reports stuff (for all languages) + property 'sonar.junit.reportPaths', [ + 'build/test-results/test'] // Comma-delimited list of paths to Surefire XML-format reports. + // scapegoat report dir + property "sonar.scala.scapegoat.reportPaths", [ + "build/reports/scapegoat/src/scapegoat-scalastyle.xml", + "build/reports/scapegoat/testsrc/scapegoat-scalastyle.xml"] // Comma-delimited list of paths to Scapegoat reports in the Scalastyle format + // scala specific stuff + property 'sonar.scala.coverage.reportPaths', 'build/reports/scoverage/scoverage.xml' + // remove auto generated RawGridModel file from sonarqube analysis + property 'sonar.exclusions', [ + "src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" + ] + } +} + +// forces sonarqube to execute integration tests +project.tasks["sonarqube"].dependsOn "pmdMain" +project.tasks["sonarqube"].dependsOn "pmdTest" +project.tasks["sonarqube"].dependsOn "check" + +project.tasks["sonarqube"].dependsOn "reportScoverage" +project.tasks["sonarqube"].dependsOn "checkScoverage" diff --git a/gradle/scripts/spotless.gradle b/gradle/scripts/spotless.gradle index bdb11fd7..9fa8559a 100644 --- a/gradle/scripts/spotless.gradle +++ b/gradle/scripts/spotless.gradle @@ -1,45 +1,45 @@ -// spotless is a code formatter - -spotless { - def ie3LicHead = '/*\n' + - ' * © $YEAR. TU Dortmund University,\n' + - ' * Institute of Energy Systems, Energy Efficiency and Energy Economics,\n' + - ' * Research group Distribution grid planning and operation\n' + - ' */\n\n' - - //sets a license header, removes unused imports and formats conforming to the google java format - java { - removeUnusedImports() // removes any unused imports - googleJavaFormat() - licenseHeader ie3LicHead - } - - /* cf. https://github.com/diffplug/spotless/tree/master/plugin-gradle */ - groovy { - licenseHeader ie3LicHead - excludeJava() // excludes all Java sources within the Groovy source dirs from formatting - // the Groovy Eclipse formatter extends the Java Eclipse formatter, - // so it formats Java files by default (unless `excludeJava` is used). - greclipse() - } - - groovyGradle { - // same as groovy, but for .gradle (defaults to '*.gradle') - target '*.gradle', 'gradle/scripts/*.gradle' - greclipse() - } - - //sets a license header, removes unused imports and formats conforming to the scala fmt formatter - scala { - scalafmt() - licenseHeader ie3LicHead, "package.*\\n" - } - - // removes unnecessary whitespace, indents with tabs and ends on new line for gradle, md and gitignore files and config-XMLs - format 'misc', { - target '**/*.md', '**/.gitignore', 'configs/**' - trimTrailingWhitespace() - indentWithTabs() - endWithNewline() - } -} +// spotless is a code formatter + +spotless { + def ie3LicHead = '/*\n' + + ' * © $YEAR. TU Dortmund University,\n' + + ' * Institute of Energy Systems, Energy Efficiency and Energy Economics,\n' + + ' * Research group Distribution grid planning and operation\n' + + ' */\n\n' + + //sets a license header, removes unused imports and formats conforming to the google java format + java { + removeUnusedImports() // removes any unused imports + googleJavaFormat() + licenseHeader ie3LicHead + } + + /* cf. https://github.com/diffplug/spotless/tree/master/plugin-gradle */ + groovy { + licenseHeader ie3LicHead + excludeJava() // excludes all Java sources within the Groovy source dirs from formatting + // the Groovy Eclipse formatter extends the Java Eclipse formatter, + // so it formats Java files by default (unless `excludeJava` is used). + greclipse() + } + + groovyGradle { + // same as groovy, but for .gradle (defaults to '*.gradle') + target '*.gradle', 'gradle/scripts/*.gradle' + greclipse() + } + + //sets a license header, removes unused imports and formats conforming to the scala fmt formatter + scala { + scalafmt() + licenseHeader ie3LicHead, "package.*\\n" + } + + // removes unnecessary whitespace, indents with tabs and ends on new line for gradle, md and gitignore files and config-XMLs + format 'misc', { + target '**/*.md', '**/.gitignore', 'configs/**' + trimTrailingWhitespace() + indentWithTabs() + endWithNewline() + } +} diff --git a/settings.gradle b/settings.gradle index 1fa5047a..072b7f8c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,10 +1,10 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.8.3/userguide/multi_project_builds.html - */ - -rootProject.name = 'powerFactory2psdm' +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.8.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'powerFactory2psdm' diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala index 66475d8f..b0d99106 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala @@ -1,150 +1,150 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.config - -import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ - Fixed, - GenerationMethod, - ModelConfigs, - NormalDistribution, - PvConfig, - PvModelGeneration, - UniformDistribution -} -import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException - -import scala.util.{Failure, Success, Try} - -object ConfigValidator { - - /** Checks the parsed [[ConversionConfig]] for general soundness. - * @param config - * the parsed config - */ - def validate(config: ConversionConfig): Unit = { - validateModelConfigs(config.modelConfigs) - } - - private[config] def validateModelConfigs(modelConfigs: ModelConfigs): Unit = { - validatePvConfig(modelConfigs.pvConfig) - } - - private[config] def validatePvConfig(pvConfig: PvConfig): Unit = { - Seq(pvConfig.conversionMode) ++ pvConfig.individualConfigs - .getOrElse(Nil) - .map(conf => conf.conversionMode) - .collect { case pvModelGeneration: PvModelGeneration => - pvModelGeneration - } - .map(validatePvModelGenerationParams) - } - - private[config] def validatePvModelGenerationParams( - params: PvModelGeneration - ): Unit = { - validateGenerationMethod(params.albedo, 0, 1) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The albedo of the plants surrounding: ${params.albedo} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.azimuth, -90, 90) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The azimuth of the plant: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.etaConv, 0, 100) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The efficiency of the plants inverter: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.kG, 0, 1) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The PV generator correction factor (kG): ${params.kG} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.kT, 0, 1) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The PV temperature correction factor (kT): ${params.kT} isn't valid. Exception: ${exc.getMessage}" - ) - } - } - - private[config] def validateGenerationMethod( - genMethod: GenerationMethod, - lowerBound: Double, - upperBound: Double - ): Try[Unit] = - genMethod match { - case Fixed(value) => - checkForBoundViolation(value, lowerBound, upperBound) - case UniformDistribution(min, max) => - if (min > max) - return Failure( - ConversionConfigException( - s"The minimum value: $min exceeds the maximum value: $max" - ) - ) - if (min < lowerBound && max > upperBound) - return lowerUpperBoundViolation(min, max, lowerBound, upperBound) - else if (min < lowerBound) return lowerBoundViolation(min, lowerBound) - else if (max > upperBound) return upperBoundViolation(max, upperBound) - Success(()) - case NormalDistribution(mean, _) => - checkForBoundViolation(mean, lowerBound, upperBound) - } - - private[config] def checkForBoundViolation( - value: Double, - lowerBound: Double, - upperBound: Double - ): Try[Unit] = { - if (value < lowerBound) return lowerBoundViolation(value, lowerBound) - if (value > upperBound) return upperBoundViolation(value, upperBound) - Success(()) - } - - private[config] def lowerBoundViolation( - value: Double, - lowerBound: Double - ): Failure[Unit] = Failure( - ConversionConfigException( - s"The parameters value: $value lies below the lower bound: $lowerBound" - ) - ) - - private[config] def upperBoundViolation( - value: Double, - upperBound: Double - ): Failure[Unit] = Failure( - ConversionConfigException( - s"The parameters value: $value exceeds the upper bound: $upperBound" - ) - ) - - private[config] def lowerUpperBoundViolation( - min: Double, - max: Double, - lowerBound: Double, - upperBound: Double - ): Failure[Unit] = - Failure( - ConversionConfigException( - s"The minimum: $min and maximum: $max of the uniform distribution lie below the lower bound: $lowerBound and above the upper bound: $upperBound " - ) - ) - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.config + +import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ + Fixed, + GenerationMethod, + ModelConfigs, + NormalDistribution, + PvConfig, + PvModelGeneration, + UniformDistribution +} +import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException + +import scala.util.{Failure, Success, Try} + +object ConfigValidator { + + /** Checks the parsed [[ConversionConfig]] for general soundness. + * @param config + * the parsed config + */ + def validate(config: ConversionConfig): Unit = { + validateModelConfigs(config.modelConfigs) + } + + private[config] def validateModelConfigs(modelConfigs: ModelConfigs): Unit = { + validatePvConfig(modelConfigs.pvConfig) + } + + private[config] def validatePvConfig(pvConfig: PvConfig): Unit = { + Seq(pvConfig.conversionMode) ++ pvConfig.individualConfigs + .getOrElse(Nil) + .map(conf => conf.conversionMode) + .collect { case pvModelGeneration: PvModelGeneration => + pvModelGeneration + } + .map(validatePvModelGenerationParams) + } + + private[config] def validatePvModelGenerationParams( + params: PvModelGeneration + ): Unit = { + validateGenerationMethod(params.albedo, 0, 1) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The albedo of the plants surrounding: ${params.albedo} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.azimuth, -90, 90) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The azimuth of the plant: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.etaConv, 0, 100) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The efficiency of the plants inverter: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.kG, 0, 1) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The PV generator correction factor (kG): ${params.kG} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.kT, 0, 1) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The PV temperature correction factor (kT): ${params.kT} isn't valid. Exception: ${exc.getMessage}" + ) + } + } + + private[config] def validateGenerationMethod( + genMethod: GenerationMethod, + lowerBound: Double, + upperBound: Double + ): Try[Unit] = + genMethod match { + case Fixed(value) => + checkForBoundViolation(value, lowerBound, upperBound) + case UniformDistribution(min, max) => + if (min > max) + return Failure( + ConversionConfigException( + s"The minimum value: $min exceeds the maximum value: $max" + ) + ) + if (min < lowerBound && max > upperBound) + return lowerUpperBoundViolation(min, max, lowerBound, upperBound) + else if (min < lowerBound) return lowerBoundViolation(min, lowerBound) + else if (max > upperBound) return upperBoundViolation(max, upperBound) + Success(()) + case NormalDistribution(mean, _) => + checkForBoundViolation(mean, lowerBound, upperBound) + } + + private[config] def checkForBoundViolation( + value: Double, + lowerBound: Double, + upperBound: Double + ): Try[Unit] = { + if (value < lowerBound) return lowerBoundViolation(value, lowerBound) + if (value > upperBound) return upperBoundViolation(value, upperBound) + Success(()) + } + + private[config] def lowerBoundViolation( + value: Double, + lowerBound: Double + ): Failure[Unit] = Failure( + ConversionConfigException( + s"The parameters value: $value lies below the lower bound: $lowerBound" + ) + ) + + private[config] def upperBoundViolation( + value: Double, + upperBound: Double + ): Failure[Unit] = Failure( + ConversionConfigException( + s"The parameters value: $value exceeds the upper bound: $upperBound" + ) + ) + + private[config] def lowerUpperBoundViolation( + min: Double, + max: Double, + lowerBound: Double, + upperBound: Double + ): Failure[Unit] = + Failure( + ConversionConfigException( + s"The minimum: $min and maximum: $max of the uniform distribution lie below the lower bound: $lowerBound and above the upper bound: $upperBound " + ) + ) + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala index 76522664..8498ed62 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala @@ -1,57 +1,57 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.config - -import edu.ie3.powerFactory2psdm.config.ConversionConfig.ModelConfigs - -final case class ConversionConfig(modelConfigs: ModelConfigs) - -object ConversionConfig { - - case class ModelConfigs( - pvConfig: PvConfig - ) - - case class PvConfig( - conversionMode: PvConversionMode, - individualConfigs: Option[List[IndividualPvConfig]] - ) - - case class IndividualPvConfig( - ids: Set[String], - conversionMode: PvConversionMode - ) - - sealed trait PvConversionMode - - case object PvFixedFeedIn extends PvConversionMode - - case class PvModelGeneration( - albedo: GenerationMethod, - azimuth: GenerationMethod, - etaConv: GenerationMethod, - kG: GenerationMethod, - kT: GenerationMethod - ) extends PvConversionMode - - sealed trait GenerationMethod - - case class Fixed( - value: Double - ) extends GenerationMethod - - case class UniformDistribution( - lowerBound: Double, - upperBound: Double - ) extends GenerationMethod - - case class NormalDistribution( - mean: Double, - standardDeviation: Double - ) extends GenerationMethod - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.config + +import edu.ie3.powerFactory2psdm.config.ConversionConfig.ModelConfigs + +final case class ConversionConfig(modelConfigs: ModelConfigs) + +object ConversionConfig { + + case class ModelConfigs( + pvConfig: PvConfig + ) + + case class PvConfig( + conversionMode: PvConversionMode, + individualConfigs: Option[List[IndividualPvConfig]] + ) + + case class IndividualPvConfig( + ids: Set[String], + conversionMode: PvConversionMode + ) + + sealed trait PvConversionMode + + case object PvFixedFeedIn extends PvConversionMode + + case class PvModelGeneration( + albedo: GenerationMethod, + azimuth: GenerationMethod, + etaConv: GenerationMethod, + kG: GenerationMethod, + kT: GenerationMethod + ) extends PvConversionMode + + sealed trait GenerationMethod + + case class Fixed( + value: Double + ) extends GenerationMethod + + case class UniformDistribution( + lowerBound: Double, + upperBound: Double + ) extends GenerationMethod + + case class NormalDistribution( + mean: Double, + standardDeviation: Double + ) extends GenerationMethod + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala index c2519226..891ac23c 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala @@ -1,36 +1,36 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.input.NodeInput -import edu.ie3.util.geo.GeoUtils -import org.locationtech.jts.geom.{Coordinate, Point} - -case object CoordinateConverter { - - /** Converts optional lat and lon values of PowerFactory elements to the - * expected position description of the PSDM. - * - * @param maybeLat - * optional lat value - * @param maybeLon - * optional lon value - * @return - * position as described by a [[Point]] - */ - def convert(maybeLat: Option[Double], maybeLon: Option[Double]): Point = { - maybeLat.zip(maybeLon) match { - // if no coord is specifically set in the power factory grid, lat and lon are 0.0 - case Some((0.0, 0.0)) => - NodeInput.DEFAULT_GEO_POSITION - case Some((lat, lon)) => - GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint(new Coordinate(lon, lat)) - case None => - NodeInput.DEFAULT_GEO_POSITION - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.input.NodeInput +import edu.ie3.util.geo.GeoUtils +import org.locationtech.jts.geom.{Coordinate, Point} + +case object CoordinateConverter { + + /** Converts optional lat and lon values of PowerFactory elements to the + * expected position description of the PSDM. + * + * @param maybeLat + * optional lat value + * @param maybeLon + * optional lon value + * @return + * position as described by a [[Point]] + */ + def convert(maybeLat: Option[Double], maybeLon: Option[Double]): Point = { + maybeLat.zip(maybeLon) match { + // if no coord is specifically set in the power factory grid, lat and lon are 0.0 + case Some((0.0, 0.0)) => + NodeInput.DEFAULT_GEO_POSITION + case Some((lat, lon)) => + GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint(new Coordinate(lon, lat)) + case None => + NodeInput.DEFAULT_GEO_POSITION + } + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala index 12f495e9..39cc8f9a 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala @@ -1,51 +1,51 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.input.NodeInput -import edu.ie3.powerFactory2psdm.model.entity.Subnet -import edu.ie3.powerFactory2psdm.model.{PreprocessedPfGridModel, RawPfGridModel} - -/** Functionalities to transform an exported and then parsed PowerFactory grid - * to the PSDM. - */ -case object GridConverter { - - def convert(pfGrid: RawPfGridModel) = { - val grid = PreprocessedPfGridModel.build(pfGrid) - val gridElements = convertGridElements(grid) - } - - /** Converts the grid elements of the PowerFactory grid - * - * @param rawGrid - * the raw parsed PowerFactoryGrid - */ - def convertGridElements( - grid: PreprocessedPfGridModel - ): Unit = { - val graph = - GridGraphBuilder.build(grid.nodes, grid.lines ++ grid.switches) - val nodeId2node = grid.nodes.map(node => (node.id, node)).toMap - val subnets = SubnetBuilder.buildSubnets(graph, nodeId2node) - val psdmNodes = subnets.flatMap(subnet => convertNodesOfSubnet(subnet)) - } - - /** Converts all nodes within a subnet to PSDM [[NodeInput]] - * - * @param subnet - * the subnet with reference to all PF nodes that live within - * @return - * list of all converted [[NodeInput]] - */ - def convertNodesOfSubnet( - subnet: Subnet - ): List[NodeInput] = - subnet.nodes - .map(node => NodeConverter.convertNode(node, subnet.id, subnet.voltLvl)) - .toList -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.input.NodeInput +import edu.ie3.powerFactory2psdm.model.entity.Subnet +import edu.ie3.powerFactory2psdm.model.{PreprocessedPfGridModel, RawPfGridModel} + +/** Functionalities to transform an exported and then parsed PowerFactory grid + * to the PSDM. + */ +case object GridConverter { + + def convert(pfGrid: RawPfGridModel) = { + val grid = PreprocessedPfGridModel.build(pfGrid) + val gridElements = convertGridElements(grid) + } + + /** Converts the grid elements of the PowerFactory grid + * + * @param rawGrid + * the raw parsed PowerFactoryGrid + */ + def convertGridElements( + grid: PreprocessedPfGridModel + ): Unit = { + val graph = + GridGraphBuilder.build(grid.nodes, grid.lines ++ grid.switches) + val nodeId2node = grid.nodes.map(node => (node.id, node)).toMap + val subnets = SubnetBuilder.buildSubnets(graph, nodeId2node) + val psdmNodes = subnets.flatMap(subnet => convertNodesOfSubnet(subnet)) + } + + /** Converts all nodes within a subnet to PSDM [[NodeInput]] + * + * @param subnet + * the subnet with reference to all PF nodes that live within + * @return + * list of all converted [[NodeInput]] + */ + def convertNodesOfSubnet( + subnet: Subnet + ): List[NodeInput] = + subnet.nodes + .map(node => NodeConverter.convertNode(node, subnet.id, subnet.voltLvl)) + .toList +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala index e20aa63a..28c98df7 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala @@ -1,64 +1,64 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException -import edu.ie3.powerFactory2psdm.model.entity.{Edge, Node} -import org.jgrapht.graph._ - -/** Builds a graph representation of the powerfactory grid - */ -object GridGraphBuilder { - - /** Builds up the graph topology of the grid. All nodes (represented by their - * ids) and the connection between nodes by edges (lines and switches) are - * added to the graph. The resulting subgraphs inside the graph represent the - * subnets of the grid. - * - * @param nodes - * nodes of the power factory grid - * @param edges - * edges (lines and switches) of the power factory grid - * @return - * [[Multigraph]] of all the ids of the nodes and their connection - */ - def build[I <: Edge]( - nodes: List[Node], - edges: List[I] - ): AsUnmodifiableGraph[String, DefaultEdge] = { - - val graph = new Multigraph[String, DefaultEdge](classOf[DefaultEdge]) - nodes.foreach(node => graph.addVertex(node.id)) - edges.map(edge => { - graph.addEdge(edge.nodeAId, edge.nodeBId) - }) - new AsUnmodifiableGraph(graph) - } - - /** Unpacks the optional ids of two busses, connected by an edge. - * - * @param edgeId - * id of edge connecting busses with bus1Id and bus2Id - * @param maybeBus1Id - * Option of id of bus 1 - * @param maybeBus2Id - * Option of id of bus 2 - * @return - * Tuple of the ids of bus 1 and bus 2 - */ - def unpackConnectedBusses( - edgeId: String, - maybeBus1Id: Option[String], - maybeBus2Id: Option[String] - ): (String, String) = maybeBus1Id.zip(maybeBus2Id) match { - case Some((bus1Id, bus2Id)) => (bus1Id, bus2Id) - case None => - throw ElementConfigurationException( - s"Exception occurred while adding an edge. Exc: Edge with id: $edgeId is missing at least one connected node" - ) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException +import edu.ie3.powerFactory2psdm.model.entity.{Edge, Node} +import org.jgrapht.graph._ + +/** Builds a graph representation of the powerfactory grid + */ +object GridGraphBuilder { + + /** Builds up the graph topology of the grid. All nodes (represented by their + * ids) and the connection between nodes by edges (lines and switches) are + * added to the graph. The resulting subgraphs inside the graph represent the + * subnets of the grid. + * + * @param nodes + * nodes of the power factory grid + * @param edges + * edges (lines and switches) of the power factory grid + * @return + * [[Multigraph]] of all the ids of the nodes and their connection + */ + def build[I <: Edge]( + nodes: List[Node], + edges: List[I] + ): AsUnmodifiableGraph[String, DefaultEdge] = { + + val graph = new Multigraph[String, DefaultEdge](classOf[DefaultEdge]) + nodes.foreach(node => graph.addVertex(node.id)) + edges.map(edge => { + graph.addEdge(edge.nodeAId, edge.nodeBId) + }) + new AsUnmodifiableGraph(graph) + } + + /** Unpacks the optional ids of two busses, connected by an edge. + * + * @param edgeId + * id of edge connecting busses with bus1Id and bus2Id + * @param maybeBus1Id + * Option of id of bus 1 + * @param maybeBus2Id + * Option of id of bus 2 + * @return + * Tuple of the ids of bus 1 and bus 2 + */ + def unpackConnectedBusses( + edgeId: String, + maybeBus1Id: Option[String], + maybeBus2Id: Option[String] + ): (String, String) = maybeBus1Id.zip(maybeBus2Id) match { + case Some((bus1Id, bus2Id)) => (bus1Id, bus2Id) + case None => + throw ElementConfigurationException( + s"Exception occurred while adding an edge. Exc: Edge with id: $edgeId is missing at least one connected node" + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala index 19359ce4..7df28947 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala @@ -1,71 +1,71 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.OperationTime -import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} -import edu.ie3.datamodel.models.voltagelevels.VoltageLevel -import edu.ie3.powerFactory2psdm.exception.pf.GridConfigurationException -import edu.ie3.powerFactory2psdm.model.entity.Node -import edu.ie3.util.quantities.PowerSystemUnits.PU -import org.locationtech.jts.geom.Point -import tech.units.indriya.quantity.Quantities - -import java.util.UUID - -object NodeConverter { - - /** Converts a PowerFactory node into a PSDM node. - * - * @param id - * id of the PF node to convert - * @param id2node - * Map of ids and their associated [[Node]] s - * @param subnet - * subnet the PF node lives in - * @return - * a PSDM [[NodeInput]] - */ - def convertNode( - node: Node, - subnetId: Int, - voltLvl: VoltageLevel - ): NodeInput = { - val vTarget = Quantities.getQuantity(node.vTarget, PU) - val geoPosition: Point = CoordinateConverter.convert(node.lat, node.lon) - val slack = isSlack(node) - new NodeInput( - UUID.randomUUID(), - node.id, - OperatorInput.NO_OPERATOR_ASSIGNED, - OperationTime.notLimited(), - vTarget, - slack, - geoPosition, - voltLvl, - subnetId - ) - } - - /** Checks if a node is a slack node by checking if there is an external grid - * connected to the node. - * - * @param node - * node to check - * @return - * true or false - */ - private def isSlack(node: Node): Boolean = - node.conElms.filter(_.pfCls == "ElmXnet") match { - case Seq(_) => true - case Nil => false - case _ => - throw GridConfigurationException( - s"There is more than one external grid connected to Node: ${node.id}" - ) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.OperationTime +import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} +import edu.ie3.datamodel.models.voltagelevels.VoltageLevel +import edu.ie3.powerFactory2psdm.exception.pf.GridConfigurationException +import edu.ie3.powerFactory2psdm.model.entity.Node +import edu.ie3.util.quantities.PowerSystemUnits.PU +import org.locationtech.jts.geom.Point +import tech.units.indriya.quantity.Quantities + +import java.util.UUID + +object NodeConverter { + + /** Converts a PowerFactory node into a PSDM node. + * + * @param id + * id of the PF node to convert + * @param id2node + * Map of ids and their associated [[Node]] s + * @param subnet + * subnet the PF node lives in + * @return + * a PSDM [[NodeInput]] + */ + def convertNode( + node: Node, + subnetId: Int, + voltLvl: VoltageLevel + ): NodeInput = { + val vTarget = Quantities.getQuantity(node.vTarget, PU) + val geoPosition: Point = CoordinateConverter.convert(node.lat, node.lon) + val slack = isSlack(node) + new NodeInput( + UUID.randomUUID(), + node.id, + OperatorInput.NO_OPERATOR_ASSIGNED, + OperationTime.notLimited(), + vTarget, + slack, + geoPosition, + voltLvl, + subnetId + ) + } + + /** Checks if a node is a slack node by checking if there is an external grid + * connected to the node. + * + * @param node + * node to check + * @return + * true or false + */ + private def isSlack(node: Node): Boolean = + node.conElms.filter(_.pfCls == "ElmXnet") match { + case Seq(_) => true + case Nil => false + case _ => + throw GridConfigurationException( + s"There is more than one external grid connected to Node: ${node.id}" + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala index eb2bf391..6b653711 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala @@ -1,95 +1,95 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.datamodel.models.StandardUnits -import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - ElementConfigurationException, - GridConfigurationException -} -import edu.ie3.powerFactory2psdm.model.entity -import edu.ie3.powerFactory2psdm.model.entity.{Node, Subnet} -import org.jgrapht.alg.connectivity.BiconnectivityInspector -import org.jgrapht.graph.{AsUnmodifiableGraph, DefaultEdge} -import tech.units.indriya.quantity.Quantities.getQuantity - -import scala.jdk.CollectionConverters.CollectionHasAsScala - -object SubnetBuilder extends LazyLogging { - - /** Takes the grid graph and builds up the different [[Subnet]] s from it. - * - * @param gridGraph - * the built grid graph represented by the UUIDS of the nodes and the lines - * connecting them - * @param id2Node - * the mapping between UUID and the corresponding node - * @return - * the list of all subnets of the grid - */ - def buildSubnets( - gridGraph: AsUnmodifiableGraph[String, DefaultEdge], - id2Node: Map[String, Node] - ): List[Subnet] = { - new BiconnectivityInspector( - gridGraph - ).getConnectedComponents.asScala.toList.zipWithIndex map { - case (subgraph, index) => - buildSubnet( - index, - subgraph.vertexSet().asScala.toSet, - id2Node - ) - } - } - - /** Builds a [[Subnet]] after checking if all nodes have the same nominal - * voltage - * - * @param subnetId - * id of the subnet - * @param nodeIds - * UUIDS of all nodes that live in the subnet - * @param id2node - * mapping between UUID and node - * @return - * the built [[Subnet]] - */ - def buildSubnet( - subnetId: Int, - nodeIds: Set[String], - id2node: Map[String, Node] - ): Subnet = { - val nodes = nodeIds.map(id => - id2node.getOrElse( - id, - throw ConversionException(s"Can't find node id $id in id2node map") - ) - ) - val nomVoltage = nodes.headOption - .getOrElse( - throw GridConfigurationException("There are no nodes in the subnet!") - ) - .nominalVoltage - val divergences = nodes - .filter(node => Math.abs(nomVoltage - node.nominalVoltage) > 0.001) - .map { node => - node.id + " -> " + node.nominalVoltage - } - if (divergences.nonEmpty) - throw ElementConfigurationException( - s"There are the following divergences from the nominal voltage $nomVoltage : $divergences" - ) - val voltLvl = GermanVoltageLevelUtils.parse( - getQuantity(nomVoltage, StandardUnits.RATED_VOLTAGE_MAGNITUDE) - ) - entity.Subnet(subnetId, nodes, voltLvl) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.datamodel.models.StandardUnits +import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + ElementConfigurationException, + GridConfigurationException +} +import edu.ie3.powerFactory2psdm.model.entity +import edu.ie3.powerFactory2psdm.model.entity.{Node, Subnet} +import org.jgrapht.alg.connectivity.BiconnectivityInspector +import org.jgrapht.graph.{AsUnmodifiableGraph, DefaultEdge} +import tech.units.indriya.quantity.Quantities.getQuantity + +import scala.jdk.CollectionConverters.CollectionHasAsScala + +object SubnetBuilder extends LazyLogging { + + /** Takes the grid graph and builds up the different [[Subnet]] s from it. + * + * @param gridGraph + * the built grid graph represented by the UUIDS of the nodes and the lines + * connecting them + * @param id2Node + * the mapping between UUID and the corresponding node + * @return + * the list of all subnets of the grid + */ + def buildSubnets( + gridGraph: AsUnmodifiableGraph[String, DefaultEdge], + id2Node: Map[String, Node] + ): List[Subnet] = { + new BiconnectivityInspector( + gridGraph + ).getConnectedComponents.asScala.toList.zipWithIndex map { + case (subgraph, index) => + buildSubnet( + index, + subgraph.vertexSet().asScala.toSet, + id2Node + ) + } + } + + /** Builds a [[Subnet]] after checking if all nodes have the same nominal + * voltage + * + * @param subnetId + * id of the subnet + * @param nodeIds + * UUIDS of all nodes that live in the subnet + * @param id2node + * mapping between UUID and node + * @return + * the built [[Subnet]] + */ + def buildSubnet( + subnetId: Int, + nodeIds: Set[String], + id2node: Map[String, Node] + ): Subnet = { + val nodes = nodeIds.map(id => + id2node.getOrElse( + id, + throw ConversionException(s"Can't find node id $id in id2node map") + ) + ) + val nomVoltage = nodes.headOption + .getOrElse( + throw GridConfigurationException("There are no nodes in the subnet!") + ) + .nominalVoltage + val divergences = nodes + .filter(node => Math.abs(nomVoltage - node.nominalVoltage) > 0.001) + .map { node => + node.id + " -> " + node.nominalVoltage + } + if (divergences.nonEmpty) + throw ElementConfigurationException( + s"There are the following divergences from the nominal voltage $nomVoltage : $divergences" + ) + val voltLvl = GermanVoltageLevelUtils.parse( + getQuantity(nomVoltage, StandardUnits.RATED_VOLTAGE_MAGNITUDE) + ) + entity.Subnet(subnetId, nodes, voltLvl) + } +} 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 ebec8a1b..f3eb1c0f 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala @@ -1,63 +1,63 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types -import edu.ie3.datamodel.models.input.connector.`type`.LineTypeInput -import edu.ie3.powerFactory2psdm.model.entity.types.LineType -import tech.units.indriya.quantity.Quantities -import edu.ie3.util.quantities.PowerSystemUnits.{ - KILOVOLT, - OHM_PER_KILOMETRE, - SIEMENS_PER_KILOMETRE -} -import tech.units.indriya.unit.Units.AMPERE - -import java.util.UUID -import javax.measure.MetricPrefix - -/** Functionality to translate a [[LineType]] to a [[LineTypeInput]] - */ -object LineTypeConverter { - - def convert(input: LineType): LineTypeInput = { - val b = Quantities.getQuantity( - input.b, - MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) - ) - val g = Quantities.getQuantity( - input.g, - MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) - ) - val r = Quantities.getQuantity( - input.r, - OHM_PER_KILOMETRE - ) - val x = Quantities.getQuantity( - input.x, - OHM_PER_KILOMETRE - ) - val iMax = Quantities.getQuantity( - input.iMax, - MetricPrefix.KILO(AMPERE) - ) - val vRated = - Quantities.getQuantity( - input.vRated, - KILOVOLT - ) - - new LineTypeInput( - UUID.randomUUID(), - input.id, - b, - g, - r, - x, - iMax, - vRated - ) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types +import edu.ie3.datamodel.models.input.connector.`type`.LineTypeInput +import edu.ie3.powerFactory2psdm.model.entity.types.LineType +import tech.units.indriya.quantity.Quantities +import edu.ie3.util.quantities.PowerSystemUnits.{ + KILOVOLT, + OHM_PER_KILOMETRE, + SIEMENS_PER_KILOMETRE +} +import tech.units.indriya.unit.Units.AMPERE + +import java.util.UUID +import javax.measure.MetricPrefix + +/** Functionality to translate a [[LineType]] to a [[LineTypeInput]] + */ +object LineTypeConverter { + + def convert(input: LineType): LineTypeInput = { + val b = Quantities.getQuantity( + input.b, + MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) + ) + val g = Quantities.getQuantity( + input.g, + MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) + ) + val r = Quantities.getQuantity( + input.r, + OHM_PER_KILOMETRE + ) + val x = Quantities.getQuantity( + input.x, + OHM_PER_KILOMETRE + ) + val iMax = Quantities.getQuantity( + input.iMax, + MetricPrefix.KILO(AMPERE) + ) + val vRated = + Quantities.getQuantity( + input.vRated, + KILOVOLT + ) + + new LineTypeInput( + UUID.randomUUID(), + input.id, + b, + g, + r, + x, + iMax, + vRated + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala index 1ba607ea..6a670113 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala @@ -1,87 +1,87 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types - -import edu.ie3.datamodel.models.input.connector.`type`.Transformer2WTypeInput -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - ElementConfigurationException -} -import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W -import tech.units.indriya.quantity.Quantities -import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS, VOLT} -import edu.ie3.util.quantities.PowerSystemUnits.{DEGREE_GEOM, VOLTAMPERE} - -import math.{pow, sqrt} -import java.util.UUID -import javax.measure.MetricPrefix - -object TransformerType2WConverter { - - def convert(input: TransformerType2W): Transformer2WTypeInput = { - - val sRated = input.sRated * 1e6 - val vRatedA = input.vRatedA * 1e3 - val vRatedB = input.vRatedB * 1e3 - val pCu = input.pCu * 1e3 - val pFe = input.pFe * 1e3 - val uk = (input.uk / 100) * vRatedA - val iRated = sRated / (math.sqrt(3) * vRatedA) - val iNoLoadNom = (input.iNoLoad / 100) * iRated - - // short circuit experiment - val rSc = pCu / (3 * pow(iRated, 2)) - val zSc = (uk / sqrt(3)) / iRated - if (rSc > zSc) { - throw ConversionException( - s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + - s"parameters. The short circuit resistance can't exceed the short circuit impedance." - ) - } - val xSc = sqrt(pow(zSc, 2) - pow(rSc, 2)) - - // no load experiment - val yNoLoad = iNoLoadNom / (vRatedA / sqrt(3)) - val gNoLoad = pFe / pow(vRatedA, 2) - if (gNoLoad > yNoLoad) { - throw ConversionException( - s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + - s"The no load conductance can't exceed the no load admittance." - ) - } - val bNoLoad = sqrt((pow(yNoLoad, 2) - pow(gNoLoad, 2)).doubleValue) - - val tapSide = input.tapSide match { - case 0 => false - case 1 => true - case _ => - throw ElementConfigurationException( - s"The tap side of the transformer-type ${input.id} is neither 0 nor 1 - I am confused!" - ) - } - - new Transformer2WTypeInput( - UUID.randomUUID(), - input.id, - Quantities.getQuantity(rSc, OHM), - Quantities.getQuantity(xSc, OHM), - Quantities.getQuantity(input.sRated, MetricPrefix.MEGA(VOLTAMPERE)), - Quantities.getQuantity(vRatedA, VOLT), - Quantities.getQuantity(vRatedB, VOLT), - Quantities.getQuantity(gNoLoad, SIEMENS), - Quantities.getQuantity(bNoLoad, SIEMENS), - Quantities.getQuantity(input.dV, PERCENT), - Quantities.getQuantity(input.dPhi, DEGREE_GEOM), - tapSide, - input.tapNeutr.toInt, - input.tapMin.toInt, - input.tapMax.toInt - ) - - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types + +import edu.ie3.datamodel.models.input.connector.`type`.Transformer2WTypeInput +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + ElementConfigurationException +} +import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W +import tech.units.indriya.quantity.Quantities +import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS, VOLT} +import edu.ie3.util.quantities.PowerSystemUnits.{DEGREE_GEOM, VOLTAMPERE} + +import math.{pow, sqrt} +import java.util.UUID +import javax.measure.MetricPrefix + +object TransformerType2WConverter { + + def convert(input: TransformerType2W): Transformer2WTypeInput = { + + val sRated = input.sRated * 1e6 + val vRatedA = input.vRatedA * 1e3 + val vRatedB = input.vRatedB * 1e3 + val pCu = input.pCu * 1e3 + val pFe = input.pFe * 1e3 + val uk = (input.uk / 100) * vRatedA + val iRated = sRated / (math.sqrt(3) * vRatedA) + val iNoLoadNom = (input.iNoLoad / 100) * iRated + + // short circuit experiment + val rSc = pCu / (3 * pow(iRated, 2)) + val zSc = (uk / sqrt(3)) / iRated + if (rSc > zSc) { + throw ConversionException( + s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + + s"parameters. The short circuit resistance can't exceed the short circuit impedance." + ) + } + val xSc = sqrt(pow(zSc, 2) - pow(rSc, 2)) + + // no load experiment + val yNoLoad = iNoLoadNom / (vRatedA / sqrt(3)) + val gNoLoad = pFe / pow(vRatedA, 2) + if (gNoLoad > yNoLoad) { + throw ConversionException( + s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + + s"The no load conductance can't exceed the no load admittance." + ) + } + val bNoLoad = sqrt((pow(yNoLoad, 2) - pow(gNoLoad, 2)).doubleValue) + + val tapSide = input.tapSide match { + case 0 => false + case 1 => true + case _ => + throw ElementConfigurationException( + s"The tap side of the transformer-type ${input.id} is neither 0 nor 1 - I am confused!" + ) + } + + new Transformer2WTypeInput( + UUID.randomUUID(), + input.id, + Quantities.getQuantity(rSc, OHM), + Quantities.getQuantity(xSc, OHM), + Quantities.getQuantity(input.sRated, MetricPrefix.MEGA(VOLTAMPERE)), + Quantities.getQuantity(vRatedA, VOLT), + Quantities.getQuantity(vRatedB, VOLT), + Quantities.getQuantity(gNoLoad, SIEMENS), + Quantities.getQuantity(bNoLoad, SIEMENS), + Quantities.getQuantity(input.dV, PERCENT), + Quantities.getQuantity(input.dPhi, DEGREE_GEOM), + tapSide, + input.tapNeutr.toInt, + input.tapMin.toInt, + input.tapMax.toInt + ) + + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala index c14694b3..eb92572e 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.io - -final case class ConversionConfigException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends IoException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.io + +final case class ConversionConfigException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends IoException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala index ae6b275d..c73d555e 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.io - -final case class GridParsingException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends IoException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.io + +final case class GridParsingException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends IoException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala index 125413fa..78c35adb 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala @@ -1,14 +1,14 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.io - -/** Base class for grouping io related exceptions - */ -class IoException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends Exception(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.io + +/** Base class for grouping io related exceptions + */ +class IoException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends Exception(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala index 73c9d421..469fc70e 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class ConversionException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class ConversionException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala index 054a4dd3..cb30a2ea 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -case class ElementConfigurationException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +case class ElementConfigurationException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala index b06fb556..be2a8919 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -case class GridConfigurationException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +case class GridConfigurationException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala index ce5d8a24..b68d7d10 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class MissingGridElementException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class MissingGridElementException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala index ed1bc3ce..17eb0a36 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class MissingParameterException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class MissingParameterException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala index 22d6445c..382ac7e4 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala @@ -1,14 +1,14 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -/** Base class for grouping power factory related exceptions - */ -class PfException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends Exception(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +/** Base class for grouping power factory related exceptions + */ +class PfException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends Exception(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala index 47d4c17a..60e62407 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class TestException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class TestException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala b/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala index 15d8ef37..33f5c1c8 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala @@ -1,35 +1,35 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.io - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.model.RawPfGridModel -import io.circe.parser.decode -import io.circe.generic.auto._ -import io.circe.parser._ - -import scala.io.Source -import java.io.File - -object PfGridParser extends LazyLogging { - - def parse(gridFile: String): Option[RawPfGridModel] = { - val source = - Source.fromFile(gridFile) - val jsonString = - try source.mkString - finally source.close - - decode[RawPfGridModel](jsonString) match { - case Left(error) => - logger.error(error.getMessage()) - None - case Right(decodingResult) => - Some(decodingResult) - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.io + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.model.RawPfGridModel +import io.circe.parser.decode +import io.circe.generic.auto._ +import io.circe.parser._ + +import scala.io.Source +import java.io.File + +object PfGridParser extends LazyLogging { + + def parse(gridFile: String): Option[RawPfGridModel] = { + val source = + Source.fromFile(gridFile) + val jsonString = + try source.mkString + finally source.close + + decode[RawPfGridModel](jsonString) match { + case Left(error) => + logger.error(error.getMessage()) + None + case Right(decodingResult) => + Some(decodingResult) + } + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala b/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala index d7fc2604..371335cd 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala @@ -1,41 +1,41 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.main - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.config.{ConfigValidator, ConversionConfig} -import edu.ie3.powerFactory2psdm.converter.GridConverter -import edu.ie3.powerFactory2psdm.io.PfGridParser - -import java.io.File -import edu.ie3.powerFactory2psdm.exception.io.GridParsingException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel -import pureconfig.ConfigSource -import pureconfig.generic.auto._ - -object RunConversion extends LazyLogging { - - def main(args: Array[String]): Unit = { - - logger.info("Parsing the config") - val config = - ConfigSource - .file("src/test/resources/application.conf") - .at("conversion-config") - .loadOrThrow[ConversionConfig] - ConfigValidator.validate(config) - val exportedGridFile = - s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" - logger.info("Parsing the json grid file.") - val pfGrid: RawPfGridModel = PfGridParser - .parse(exportedGridFile) - .getOrElse( - throw GridParsingException("Parsing the Json grid file failed") - ) - val psdmGrid = GridConverter.convert(pfGrid) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.main + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.config.{ConfigValidator, ConversionConfig} +import edu.ie3.powerFactory2psdm.converter.GridConverter +import edu.ie3.powerFactory2psdm.io.PfGridParser + +import java.io.File +import edu.ie3.powerFactory2psdm.exception.io.GridParsingException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel +import pureconfig.ConfigSource +import pureconfig.generic.auto._ + +object RunConversion extends LazyLogging { + + def main(args: Array[String]): Unit = { + + logger.info("Parsing the config") + val config = + ConfigSource + .file("src/test/resources/application.conf") + .at("conversion-config") + .loadOrThrow[ConversionConfig] + ConfigValidator.validate(config) + val exportedGridFile = + s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" + logger.info("Parsing the json grid file.") + val pfGrid: RawPfGridModel = PfGridParser + .parse(exportedGridFile) + .getOrElse( + throw GridParsingException("Parsing the Json grid file failed") + ) + val psdmGrid = GridConverter.convert(pfGrid) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala index cddbf9be..4239c5fc 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala @@ -1,203 +1,203 @@ -/* - * © 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 - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - GridConfigurationException, - MissingParameterException -} -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ - LineTypes, - Lines, - Nodes, - ProjectSettings, - Switches, - TrafoTypes2w, - Trafos2w -} -import edu.ie3.powerFactory2psdm.model.entity.{Line, Node, Switch} -import edu.ie3.powerFactory2psdm.model.entity.types.{ - LineType, - TransformerType2W -} -import edu.ie3.powerFactory2psdm.model.setting.{ConversionPrefixes, UnitSystem} - -/** Representation of the grid which is to be converted to a PSDM - * [[edu.ie3.datamodel.models.input.container.GridContainer]]. The - * preprocessing is done in order to unpack and check Optionals of the - * [[RawPfGridModel]], check for duplicated ids as well as allow for building - * inheritance relationships of all the custom classes of grid elements (e.g. - * nodes, lines, generators) since these can not sensibly be actualized by the - * [[SchemaGenerator]]. - * - * @param nodes - * electrical nodes of the grid - * @param lineTypes - * line types of the grid - * @param lines - * electrical lines of the grid - * @param switches - * electrical switches of the grid - * @param transformerTypes2W - * two-winding transformer types - * @param conversionPrefixes - * conversion prefixes for certain model parameters - */ -final case class PreprocessedPfGridModel( - nodes: List[Node], - lineTypes: List[LineType], - lines: List[Line], - switches: List[Switch], - transformerTypes2W: List[TransformerType2W], - conversionPrefixes: ConversionPrefixes -) - -object PreprocessedPfGridModel extends LazyLogging { - - /** Builds the preprocessed grid model from the unprocessed generated grid - * model. - * - * @param rawGrid - * the raw PowerFactory grid model as generated by the [[SchemaGenerator]] - * @return - * the preprocessed PowerFactory grid model - */ - def build(rawGrid: RawPfGridModel): PreprocessedPfGridModel = { - val projectSettings = extractProjectSettings(rawGrid.projectSettings) - checkUnitSystem( - projectSettings.unitSystem.getOrElse( - throw ConversionException( - "There is no unit system defined." - ) - ) - ) - val conversionPrefixes = buildConversionPrefixes(projectSettings) - val rawNodes = rawGrid.nodes.getOrElse( - throw GridConfigurationException("There are no nodes in the grid.") - ) - val rawLines = rawGrid.lines.getOrElse({ - logger.debug("There are no lines in the grid.") - List.empty[Lines] - }) - val rawLineTypes = rawGrid.lineTypes.getOrElse({ - logger.debug("There are no lines in the grid.") - List.empty[LineTypes] - }) - val rawSwitches = rawGrid.switches.getOrElse({ - logger.debug("There are no switches in the grid.") - List.empty[Switches] - }) - val rawTrafos2W = rawGrid.trafos2w.getOrElse({ - logger.debug("There are no switches in the grid.") - List.empty[Trafos2w] - }) - val rawTrafoTpyes2W = rawGrid.trafoTypes2w.getOrElse({ - logger.debug("There are no 2w trafo types in the grid.") - List.empty[TrafoTypes2w] - }) - - val models = - rawNodes ++ rawLines ++ rawLineTypes ++ rawSwitches ++ rawTrafos2W ++ rawTrafoTpyes2W - val modelIds = models.map { - case node: Nodes => - node.id.getOrElse( - throw MissingParameterException(s"Node $node has no defined id") - ) - case line: Lines => - line.id.getOrElse( - throw MissingParameterException(s"Line $line has no defined id") - ) - case lineType: LineTypes => - lineType.id.getOrElse( - throw MissingParameterException( - s"Line type $lineType has no defined id" - ) - ) - case switch: Switches => - switch.id.getOrElse( - throw MissingParameterException(s"Switch $switch has no defined id") - ) - case trafo2w: Trafos2w => - trafo2w.id.getOrElse( - throw MissingParameterException( - s"Transformer $trafo2w has no defined id" - ) - ) - case trafoTypes2w: TrafoTypes2w => - trafoTypes2w.id.getOrElse( - throw MissingParameterException( - s"Transformer type $trafoTypes2w has no defined id" - ) - ) - } - val uniqueIds = modelIds.distinct - if (uniqueIds.size < modelIds.size) { - val duplicateIds = modelIds.diff(uniqueIds) - throw GridConfigurationException( - s"Can't build grid as there are grid elements with duplicated ids: $duplicateIds" - ) - } - - val nodes = rawNodes.map(Node.build) - val lines = rawLines.map(line => Line.build(line)) - val lineTypes = rawLineTypes.map(LineType.build) - val switches = rawSwitches.flatMap(Switch.maybeBuild) - val trafoTypes2W = rawTrafoTpyes2W.map(TransformerType2W.build) - - PreprocessedPfGridModel( - nodes, - lineTypes, - lines, - switches, - trafoTypes2W, - conversionPrefixes - ) - } - - private def extractProjectSettings( - rawSettings: Option[List[ProjectSettings]] - ): ProjectSettings = { - rawSettings match { - case Some(List(settings)) => settings - case Some(List(_, _, _*)) => - throw ConversionException( - "There are multiple project settings defined." - ) - case None => - throw ConversionException("There are no project settings defined.") - } - } - - private def checkUnitSystem(unitSystem: Double): Unit = { - unitSystem match { - case UnitSystem.metric => () - case _ => - throw ConversionException( - "Conversion is currently only implemented for the metric unit system" - ) - } - } - - private def buildConversionPrefixes( - settings: ProjectSettings - ): ConversionPrefixes = { - ConversionPrefixes( - settings.prefixPQS.getOrElse( - throw MissingParameterException( - "The projects settings miss the prefix specification for active/reactive/apparent power values" - ) - ), - settings.prefixLength.getOrElse( - throw MissingParameterException( - "The project settings miss the prefix specification for line length." - ) - ) - ) - } -} +/* + * © 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 + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + GridConfigurationException, + MissingParameterException +} +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ + LineTypes, + Lines, + Nodes, + ProjectSettings, + Switches, + TrafoTypes2w, + Trafos2w +} +import edu.ie3.powerFactory2psdm.model.entity.{Line, Node, Switch} +import edu.ie3.powerFactory2psdm.model.entity.types.{ + LineType, + TransformerType2W +} +import edu.ie3.powerFactory2psdm.model.setting.{ConversionPrefixes, UnitSystem} + +/** Representation of the grid which is to be converted to a PSDM + * [[edu.ie3.datamodel.models.input.container.GridContainer]]. The + * preprocessing is done in order to unpack and check Optionals of the + * [[RawPfGridModel]], check for duplicated ids as well as allow for building + * inheritance relationships of all the custom classes of grid elements (e.g. + * nodes, lines, generators) since these can not sensibly be actualized by the + * [[SchemaGenerator]]. + * + * @param nodes + * electrical nodes of the grid + * @param lineTypes + * line types of the grid + * @param lines + * electrical lines of the grid + * @param switches + * electrical switches of the grid + * @param transformerTypes2W + * two-winding transformer types + * @param conversionPrefixes + * conversion prefixes for certain model parameters + */ +final case class PreprocessedPfGridModel( + nodes: List[Node], + lineTypes: List[LineType], + lines: List[Line], + switches: List[Switch], + transformerTypes2W: List[TransformerType2W], + conversionPrefixes: ConversionPrefixes +) + +object PreprocessedPfGridModel extends LazyLogging { + + /** Builds the preprocessed grid model from the unprocessed generated grid + * model. + * + * @param rawGrid + * the raw PowerFactory grid model as generated by the [[SchemaGenerator]] + * @return + * the preprocessed PowerFactory grid model + */ + def build(rawGrid: RawPfGridModel): PreprocessedPfGridModel = { + val projectSettings = extractProjectSettings(rawGrid.projectSettings) + checkUnitSystem( + projectSettings.unitSystem.getOrElse( + throw ConversionException( + "There is no unit system defined." + ) + ) + ) + val conversionPrefixes = buildConversionPrefixes(projectSettings) + val rawNodes = rawGrid.nodes.getOrElse( + throw GridConfigurationException("There are no nodes in the grid.") + ) + val rawLines = rawGrid.lines.getOrElse({ + logger.debug("There are no lines in the grid.") + List.empty[Lines] + }) + val rawLineTypes = rawGrid.lineTypes.getOrElse({ + logger.debug("There are no lines in the grid.") + List.empty[LineTypes] + }) + val rawSwitches = rawGrid.switches.getOrElse({ + logger.debug("There are no switches in the grid.") + List.empty[Switches] + }) + val rawTrafos2W = rawGrid.trafos2w.getOrElse({ + logger.debug("There are no switches in the grid.") + List.empty[Trafos2w] + }) + val rawTrafoTpyes2W = rawGrid.trafoTypes2w.getOrElse({ + logger.debug("There are no 2w trafo types in the grid.") + List.empty[TrafoTypes2w] + }) + + val models = + rawNodes ++ rawLines ++ rawLineTypes ++ rawSwitches ++ rawTrafos2W ++ rawTrafoTpyes2W + val modelIds = models.map { + case node: Nodes => + node.id.getOrElse( + throw MissingParameterException(s"Node $node has no defined id") + ) + case line: Lines => + line.id.getOrElse( + throw MissingParameterException(s"Line $line has no defined id") + ) + case lineType: LineTypes => + lineType.id.getOrElse( + throw MissingParameterException( + s"Line type $lineType has no defined id" + ) + ) + case switch: Switches => + switch.id.getOrElse( + throw MissingParameterException(s"Switch $switch has no defined id") + ) + case trafo2w: Trafos2w => + trafo2w.id.getOrElse( + throw MissingParameterException( + s"Transformer $trafo2w has no defined id" + ) + ) + case trafoTypes2w: TrafoTypes2w => + trafoTypes2w.id.getOrElse( + throw MissingParameterException( + s"Transformer type $trafoTypes2w has no defined id" + ) + ) + } + val uniqueIds = modelIds.distinct + if (uniqueIds.size < modelIds.size) { + val duplicateIds = modelIds.diff(uniqueIds) + throw GridConfigurationException( + s"Can't build grid as there are grid elements with duplicated ids: $duplicateIds" + ) + } + + val nodes = rawNodes.map(Node.build) + val lines = rawLines.map(line => Line.build(line)) + val lineTypes = rawLineTypes.map(LineType.build) + val switches = rawSwitches.flatMap(Switch.maybeBuild) + val trafoTypes2W = rawTrafoTpyes2W.map(TransformerType2W.build) + + PreprocessedPfGridModel( + nodes, + lineTypes, + lines, + switches, + trafoTypes2W, + conversionPrefixes + ) + } + + private def extractProjectSettings( + rawSettings: Option[List[ProjectSettings]] + ): ProjectSettings = { + rawSettings match { + case Some(List(settings)) => settings + case Some(List(_, _, _*)) => + throw ConversionException( + "There are multiple project settings defined." + ) + case None => + throw ConversionException("There are no project settings defined.") + } + } + + private def checkUnitSystem(unitSystem: Double): Unit = { + unitSystem match { + case UnitSystem.metric => () + case _ => + throw ConversionException( + "Conversion is currently only implemented for the metric unit system" + ) + } + } + + private def buildConversionPrefixes( + settings: ProjectSettings + ): ConversionPrefixes = { + ConversionPrefixes( + settings.prefixPQS.getOrElse( + throw MissingParameterException( + "The projects settings miss the prefix specification for active/reactive/apparent power values" + ) + ), + settings.prefixLength.getOrElse( + throw MissingParameterException( + "The project settings miss the prefix specification for line length." + ) + ) + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala index c2aae419..4943250f 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala @@ -1,128 +1,128 @@ -/* - * © 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 - -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ - ExtGrid, - LineTypes, - Lines, - Loads, - LoadsLV, - LoadsMV, - Nodes, - PowerPlants, - ProjectSettings, - Pvs, - StatGen, - Switches, - TrafoTypes2w, - TrafoTypes3w, - Trafos2w, - Trafos3w -} - -final case class RawPfGridModel( - trafos2w: Option[List[Trafos2w]], - loadsMV: Option[List[LoadsMV]], - nodes: Option[List[Nodes]], - projectSettings: Option[List[ProjectSettings]], - powerPlants: Option[List[PowerPlants]], - trafoTypes3w: Option[List[TrafoTypes3w]], - pvs: Option[List[Pvs]], - lineTypes: Option[List[LineTypes]], - switches: Option[List[Switches]], - loadsLV: Option[List[LoadsLV]], - statGen: Option[List[StatGen]], - loads: Option[List[Loads]], - trafos3w: Option[List[Trafos3w]], - extGrid: Option[List[ExtGrid]], - trafoTypes2w: Option[List[TrafoTypes2w]], - lines: Option[List[Lines]] -) - -object RawPfGridModel { - - final case class Switches( - id: Option[String], - bus1Id: Option[String], - bus2Id: Option[String] - ) - - final case class Pvs() - - final case class ConElms(id: Option[String], pfCls: Option[String]) - - final case class Loads(id: Option[String]) - - final case class LineTypes( - bline: Option[Double], - gline: Option[Double], - id: Option[String], - sline: Option[Double], - uline: Option[Double], - xline: Option[Double], - rline: Option[Double] - ) - - final case class StatGen(id: Option[String]) - - final case class Lines( - id: Option[String], - bus1Id: Option[String], - bus2Id: Option[String] - ) - - final case class PowerPlants(id: Option[String]) - - final case class Trafos3w() - - final case class ExtGrid(id: Option[String]) - - final case class TrafoTypes2w( - nntap0: Option[Double], - pfe: Option[Double], - uktr: Option[Double], - id: Option[String], - ntpmn: Option[Double], - dutap: Option[Double], - strn: Option[Double], - utrn_l: Option[Double], - curmg: Option[Double], - tap_side: Option[Double], - ntpmx: Option[Double], - pcutr: Option[Double], - phitr: Option[Double], - utrn_h: Option[Double] - ) - - final case class ProjectSettings( - unitSystem: Option[Double], - prefixPQS: Option[String], - prefixLength: Option[String] - ) - - final case class LoadsLV(id: Option[String]) - - final case class Nodes( - vtarget: Option[Double], - conElms: Option[List[Option[ConElms]]], - GPSlat: Option[Double], - id: Option[String], - GPSlon: Option[Double], - uknom: Option[Double] - ) - - final case class Trafos2w( - id: Option[String], - conElms: Option[List[Option[ConElms]]] - ) - - final case class TrafoTypes3w() - - final case class LoadsMV(id: Option[String]) - -} +/* + * © 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 + +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ + ExtGrid, + LineTypes, + Lines, + Loads, + LoadsLV, + LoadsMV, + Nodes, + PowerPlants, + ProjectSettings, + Pvs, + StatGen, + Switches, + TrafoTypes2w, + TrafoTypes3w, + Trafos2w, + Trafos3w +} + +final case class RawPfGridModel( + trafos2w: Option[List[Trafos2w]], + loadsMV: Option[List[LoadsMV]], + nodes: Option[List[Nodes]], + projectSettings: Option[List[ProjectSettings]], + powerPlants: Option[List[PowerPlants]], + trafoTypes3w: Option[List[TrafoTypes3w]], + pvs: Option[List[Pvs]], + lineTypes: Option[List[LineTypes]], + switches: Option[List[Switches]], + loadsLV: Option[List[LoadsLV]], + statGen: Option[List[StatGen]], + loads: Option[List[Loads]], + trafos3w: Option[List[Trafos3w]], + extGrid: Option[List[ExtGrid]], + trafoTypes2w: Option[List[TrafoTypes2w]], + lines: Option[List[Lines]] +) + +object RawPfGridModel { + + final case class Switches( + id: Option[String], + bus1Id: Option[String], + bus2Id: Option[String] + ) + + final case class Pvs() + + final case class ConElms(id: Option[String], pfCls: Option[String]) + + final case class Loads(id: Option[String]) + + final case class LineTypes( + bline: Option[Double], + gline: Option[Double], + id: Option[String], + sline: Option[Double], + uline: Option[Double], + xline: Option[Double], + rline: Option[Double] + ) + + final case class StatGen(id: Option[String]) + + final case class Lines( + id: Option[String], + bus1Id: Option[String], + bus2Id: Option[String] + ) + + final case class PowerPlants(id: Option[String]) + + final case class Trafos3w() + + final case class ExtGrid(id: Option[String]) + + final case class TrafoTypes2w( + nntap0: Option[Double], + pfe: Option[Double], + uktr: Option[Double], + id: Option[String], + ntpmn: Option[Double], + dutap: Option[Double], + strn: Option[Double], + utrn_l: Option[Double], + curmg: Option[Double], + tap_side: Option[Double], + ntpmx: Option[Double], + pcutr: Option[Double], + phitr: Option[Double], + utrn_h: Option[Double] + ) + + final case class ProjectSettings( + unitSystem: Option[Double], + prefixPQS: Option[String], + prefixLength: Option[String] + ) + + final case class LoadsLV(id: Option[String]) + + final case class Nodes( + vtarget: Option[Double], + conElms: Option[List[Option[ConElms]]], + GPSlat: Option[Double], + id: Option[String], + GPSlon: Option[Double], + uknom: Option[Double] + ) + + final case class Trafos2w( + id: Option[String], + conElms: Option[List[Option[ConElms]]] + ) + + final case class TrafoTypes3w() + + final case class LoadsMV(id: Option[String]) + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala index d5919ee3..5e5ad46d 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala @@ -1,38 +1,38 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ConElms - -/** Data type that represents a ConnectedElement - * - * @param id - * identifier - * @param pfCls - * PowerFactory class that element represents - */ -case class ConnectedElement( - id: String, - pfCls: String -) extends EntityModel - -object ConnectedElement { - def build(conElm: ConElms): ConnectedElement = { - val id = conElm.id.getOrElse( - throw MissingParameterException( - s"There is no id for the connected element: $conElm" - ) - ) - val pfCls = conElm.pfCls.getOrElse( - throw MissingParameterException( - s"There is no PowerFactory class mentioned for connected element: $conElm" - ) - ) - ConnectedElement(id, pfCls) - } -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ConElms + +/** Data type that represents a ConnectedElement + * + * @param id + * identifier + * @param pfCls + * PowerFactory class that element represents + */ +case class ConnectedElement( + id: String, + pfCls: String +) extends EntityModel + +object ConnectedElement { + def build(conElm: ConElms): ConnectedElement = { + val id = conElm.id.getOrElse( + throw MissingParameterException( + s"There is no id for the connected element: $conElm" + ) + ) + val pfCls = conElm.pfCls.getOrElse( + throw MissingParameterException( + s"There is no PowerFactory class mentioned for connected element: $conElm" + ) + ) + ConnectedElement(id, pfCls) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala index 0a635f82..3f09a921 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala @@ -1,14 +1,14 @@ -/* - * © 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 - -/** Denotes an element that connects two nodes within a Subnet - */ -trait Edge { - val nodeAId: String - val nodeBId: String -} +/* + * © 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 + +/** Denotes an element that connects two nodes within a Subnet + */ +trait Edge { + val nodeAId: String + val nodeBId: String +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala index fcd0cd5c..286eeb43 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala @@ -1,16 +1,16 @@ -/* - * © 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 - -/** Common Entity parameters - */ -trait EntityModel { - - /** Id of the entity - */ - val id: String -} +/* + * © 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 + +/** Common Entity parameters + */ +trait EntityModel { + + /** Id of the entity + */ + val id: String +} 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 31db92b8..1d6a612c 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala @@ -1,46 +1,46 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines - -/** Electrical line - * - * @param id - * identifier - * @param nodeAId - * id of connected node - * @param nodeBId - * id of connected node - */ -final case class Line( - id: String, - nodeAId: String, - nodeBId: String -) extends EntityModel - with Edge - -object Line { - - def build(rawLine: Lines): Line = { - val id = rawLine.id.getOrElse( - throw MissingParameterException(s"There is no id for line $rawLine") - ) - val nodeAId = rawLine.bus1Id.getOrElse( - throw MissingParameterException(s"Line: $id has no defined node a") - ) - val nodeBId = rawLine.bus2Id.getOrElse( - throw MissingParameterException(s"Line: $id has no defined node b") - ) - Line( - id, - nodeAId, - nodeBId - ) - } -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines + +/** Electrical line + * + * @param id + * identifier + * @param nodeAId + * id of connected node + * @param nodeBId + * id of connected node + */ +final case class Line( + id: String, + nodeAId: String, + nodeBId: String +) extends EntityModel + with Edge + +object Line { + + def build(rawLine: Lines): Line = { + val id = rawLine.id.getOrElse( + throw MissingParameterException(s"There is no id for line $rawLine") + ) + val nodeAId = rawLine.bus1Id.getOrElse( + throw MissingParameterException(s"Line: $id has no defined node a") + ) + val nodeBId = rawLine.bus2Id.getOrElse( + throw MissingParameterException(s"Line: $id has no defined node b") + ) + Line( + id, + nodeAId, + nodeBId + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala index 51b7fd3e..a2e5649c 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala @@ -1,75 +1,75 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Nodes - -/** Electrical node - * - * @param id - * identifier - * @param nominalVoltage - * nominal voltage in kV - * @param vTarget - * target voltage in p.u. - * @param lat - * latitude - * @param lon - * longitude - * @param conElms - * connected elements to the node - */ -final case class Node( - id: String, - nominalVoltage: Double, - vTarget: Double, - lat: Option[Double], - lon: Option[Double], - conElms: List[ConnectedElement] -) extends EntityModel - -object Node { - - /** Build a [[Node]] from a raw [[Nodes]] - * - * @param rawNode - * raw schema generated node - * @return - * [[Node]] - */ - def build(rawNode: Nodes): Node = { - val id = rawNode.id.getOrElse( - throw MissingParameterException(s"There is no id for node $rawNode") - ) - val nominalVoltage = rawNode.uknom.getOrElse( - throw MissingParameterException( - s"Node: $id has no defined nominal voltage" - ) - ) - val vTarget = rawNode.vtarget.getOrElse( - throw MissingParameterException( - s"Node: $id has no defined target voltage" - ) - ) - val conElms = rawNode.conElms - .getOrElse( - throw MissingParameterException(s"Node: $id has no connected elements") - ) - .flatten - .map(conElm => ConnectedElement.build(conElm)) - - Node( - id, - nominalVoltage, - vTarget, - rawNode.GPSlat, - rawNode.GPSlon, - conElms - ) - } -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Nodes + +/** Electrical node + * + * @param id + * identifier + * @param nominalVoltage + * nominal voltage in kV + * @param vTarget + * target voltage in p.u. + * @param lat + * latitude + * @param lon + * longitude + * @param conElms + * connected elements to the node + */ +final case class Node( + id: String, + nominalVoltage: Double, + vTarget: Double, + lat: Option[Double], + lon: Option[Double], + conElms: List[ConnectedElement] +) extends EntityModel + +object Node { + + /** Build a [[Node]] from a raw [[Nodes]] + * + * @param rawNode + * raw schema generated node + * @return + * [[Node]] + */ + def build(rawNode: Nodes): Node = { + val id = rawNode.id.getOrElse( + throw MissingParameterException(s"There is no id for node $rawNode") + ) + val nominalVoltage = rawNode.uknom.getOrElse( + throw MissingParameterException( + s"Node: $id has no defined nominal voltage" + ) + ) + val vTarget = rawNode.vtarget.getOrElse( + throw MissingParameterException( + s"Node: $id has no defined target voltage" + ) + ) + val conElms = rawNode.conElms + .getOrElse( + throw MissingParameterException(s"Node: $id has no connected elements") + ) + .flatten + .map(conElm => ConnectedElement.build(conElm)) + + Node( + id, + nominalVoltage, + vTarget, + rawNode.GPSlat, + rawNode.GPSlon, + conElms + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala index e156eba2..f2136e39 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala @@ -1,20 +1,20 @@ -/* - * © 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.datamodel.models.voltagelevels.VoltageLevel - -/** Data type that wraps necessary information for the different subnets - * - * @param id - * of the subnet - * @param nodes - * UUIDS of the nodes inside the subnet - * @param voltLvl - * voltage level that the nodes live in - */ -final case class Subnet(id: Int, nodes: Set[Node], voltLvl: VoltageLevel) +/* + * © 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.datamodel.models.voltagelevels.VoltageLevel + +/** Data type that wraps necessary information for the different subnets + * + * @param id + * of the subnet + * @param nodes + * UUIDS of the nodes inside the subnet + * @param voltLvl + * voltage level that the nodes live in + */ +final case class Subnet(id: Int, nodes: Set[Node], voltLvl: VoltageLevel) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala index e104209d..a005cf38 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala @@ -1,67 +1,67 @@ -/* - * © 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 com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches - -/** Electrical Switch - * - * @param id - * identifier - * @param nodeAId - * id of one of the connected nodes - * @param nodeBId - * id of one of the connected nodes - */ -final case class Switch( - id: String, - nodeAId: String, - nodeBId: String -) extends EntityModel - with Edge - -object Switch extends LazyLogging { - - /** Builds an Option that probably contains a [[Switch]] - * - * Note: We are not throwing an Exception if one connection of the switches - * is missing, since unused singly connected switches in PowerFactoryGrid - * substations are a thing. In that case we don't build it and return None. - * - * @param rawSwitch - * the schema generated switch - * @return - * a [[Switch]] - */ - def maybeBuild(rawSwitch: Switches): Option[Switch] = { - val id = rawSwitch.id.getOrElse( - throw MissingParameterException(s"There is no id for switch $rawSwitch") - ) - (rawSwitch.bus1Id, rawSwitch.bus2Id) match { - case (Some(bus1Id), Some(bus2Id)) => - Some( - Switch( - id, - bus1Id, - bus2Id - ) - ) - case (None, Some(_)) | (Some(_), None) => - logger.warn( - s"Switch: $id is not being built, as it's only connected to a single node" - ) - None - case _ => - throw MissingParameterException( - s"Switch: $id is not connected to any node" - ) - } - } -} +/* + * © 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 com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches + +/** Electrical Switch + * + * @param id + * identifier + * @param nodeAId + * id of one of the connected nodes + * @param nodeBId + * id of one of the connected nodes + */ +final case class Switch( + id: String, + nodeAId: String, + nodeBId: String +) extends EntityModel + with Edge + +object Switch extends LazyLogging { + + /** Builds an Option that probably contains a [[Switch]] + * + * Note: We are not throwing an Exception if one connection of the switches + * is missing, since unused singly connected switches in PowerFactoryGrid + * substations are a thing. In that case we don't build it and return None. + * + * @param rawSwitch + * the schema generated switch + * @return + * a [[Switch]] + */ + def maybeBuild(rawSwitch: Switches): Option[Switch] = { + val id = rawSwitch.id.getOrElse( + throw MissingParameterException(s"There is no id for switch $rawSwitch") + ) + (rawSwitch.bus1Id, rawSwitch.bus2Id) match { + case (Some(bus1Id), Some(bus2Id)) => + Some( + Switch( + id, + bus1Id, + bus2Id + ) + ) + case (None, Some(_)) | (Some(_), None) => + logger.warn( + s"Switch: $id is not being built, as it's only connected to a single node" + ) + None + case _ => + throw MissingParameterException( + s"Switch: $id is not connected to any node" + ) + } + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala index 014bce44..c55325d0 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala @@ -1,89 +1,89 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes -import edu.ie3.powerFactory2psdm.model.entity.EntityModel - -/** Electrical line - * - * @param id - * identifier - * @param vRated - * rated voltage in kA - * @param iMax - * thermal current in kA - * @param r - * specific resistance in Ohm/km - * @param x - * specific reactance in Ohm/km - * @param b - * phase-to-ground conductance in micro Siemens/km - * @param g - * phase-to-ground conductance in micro Siemens/km - */ -final case class LineType( - id: String, - vRated: Double, - iMax: Double, - r: Double, - x: Double, - b: Double, - g: Double -) extends EntityModel - -object LineType { - - def build(rawLineType: LineTypes): LineType = { - val id = rawLineType.id.getOrElse( - throw MissingParameterException( - s"There is no id for line type $rawLineType" - ) - ) - val vRated = rawLineType.uline.getOrElse( - throw MissingParameterException( - s"There is no rated voltage defined for line type: $id" - ) - ) - val iMax = rawLineType.sline.getOrElse( - throw MissingParameterException( - s"There is no maximum thermal current defined for line type: $id" - ) - ) - val r = rawLineType.rline.getOrElse( - throw MissingParameterException( - s"There is no specific resistance defined for line type: $id" - ) - ) - val x = rawLineType.xline.getOrElse( - throw MissingParameterException( - s"There is no specific reactance defined for line type: $id" - ) - ) - val b = rawLineType.bline.getOrElse( - throw MissingParameterException( - s"There is no phase-to-ground conductance defined for line type: $id" - ) - ) - val g = rawLineType.gline.getOrElse( - throw MissingParameterException( - s"There is no phase-to-ground susceptance defined for line type: $id" - ) - ) - - LineType( - id, - vRated, - iMax, - r, - x, - b, - g - ) - } -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes +import edu.ie3.powerFactory2psdm.model.entity.EntityModel + +/** Electrical line + * + * @param id + * identifier + * @param vRated + * rated voltage in kA + * @param iMax + * thermal current in kA + * @param r + * specific resistance in Ohm/km + * @param x + * specific reactance in Ohm/km + * @param b + * phase-to-ground conductance in micro Siemens/km + * @param g + * phase-to-ground conductance in micro Siemens/km + */ +final case class LineType( + id: String, + vRated: Double, + iMax: Double, + r: Double, + x: Double, + b: Double, + g: Double +) extends EntityModel + +object LineType { + + def build(rawLineType: LineTypes): LineType = { + val id = rawLineType.id.getOrElse( + throw MissingParameterException( + s"There is no id for line type $rawLineType" + ) + ) + val vRated = rawLineType.uline.getOrElse( + throw MissingParameterException( + s"There is no rated voltage defined for line type: $id" + ) + ) + val iMax = rawLineType.sline.getOrElse( + throw MissingParameterException( + s"There is no maximum thermal current defined for line type: $id" + ) + ) + val r = rawLineType.rline.getOrElse( + throw MissingParameterException( + s"There is no specific resistance defined for line type: $id" + ) + ) + val x = rawLineType.xline.getOrElse( + throw MissingParameterException( + s"There is no specific reactance defined for line type: $id" + ) + ) + val b = rawLineType.bline.getOrElse( + throw MissingParameterException( + s"There is no phase-to-ground conductance defined for line type: $id" + ) + ) + val g = rawLineType.gline.getOrElse( + throw MissingParameterException( + s"There is no phase-to-ground susceptance defined for line type: $id" + ) + ) + + LineType( + id, + vRated, + iMax, + r, + x, + b, + g + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala index bb421809..8b725dc3 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala @@ -1,166 +1,166 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w -import edu.ie3.powerFactory2psdm.model.entity.EntityModel - -/** Transformer type - * - * @param id - * Identifier - * @param sRated - * Rated apparent power in MVA - * @param vRatedA - * Rated voltage of the high voltage winding in kV - * @param vRatedB - * Rated voltage of the low voltage winding in kV - * @param dV - * Voltage magnitude deviation per tap position in % - * @param dPhi - * Voltage angle deviation per tap position in ° - * @param tapSide - * Selection of winding, where the tap changer is installed (0 = OS, 1 = US). - * @param tapNeutr - * Neutral tap position - * @param tapMin - * Minimum available tap position - * @param tapMax - * Maximum available tap position - * @param uk - * Short circuit voltage in % - * @param iNoLoad - * No load current in % - * @param pFe - * Iron losses in kW - * @param pCu - * Copper losses in kW - */ -final case class TransformerType2W( - id: String, - sRated: Double, - vRatedA: Double, - vRatedB: Double, - dV: Double, - dPhi: Double, - tapSide: Double, - tapNeutr: Double, - tapMin: Double, - tapMax: Double, - uk: Double, - iNoLoad: Double, - pFe: Double, - pCu: Double -) extends EntityModel - -object TransformerType2W { - - def build(rawType: TrafoTypes2w): TransformerType2W = { - val id = rawType.id.getOrElse( - throw MissingParameterException( - s"There is no id for transformer-type: $rawType" - ) - ) - - val sRated = rawType.strn.getOrElse( - throw MissingParameterException( - s"There is no rated apparent power for transformer-type: $id" - ) - ) - - val vRatedA = rawType.utrn_h.getOrElse( - throw MissingParameterException( - s"There is no voltage of high winding side for transformer-type: $id" - ) - ) - - val vRatedB = rawType.utrn_l.getOrElse( - throw MissingParameterException( - s"There is no voltage of low winding side for transformer-type: $id" - ) - ) - - val dV = rawType.dutap.getOrElse( - throw MissingParameterException( - s"There is no voltage magnitude deviation per tap position for transfomer type: $id" - ) - ) - - val dPhi = rawType.phitr.getOrElse( - throw MissingParameterException( - s"There is no voltage angle deviation per tap position for transformer-type: $id" - ) - ) - - val tapSide = rawType.tap_side.getOrElse( - throw MissingParameterException( - s"There is no selection of winding where tap changer is installed for transformer-type: $id" - ) - ) - - val tapNeutr = rawType.nntap0.getOrElse( - throw MissingParameterException( - s"There is no neutral tap position defined for transformer-type: $id" - ) - ) - - val tapMin = rawType.ntpmn.getOrElse( - throw MissingParameterException( - s"There is no minmum tap position defined for transformer-type: $id" - ) - ) - - val tapMax = rawType.ntpmx.getOrElse( - throw MissingParameterException( - s"There is no maximum tap position defined for transformer-type: $id" - ) - ) - - val uk = rawType.uktr.getOrElse( - throw MissingParameterException( - s"There is no short circuit voltage defined for transformer-type: $id" - ) - ) - - val iNoLoad = rawType.curmg.getOrElse( - throw MissingParameterException( - s"There is no no load current defined for transformer-type: $id" - ) - ) - - val pFe = rawType.pfe.getOrElse( - throw MissingParameterException( - s"There is no iron loss defined for transformer-type: $id" - ) - ) - - val pCu = rawType.pcutr.getOrElse( - throw MissingParameterException( - s"There is no iron loss defined for transformer-type: $id" - ) - ) - - TransformerType2W( - id, - sRated, - vRatedA, - vRatedB, - dV, - dPhi, - tapSide, - tapNeutr, - tapMin, - tapMax, - uk, - iNoLoad, - pFe, - pCu - ) - } - -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w +import edu.ie3.powerFactory2psdm.model.entity.EntityModel + +/** Transformer type + * + * @param id + * Identifier + * @param sRated + * Rated apparent power in MVA + * @param vRatedA + * Rated voltage of the high voltage winding in kV + * @param vRatedB + * Rated voltage of the low voltage winding in kV + * @param dV + * Voltage magnitude deviation per tap position in % + * @param dPhi + * Voltage angle deviation per tap position in ° + * @param tapSide + * Selection of winding, where the tap changer is installed (0 = OS, 1 = US). + * @param tapNeutr + * Neutral tap position + * @param tapMin + * Minimum available tap position + * @param tapMax + * Maximum available tap position + * @param uk + * Short circuit voltage in % + * @param iNoLoad + * No load current in % + * @param pFe + * Iron losses in kW + * @param pCu + * Copper losses in kW + */ +final case class TransformerType2W( + id: String, + sRated: Double, + vRatedA: Double, + vRatedB: Double, + dV: Double, + dPhi: Double, + tapSide: Double, + tapNeutr: Double, + tapMin: Double, + tapMax: Double, + uk: Double, + iNoLoad: Double, + pFe: Double, + pCu: Double +) extends EntityModel + +object TransformerType2W { + + def build(rawType: TrafoTypes2w): TransformerType2W = { + val id = rawType.id.getOrElse( + throw MissingParameterException( + s"There is no id for transformer-type: $rawType" + ) + ) + + val sRated = rawType.strn.getOrElse( + throw MissingParameterException( + s"There is no rated apparent power for transformer-type: $id" + ) + ) + + val vRatedA = rawType.utrn_h.getOrElse( + throw MissingParameterException( + s"There is no voltage of high winding side for transformer-type: $id" + ) + ) + + val vRatedB = rawType.utrn_l.getOrElse( + throw MissingParameterException( + s"There is no voltage of low winding side for transformer-type: $id" + ) + ) + + val dV = rawType.dutap.getOrElse( + throw MissingParameterException( + s"There is no voltage magnitude deviation per tap position for transfomer type: $id" + ) + ) + + val dPhi = rawType.phitr.getOrElse( + throw MissingParameterException( + s"There is no voltage angle deviation per tap position for transformer-type: $id" + ) + ) + + val tapSide = rawType.tap_side.getOrElse( + throw MissingParameterException( + s"There is no selection of winding where tap changer is installed for transformer-type: $id" + ) + ) + + val tapNeutr = rawType.nntap0.getOrElse( + throw MissingParameterException( + s"There is no neutral tap position defined for transformer-type: $id" + ) + ) + + val tapMin = rawType.ntpmn.getOrElse( + throw MissingParameterException( + s"There is no minmum tap position defined for transformer-type: $id" + ) + ) + + val tapMax = rawType.ntpmx.getOrElse( + throw MissingParameterException( + s"There is no maximum tap position defined for transformer-type: $id" + ) + ) + + val uk = rawType.uktr.getOrElse( + throw MissingParameterException( + s"There is no short circuit voltage defined for transformer-type: $id" + ) + ) + + val iNoLoad = rawType.curmg.getOrElse( + throw MissingParameterException( + s"There is no no load current defined for transformer-type: $id" + ) + ) + + val pFe = rawType.pfe.getOrElse( + throw MissingParameterException( + s"There is no iron loss defined for transformer-type: $id" + ) + ) + + val pCu = rawType.pcutr.getOrElse( + throw MissingParameterException( + s"There is no iron loss defined for transformer-type: $id" + ) + ) + + TransformerType2W( + id, + sRated, + vRatedA, + vRatedB, + dV, + dPhi, + tapSide, + tapNeutr, + tapMin, + tapMax, + uk, + iNoLoad, + pFe, + pCu + ) + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala index 01ec0a68..3864ebee 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala @@ -1,53 +1,53 @@ -/* - * © 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.setting - -/** For the active, reactive and apparent power of loads and the length of lines - * the unit system within power factory can be adjusted. This class holds the - * proper prefix values for their conversion. - * - * @param pqsPrefix - * prefix value for active, reactive and apparent power of loads - * @param lengthPrefix - * prefix value for length of lines - */ -class ConversionPrefixes private ( - pqsPrefix: Double, - lengthPrefix: Double -) { - def loadPQSPrefixValue(): Double = pqsPrefix - def lineLengthPrefixValue(): Double = lengthPrefix -} - -object ConversionPrefixes { - def apply( - pqsPrefSymbol: String, - lengthPrefSymbol: String - ): ConversionPrefixes = { - new ConversionPrefixes( - getMetricPrefixBySymbol(pqsPrefSymbol), - getMetricPrefixBySymbol(lengthPrefSymbol) - ) - } - - private def getMetricPrefixBySymbol(symbol: String): Double = symbol match { - case "a" => 1e-18 - case "f" => 1e-15 - case "p" => 1e-12 - case "n" => 1e-9 - case "u" => 1e-6 - case "m" => 1e-3 - case "" => 1 - case "k" => 1e3 - case "M" => 1e6 - case "G" => 1e9 - case "T" => 1e12 - case "P" => 1e15 - case "E" => 1e18 - } - -} +/* + * © 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.setting + +/** For the active, reactive and apparent power of loads and the length of lines + * the unit system within power factory can be adjusted. This class holds the + * proper prefix values for their conversion. + * + * @param pqsPrefix + * prefix value for active, reactive and apparent power of loads + * @param lengthPrefix + * prefix value for length of lines + */ +class ConversionPrefixes private ( + pqsPrefix: Double, + lengthPrefix: Double +) { + def loadPQSPrefixValue(): Double = pqsPrefix + def lineLengthPrefixValue(): Double = lengthPrefix +} + +object ConversionPrefixes { + def apply( + pqsPrefSymbol: String, + lengthPrefSymbol: String + ): ConversionPrefixes = { + new ConversionPrefixes( + getMetricPrefixBySymbol(pqsPrefSymbol), + getMetricPrefixBySymbol(lengthPrefSymbol) + ) + } + + private def getMetricPrefixBySymbol(symbol: String): Double = symbol match { + case "a" => 1e-18 + case "f" => 1e-15 + case "p" => 1e-12 + case "n" => 1e-9 + case "u" => 1e-6 + case "m" => 1e-3 + case "" => 1 + case "k" => 1e3 + case "M" => 1e6 + case "G" => 1e9 + case "T" => 1e12 + case "P" => 1e15 + case "E" => 1e18 + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala index 7090557b..d39d5f6d 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala @@ -1,16 +1,16 @@ -/* - * © 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.setting - -/** Enumeration of the different unit system options within the project - * settings. - */ -object UnitSystem extends Enumeration { - val metric = 0 - val englishTransportation = 1d - val englishIndustry = 2d -} +/* + * © 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.setting + +/** Enumeration of the different unit system options within the project + * settings. + */ +object UnitSystem extends Enumeration { + val metric = 0 + val englishTransportation = 1d + val englishIndustry = 2d +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala index a29db96d..6a78e9cc 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala @@ -1,32 +1,31 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import java.nio.file.{Files, Paths} -import org.scalafmt.interfaces.Scalafmt - -import java.io.File - -object Formatter { - - def format(str: String, fmtPath: Option[String]): String = { - val classLoader = this.getClass.getClassLoader - val scalafmt = Scalafmt.create(classLoader) - val configFile = new File(classLoader.getResource(".scalafmt.conf").getFile) - val defaultConfigPath = - Paths.get(configFile.getPath) - val defaultConfig = - if (Files.exists(defaultConfigPath)) defaultConfigPath else Paths.get("") - val config = fmtPath.fold(defaultConfig)(Paths.get(_)) - val result = scalafmt - .withRespectVersion(false) - .format(config, Paths.get("Nil.scala"), str) - scalafmt.clear() - result - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import java.nio.file.{Files, Paths} +import org.scalafmt.interfaces.Scalafmt +import java.io.File + +object Formatter { + + def format(str: String, fmtPath: Option[String]): String = { + val classLoader = this.getClass.getClassLoader + val scalafmt = Scalafmt.create(classLoader) + val configFile = new File(classLoader.getResource(".scalafmt.conf").getFile) + val defaultConfigPath = + Paths.get(configFile.getPath) + val defaultConfig = + if (Files.exists(defaultConfigPath)) defaultConfigPath else Paths.get("") + val config = fmtPath.fold(defaultConfig)(Paths.get(_)) + val result = scalafmt + .withRespectVersion(false) + .format(config, Paths.get("Nil.scala"), str) + scalafmt.clear() + result + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala index 3c33b16e..3ea0ef39 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala @@ -1,50 +1,50 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.model.RawPfGridModel -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches - -object GridPreparator extends LazyLogging { - - /** Perform preparation of the [[RawPfGridModel]] before the actual conversion - * can happen. - * - * @param pfGrid - * the [[RawPfGridModel]] to prepare - * @return - * the prepared [[RawPfGridModel]] - */ - def prepare(pfGrid: RawPfGridModel): RawPfGridModel = { - val filteredSwitches = removeSinglyConnectedSwitches(pfGrid.switches) - pfGrid.copy(switches = filteredSwitches) - } - - /** Removes [[Switches]] from a [[RawPfGridModel]] that are only connected to - * a single node. - * - * @param maybeSwitches - * @return - */ - def removeSinglyConnectedSwitches( - maybeSwitches: Option[List[RawPfGridModel.Switches]] - ): Option[List[RawPfGridModel.Switches]] = - maybeSwitches.map(switches => { - val (fullyConnected, singlyConnected) = - switches.partition(isFullyConnectedSwitch) - singlyConnected.foreach(switch => - logger.debug( - s"Removed switch with id: ${switch.id.getOrElse("NO_ID")}, since it only has a single connection." - ) - ) - fullyConnected - }) - - def isFullyConnectedSwitch(switch: Switches): Boolean = - switch.bus1Id.isDefined & switch.bus2Id.isDefined -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.model.RawPfGridModel +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches + +object GridPreparator extends LazyLogging { + + /** Perform preparation of the [[RawPfGridModel]] before the actual conversion + * can happen. + * + * @param pfGrid + * the [[RawPfGridModel]] to prepare + * @return + * the prepared [[RawPfGridModel]] + */ + def prepare(pfGrid: RawPfGridModel): RawPfGridModel = { + val filteredSwitches = removeSinglyConnectedSwitches(pfGrid.switches) + pfGrid.copy(switches = filteredSwitches) + } + + /** Removes [[Switches]] from a [[RawPfGridModel]] that are only connected to + * a single node. + * + * @param maybeSwitches + * @return + */ + def removeSinglyConnectedSwitches( + maybeSwitches: Option[List[RawPfGridModel.Switches]] + ): Option[List[RawPfGridModel.Switches]] = + maybeSwitches.map(switches => { + val (fullyConnected, singlyConnected) = + switches.partition(isFullyConnectedSwitch) + singlyConnected.foreach(switch => + logger.debug( + s"Removed switch with id: ${switch.id.getOrElse("NO_ID")}, since it only has a single connection." + ) + ) + fullyConnected + }) + + def isFullyConnectedSwitch(switch: Switches): Boolean = + switch.bus1Id.isDefined & switch.bus2Id.isDefined +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala index d0c3021d..014aec9c 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala @@ -1,420 +1,420 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import java.io.{File, PrintWriter} -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.util.StringUtils -import io.circe.Json.Folder -import io.circe.{Json, JsonNumber, JsonObject} -import io.circe.parser._ - -import scala.io.Source - -object SchemaGenerator extends LazyLogging { - def main(args: Array[String]): Unit = { - val source = - Source.fromFile( - s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" - ) - val jsonString = - try source.mkString - finally source.close - run(jsonString) - } - - def run(jsonString: String): Unit = { - run( - jsonString, - "RawGridModel", - "edu.ie3.powerFactory2psdm.model.powerfactory" - ).foreach(formatedClassString => { - val pw = new PrintWriter( - new File( - s"${new File(".").getCanonicalPath}/src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" - ) - ) - pw.write(formatedClassString) - pw.close() - }) - } - - def run( - jsonString: String, - className: String, - `package`: String - ): Option[String] = - parse(jsonString) match { - case Left(error) => - logger.error( - s"Exception during json file parsing: '${error.getMessage()}'" - ) - None - case Right(json) => - generateClass( - json, - className, - `package` - ).map(Formatter.format(_, None)) - } - - private def generateClass( - json: Json, - className: String, - `package`: String - ): Option[String] = { - - json.asObject match { - case Some(jsonObject) if !jsonObject.isEmpty => - val classes: Iterable[SimpleClass] = - json - .foldWith(ClassFolder(className, `package`)) - .flatMap(_.classes) - .groupBy(_.name) - .flatMap { - case (name, elems) if elems.size > 1 => - // duplicated class name, merge the fields and provide default None for all of them - Vector(SimpleClass(name, elems.flatMap(_.fields).distinct)) - case (_, elems) => elems - } - - val wrapperClass = - s""" - | final case class ${this.className(className)}( - | ${jsonObject.toMap.keys - .map(key => s"$key: Option[List[${this.className(key)}]]") - .mkString(",\n")} - | ) - |""".stripMargin - - val importStatement = - s""" - | import ${`package`}.${this.className(className)}.{ - | ${jsonObject.toMap.keys - .map(key => this.className(key)) - .mkString(",\n")} - | } - |""".stripMargin - - val packageStatement = - s"package ${`package`}" - - val wrapperObj = - s""" - | object ${this.className(className)}{ - | - | ${classes.map(_.toString).mkString("")} - | } - |""".stripMargin - - Some(packageStatement + importStatement + wrapperClass + wrapperObj) - case _ => - None - } - - } - - private def className(name: String) = - StringUtils.snakeCaseToCamelCase(StringUtils.cleanString(name)).capitalize - - final case class FieldMeta( - rawFieldType: String, - rawFieldName: String, - collectionStack: Int - ) { - - def fieldString: String = - simpleString(rawFieldName, rawFieldType, collectionStack) - - private def simpleString( - name: String, - `type`: String, - collectionStack: Int - ): String = - if (collectionStack == 0) { - s"$name: Option[${`type`}]" - } else { - s"$name:" + (0 until collectionStack) - .foldLeft("Option[")((cur, _) => - cur + s"List[Option[" - ) + `type` + "]" * (collectionStack * 2 + 1) - } - } - - final case class ComplexClass( - name: String, - fields: Iterable[FieldMeta], - classes: Iterable[SimpleClass] = Vector.empty, - cStack: Int = 0, - isObj: Boolean = false - ) - - final case class SimpleClass( - name: String, - fields: Iterable[String] - ) { - - private def caseClassString(name: String, fields: String) = - s""" - |final case class ${className(name)} ($fields) - |""".stripMargin - - override def toString: String = caseClassString(name, fields.mkString(",")) - } - - final case class ClassFolder( - name: String, - `package`: String, - isRoot: Boolean = true, - collectionStack: Int = 0, - isObj: Boolean = false, - defaultOnNullType: String = "String" - ) extends Folder[Vector[ComplexClass]] { - - override def onNull: Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta(defaultOnNullType, name, collectionStack) - ) - ) - ) - - override def onBoolean(value: Boolean): Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta("Boolean", name, collectionStack) - ) - ) - ) - - override def onNumber(value: JsonNumber): Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta("Double", name, collectionStack) - ) - ) - ) - - override def onString(value: String): Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta("String", name, collectionStack) - ) - ) - ) - - override def onArray(value: Vector[Json]): Vector[ComplexClass] = - value - .flatMap( - _.foldWith( - this.copy(isRoot = false, collectionStack = collectionStack + 1) - ) - ) match { - case array - if array.isEmpty => // if empty default to collection with default type - Vector( - ComplexClass( - name, - Vector( - FieldMeta(defaultOnNullType, name, collectionStack + 1) - ) - ) - ) - case nonEmptyArray => - nonEmptyArray.distinct // keep only uniques - } - - override def onObject(value: JsonObject): Vector[ComplexClass] = { - - val fieldsOrClasses1 = value.toMap - .map { case (objName, jsonObjs) => - ( - objName, - jsonObjs.asArray match { - case Some(objArr) => - // filter multiple json objects - objArr - .flatMap( - _.foldWith( - this.copy( - name = objName, - isRoot = false, - collectionStack = 1, - isObj = true - ) - ) - ) - case _ => - jsonObjs.foldWith( - this.copy( - name = objName, - isRoot = false, - collectionStack = 0, - isObj = true - ) - ) - } - ) - } - - val fieldsOrClasses = fieldsOrClasses1.flatMap { - case (className, cplxClasses) if isRoot && cplxClasses.isEmpty => - // empty case class @ root level - Vector( - ComplexClass( - name, - Vector.empty, - Vector(SimpleClass(className, Vector.empty)) - ) - ) - case (className, cplxClasses) if isRoot && cplxClasses.nonEmpty => - // case class @ root level - collapseClasses( - cplxClasses, - defaultOnNullType, - collectionStack, - isObj - ).map(cplxClass => - ComplexClass( - name, - Vector.empty, - cplxClasses.flatMap(_.classes) ++ Vector( - SimpleClass(className, cplxClass.fields.map(_.fieldString)) - ) - ) - ) - - case (cName, cplxClasses) - if cplxClasses.nonEmpty && - cplxClasses.head.isObj && - !isRoot => - // complex nested case class - Vector( - ComplexClass( - name, - Vector( - FieldMeta(className(cName), cName, cplxClasses.head.cStack) - ), - cplxClasses.flatMap(_.classes) :+ SimpleClass( - cName, - cplxClasses.head.fields.map(_.fieldString) - ) - ) - ) - case (_, cplxClasses) => - // if not root level and not an object, map the field vals - Vector( - ComplexClass( - name, - collapseSameFieldTypes( - cplxClasses.flatMap(_.fields), - defaultOnNullType - ), - cplxClasses.flatMap(_.classes) - ) - ) - } - - Vector( - ComplexClass( - name, - fieldsOrClasses - .flatMap(_.fields) - .filterNot(_.fieldString.isBlank) - .filterNot(_.fieldString.isEmpty), - fieldsOrClasses.flatMap(_.classes), - collectionStack, - isObj - ) - ) - } - } - - private def collapseSameFieldTypes( - fields: Iterable[FieldMeta], - defaultOnNullType: String - ): Iterable[FieldMeta] = { - // check if field types contains a non default value - // if any use this one, of not, keep string - fields.filterNot(_.rawFieldType.equals(defaultOnNullType)).toSet match { - case noneDefaultType if noneDefaultType.size == 1 => - noneDefaultType.headOption - case noneDefaultType if noneDefaultType.size > 1 => - throw new IllegalArgumentException( - s"More than one field type identified: ${noneDefaultType.mkString(",")}" - ) - case empty if empty.isEmpty => - // we just filtered the defaultOnNullType and the vector is empty - // => field is the same as the defaultType and we can just return - fields - } - } - - private def collapseClasses( - classes: Seq[ComplexClass], - defaultOnNullType: String, - collectionStack: Int, - isObj: Boolean - ): Iterable[ComplexClass] = classes.distinct.groupBy(_.name).flatMap { - case (_, distClasses) - if distClasses.size == 1 => // all classes are equal, return only one of them - distClasses - case ( - name, - distClasses - ) => // multiple classes with same name but maybe different fields, try to merge them ... - // merge and collapse fields - val allFields = distClasses - .flatMap(_.fields) - .groupMap(fieldMeta => - (fieldMeta.rawFieldName, fieldMeta.collectionStack) - )(_.rawFieldType) - .flatMap { case ((name, colStack), types) => - types.distinct.filterNot(_.equals(defaultOnNullType)) match { - case noneDefaultType if noneDefaultType.size == 1 => - noneDefaultType.headOption.map(fieldType => - FieldMeta( - fieldType, - name, - colStack - ) - ) - case noneDefaultType if noneDefaultType.size > 1 => - throw new IllegalArgumentException( - s"More than one field type identified: ${noneDefaultType.mkString(",")}" - ) - case empty - if empty.isEmpty => // if default type is given we end up here, as we filtered all default types - Some( - FieldMeta( - defaultOnNullType, - name, - colStack - ) - ) - } - } - Iterable( - ComplexClass( - name, - allFields, - distClasses.flatMap(_.classes), - collectionStack, - isObj - ) - ) - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import java.io.{File, PrintWriter} +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.util.StringUtils +import io.circe.Json.Folder +import io.circe.{Json, JsonNumber, JsonObject} +import io.circe.parser._ + +import scala.io.Source + +object SchemaGenerator extends LazyLogging { + def main(args: Array[String]): Unit = { + val source = + Source.fromFile( + s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" + ) + val jsonString = + try source.mkString + finally source.close + run(jsonString) + } + + def run(jsonString: String): Unit = { + run( + jsonString, + "RawGridModel", + "edu.ie3.powerFactory2psdm.model.powerfactory" + ).foreach(formatedClassString => { + val pw = new PrintWriter( + new File( + s"${new File(".").getCanonicalPath}/src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" + ) + ) + pw.write(formatedClassString) + pw.close() + }) + } + + def run( + jsonString: String, + className: String, + `package`: String + ): Option[String] = + parse(jsonString) match { + case Left(error) => + logger.error( + s"Exception during json file parsing: '${error.getMessage()}'" + ) + None + case Right(json) => + generateClass( + json, + className, + `package` + ).map(Formatter.format(_, None)) + } + + private def generateClass( + json: Json, + className: String, + `package`: String + ): Option[String] = { + + json.asObject match { + case Some(jsonObject) if !jsonObject.isEmpty => + val classes: Iterable[SimpleClass] = + json + .foldWith(ClassFolder(className, `package`)) + .flatMap(_.classes) + .groupBy(_.name) + .flatMap { + case (name, elems) if elems.size > 1 => + // duplicated class name, merge the fields and provide default None for all of them + Vector(SimpleClass(name, elems.flatMap(_.fields).distinct)) + case (_, elems) => elems + } + + val wrapperClass = + s""" + | final case class ${this.className(className)}( + | ${jsonObject.toMap.keys + .map(key => s"$key: Option[List[${this.className(key)}]]") + .mkString(",\n")} + | ) + |""".stripMargin + + val importStatement = + s""" + | import ${`package`}.${this.className(className)}.{ + | ${jsonObject.toMap.keys + .map(key => this.className(key)) + .mkString(",\n")} + | } + |""".stripMargin + + val packageStatement = + s"package ${`package`}" + + val wrapperObj = + s""" + | object ${this.className(className)}{ + | + | ${classes.map(_.toString).mkString("")} + | } + |""".stripMargin + + Some(packageStatement + importStatement + wrapperClass + wrapperObj) + case _ => + None + } + + } + + private def className(name: String) = + StringUtils.snakeCaseToCamelCase(StringUtils.cleanString(name)).capitalize + + final case class FieldMeta( + rawFieldType: String, + rawFieldName: String, + collectionStack: Int + ) { + + def fieldString: String = + simpleString(rawFieldName, rawFieldType, collectionStack) + + private def simpleString( + name: String, + `type`: String, + collectionStack: Int + ): String = + if (collectionStack == 0) { + s"$name: Option[${`type`}]" + } else { + s"$name:" + (0 until collectionStack) + .foldLeft("Option[")((cur, _) => + cur + s"List[Option[" + ) + `type` + "]" * (collectionStack * 2 + 1) + } + } + + final case class ComplexClass( + name: String, + fields: Iterable[FieldMeta], + classes: Iterable[SimpleClass] = Vector.empty, + cStack: Int = 0, + isObj: Boolean = false + ) + + final case class SimpleClass( + name: String, + fields: Iterable[String] + ) { + + private def caseClassString(name: String, fields: String) = + s""" + |final case class ${className(name)} ($fields) + |""".stripMargin + + override def toString: String = caseClassString(name, fields.mkString(",")) + } + + final case class ClassFolder( + name: String, + `package`: String, + isRoot: Boolean = true, + collectionStack: Int = 0, + isObj: Boolean = false, + defaultOnNullType: String = "String" + ) extends Folder[Vector[ComplexClass]] { + + override def onNull: Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta(defaultOnNullType, name, collectionStack) + ) + ) + ) + + override def onBoolean(value: Boolean): Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta("Boolean", name, collectionStack) + ) + ) + ) + + override def onNumber(value: JsonNumber): Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta("Double", name, collectionStack) + ) + ) + ) + + override def onString(value: String): Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta("String", name, collectionStack) + ) + ) + ) + + override def onArray(value: Vector[Json]): Vector[ComplexClass] = + value + .flatMap( + _.foldWith( + this.copy(isRoot = false, collectionStack = collectionStack + 1) + ) + ) match { + case array + if array.isEmpty => // if empty default to collection with default type + Vector( + ComplexClass( + name, + Vector( + FieldMeta(defaultOnNullType, name, collectionStack + 1) + ) + ) + ) + case nonEmptyArray => + nonEmptyArray.distinct // keep only uniques + } + + override def onObject(value: JsonObject): Vector[ComplexClass] = { + + val fieldsOrClasses1 = value.toMap + .map { case (objName, jsonObjs) => + ( + objName, + jsonObjs.asArray match { + case Some(objArr) => + // filter multiple json objects + objArr + .flatMap( + _.foldWith( + this.copy( + name = objName, + isRoot = false, + collectionStack = 1, + isObj = true + ) + ) + ) + case _ => + jsonObjs.foldWith( + this.copy( + name = objName, + isRoot = false, + collectionStack = 0, + isObj = true + ) + ) + } + ) + } + + val fieldsOrClasses = fieldsOrClasses1.flatMap { + case (className, cplxClasses) if isRoot && cplxClasses.isEmpty => + // empty case class @ root level + Vector( + ComplexClass( + name, + Vector.empty, + Vector(SimpleClass(className, Vector.empty)) + ) + ) + case (className, cplxClasses) if isRoot && cplxClasses.nonEmpty => + // case class @ root level + collapseClasses( + cplxClasses, + defaultOnNullType, + collectionStack, + isObj + ).map(cplxClass => + ComplexClass( + name, + Vector.empty, + cplxClasses.flatMap(_.classes) ++ Vector( + SimpleClass(className, cplxClass.fields.map(_.fieldString)) + ) + ) + ) + + case (cName, cplxClasses) + if cplxClasses.nonEmpty && + cplxClasses.head.isObj && + !isRoot => + // complex nested case class + Vector( + ComplexClass( + name, + Vector( + FieldMeta(className(cName), cName, cplxClasses.head.cStack) + ), + cplxClasses.flatMap(_.classes) :+ SimpleClass( + cName, + cplxClasses.head.fields.map(_.fieldString) + ) + ) + ) + case (_, cplxClasses) => + // if not root level and not an object, map the field vals + Vector( + ComplexClass( + name, + collapseSameFieldTypes( + cplxClasses.flatMap(_.fields), + defaultOnNullType + ), + cplxClasses.flatMap(_.classes) + ) + ) + } + + Vector( + ComplexClass( + name, + fieldsOrClasses + .flatMap(_.fields) + .filterNot(_.fieldString.isBlank) + .filterNot(_.fieldString.isEmpty), + fieldsOrClasses.flatMap(_.classes), + collectionStack, + isObj + ) + ) + } + } + + private def collapseSameFieldTypes( + fields: Iterable[FieldMeta], + defaultOnNullType: String + ): Iterable[FieldMeta] = { + // check if field types contains a non default value + // if any use this one, of not, keep string + fields.filterNot(_.rawFieldType.equals(defaultOnNullType)).toSet match { + case noneDefaultType if noneDefaultType.size == 1 => + noneDefaultType.headOption + case noneDefaultType if noneDefaultType.size > 1 => + throw new IllegalArgumentException( + s"More than one field type identified: ${noneDefaultType.mkString(",")}" + ) + case empty if empty.isEmpty => + // we just filtered the defaultOnNullType and the vector is empty + // => field is the same as the defaultType and we can just return + fields + } + } + + private def collapseClasses( + classes: Seq[ComplexClass], + defaultOnNullType: String, + collectionStack: Int, + isObj: Boolean + ): Iterable[ComplexClass] = classes.distinct.groupBy(_.name).flatMap { + case (_, distClasses) + if distClasses.size == 1 => // all classes are equal, return only one of them + distClasses + case ( + name, + distClasses + ) => // multiple classes with same name but maybe different fields, try to merge them ... + // merge and collapse fields + val allFields = distClasses + .flatMap(_.fields) + .groupMap(fieldMeta => + (fieldMeta.rawFieldName, fieldMeta.collectionStack) + )(_.rawFieldType) + .flatMap { case ((name, colStack), types) => + types.distinct.filterNot(_.equals(defaultOnNullType)) match { + case noneDefaultType if noneDefaultType.size == 1 => + noneDefaultType.headOption.map(fieldType => + FieldMeta( + fieldType, + name, + colStack + ) + ) + case noneDefaultType if noneDefaultType.size > 1 => + throw new IllegalArgumentException( + s"More than one field type identified: ${noneDefaultType.mkString(",")}" + ) + case empty + if empty.isEmpty => // if default type is given we end up here, as we filtered all default types + Some( + FieldMeta( + defaultOnNullType, + name, + colStack + ) + ) + } + } + Iterable( + ComplexClass( + name, + allFields, + distClasses.flatMap(_.classes), + collectionStack, + isObj + ) + ) + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala b/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala index 7a8d583a..bd75fb28 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala @@ -1,321 +1,321 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.common - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.datamodel.models.input.connector.`type`.{ - LineTypeInput, - Transformer2WTypeInput -} -import edu.ie3.datamodel.models.{OperationTime, StandardUnits, UniqueEntity} -import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} -import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils.LV -import edu.ie3.powerFactory2psdm.config.ConversionConfig - -import java.io.File -import edu.ie3.powerFactory2psdm.exception.io.GridParsingException -import edu.ie3.powerFactory2psdm.exception.pf.TestException -import edu.ie3.powerFactory2psdm.io.PfGridParser -import edu.ie3.powerFactory2psdm.model.entity.{ - ConnectedElement, - EntityModel, - Node, - Subnet -} -import edu.ie3.powerFactory2psdm.model.entity.types.{ - LineType, - TransformerType2W -} -import edu.ie3.powerFactory2psdm.model.PreprocessedPfGridModel -import edu.ie3.util.quantities.PowerSystemUnits.PU -import org.locationtech.jts.geom.{Coordinate, GeometryFactory} -import pureconfig.ConfigSource -import tech.units.indriya.quantity.Quantities -import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS} -import edu.ie3.util.quantities.PowerSystemUnits.{ - DEGREE_GEOM, - KILOVOLT, - VOLTAMPERE -} -import pureconfig.generic.auto._ - -import java.util.UUID -import javax.measure.MetricPrefix - -object ConverterTestData extends LazyLogging { - - val config: ConversionConfig = - ConfigSource.default.at("conversion-config").loadOrThrow[ConversionConfig] - - /** Case class to denote a consistent pair of input and expected output of a - * conversion - * - * @param input - * Input model - * @param result - * Resulting, converted model - * @tparam I - * Type of input model - * @tparam R - * Type of result class - */ - final case class ConversionPair[I <: EntityModel, R <: UniqueEntity]( - input: I, - result: R - ) { - def getPair: (I, R) = (input, result) - } - - logger.warn("Building the grid model") - - val testGridFile = - s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" - - val testGrid: PreprocessedPfGridModel = PreprocessedPfGridModel.build( - PfGridParser - .parse(testGridFile) - .getOrElse( - throw GridParsingException( - s"Couldn't parse the grid file $testGridFile" - ) - ) - ) - - val id2node: Map[String, Node] = - testGrid.nodes.map(node => (node.id, node)).toMap - - val bus1Id = "Grid.ElmNet\\Bus_0001.ElmTerm" - val bus2Id = "Grid.ElmNet\\Bus_0002.ElmTerm" - val bus3Id = "Grid.ElmNet\\Bus_0003.ElmTerm" - val bus4Id = "Grid.ElmNet\\Bus_0004.ElmTerm" - val bus5Id = "Grid.ElmNet\\Bus_0005.ElmTerm" - val bus6Id = "Grid.ElmNet\\Bus_0006.ElmTerm" - val bus7Id = "Grid.ElmNet\\Bus_0007.ElmTerm" - val bus8Id = "Grid.ElmNet\\Bus_0008.ElmTerm" - val bus9Id = "Grid.ElmNet\\Bus_0009.ElmTerm" - val bus10Id = "Grid.ElmNet\\Bus_0010.ElmTerm" - val bus11Id = "Grid.ElmNet\\Bus_0011.ElmTerm" - val bus12Id = "Grid.ElmNet\\Bus_0012.ElmTerm" - val bus13Id = "Grid.ElmNet\\Bus_0013.ElmTerm" - val bus14Id = "Grid.ElmNet\\Bus_0014.ElmTerm" - val bus15Id = "Grid.ElmNet\\Bus_0015.ElmTerm" - val busOns1Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\1.ElmTerm" - val busOns2Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\2.ElmTerm" - val busOnsLv = - "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\ON_Station_Lower.ElmTerm" - - val subnet1Ids: Set[String] = - Set( - bus1Id, - bus2Id, - bus3Id, - bus4Id, - bus5Id - ) - - val subnet2Ids: Set[String] = Set(bus7Id) - - val subnet3Ids: Set[String] = Set(bus8Id) - - val subnet4Ids: Set[String] = - Set( - bus6Id, - bus9Id, - bus10Id, - bus11Id, - bus12Id, - bus13Id, - bus14Id, - bus15Id, - busOns1Id, - busOns2Id, - busOnsLv - ) - - val geometryFactory = new GeometryFactory() - - val nodes = Map( - "someNode" -> ConversionPair( - Node( - "someNode", - 0.4, - 1.0, - Some(11.1123), - Some(52.1425), - List( - ConnectedElement( - "someConnectedElement", - "ElmLne" - ) - ) - ), - new NodeInput( - UUID.randomUUID(), - "someNode", - OperatorInput.NO_OPERATOR_ASSIGNED, - OperationTime.notLimited(), - Quantities.getQuantity(1d, PU), - false, - geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), - LV, - 1 - ) - ), - "someSlackNode" -> ConversionPair( - Node( - "someSlackNode", - 0.4, - 1.0, - Some(11.1123), - Some(52.1425), - List( - ConnectedElement( - "someConnectedElement", - "ElmXnet" - ) - ) - ), - new NodeInput( - UUID.randomUUID(), - "someSlackNode", - OperatorInput.NO_OPERATOR_ASSIGNED, - OperationTime.notLimited(), - Quantities.getQuantity(1d, PU), - true, - geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), - LV, - 2 - ) - ) - ) - - def getNodePair(key: String): ConversionPair[Node, NodeInput] = { - nodes.getOrElse( - key, - throw TestException( - s"Cannot find input/result pair for ${Node.getClass.getSimpleName} with key: $key " - ) - ) - } - - val subnets = Map( - "someSubnet" -> Subnet( - 1, - Set(getNodePair("someNode").input), - LV - ) - ) - - def getSubnet(key: String): Subnet = subnets.getOrElse( - key, - throw TestException(s"Cannot find subnet with key: $key") - ) - - val lineTypes = Map( - "someLineType" -> - ConversionPair( - LineType( - "someLineType", - 132.0, - 1.0, - 6.753542423248291, - 20.61956214904785, - 151.51515197753906, - 1.543 - ), - new LineTypeInput( - UUID.randomUUID(), - "someLineType", - Quantities.getQuantity( - 151.51515197753906, - StandardUnits.ADMITTANCE_PER_LENGTH - ), - Quantities.getQuantity( - 1.543, - StandardUnits.ADMITTANCE_PER_LENGTH - ), - Quantities.getQuantity( - 6.753542423248291, - StandardUnits.IMPEDANCE_PER_LENGTH - ), - Quantities.getQuantity( - 20.61956214904785, - StandardUnits.IMPEDANCE_PER_LENGTH - ), - Quantities.getQuantity( - 1000, - StandardUnits.ELECTRIC_CURRENT_MAGNITUDE - ), - Quantities.getQuantity( - 132.0, - StandardUnits.RATED_VOLTAGE_MAGNITUDE - ) - ) - ) - ) - - def getLineType(key: String): ConversionPair[LineType, LineTypeInput] = { - lineTypes.getOrElse( - key, - throw TestException( - s"Cannot find input/result pair for ${LineType.getClass.getSimpleName} with key: $key " - ) - ) - } - - val transformerTypes = Map( - "SomeTrafo2wType" -> ConversionPair( - TransformerType2W( - id = "SomeTrafo2wType", - sRated = 40d, - vRatedA = 110d, - vRatedB = 10d, - dV = 2.5, - dPhi = 5d, - tapSide = 0, - tapNeutr = 0, - tapMin = -10, - tapMax = 10, - uk = 5, - iNoLoad = 1, - pFe = 10, - pCu = 6 - ), - new Transformer2WTypeInput( - UUID.randomUUID(), - "SomeTrafo2wType", - Quantities.getQuantity(45.375, MetricPrefix.MILLI(OHM)), - Quantities.getQuantity(15.1249319, OHM), - Quantities.getQuantity(40d, MetricPrefix.MEGA(VOLTAMPERE)), - Quantities.getQuantity(110d, KILOVOLT), - Quantities.getQuantity(10d, KILOVOLT), - Quantities.getQuantity(826.4462809, MetricPrefix.NANO(SIEMENS)), - Quantities - .getQuantity(33047.519046, MetricPrefix.NANO(SIEMENS)) - .to(MetricPrefix.NANO(SIEMENS)), - Quantities.getQuantity(2.5, PERCENT), - Quantities.getQuantity(5d, DEGREE_GEOM), - false, - 0, - -10, - 10 - ) - ) - ) - - def getTransformer2wType( - key: String - ): ConversionPair[TransformerType2W, Transformer2WTypeInput] = { - transformerTypes.getOrElse( - key, - throw TestException( - s"Cannot find input/result pair for ${TransformerType2W.getClass.getSimpleName} with key: $key " - ) - ) - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.common + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.datamodel.models.input.connector.`type`.{ + LineTypeInput, + Transformer2WTypeInput +} +import edu.ie3.datamodel.models.{OperationTime, StandardUnits, UniqueEntity} +import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} +import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils.LV +import edu.ie3.powerFactory2psdm.config.ConversionConfig + +import java.io.File +import edu.ie3.powerFactory2psdm.exception.io.GridParsingException +import edu.ie3.powerFactory2psdm.exception.pf.TestException +import edu.ie3.powerFactory2psdm.io.PfGridParser +import edu.ie3.powerFactory2psdm.model.entity.{ + ConnectedElement, + EntityModel, + Node, + Subnet +} +import edu.ie3.powerFactory2psdm.model.entity.types.{ + LineType, + TransformerType2W +} +import edu.ie3.powerFactory2psdm.model.PreprocessedPfGridModel +import edu.ie3.util.quantities.PowerSystemUnits.PU +import org.locationtech.jts.geom.{Coordinate, GeometryFactory} +import pureconfig.ConfigSource +import tech.units.indriya.quantity.Quantities +import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS} +import edu.ie3.util.quantities.PowerSystemUnits.{ + DEGREE_GEOM, + KILOVOLT, + VOLTAMPERE +} +import pureconfig.generic.auto._ + +import java.util.UUID +import javax.measure.MetricPrefix + +object ConverterTestData extends LazyLogging { + + val config: ConversionConfig = + ConfigSource.default.at("conversion-config").loadOrThrow[ConversionConfig] + + /** Case class to denote a consistent pair of input and expected output of a + * conversion + * + * @param input + * Input model + * @param result + * Resulting, converted model + * @tparam I + * Type of input model + * @tparam R + * Type of result class + */ + final case class ConversionPair[I <: EntityModel, R <: UniqueEntity]( + input: I, + result: R + ) { + def getPair: (I, R) = (input, result) + } + + logger.warn("Building the grid model") + + val testGridFile = + s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" + + val testGrid: PreprocessedPfGridModel = PreprocessedPfGridModel.build( + PfGridParser + .parse(testGridFile) + .getOrElse( + throw GridParsingException( + s"Couldn't parse the grid file $testGridFile" + ) + ) + ) + + val id2node: Map[String, Node] = + testGrid.nodes.map(node => (node.id, node)).toMap + + val bus1Id = "Grid.ElmNet\\Bus_0001.ElmTerm" + val bus2Id = "Grid.ElmNet\\Bus_0002.ElmTerm" + val bus3Id = "Grid.ElmNet\\Bus_0003.ElmTerm" + val bus4Id = "Grid.ElmNet\\Bus_0004.ElmTerm" + val bus5Id = "Grid.ElmNet\\Bus_0005.ElmTerm" + val bus6Id = "Grid.ElmNet\\Bus_0006.ElmTerm" + val bus7Id = "Grid.ElmNet\\Bus_0007.ElmTerm" + val bus8Id = "Grid.ElmNet\\Bus_0008.ElmTerm" + val bus9Id = "Grid.ElmNet\\Bus_0009.ElmTerm" + val bus10Id = "Grid.ElmNet\\Bus_0010.ElmTerm" + val bus11Id = "Grid.ElmNet\\Bus_0011.ElmTerm" + val bus12Id = "Grid.ElmNet\\Bus_0012.ElmTerm" + val bus13Id = "Grid.ElmNet\\Bus_0013.ElmTerm" + val bus14Id = "Grid.ElmNet\\Bus_0014.ElmTerm" + val bus15Id = "Grid.ElmNet\\Bus_0015.ElmTerm" + val busOns1Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\1.ElmTerm" + val busOns2Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\2.ElmTerm" + val busOnsLv = + "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\ON_Station_Lower.ElmTerm" + + val subnet1Ids: Set[String] = + Set( + bus1Id, + bus2Id, + bus3Id, + bus4Id, + bus5Id + ) + + val subnet2Ids: Set[String] = Set(bus7Id) + + val subnet3Ids: Set[String] = Set(bus8Id) + + val subnet4Ids: Set[String] = + Set( + bus6Id, + bus9Id, + bus10Id, + bus11Id, + bus12Id, + bus13Id, + bus14Id, + bus15Id, + busOns1Id, + busOns2Id, + busOnsLv + ) + + val geometryFactory = new GeometryFactory() + + val nodes = Map( + "someNode" -> ConversionPair( + Node( + "someNode", + 0.4, + 1.0, + Some(11.1123), + Some(52.1425), + List( + ConnectedElement( + "someConnectedElement", + "ElmLne" + ) + ) + ), + new NodeInput( + UUID.randomUUID(), + "someNode", + OperatorInput.NO_OPERATOR_ASSIGNED, + OperationTime.notLimited(), + Quantities.getQuantity(1d, PU), + false, + geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), + LV, + 1 + ) + ), + "someSlackNode" -> ConversionPair( + Node( + "someSlackNode", + 0.4, + 1.0, + Some(11.1123), + Some(52.1425), + List( + ConnectedElement( + "someConnectedElement", + "ElmXnet" + ) + ) + ), + new NodeInput( + UUID.randomUUID(), + "someSlackNode", + OperatorInput.NO_OPERATOR_ASSIGNED, + OperationTime.notLimited(), + Quantities.getQuantity(1d, PU), + true, + geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), + LV, + 2 + ) + ) + ) + + def getNodePair(key: String): ConversionPair[Node, NodeInput] = { + nodes.getOrElse( + key, + throw TestException( + s"Cannot find input/result pair for ${Node.getClass.getSimpleName} with key: $key " + ) + ) + } + + val subnets = Map( + "someSubnet" -> Subnet( + 1, + Set(getNodePair("someNode").input), + LV + ) + ) + + def getSubnet(key: String): Subnet = subnets.getOrElse( + key, + throw TestException(s"Cannot find subnet with key: $key") + ) + + val lineTypes = Map( + "someLineType" -> + ConversionPair( + LineType( + "someLineType", + 132.0, + 1.0, + 6.753542423248291, + 20.61956214904785, + 151.51515197753906, + 1.543 + ), + new LineTypeInput( + UUID.randomUUID(), + "someLineType", + Quantities.getQuantity( + 151.51515197753906, + StandardUnits.ADMITTANCE_PER_LENGTH + ), + Quantities.getQuantity( + 1.543, + StandardUnits.ADMITTANCE_PER_LENGTH + ), + Quantities.getQuantity( + 6.753542423248291, + StandardUnits.IMPEDANCE_PER_LENGTH + ), + Quantities.getQuantity( + 20.61956214904785, + StandardUnits.IMPEDANCE_PER_LENGTH + ), + Quantities.getQuantity( + 1000, + StandardUnits.ELECTRIC_CURRENT_MAGNITUDE + ), + Quantities.getQuantity( + 132.0, + StandardUnits.RATED_VOLTAGE_MAGNITUDE + ) + ) + ) + ) + + def getLineType(key: String): ConversionPair[LineType, LineTypeInput] = { + lineTypes.getOrElse( + key, + throw TestException( + s"Cannot find input/result pair for ${LineType.getClass.getSimpleName} with key: $key " + ) + ) + } + + val transformerTypes = Map( + "SomeTrafo2wType" -> ConversionPair( + TransformerType2W( + id = "SomeTrafo2wType", + sRated = 40d, + vRatedA = 110d, + vRatedB = 10d, + dV = 2.5, + dPhi = 5d, + tapSide = 0, + tapNeutr = 0, + tapMin = -10, + tapMax = 10, + uk = 5, + iNoLoad = 1, + pFe = 10, + pCu = 6 + ), + new Transformer2WTypeInput( + UUID.randomUUID(), + "SomeTrafo2wType", + Quantities.getQuantity(45.375, MetricPrefix.MILLI(OHM)), + Quantities.getQuantity(15.1249319, OHM), + Quantities.getQuantity(40d, MetricPrefix.MEGA(VOLTAMPERE)), + Quantities.getQuantity(110d, KILOVOLT), + Quantities.getQuantity(10d, KILOVOLT), + Quantities.getQuantity(826.4462809, MetricPrefix.NANO(SIEMENS)), + Quantities + .getQuantity(33047.519046, MetricPrefix.NANO(SIEMENS)) + .to(MetricPrefix.NANO(SIEMENS)), + Quantities.getQuantity(2.5, PERCENT), + Quantities.getQuantity(5d, DEGREE_GEOM), + false, + 0, + -10, + 10 + ) + ) + ) + + def getTransformer2wType( + key: String + ): ConversionPair[TransformerType2W, Transformer2WTypeInput] = { + transformerTypes.getOrElse( + key, + throw TestException( + s"Cannot find input/result pair for ${TransformerType2W.getClass.getSimpleName} with key: $key " + ) + ) + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala index 8f38a5dc..5bad54c9 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala @@ -1,117 +1,117 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.config - -import edu.ie3.powerFactory2psdm.common.ConverterTestData -import edu.ie3.powerFactory2psdm.config.ConfigValidator.{ - lowerBoundViolation, - lowerUpperBoundViolation, - upperBoundViolation, - validatePvModelGenerationParams -} -import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ - Fixed, - PvFixedFeedIn, - PvModelGeneration, - UniformDistribution -} -import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException -import edu.ie3.powerFactory2psdm.exception.pf.TestException -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class ConfigValidatorSpec extends Matchers with AnyWordSpecLike { - - "A ConfigValidator " should { - - val pvModelGeneration: PvModelGeneration = - ConverterTestData.config.modelConfigs.pvConfig.conversionMode match { - case PvFixedFeedIn => - throw TestException( - "The test pv config is supposed to be configured for PvModelGeneration" - ) - case x: PvModelGeneration => x - } - - "validate a conversion config" in { - ConfigValidator.validate(ConverterTestData.config) - } - - "validate model configs" in { - ConfigValidator.validateModelConfigs( - ConverterTestData.config.modelConfigs - ) - } - - "validate pv configs" in { - ConfigValidator.validatePvConfig( - ConverterTestData.config.modelConfigs.pvConfig - ) - } - - "validate correct pv params" in { - validatePvModelGenerationParams(pvModelGeneration) - } - - "throw an exception for invalid pv param albedo" in { - val faultyParams = pvModelGeneration.copy(albedo = Fixed(1.1)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The albedo of the plants surrounding: ${faultyParams.albedo} isn't valid. Exception: ${upperBoundViolation(1.1, 1.0).exception.getMessage}" - } - - "throw an exception for invalid pv param azimuth" in { - val faultyParams = - pvModelGeneration.copy(azimuth = UniformDistribution(-91, 92)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: ${lowerUpperBoundViolation(-91, 92, -90, 90).exception.getMessage}" - } - - "throw an exception for min/max error of pv param azimuth" in { - val faultyParams = - pvModelGeneration.copy(azimuth = UniformDistribution(20, -10)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: The minimum value: 20.0 exceeds the maximum value: -10.0" - } - - "throw an exception for invalid pv param etaConv" in { - val faultyParams = pvModelGeneration.copy(etaConv = Fixed(101)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The efficiency of the plants inverter: ${faultyParams.azimuth} isn't valid. Exception: ${upperBoundViolation(101, 100).exception.getMessage}" - } - - "throw an exception for invalid pv param kG" in { - val faultyParams = pvModelGeneration.copy(kG = Fixed(-0.1)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The PV generator correction factor (kG): ${faultyParams.kG} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" - } - - "throw an exception for invalid pv param kT" in { - val faultyParams = pvModelGeneration.copy(kT = Fixed(-0.1)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The PV temperature correction factor (kT): ${faultyParams.kT} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" - } - - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.config + +import edu.ie3.powerFactory2psdm.common.ConverterTestData +import edu.ie3.powerFactory2psdm.config.ConfigValidator.{ + lowerBoundViolation, + lowerUpperBoundViolation, + upperBoundViolation, + validatePvModelGenerationParams +} +import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ + Fixed, + PvFixedFeedIn, + PvModelGeneration, + UniformDistribution +} +import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException +import edu.ie3.powerFactory2psdm.exception.pf.TestException +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class ConfigValidatorSpec extends Matchers with AnyWordSpecLike { + + "A ConfigValidator " should { + + val pvModelGeneration: PvModelGeneration = + ConverterTestData.config.modelConfigs.pvConfig.conversionMode match { + case PvFixedFeedIn => + throw TestException( + "The test pv config is supposed to be configured for PvModelGeneration" + ) + case x: PvModelGeneration => x + } + + "validate a conversion config" in { + ConfigValidator.validate(ConverterTestData.config) + } + + "validate model configs" in { + ConfigValidator.validateModelConfigs( + ConverterTestData.config.modelConfigs + ) + } + + "validate pv configs" in { + ConfigValidator.validatePvConfig( + ConverterTestData.config.modelConfigs.pvConfig + ) + } + + "validate correct pv params" in { + validatePvModelGenerationParams(pvModelGeneration) + } + + "throw an exception for invalid pv param albedo" in { + val faultyParams = pvModelGeneration.copy(albedo = Fixed(1.1)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The albedo of the plants surrounding: ${faultyParams.albedo} isn't valid. Exception: ${upperBoundViolation(1.1, 1.0).exception.getMessage}" + } + + "throw an exception for invalid pv param azimuth" in { + val faultyParams = + pvModelGeneration.copy(azimuth = UniformDistribution(-91, 92)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: ${lowerUpperBoundViolation(-91, 92, -90, 90).exception.getMessage}" + } + + "throw an exception for min/max error of pv param azimuth" in { + val faultyParams = + pvModelGeneration.copy(azimuth = UniformDistribution(20, -10)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: The minimum value: 20.0 exceeds the maximum value: -10.0" + } + + "throw an exception for invalid pv param etaConv" in { + val faultyParams = pvModelGeneration.copy(etaConv = Fixed(101)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The efficiency of the plants inverter: ${faultyParams.azimuth} isn't valid. Exception: ${upperBoundViolation(101, 100).exception.getMessage}" + } + + "throw an exception for invalid pv param kG" in { + val faultyParams = pvModelGeneration.copy(kG = Fixed(-0.1)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The PV generator correction factor (kG): ${faultyParams.kG} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" + } + + "throw an exception for invalid pv param kT" in { + val faultyParams = pvModelGeneration.copy(kT = Fixed(-0.1)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The PV temperature correction factor (kT): ${faultyParams.kT} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" + } + + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala index df6bbd03..e6c9d3c8 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala @@ -1,43 +1,43 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.input.NodeInput -import edu.ie3.util.geo.GeoUtils -import org.locationtech.jts.geom.{Coordinate, Point} -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class CoordinateConverterSpec extends Matchers with AnyWordSpecLike { - - "A coordinate converter" should { - - "convert the given values to the proper coordinate" in { - val maybeTestLat = Some(40.415634765229235) - val maybeTestLon = Some(-3.7071948736763316) - val actual = CoordinateConverter.convert(maybeTestLat, maybeTestLon) - val expected = GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint( - new Coordinate(-3.7071948736763316, 40.415634765229235) - ) - actual shouldBe expected - } - - "return the default geo position for default PowerFactory values" in { - val maybeDefaultLat = Some(0.0) - val maybeDefaultLon = Some(0.0) - CoordinateConverter.convert( - maybeDefaultLat, - maybeDefaultLon - ) shouldBe NodeInput.DEFAULT_GEO_POSITION - } - - "return the default geo position when given None" in { - val actual = CoordinateConverter.convert(None, None) - actual shouldBe NodeInput.DEFAULT_GEO_POSITION - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.input.NodeInput +import edu.ie3.util.geo.GeoUtils +import org.locationtech.jts.geom.{Coordinate, Point} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class CoordinateConverterSpec extends Matchers with AnyWordSpecLike { + + "A coordinate converter" should { + + "convert the given values to the proper coordinate" in { + val maybeTestLat = Some(40.415634765229235) + val maybeTestLon = Some(-3.7071948736763316) + val actual = CoordinateConverter.convert(maybeTestLat, maybeTestLon) + val expected = GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint( + new Coordinate(-3.7071948736763316, 40.415634765229235) + ) + actual shouldBe expected + } + + "return the default geo position for default PowerFactory values" in { + val maybeDefaultLat = Some(0.0) + val maybeDefaultLon = Some(0.0) + CoordinateConverter.convert( + maybeDefaultLat, + maybeDefaultLon + ) shouldBe NodeInput.DEFAULT_GEO_POSITION + } + + "return the default geo position when given None" in { + val actual = CoordinateConverter.convert(None, None) + actual shouldBe NodeInput.DEFAULT_GEO_POSITION + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala index 748b9758..a55eb75f 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala @@ -1,75 +1,75 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ - subnet1Ids, - subnet2Ids, - subnet3Ids, - subnet4Ids, - testGrid -} -import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException -import org.jgrapht.alg.connectivity.BiconnectivityInspector -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import scala.jdk.CollectionConverters._ - -class GridGraphBuilderSpec extends Matchers with AnyWordSpecLike { - - "The GridGraphBuilder" should { - - val gridGraph = GridGraphBuilder.build( - testGrid.nodes, - testGrid.lines ++ testGrid.switches - ) - val inspect = new BiconnectivityInspector(gridGraph) - val vertexSets = inspect.getConnectedComponents.asScala - .map(graph => graph.vertexSet()) - - "add the correct number of nodes to the gridGraph" in { - gridGraph.vertexSet().size shouldBe testGrid.nodes.size - } - - "add the correct number of edges to the gridGraph" in { - gridGraph - .edgeSet() - .size shouldBe (testGrid.lines ++ testGrid.switches).size - } - - "generate the correct number of subnets" in { - inspect.getConnectedComponents.size shouldBe 5 - } - - "aggregate all nodes of subnet 1 in one of the subgraphs" in { - vertexSets.contains(subnet1Ids.asJava) shouldBe true - } - - "aggregate all nodes of subnet 2 in one of the subgraphs" in { - vertexSets.contains(subnet2Ids.asJava) shouldBe true - } - - "aggregate all nodes of subnet 3 in one of the subgraphs" in { - vertexSets.contains(subnet3Ids.asJava) shouldBe true - } - - "aggregate all nodes of subnet 4 in one of the subgraphs" in { - vertexSets.contains(subnet4Ids.asJava) shouldBe true - } - - "throw an Exception when trying to unpack busses of singly connected edge" in { - - val thrown = intercept[ElementConfigurationException]( - GridGraphBuilder.unpackConnectedBusses("myEdge", Some("bus1"), None) - ) - - thrown.getMessage shouldBe "Exception occurred while adding an edge. " + - "Exc: Edge with id: myEdge is missing at least one connected node" - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ + subnet1Ids, + subnet2Ids, + subnet3Ids, + subnet4Ids, + testGrid +} +import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException +import org.jgrapht.alg.connectivity.BiconnectivityInspector +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import scala.jdk.CollectionConverters._ + +class GridGraphBuilderSpec extends Matchers with AnyWordSpecLike { + + "The GridGraphBuilder" should { + + val gridGraph = GridGraphBuilder.build( + testGrid.nodes, + testGrid.lines ++ testGrid.switches + ) + val inspect = new BiconnectivityInspector(gridGraph) + val vertexSets = inspect.getConnectedComponents.asScala + .map(graph => graph.vertexSet()) + + "add the correct number of nodes to the gridGraph" in { + gridGraph.vertexSet().size shouldBe testGrid.nodes.size + } + + "add the correct number of edges to the gridGraph" in { + gridGraph + .edgeSet() + .size shouldBe (testGrid.lines ++ testGrid.switches).size + } + + "generate the correct number of subnets" in { + inspect.getConnectedComponents.size shouldBe 5 + } + + "aggregate all nodes of subnet 1 in one of the subgraphs" in { + vertexSets.contains(subnet1Ids.asJava) shouldBe true + } + + "aggregate all nodes of subnet 2 in one of the subgraphs" in { + vertexSets.contains(subnet2Ids.asJava) shouldBe true + } + + "aggregate all nodes of subnet 3 in one of the subgraphs" in { + vertexSets.contains(subnet3Ids.asJava) shouldBe true + } + + "aggregate all nodes of subnet 4 in one of the subgraphs" in { + vertexSets.contains(subnet4Ids.asJava) shouldBe true + } + + "throw an Exception when trying to unpack busses of singly connected edge" in { + + val thrown = intercept[ElementConfigurationException]( + GridGraphBuilder.unpackConnectedBusses("myEdge", Some("bus1"), None) + ) + + thrown.getMessage shouldBe "Exception occurred while adding an edge. " + + "Exc: Edge with id: myEdge is missing at least one connected node" + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala index 7c97542d..b87d1256 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala @@ -1,52 +1,52 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils -import edu.ie3.powerFactory2psdm.common.ConverterTestData.getNodePair -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class NodeConverterSpec extends Matchers with AnyWordSpecLike { - - "The node converter" should { - - "convert a correctly configured pf node to a correctly configured PSDM Node" in { - val conversionPair = getNodePair("someNode") - val input = conversionPair.input - val expected = conversionPair.result - - val actual = NodeConverter.convertNode( - input, - 1, - GermanVoltageLevelUtils.LV - ) - actual.getId shouldBe expected.getId - actual.getVoltLvl shouldBe expected.getVoltLvl - actual.getSubnet shouldBe expected.getSubnet - actual.isSlack shouldBe expected.isSlack - } - - } - - "convert a correctly configured slack pf node to a correctly configured slack PSDM Node" in { - val conversionPair = getNodePair("someSlackNode") - val input = conversionPair.input - val expected = conversionPair.result - - val actual = NodeConverter.convertNode( - input, - 2, - GermanVoltageLevelUtils.LV - ) - actual.getId shouldBe expected.getId - actual.getVoltLvl shouldBe expected.getVoltLvl - actual.getSubnet shouldBe expected.getSubnet - actual.isSlack shouldBe expected.isSlack - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils +import edu.ie3.powerFactory2psdm.common.ConverterTestData.getNodePair +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class NodeConverterSpec extends Matchers with AnyWordSpecLike { + + "The node converter" should { + + "convert a correctly configured pf node to a correctly configured PSDM Node" in { + val conversionPair = getNodePair("someNode") + val input = conversionPair.input + val expected = conversionPair.result + + val actual = NodeConverter.convertNode( + input, + 1, + GermanVoltageLevelUtils.LV + ) + actual.getId shouldBe expected.getId + actual.getVoltLvl shouldBe expected.getVoltLvl + actual.getSubnet shouldBe expected.getSubnet + actual.isSlack shouldBe expected.isSlack + } + + } + + "convert a correctly configured slack pf node to a correctly configured slack PSDM Node" in { + val conversionPair = getNodePair("someSlackNode") + val input = conversionPair.input + val expected = conversionPair.result + + val actual = NodeConverter.convertNode( + input, + 2, + GermanVoltageLevelUtils.LV + ) + actual.getId shouldBe expected.getId + actual.getVoltLvl shouldBe expected.getVoltLvl + actual.getSubnet shouldBe expected.getSubnet + actual.isSlack shouldBe expected.isSlack + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala index 4a2f53f2..21328eaa 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala @@ -1,65 +1,65 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ - id2node, - subnet1Ids, - subnet2Ids, - testGrid -} -import edu.ie3.powerFactory2psdm.exception.pf.{ - ElementConfigurationException, - TestException -} -import edu.ie3.powerFactory2psdm.model.entity.Subnet -import org.jgrapht.alg.connectivity.BiconnectivityInspector -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class SubnetBuilderSpec extends Matchers with AnyWordSpecLike { - - "The SubnetBuilder" should { - - val gridGraph = GridGraphBuilder.build( - testGrid.nodes, - testGrid.lines ++ testGrid.switches - ) - val subgraphs = - new BiconnectivityInspector(gridGraph).getConnectedComponents - - "build a subnet for each subgraph" in { - SubnetBuilder - .buildSubnets(gridGraph, id2node) - .size shouldBe subgraphs.size - } - - "throw an exception if at least one of the nodes has a deviating nominal voltage" in { - val nodeId = "Grid.ElmNet\\Bus_0003.ElmTerm" - val node = id2node.getOrElse( - nodeId, - throw TestException(s"No node with id $nodeId in the id2node map") - ) - val faultyNode = node.copy(nominalVoltage = 131.0) - val updatedMap = id2node.updated(nodeId, faultyNode) - intercept[ElementConfigurationException] { - SubnetBuilder.buildSubnet(1, subnet1Ids, updatedMap) - }.getMessage shouldBe (s"There are the following divergences from the nominal voltage 132.0 : HashSet($nodeId -> 131.0)") - } - - "identify the correct voltage level id for the voltage level " in { - val subnet1: Subnet = - SubnetBuilder.buildSubnet(1, subnet1Ids, id2node) - subnet1.voltLvl.getId shouldBe "Hochspannung" - val subnet2: Subnet = - SubnetBuilder.buildSubnet(2, subnet2Ids, id2node) - subnet2.voltLvl.getId shouldBe "Niederspannung" - } - - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ + id2node, + subnet1Ids, + subnet2Ids, + testGrid +} +import edu.ie3.powerFactory2psdm.exception.pf.{ + ElementConfigurationException, + TestException +} +import edu.ie3.powerFactory2psdm.model.entity.Subnet +import org.jgrapht.alg.connectivity.BiconnectivityInspector +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class SubnetBuilderSpec extends Matchers with AnyWordSpecLike { + + "The SubnetBuilder" should { + + val gridGraph = GridGraphBuilder.build( + testGrid.nodes, + testGrid.lines ++ testGrid.switches + ) + val subgraphs = + new BiconnectivityInspector(gridGraph).getConnectedComponents + + "build a subnet for each subgraph" in { + SubnetBuilder + .buildSubnets(gridGraph, id2node) + .size shouldBe subgraphs.size + } + + "throw an exception if at least one of the nodes has a deviating nominal voltage" in { + val nodeId = "Grid.ElmNet\\Bus_0003.ElmTerm" + val node = id2node.getOrElse( + nodeId, + throw TestException(s"No node with id $nodeId in the id2node map") + ) + val faultyNode = node.copy(nominalVoltage = 131.0) + val updatedMap = id2node.updated(nodeId, faultyNode) + intercept[ElementConfigurationException] { + SubnetBuilder.buildSubnet(1, subnet1Ids, updatedMap) + }.getMessage shouldBe (s"There are the following divergences from the nominal voltage 132.0 : HashSet($nodeId -> 131.0)") + } + + "identify the correct voltage level id for the voltage level " in { + val subnet1: Subnet = + SubnetBuilder.buildSubnet(1, subnet1Ids, id2node) + subnet1.voltLvl.getId shouldBe "Hochspannung" + val subnet2: Subnet = + SubnetBuilder.buildSubnet(2, subnet2Ids, id2node) + subnet2.voltLvl.getId shouldBe "Niederspannung" + } + + } + +} 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 2d2bc8ea..79815793 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala @@ -1,32 +1,32 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.getLineType -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class LineTypeConverterSpec extends Matchers with AnyWordSpecLike { - - "The line type converter" should { - val conversionPair = getLineType("someLineType") - - "convert a line type correctly" in { - val actual = LineTypeConverter.convert(conversionPair.input) - val expected = conversionPair.result - - actual.getB shouldBe expected.getB - actual.getG shouldBe expected.getG - actual.getR shouldBe expected.getR - actual.getX shouldBe expected.getX - actual.getiMax shouldBe expected.getiMax - actual.getvRated shouldBe expected.getvRated - } - - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.getLineType +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class LineTypeConverterSpec extends Matchers with AnyWordSpecLike { + + "The line type converter" should { + val conversionPair = getLineType("someLineType") + + "convert a line type correctly" in { + val actual = LineTypeConverter.convert(conversionPair.input) + val expected = conversionPair.result + + actual.getB shouldBe expected.getB + actual.getG shouldBe expected.getG + actual.getR shouldBe expected.getR + actual.getX shouldBe expected.getX + actual.getiMax shouldBe expected.getiMax + actual.getvRated shouldBe expected.getvRated + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala index 4d681ad5..46366f01 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala @@ -1,67 +1,67 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.getTransformer2wType -import edu.ie3.powerFactory2psdm.exception.pf.ConversionException -import edu.ie3.scalatest.QuantityMatchers -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import math.abs - -class TransformerType2WConverterSpec - extends Matchers - with AnyWordSpecLike - with QuantityMatchers { - - "A Transformer2wTypeConverter" should { - val conversionPair = getTransformer2wType("SomeTrafo2wType") - val input = conversionPair.input - val expected = conversionPair.result - implicit val quantityTolerance: Double = 1e-6 - - "convert a transformer type correctly" in { - val actual = TransformerType2WConverter.convert(input) - - actual.getId shouldBe expected.getId - - actual.getvRatedA should equalWithTolerance(expected.getvRatedA) - actual.getvRatedB should equalWithTolerance(expected.getvRatedB) - actual.getdV should equalWithTolerance(expected.getdV) - actual.getdPhi should equalWithTolerance(expected.getdPhi) - - actual.getrSc should equalWithTolerance(expected.getrSc) - actual.getxSc should equalWithTolerance(expected.getxSc) - actual.getgM should equalWithTolerance(expected.getgM) - - actual.isTapSide shouldBe expected.isTapSide - actual.getTapNeutr shouldBe expected.getTapNeutr - actual.getTapMin shouldBe expected.getTapMin - actual.getTapMax shouldBe expected.getTapMax - } - - "throw an exception, when the input model does not allow to calculate short circuit parameters correctly" in { - val invalidInput = input.copy(pCu = 3000) - val thrown = intercept[ConversionException]( - TransformerType2WConverter.convert(invalidInput) - ) - thrown.getMessage shouldBe s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + - s"parameters. The short circuit resistance can't exceed the short circuit impedance." - } - - "throw an exception, when the input model does not allow to calculate no load circuit parameters correctly" in { - val invalidInput = input.copy(pFe = 1000) - val thrown = intercept[ConversionException]( - TransformerType2WConverter.convert(invalidInput) - ) - thrown.getMessage shouldBe s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + - s"The no load conductance can't exceed the no load admittance." - } - - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.getTransformer2wType +import edu.ie3.powerFactory2psdm.exception.pf.ConversionException +import edu.ie3.scalatest.QuantityMatchers +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import math.abs + +class TransformerType2WConverterSpec + extends Matchers + with AnyWordSpecLike + with QuantityMatchers { + + "A Transformer2wTypeConverter" should { + val conversionPair = getTransformer2wType("SomeTrafo2wType") + val input = conversionPair.input + val expected = conversionPair.result + implicit val quantityTolerance: Double = 1e-6 + + "convert a transformer type correctly" in { + val actual = TransformerType2WConverter.convert(input) + + actual.getId shouldBe expected.getId + + actual.getvRatedA should equalWithTolerance(expected.getvRatedA) + actual.getvRatedB should equalWithTolerance(expected.getvRatedB) + actual.getdV should equalWithTolerance(expected.getdV) + actual.getdPhi should equalWithTolerance(expected.getdPhi) + + actual.getrSc should equalWithTolerance(expected.getrSc) + actual.getxSc should equalWithTolerance(expected.getxSc) + actual.getgM should equalWithTolerance(expected.getgM) + + actual.isTapSide shouldBe expected.isTapSide + actual.getTapNeutr shouldBe expected.getTapNeutr + actual.getTapMin shouldBe expected.getTapMin + actual.getTapMax shouldBe expected.getTapMax + } + + "throw an exception, when the input model does not allow to calculate short circuit parameters correctly" in { + val invalidInput = input.copy(pCu = 3000) + val thrown = intercept[ConversionException]( + TransformerType2WConverter.convert(invalidInput) + ) + thrown.getMessage shouldBe s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + + s"parameters. The short circuit resistance can't exceed the short circuit impedance." + } + + "throw an exception, when the input model does not allow to calculate no load circuit parameters correctly" in { + val invalidInput = input.copy(pFe = 1000) + val thrown = intercept[ConversionException]( + TransformerType2WConverter.convert(invalidInput) + ) + thrown.getMessage shouldBe s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + + s"The no load conductance can't exceed the no load admittance." + } + + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala index 49d58472..589ed2e3 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala @@ -1,94 +1,94 @@ -/* - * © 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 - -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - MissingParameterException -} -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ProjectSettings -import edu.ie3.powerFactory2psdm.model.setting.ConversionPrefixes -import org.scalatest.PrivateMethodTester -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class PreprocessedPfGridModelSpec - extends Matchers - with AnyWordSpecLike - with PrivateMethodTester { - - "A GridModel" should { - val settings = ProjectSettings(Some(0), Some("k"), Some("k")) - - "extract project settings" in { - val extractProjectSettings = - PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) - val extractedSettings = - PreprocessedPfGridModel invokePrivate extractProjectSettings( - Some(List(settings)) - ) - extractedSettings shouldBe settings - } - - "throw an exception if there are multiple project settings" in { - val extractProjectSettings = - PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) - val exc = intercept[ConversionException]( - PreprocessedPfGridModel invokePrivate extractProjectSettings( - Some(List(settings, settings)) - ) - ) - exc.getMessage shouldBe "There are multiple project settings defined." - } - - "throw an exception if there are no project settings" in { - val extractProjectSettings = - PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) - val exc = intercept[ConversionException]( - PreprocessedPfGridModel invokePrivate extractProjectSettings( - None - ) - ) - exc.getMessage shouldBe "There are no project settings defined." - } - - "build conversion prefixes" in { - val buildConversionPrefixes = - PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) - val conversionPrefixes = - PreprocessedPfGridModel invokePrivate buildConversionPrefixes( - settings - ) - conversionPrefixes.loadPQSPrefixValue shouldBe 1e3 - conversionPrefixes.lineLengthPrefixValue shouldBe 1e3 - } - - "throw an exception when the pqsPrefix is missing" in { - val buildConversionPrefixes = - PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) - val exc = intercept[MissingParameterException]( - PreprocessedPfGridModel invokePrivate buildConversionPrefixes( - settings.copy(prefixPQS = None) - ) - ) - exc.getMessage shouldBe "The projects settings miss the prefix specification for active/reactive/apparent power values" - } - - "throw an exception when the prefixLength is missing" in { - val buildConversionPrefixes = - PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) - val exc = intercept[MissingParameterException]( - PreprocessedPfGridModel invokePrivate buildConversionPrefixes( - settings.copy(prefixLength = None) - ) - ) - exc.getMessage shouldBe "The project settings miss the prefix specification for line length." - } - - } - -} +/* + * © 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 + +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + MissingParameterException +} +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ProjectSettings +import edu.ie3.powerFactory2psdm.model.setting.ConversionPrefixes +import org.scalatest.PrivateMethodTester +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class PreprocessedPfGridModelSpec + extends Matchers + with AnyWordSpecLike + with PrivateMethodTester { + + "A GridModel" should { + val settings = ProjectSettings(Some(0), Some("k"), Some("k")) + + "extract project settings" in { + val extractProjectSettings = + PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) + val extractedSettings = + PreprocessedPfGridModel invokePrivate extractProjectSettings( + Some(List(settings)) + ) + extractedSettings shouldBe settings + } + + "throw an exception if there are multiple project settings" in { + val extractProjectSettings = + PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) + val exc = intercept[ConversionException]( + PreprocessedPfGridModel invokePrivate extractProjectSettings( + Some(List(settings, settings)) + ) + ) + exc.getMessage shouldBe "There are multiple project settings defined." + } + + "throw an exception if there are no project settings" in { + val extractProjectSettings = + PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) + val exc = intercept[ConversionException]( + PreprocessedPfGridModel invokePrivate extractProjectSettings( + None + ) + ) + exc.getMessage shouldBe "There are no project settings defined." + } + + "build conversion prefixes" in { + val buildConversionPrefixes = + PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) + val conversionPrefixes = + PreprocessedPfGridModel invokePrivate buildConversionPrefixes( + settings + ) + conversionPrefixes.loadPQSPrefixValue shouldBe 1e3 + conversionPrefixes.lineLengthPrefixValue shouldBe 1e3 + } + + "throw an exception when the pqsPrefix is missing" in { + val buildConversionPrefixes = + PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) + val exc = intercept[MissingParameterException]( + PreprocessedPfGridModel invokePrivate buildConversionPrefixes( + settings.copy(prefixPQS = None) + ) + ) + exc.getMessage shouldBe "The projects settings miss the prefix specification for active/reactive/apparent power values" + } + + "throw an exception when the prefixLength is missing" in { + val buildConversionPrefixes = + PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) + val exc = intercept[MissingParameterException]( + PreprocessedPfGridModel invokePrivate buildConversionPrefixes( + settings.copy(prefixLength = None) + ) + ) + exc.getMessage shouldBe "The project settings miss the prefix specification for line length." + } + + } + +} 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 ff845ef9..06012200 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala @@ -1,54 +1,54 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class LineSpec extends Matchers with AnyWordSpecLike { - "A Line " should { - - val id = "SomeLine.ElmLne" - - val input = Lines( - id = Some(id), - bus1Id = Some("SomeBusA"), - bus2Id = Some("SomeBusB") - ) - - "throw an exception if the id is missing" in { - val line = input.copy(id = None) - val exc = intercept[MissingParameterException](Line.build(line)) - 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)) - 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)) - exc.getMessage shouldBe s"Line: $id has no defined node b" - } - - "build a fully configured line correctly" in { - val line = Line.build(input) - line.id shouldBe "SomeLine.ElmLne" - line.nodeAId shouldBe "SomeBusA" - line.nodeBId shouldBe "SomeBusB" - } - - } - -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class LineSpec extends Matchers with AnyWordSpecLike { + "A Line " should { + + val id = "SomeLine.ElmLne" + + val input = Lines( + id = Some(id), + bus1Id = Some("SomeBusA"), + bus2Id = Some("SomeBusB") + ) + + "throw an exception if the id is missing" in { + val line = input.copy(id = None) + val exc = intercept[MissingParameterException](Line.build(line)) + 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)) + 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)) + exc.getMessage shouldBe s"Line: $id has no defined node b" + } + + "build a fully configured line correctly" in { + val line = Line.build(input) + line.id shouldBe "SomeLine.ElmLne" + line.nodeAId shouldBe "SomeBusA" + line.nodeBId shouldBe "SomeBusB" + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala index a9b59199..eb079313 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala @@ -1,75 +1,75 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ConElms, Nodes} -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class NodeSpec extends Matchers with AnyWordSpecLike { - "A Node " should { - - val conElm = ConElms( - Some( - "SomeLine.ElmLne" - ), - Some( - "ElmLne" - ) - ) - - val id = "SomeBus.ElmTerm" - - val input = Nodes( - id = Some(id), - vtarget = Some(1.0), - uknom = Some(132.0), - conElms = Some(List(Some(conElm))), - GPSlat = Some(1.0), - GPSlon = Some(1.0) - ) - - "throw an exception if the id is missing" in { - val node = input.copy(id = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"There is no id for node $node" - } - - "throw an exception if the nominal voltage is missing" in { - val id = "BrokenBus1.ElmTerm" - val node = input.copy(id = Some(id), uknom = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"Node: $id has no defined nominal voltage" - } - - "throw an exception if the target voltage is missing" in { - val id = "BrokenBus2.ElmTerm" - val node = input.copy(id = Some(id), vtarget = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"Node: $id has no defined target voltage" - } - - "throw an exception if there is no connected element" in { - val id = "BrokenBus3.ElmTerm" - val node = input.copy(id = Some(id), conElms = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"Node: $id has no connected elements" - } - - "build a fully configured node correctly" in { - val node = Node.build(input) - node.id shouldBe "SomeBus.ElmTerm" - node.vTarget shouldBe 1.0 - node.nominalVoltage shouldBe 132.0 - node.lat shouldBe Some(1.0) - node.lon shouldBe Some(1.0) - } - - } - -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ConElms, Nodes} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class NodeSpec extends Matchers with AnyWordSpecLike { + "A Node " should { + + val conElm = ConElms( + Some( + "SomeLine.ElmLne" + ), + Some( + "ElmLne" + ) + ) + + val id = "SomeBus.ElmTerm" + + val input = Nodes( + id = Some(id), + vtarget = Some(1.0), + uknom = Some(132.0), + conElms = Some(List(Some(conElm))), + GPSlat = Some(1.0), + GPSlon = Some(1.0) + ) + + "throw an exception if the id is missing" in { + val node = input.copy(id = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"There is no id for node $node" + } + + "throw an exception if the nominal voltage is missing" in { + val id = "BrokenBus1.ElmTerm" + val node = input.copy(id = Some(id), uknom = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"Node: $id has no defined nominal voltage" + } + + "throw an exception if the target voltage is missing" in { + val id = "BrokenBus2.ElmTerm" + val node = input.copy(id = Some(id), vtarget = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"Node: $id has no defined target voltage" + } + + "throw an exception if there is no connected element" in { + val id = "BrokenBus3.ElmTerm" + val node = input.copy(id = Some(id), conElms = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"Node: $id has no connected elements" + } + + "build a fully configured node correctly" in { + val node = Node.build(input) + node.id shouldBe "SomeBus.ElmTerm" + node.vTarget shouldBe 1.0 + node.nominalVoltage shouldBe 132.0 + node.lat shouldBe Some(1.0) + node.lon shouldBe Some(1.0) + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala index 8f7bed41..a81c9e7d 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala @@ -1,57 +1,57 @@ -/* - * © 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.{ - MissingParameterException, - TestException -} -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class SwitchSpec extends Matchers with AnyWordSpecLike { - "A switch " should { - - val id = "Some_Switch.ElmCoup" - - val input = Switches( - id = Some(id), - bus1Id = Some("SomeBusA"), - bus2Id = Some("SomeBusB") - ) - - "throw an exception when building if the id is missing" in { - val switch = input.copy(id = None) - val exc = intercept[MissingParameterException](Switch.maybeBuild(switch)) - exc.getMessage shouldBe s"There is no id for switch $switch" - } - - "return None when building if the bus1Id is missing" in { - val id = "Broken_Switch1.ElmLne" - val switch = Switch.maybeBuild(input.copy(id = Some(id), bus1Id = None)) - switch shouldBe None - } - - "return None when building if the bus2Id is missing" in { - val id = "Broken_Switch2.ElmLne" - val switch = Switch.maybeBuild(input.copy(id = Some(id), bus2Id = None)) - switch shouldBe None - } - - "build a fully configured switch correctly" in { - val switch = Switch - .maybeBuild(input) - .getOrElse(throw TestException("We shouldn't get None here!")) - switch.id shouldBe "Some_Switch.ElmCoup" - switch.nodeAId shouldBe "SomeBusA" - switch.nodeBId shouldBe "SomeBusB" - } - - } - -} +/* + * © 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.{ + MissingParameterException, + TestException +} +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class SwitchSpec extends Matchers with AnyWordSpecLike { + "A switch " should { + + val id = "Some_Switch.ElmCoup" + + val input = Switches( + id = Some(id), + bus1Id = Some("SomeBusA"), + bus2Id = Some("SomeBusB") + ) + + "throw an exception when building if the id is missing" in { + val switch = input.copy(id = None) + val exc = intercept[MissingParameterException](Switch.maybeBuild(switch)) + exc.getMessage shouldBe s"There is no id for switch $switch" + } + + "return None when building if the bus1Id is missing" in { + val id = "Broken_Switch1.ElmLne" + val switch = Switch.maybeBuild(input.copy(id = Some(id), bus1Id = None)) + switch shouldBe None + } + + "return None when building if the bus2Id is missing" in { + val id = "Broken_Switch2.ElmLne" + val switch = Switch.maybeBuild(input.copy(id = Some(id), bus2Id = None)) + switch shouldBe None + } + + "build a fully configured switch correctly" in { + val switch = Switch + .maybeBuild(input) + .getOrElse(throw TestException("We shouldn't get None here!")) + switch.id shouldBe "Some_Switch.ElmCoup" + switch.nodeAId shouldBe "SomeBusA" + switch.nodeBId shouldBe "SomeBusB" + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala index 24e151d3..0d2af587 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala @@ -1,75 +1,75 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes -import edu.ie3.powerFactory2psdm.model.entity.types.LineType -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class LineTypeSpec extends Matchers with AnyWordSpecLike { - "A line type" should { - - val input = LineTypes( - id = Some("testLineType"), - uline = Some(132.0), - sline = Some(1.0), - rline = Some(6.753542423248291), - xline = Some(20.61956214904785), - bline = Some(151.51515197753906), - gline = Some(1.543) - ) - "throw an exception when building if the id is missing" in { - val lineType = input.copy(id = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no id for line type $lineType" - } - - "throw an exception when building if no rated voltage is defined" in { - val id = "BrokenLineType1" - val lineType = input.copy(id = Some(id), uline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no rated voltage defined for line type: $id" - } - - "throw an exception when building no thermal current is defined" in { - val id = "BrokenLineType2" - val lineType = input.copy(id = Some(id), sline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no maximum thermal current defined for line type: $id" - } - - "throw an exception when building if no specific resistance is defined" in { - val id = "BrokenLineType3" - val lineType = input.copy(id = Some(id), rline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no specific resistance defined for line type: $id" - } - - "throw an exception when building if no specific reactance is defined" in { - val id = "BrokenLineType4" - val lineType = input.copy(id = Some(id), xline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no specific reactance defined for line type: $id" - } - - "throw an exception when building if no phase-to-ground conductance is defined" in { - val id = "BrokenLineType5" - val lineType = input.copy(id = Some(id), bline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no phase-to-ground conductance defined for line type: $id" - } - - "throw an exception when building if no phase-to-ground susceptance is defined" in { - val id = "BrokenLineType6" - val lineType = input.copy(id = Some(id), gline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no phase-to-ground susceptance defined for line type: $id" - } - } -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes +import edu.ie3.powerFactory2psdm.model.entity.types.LineType +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class LineTypeSpec extends Matchers with AnyWordSpecLike { + "A line type" should { + + val input = LineTypes( + id = Some("testLineType"), + uline = Some(132.0), + sline = Some(1.0), + rline = Some(6.753542423248291), + xline = Some(20.61956214904785), + bline = Some(151.51515197753906), + gline = Some(1.543) + ) + "throw an exception when building if the id is missing" in { + val lineType = input.copy(id = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no id for line type $lineType" + } + + "throw an exception when building if no rated voltage is defined" in { + val id = "BrokenLineType1" + val lineType = input.copy(id = Some(id), uline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no rated voltage defined for line type: $id" + } + + "throw an exception when building no thermal current is defined" in { + val id = "BrokenLineType2" + val lineType = input.copy(id = Some(id), sline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no maximum thermal current defined for line type: $id" + } + + "throw an exception when building if no specific resistance is defined" in { + val id = "BrokenLineType3" + val lineType = input.copy(id = Some(id), rline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no specific resistance defined for line type: $id" + } + + "throw an exception when building if no specific reactance is defined" in { + val id = "BrokenLineType4" + val lineType = input.copy(id = Some(id), xline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no specific reactance defined for line type: $id" + } + + "throw an exception when building if no phase-to-ground conductance is defined" in { + val id = "BrokenLineType5" + val lineType = input.copy(id = Some(id), bline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no phase-to-ground conductance defined for line type: $id" + } + + "throw an exception when building if no phase-to-ground susceptance is defined" in { + val id = "BrokenLineType6" + val lineType = input.copy(id = Some(id), gline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no phase-to-ground susceptance defined for line type: $id" + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala index fc289b41..b5bec997 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala @@ -1,170 +1,170 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w -import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class TransformerType2WSpec extends Matchers with AnyWordSpecLike { - - "A Transformer2wTypeSpec" should { - - val id = "Some2wTransformerType" - - val input = TrafoTypes2w( - id = Some(id), - strn = Some(40), - utrn_h = Some(10), - utrn_l = Some(0.4), - dutap = Some(1.2), - phitr = Some(12), - tap_side = Some(0), - nntap0 = Some(-1), - ntpmn = Some(-3), - ntpmx = Some(5), - uktr = Some(5.1345), - curmg = Some(1), - pfe = Some(10), - pcutr = Some(13) - ) - - "throw an exception when trying to build a TransformerType without an id" in { - val faulty1 = input.copy(id = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty1) - ) - exc.getMessage shouldBe s"There is no id for transformer-type: $faulty1" - } - - "throw an exception when trying to build a TransformerType without parameter strn" in { - val faulty2 = input.copy(strn = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty2) - ) - exc.getMessage shouldBe s"There is no rated apparent power for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter utrn_h" in { - val faulty3 = input.copy(utrn_h = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty3) - ) - exc.getMessage shouldBe s"There is no voltage of high winding side for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter utrn_l" in { - val faulty4 = input.copy(utrn_l = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty4) - ) - exc.getMessage shouldBe s"There is no voltage of low winding side for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter dutap" in { - val faulty5 = input.copy(dutap = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty5) - ) - exc.getMessage shouldBe s"There is no voltage magnitude deviation per tap position for transfomer type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter dV" in { - val faulty6 = input.copy(phitr = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty6) - ) - exc.getMessage shouldBe s"There is no voltage angle deviation per tap position for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter tap_side" in { - val faulty7 = input.copy(tap_side = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty7) - ) - exc.getMessage shouldBe s"There is no selection of winding where tap changer is installed for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter nntap0" in { - val faulty8 = input.copy(nntap0 = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty8) - ) - exc.getMessage shouldBe s"There is no neutral tap position defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter ntpmn" in { - val faulty9 = input.copy(ntpmn = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty9) - ) - exc.getMessage shouldBe s"There is no minmum tap position defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter ntpmx" in { - val faulty10 = input.copy(ntpmx = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty10) - ) - exc.getMessage shouldBe s"There is no maximum tap position defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter uktr" in { - val faulty11 = input.copy(uktr = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty11) - ) - exc.getMessage shouldBe s"There is no short circuit voltage defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter curmg" in { - val faulty12 = input.copy(curmg = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty12) - ) - exc.getMessage shouldBe s"There is no no load current defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter pfe" in { - val faulty13 = input.copy(pfe = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty13) - ) - exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter pcutr" in { - val faulty13 = input.copy(pcutr = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty13) - ) - exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" - } - - "build a correctly configured TransformerType2w" in { - val actual = TransformerType2W.build(input) - actual.id shouldBe "Some2wTransformerType" - actual.sRated shouldBe 40 - actual.vRatedA shouldBe 10 - actual.vRatedB shouldBe 0.4 - actual.dV shouldBe 1.2 - actual.dPhi shouldBe 12 - actual.tapSide shouldBe 0 - actual.tapNeutr shouldBe -1 - actual.tapMin shouldBe -3 - actual.tapMax shouldBe 5 - actual.uk shouldBe 5.1345 - actual.iNoLoad shouldBe 1 - actual.pFe shouldBe 10 - actual.pCu shouldBe 13 - } - - } - -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w +import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class TransformerType2WSpec extends Matchers with AnyWordSpecLike { + + "A Transformer2wTypeSpec" should { + + val id = "Some2wTransformerType" + + val input = TrafoTypes2w( + id = Some(id), + strn = Some(40), + utrn_h = Some(10), + utrn_l = Some(0.4), + dutap = Some(1.2), + phitr = Some(12), + tap_side = Some(0), + nntap0 = Some(-1), + ntpmn = Some(-3), + ntpmx = Some(5), + uktr = Some(5.1345), + curmg = Some(1), + pfe = Some(10), + pcutr = Some(13) + ) + + "throw an exception when trying to build a TransformerType without an id" in { + val faulty1 = input.copy(id = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty1) + ) + exc.getMessage shouldBe s"There is no id for transformer-type: $faulty1" + } + + "throw an exception when trying to build a TransformerType without parameter strn" in { + val faulty2 = input.copy(strn = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty2) + ) + exc.getMessage shouldBe s"There is no rated apparent power for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter utrn_h" in { + val faulty3 = input.copy(utrn_h = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty3) + ) + exc.getMessage shouldBe s"There is no voltage of high winding side for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter utrn_l" in { + val faulty4 = input.copy(utrn_l = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty4) + ) + exc.getMessage shouldBe s"There is no voltage of low winding side for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter dutap" in { + val faulty5 = input.copy(dutap = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty5) + ) + exc.getMessage shouldBe s"There is no voltage magnitude deviation per tap position for transfomer type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter dV" in { + val faulty6 = input.copy(phitr = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty6) + ) + exc.getMessage shouldBe s"There is no voltage angle deviation per tap position for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter tap_side" in { + val faulty7 = input.copy(tap_side = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty7) + ) + exc.getMessage shouldBe s"There is no selection of winding where tap changer is installed for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter nntap0" in { + val faulty8 = input.copy(nntap0 = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty8) + ) + exc.getMessage shouldBe s"There is no neutral tap position defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter ntpmn" in { + val faulty9 = input.copy(ntpmn = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty9) + ) + exc.getMessage shouldBe s"There is no minmum tap position defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter ntpmx" in { + val faulty10 = input.copy(ntpmx = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty10) + ) + exc.getMessage shouldBe s"There is no maximum tap position defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter uktr" in { + val faulty11 = input.copy(uktr = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty11) + ) + exc.getMessage shouldBe s"There is no short circuit voltage defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter curmg" in { + val faulty12 = input.copy(curmg = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty12) + ) + exc.getMessage shouldBe s"There is no no load current defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter pfe" in { + val faulty13 = input.copy(pfe = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty13) + ) + exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter pcutr" in { + val faulty13 = input.copy(pcutr = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty13) + ) + exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" + } + + "build a correctly configured TransformerType2w" in { + val actual = TransformerType2W.build(input) + actual.id shouldBe "Some2wTransformerType" + actual.sRated shouldBe 40 + actual.vRatedA shouldBe 10 + actual.vRatedB shouldBe 0.4 + actual.dV shouldBe 1.2 + actual.dPhi shouldBe 12 + actual.tapSide shouldBe 0 + actual.tapNeutr shouldBe -1 + actual.tapMin shouldBe -3 + actual.tapMax shouldBe 5 + actual.uk shouldBe 5.1345 + actual.iNoLoad shouldBe 1 + actual.pFe shouldBe 10 + actual.pCu shouldBe 13 + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala index 05747c44..9dddd124 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala @@ -1,45 +1,45 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class GridPreparatorSpec extends Matchers with AnyWordSpecLike { - - "The grid preparator" should { - - "filter out a singly connected switch" in { - val singlyConnectedSwitch = Switches( - Some("singlyConnectedSwitch"), - Some("someNodeId"), - None - ) - val anotherSinglyConnectedSwitch = Switches( - Some("anotherSinglyConnectedSwitch"), - None, - Some("someNodeId") - ) - val fullyConnectedSwitch = Switches( - Some("fullyConnectedSwitch"), - Some("someNodeId"), - Some("aDifferentNodeId") - ) - val filteredSwitches = GridPreparator.removeSinglyConnectedSwitches( - Some( - List( - singlyConnectedSwitch, - anotherSinglyConnectedSwitch, - fullyConnectedSwitch - ) - ) - ) - filteredSwitches shouldEqual Some(List(fullyConnectedSwitch)) - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class GridPreparatorSpec extends Matchers with AnyWordSpecLike { + + "The grid preparator" should { + + "filter out a singly connected switch" in { + val singlyConnectedSwitch = Switches( + Some("singlyConnectedSwitch"), + Some("someNodeId"), + None + ) + val anotherSinglyConnectedSwitch = Switches( + Some("anotherSinglyConnectedSwitch"), + None, + Some("someNodeId") + ) + val fullyConnectedSwitch = Switches( + Some("fullyConnectedSwitch"), + Some("someNodeId"), + Some("aDifferentNodeId") + ) + val filteredSwitches = GridPreparator.removeSinglyConnectedSwitches( + Some( + List( + singlyConnectedSwitch, + anotherSinglyConnectedSwitch, + fullyConnectedSwitch + ) + ) + ) + filteredSwitches shouldEqual Some(List(fullyConnectedSwitch)) + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala index 8697be9c..9897b7a0 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala @@ -1,67 +1,67 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import edu.ie3.powerFactory2psdm.util.SchemaGenerator.run -import edu.ie3.util.io.FileIOUtils -import org.scalatest.BeforeAndAfterAll -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import java.io.{File, PrintWriter} -import scala.io.Source -import scala.reflect.runtime.universe -import scala.util.{Try, Using} - -class SchemaGeneratorIT - extends Matchers - with AnyWordSpecLike - with BeforeAndAfterAll { - - // create test dir - protected val testTmpDir: String = System.getProperty( - "user.dir" - ) + File.separator + "test" + File.separator + "tmp_" + this.getClass.getSimpleName - - override protected def beforeAll(): Unit = new File(testTmpDir).mkdirs() - - override protected def afterAll(): Unit = - FileIOUtils.deleteRecursively(testTmpDir) - - "A SchemaGenerator" should { - - "generate the schema from a test file correctly" in { - - val source = - Source.fromFile( - s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" - ) - val jsonString = - try source.mkString - finally source.close - - val outputFile = new File( - s"$testTmpDir${File.separator}PowerFactoryGrid.scala" - ) - - SchemaGenerator - .run( - jsonString, - "PowerFactoryGrid", - "edu.ie3.powerFactory2psdm.model.powerfactory" - ) - .foreach(formatedClassString => { - val pw = new PrintWriter( - outputFile - ) - pw.write(formatedClassString) - pw.close() - }) - assert(outputFile.exists()) - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import edu.ie3.powerFactory2psdm.util.SchemaGenerator.run +import edu.ie3.util.io.FileIOUtils +import org.scalatest.BeforeAndAfterAll +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import java.io.{File, PrintWriter} +import scala.io.Source +import scala.reflect.runtime.universe +import scala.util.{Try, Using} + +class SchemaGeneratorIT + extends Matchers + with AnyWordSpecLike + with BeforeAndAfterAll { + + // create test dir + protected val testTmpDir: String = System.getProperty( + "user.dir" + ) + File.separator + "test" + File.separator + "tmp_" + this.getClass.getSimpleName + + override protected def beforeAll(): Unit = new File(testTmpDir).mkdirs() + + override protected def afterAll(): Unit = + FileIOUtils.deleteRecursively(testTmpDir) + + "A SchemaGenerator" should { + + "generate the schema from a test file correctly" in { + + val source = + Source.fromFile( + s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" + ) + val jsonString = + try source.mkString + finally source.close + + val outputFile = new File( + s"$testTmpDir${File.separator}PowerFactoryGrid.scala" + ) + + SchemaGenerator + .run( + jsonString, + "PowerFactoryGrid", + "edu.ie3.powerFactory2psdm.model.powerfactory" + ) + .foreach(formatedClassString => { + val pw = new PrintWriter( + outputFile + ) + pw.write(formatedClassString) + pw.close() + }) + assert(outputFile.exists()) + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala index d191176c..60a5e597 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala @@ -1,538 +1,538 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import org.scalatest.PrivateMethodTester -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class SchemaGeneratorSpec - extends Matchers - with AnyWordSpecLike - with PrivateMethodTester { - - private val className = "PowerFactoryGrid" - private val packageName = "edu.ie3.powerFactory2psdm.model.powerfactory" - - private def genCode(json: String): Option[String] = - SchemaGenerator.run(json, className, packageName) - - "A SchemaGenerator" should { - - "not generate code if the json object is empty" in { - - val json = - """ - | {} - |""".stripMargin - - genCode(json) shouldBe None - } - - "generate a valid class for an empty top level collection" in { - - val json = - """ - |{ - | "lines": [] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines() - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for multiple empty top level collections" in { - val json = - """ - |{ - | "lines": [], - | "nodes": [] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ - | Lines, - | Nodes - |} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]], - | nodes: Option[List[Nodes]] - |) - | - |object PowerFactoryGrid { - | - | final case class Nodes() - | - | final case class Lines() - | - |} - |""".stripMargin - ) - } - - "generate a valid class for a top level collection with one simple attribute" in { - - val json = - """ - |{ - | "lines": [ - | { - | "length": 1 - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines(length: Option[Double]) - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for a top level collection with multiple simple attribute" in { - - val json = - """ - |{ - | "lines": [ - | { - | "length": 1, - | "AccessTime": 0.0, - | "GPSlat": 0.0, - | "GPSlon": 0.0, - | "Vtarget": 11.0, - | "cpSubstat": null - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines( - | AccessTime: Option[Double], - | GPSlat: Option[Double], - | GPSlon: Option[Double], - | length: Option[Double], - | cpSubstat: Option[String], - | Vtarget: Option[Double] - | ) - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for a top level collection with nested class fields" in { - - val json = - """ - |{ - | "lines": [ - | { - | "obj": { - | "a": "x", - | "b": "y" - | } - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Obj(a: Option[String], b: Option[String]) - | - | final case class Lines(obj: Option[Obj]) - | - |} - |""".stripMargin - ) - - } - } - - "generate a valid class if we have multiple nested complex fields that are nested in several objects" in { - - val json = - """ - |{ - | "lines": [ - | { - | "conElms": [ - | { - | "loc_name": "Klemmleiste(1)", - | "pfCls": "ElmTerm" - | }, - | { - | "loc_name": "Klemmleiste", - | "pfCls": "ElmTerm" - | } - | ] - | } - | ], - | "trafos3w": [ - | { - | "conElms": [ - | { - | "loc_name": "Klemmleiste OS", - | "pfCls": "ElmTerm" - | }, - | { - | "loc_name": "Klemmleiste MS", - | "pfCls": "ElmTerm" - | }, - | { - | "loc_name": "Klemmleiste MS2", - | "pfCls": "ElmTerm" - | } - | ] - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ - | Lines, - | Trafos3w - |} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]], - | trafos3w: Option[List[Trafos3w]] - |) - | - |object PowerFactoryGrid { - | - | final case class Trafos3w(conElms: Option[List[Option[ConElms]]]) - | - | final case class ConElms(loc_name: Option[String], pfCls: Option[String]) - | - | final case class Lines(conElms: Option[List[Option[ConElms]]]) - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for a complex json" in { - - val json = - """ - |{ - | "nodes": [ - | { - | "AccessTime": 0.0, - | "GPSlat": 0.0, - | "GPSlon": 0.0, - | "Vtarget": 11.0, - | "cpSubstat": null, - | "bar": { - | "a": "asd", - | "b": "ass" - | }, - | "foo": { - | }, - | "cpZone": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | }, - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | } - | ], - | "nestedArray": [ - | [] - | ], - | "loc_name": "Klemmleiste MS2", - | "root_id": null, - | "uknom": 11.0, - | "vmax": 1.0499999523162842, - | "vmin": 0.0, - | "vtarget": 1.0 - | }, - | { - | "AccessTime": 0.0, - | "GPSlat": 0.0, - | "GPSlon": 0.0, - | "Vtarget": 11.0, - | "cpSubstat": null, - | "cpZone": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | }, - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | } - | ], - | "nestedArray": [ - | [] - | ], - | "loc_name": "Klemmleiste MS2", - | "root_id": null, - | "uknom": 11.0, - | "vmax": 1.0499999523162842, - | "vmin": 0.0, - | "vtarget": 1.0 - | } - | ], - | "lines": [ - | { - | "length": 1 - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ - | Nodes, - | Lines - |} - | - |final case class PowerFactoryGrid( - | nodes: Option[List[Nodes]], - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class CpZone( - | loc_name: Option[String], - | additionalParam: Option[String], - | leet: Option[List[Option[Leet]]] - | ) - | - | final case class Nodes( - | loc_name: Option[String], - | root_id: Option[String], - | AccessTime: Option[Double], - | GPSlon: Option[Double], - | nestedArray: Option[List[Option[List[Option[String]]]]], - | cpZone: Option[List[Option[CpZone]]], - | vmin: Option[Double], - | GPSlat: Option[Double], - | foo: Option[Foo], - | cpSubstat: Option[String], - | Vtarget: Option[Double], - | uknom: Option[Double], - | bar: Option[Bar], - | vtarget: Option[Double], - | vmax: Option[Double] - | ) - | - | final case class Leet( - | loc_name: Option[String], - | additionalParam: Option[String] - | ) - | - | final case class Bar(a: Option[String], b: Option[String]) - | - | final case class Lines(length: Option[Double]) - | - | final case class Foo() - | - |} - |""".stripMargin - ) - - } - - "generate valid GPSCoords for simple case" in { - val json = - """ - |{ - | "lines": [ - | { - | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", - | "GPScoords": [ - | [ - | 51.4843281, - | 7.4116482 - | ], - | [ - | 52.2895, - | 12.8273 - | ], - | [ - | 52.4895, - | 13.8273 - | ] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" - | } - | ] - |}""".stripMargin - - genCode(json) shouldBe Some( - """package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines( - | id: Option[String], - | GPScoords: Option[List[Option[List[Option[Double]]]]], - | bus1Id: Option[String], - | bus2Id: Option[String] - | ) - | - |} - |""".stripMargin - ) - } - - "generate valid GPSCoords if field values differ" in { - val json = - """ - |{ - | "lines": [ - | { - | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", - | "GPScoords": [ - | [] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" - | }, - | { - | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", - | "GPScoords": [ - | [ - | 51.4843281, - | 7.4116482 - | ], - | [ - | 52.2895, - | 12.8273 - | ], - | [ - | 52.4895, - | 13.8273 - | ] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" - | }, - | { - | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", - | "GPScoords": [ - | [] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" - | } - | ] - |}""".stripMargin - - genCode(json) shouldBe Some( - """package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines( - | id: Option[String], - | GPScoords: Option[List[Option[List[Option[Double]]]]], - | bus1Id: Option[String], - | bus2Id: Option[String] - | ) - | - |} - |""".stripMargin - ) - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import org.scalatest.PrivateMethodTester +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class SchemaGeneratorSpec + extends Matchers + with AnyWordSpecLike + with PrivateMethodTester { + + private val className = "PowerFactoryGrid" + private val packageName = "edu.ie3.powerFactory2psdm.model.powerfactory" + + private def genCode(json: String): Option[String] = + SchemaGenerator.run(json, className, packageName) + + "A SchemaGenerator" should { + + "not generate code if the json object is empty" in { + + val json = + """ + | {} + |""".stripMargin + + genCode(json) shouldBe None + } + + "generate a valid class for an empty top level collection" in { + + val json = + """ + |{ + | "lines": [] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines() + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for multiple empty top level collections" in { + val json = + """ + |{ + | "lines": [], + | "nodes": [] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ + | Lines, + | Nodes + |} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]], + | nodes: Option[List[Nodes]] + |) + | + |object PowerFactoryGrid { + | + | final case class Nodes() + | + | final case class Lines() + | + |} + |""".stripMargin + ) + } + + "generate a valid class for a top level collection with one simple attribute" in { + + val json = + """ + |{ + | "lines": [ + | { + | "length": 1 + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines(length: Option[Double]) + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for a top level collection with multiple simple attribute" in { + + val json = + """ + |{ + | "lines": [ + | { + | "length": 1, + | "AccessTime": 0.0, + | "GPSlat": 0.0, + | "GPSlon": 0.0, + | "Vtarget": 11.0, + | "cpSubstat": null + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines( + | AccessTime: Option[Double], + | GPSlat: Option[Double], + | GPSlon: Option[Double], + | length: Option[Double], + | cpSubstat: Option[String], + | Vtarget: Option[Double] + | ) + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for a top level collection with nested class fields" in { + + val json = + """ + |{ + | "lines": [ + | { + | "obj": { + | "a": "x", + | "b": "y" + | } + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Obj(a: Option[String], b: Option[String]) + | + | final case class Lines(obj: Option[Obj]) + | + |} + |""".stripMargin + ) + + } + } + + "generate a valid class if we have multiple nested complex fields that are nested in several objects" in { + + val json = + """ + |{ + | "lines": [ + | { + | "conElms": [ + | { + | "loc_name": "Klemmleiste(1)", + | "pfCls": "ElmTerm" + | }, + | { + | "loc_name": "Klemmleiste", + | "pfCls": "ElmTerm" + | } + | ] + | } + | ], + | "trafos3w": [ + | { + | "conElms": [ + | { + | "loc_name": "Klemmleiste OS", + | "pfCls": "ElmTerm" + | }, + | { + | "loc_name": "Klemmleiste MS", + | "pfCls": "ElmTerm" + | }, + | { + | "loc_name": "Klemmleiste MS2", + | "pfCls": "ElmTerm" + | } + | ] + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ + | Lines, + | Trafos3w + |} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]], + | trafos3w: Option[List[Trafos3w]] + |) + | + |object PowerFactoryGrid { + | + | final case class Trafos3w(conElms: Option[List[Option[ConElms]]]) + | + | final case class ConElms(loc_name: Option[String], pfCls: Option[String]) + | + | final case class Lines(conElms: Option[List[Option[ConElms]]]) + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for a complex json" in { + + val json = + """ + |{ + | "nodes": [ + | { + | "AccessTime": 0.0, + | "GPSlat": 0.0, + | "GPSlon": 0.0, + | "Vtarget": 11.0, + | "cpSubstat": null, + | "bar": { + | "a": "asd", + | "b": "ass" + | }, + | "foo": { + | }, + | "cpZone": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | }, + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | } + | ], + | "nestedArray": [ + | [] + | ], + | "loc_name": "Klemmleiste MS2", + | "root_id": null, + | "uknom": 11.0, + | "vmax": 1.0499999523162842, + | "vmin": 0.0, + | "vtarget": 1.0 + | }, + | { + | "AccessTime": 0.0, + | "GPSlat": 0.0, + | "GPSlon": 0.0, + | "Vtarget": 11.0, + | "cpSubstat": null, + | "cpZone": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | }, + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | } + | ], + | "nestedArray": [ + | [] + | ], + | "loc_name": "Klemmleiste MS2", + | "root_id": null, + | "uknom": 11.0, + | "vmax": 1.0499999523162842, + | "vmin": 0.0, + | "vtarget": 1.0 + | } + | ], + | "lines": [ + | { + | "length": 1 + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ + | Nodes, + | Lines + |} + | + |final case class PowerFactoryGrid( + | nodes: Option[List[Nodes]], + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class CpZone( + | loc_name: Option[String], + | additionalParam: Option[String], + | leet: Option[List[Option[Leet]]] + | ) + | + | final case class Nodes( + | loc_name: Option[String], + | root_id: Option[String], + | AccessTime: Option[Double], + | GPSlon: Option[Double], + | nestedArray: Option[List[Option[List[Option[String]]]]], + | cpZone: Option[List[Option[CpZone]]], + | vmin: Option[Double], + | GPSlat: Option[Double], + | foo: Option[Foo], + | cpSubstat: Option[String], + | Vtarget: Option[Double], + | uknom: Option[Double], + | bar: Option[Bar], + | vtarget: Option[Double], + | vmax: Option[Double] + | ) + | + | final case class Leet( + | loc_name: Option[String], + | additionalParam: Option[String] + | ) + | + | final case class Bar(a: Option[String], b: Option[String]) + | + | final case class Lines(length: Option[Double]) + | + | final case class Foo() + | + |} + |""".stripMargin + ) + + } + + "generate valid GPSCoords for simple case" in { + val json = + """ + |{ + | "lines": [ + | { + | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", + | "GPScoords": [ + | [ + | 51.4843281, + | 7.4116482 + | ], + | [ + | 52.2895, + | 12.8273 + | ], + | [ + | 52.4895, + | 13.8273 + | ] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" + | } + | ] + |}""".stripMargin + + genCode(json) shouldBe Some( + """package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines( + | id: Option[String], + | GPScoords: Option[List[Option[List[Option[Double]]]]], + | bus1Id: Option[String], + | bus2Id: Option[String] + | ) + | + |} + |""".stripMargin + ) + } + + "generate valid GPSCoords if field values differ" in { + val json = + """ + |{ + | "lines": [ + | { + | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", + | "GPScoords": [ + | [] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" + | }, + | { + | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", + | "GPScoords": [ + | [ + | 51.4843281, + | 7.4116482 + | ], + | [ + | 52.2895, + | 12.8273 + | ], + | [ + | 52.4895, + | 13.8273 + | ] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" + | }, + | { + | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", + | "GPScoords": [ + | [] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" + | } + | ] + |}""".stripMargin + + genCode(json) shouldBe Some( + """package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines( + | id: Option[String], + | GPScoords: Option[List[Option[List[Option[Double]]]]], + | bus1Id: Option[String], + | bus2Id: Option[String] + | ) + | + |} + |""".stripMargin + ) + } + +} diff --git a/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala b/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala index 3ff3b991..df4b6fbc 100644 --- a/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala +++ b/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala @@ -1,43 +1,43 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.scalatest - -import edu.ie3.util.quantities.QuantityUtil -import javax.measure.Quantity -import org.scalatest.matchers.{MatchResult, Matcher} - -/** Trait, to simplify test coding, that is reliant on [[Quantity]] s - */ -trait QuantityMatchers { - class QuantityMatcher[Q <: Quantity[Q]](right: Quantity[Q], tolerance: Double) - extends Matcher[Quantity[Q]] - with QuantityMatchers { - override def apply(left: Quantity[Q]): MatchResult = MatchResult( - QuantityUtil.equals(left, right, tolerance), - QuantityMatchers.assembleRawFailureMessage(left, right, tolerance), - QuantityMatchers.assembleNegatedFailureMessage(left, right, tolerance) - ) - } - - def equalWithTolerance[Q <: Quantity[Q]]( - right: Quantity[Q] - )(implicit quantityTolerance: Double = 1e-10) = - new QuantityMatcher(right, quantityTolerance) -} - -case object QuantityMatchers extends QuantityMatchers { - private def assembleRawFailureMessage[Q <: Quantity[Q]]( - lhs: Quantity[Q], - rhs: Quantity[Q], - tolerance: Double - ) = s"The quantities $lhs and $rhs differ more than $tolerance in value" - private def assembleNegatedFailureMessage[Q <: Quantity[Q]]( - lhs: Quantity[Q], - rhs: Quantity[Q], - tolerance: Double - ) = s"The quantities $lhs and $rhs differ less than $tolerance in value" -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.scalatest + +import edu.ie3.util.quantities.QuantityUtil +import javax.measure.Quantity +import org.scalatest.matchers.{MatchResult, Matcher} + +/** Trait, to simplify test coding, that is reliant on [[Quantity]] s + */ +trait QuantityMatchers { + class QuantityMatcher[Q <: Quantity[Q]](right: Quantity[Q], tolerance: Double) + extends Matcher[Quantity[Q]] + with QuantityMatchers { + override def apply(left: Quantity[Q]): MatchResult = MatchResult( + QuantityUtil.equals(left, right, tolerance), + QuantityMatchers.assembleRawFailureMessage(left, right, tolerance), + QuantityMatchers.assembleNegatedFailureMessage(left, right, tolerance) + ) + } + + def equalWithTolerance[Q <: Quantity[Q]]( + right: Quantity[Q] + )(implicit quantityTolerance: Double = 1e-10) = + new QuantityMatcher(right, quantityTolerance) +} + +case object QuantityMatchers extends QuantityMatchers { + private def assembleRawFailureMessage[Q <: Quantity[Q]]( + lhs: Quantity[Q], + rhs: Quantity[Q], + tolerance: Double + ) = s"The quantities $lhs and $rhs differ more than $tolerance in value" + private def assembleNegatedFailureMessage[Q <: Quantity[Q]]( + lhs: Quantity[Q], + rhs: Quantity[Q], + tolerance: Double + ) = s"The quantities $lhs and $rhs differ less than $tolerance in value" +} From 27d4104cb2991907f7e0a7a7af20805199d2c7cf Mon Sep 17 00:00:00 2001 From: t-ober <63147366+t-ober@users.noreply.github.com> Date: Wed, 8 Sep 2021 13:25:21 +0200 Subject: [PATCH 6/8] Revert "fmt" This reverts commit 4a9cffb62435bea2853776fb8a5ca6d4077a2bc0. --- CHANGELOG.md | 28 +- CONTRIBUTING.md | 146 +-- README.md | 10 +- build.gradle | 286 ++--- gradle/scripts/checkJavaVersion.gradle | 28 +- gradle/scripts/pmd.gradle | 14 +- gradle/scripts/scoverage.gradle | 24 +- gradle/scripts/sonarqube.gradle | 70 +- gradle/scripts/spotless.gradle | 90 +- settings.gradle | 20 +- .../config/ConfigValidator.scala | 300 ++--- .../config/ConversionConfig.scala | 114 +- .../converter/CoordinateConverter.scala | 72 +- .../converter/GridConverter.scala | 102 +- .../converter/GridGraphBuilder.scala | 128 +- .../converter/NodeConverter.scala | 142 +-- .../converter/SubnetBuilder.scala | 190 +-- .../converter/types/LineTypeConverter.scala | 126 +- .../types/TransformerType2WConverter.scala | 174 +-- .../io/ConversionConfigException.scala | 24 +- .../exception/io/GridParsingException.scala | 24 +- .../exception/io/IoException.scala | 28 +- .../exception/pf/ConversionException.scala | 24 +- .../pf/ElementConfigurationException.scala | 24 +- .../pf/GridConfigurationException.scala | 24 +- .../pf/MissingGridElementException.scala | 24 +- .../pf/MissingParameterException.scala | 24 +- .../exception/pf/PfException.scala | 28 +- .../exception/pf/TestException.scala | 24 +- .../powerFactory2psdm/io/PfGridParser.scala | 70 +- .../main/RunConversion.scala | 82 +- .../model/PreprocessedPfGridModel.scala | 406 +++---- .../model/RawPfGridModel.scala | 256 ++-- .../model/entity/ConnectedElement.scala | 76 +- .../powerFactory2psdm/model/entity/Edge.scala | 28 +- .../model/entity/EntityModel.scala | 32 +- .../powerFactory2psdm/model/entity/Line.scala | 92 +- .../powerFactory2psdm/model/entity/Node.scala | 150 +-- .../model/entity/Subnet.scala | 40 +- .../model/entity/Switch.scala | 134 +- .../model/entity/types/LineType.scala | 178 +-- .../entity/types/TransformerType2W.scala | 332 ++--- .../model/setting/ConversionPrefixes.scala | 106 +- .../model/setting/UnitSystem.scala | 32 +- .../powerFactory2psdm/util/Formatter.scala | 63 +- .../util/GridPreparator.scala | 100 +- .../util/SchemaGenerator.scala | 840 ++++++------- .../common/ConverterTestData.scala | 642 +++++----- .../config/ConfigValidatorSpec.scala | 234 ++-- .../converter/CoordinateConverterSpec.scala | 86 +- .../converter/GridGraphBuilderSpec.scala | 150 +-- .../converter/NodeConverterSpec.scala | 104 +- .../converter/SubnetBuilderSpec.scala | 130 +- .../types/LineTypeConverterSpec.scala | 64 +- .../TransformerType2WConverterSpec.scala | 134 +- .../model/PreprocessedPfGridModelSpec.scala | 188 +-- .../model/entity/LineSpec.scala | 108 +- .../model/entity/NodeSpec.scala | 150 +-- .../model/entity/SwitchSpec.scala | 114 +- .../model/types/LineTypeSpec.scala | 150 +-- .../model/types/TransformerType2WSpec.scala | 340 +++--- .../util/GridPreparatorSpec.scala | 90 +- .../util/SchemaGeneratorIT.scala | 134 +- .../util/SchemaGeneratorSpec.scala | 1076 ++++++++--------- .../edu/ie3/scalatest/QuantityMatchers.scala | 86 +- 65 files changed, 4655 insertions(+), 4654 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63020fa7..a9014d2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,14 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased / SNAPSHOT] - -### Added - -### Changed - -[Unreleased / SNAPSHOT]: https://github.com/ie3-institute/powerFactory2psdm/compare/v1.0...HEAD -[v1.0]: https://github.com/ie3-institute/powerFactory2psdm/releases/tag/v1.0 +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased / SNAPSHOT] + +### Added + +### Changed + +[Unreleased / SNAPSHOT]: https://github.com/ie3-institute/powerFactory2psdm/compare/v1.0...HEAD +[v1.0]: https://github.com/ie3-institute/powerFactory2psdm/releases/tag/v1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6cb8ebec..ff987f35 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,73 +1,73 @@ -# Contributing to powerFactory2psdm -Welcome dear fellow of sophisticated power system modelling! :wave: -And thank you for considering your contribution to this project! -With this document we would like to give you some orientation on how you can contribute. - -## Table of contents -* [Testing and reporting bugs](#testing-and-reporting-bugs) -* [Suggest extensions](#suggest-extensions) -* [Contributing code](#contributing-code) -* [Branching and handing in pull requests](#branching-and-handing-in-pull-requests) -* [General (software) design guidelines](#general-software-design-guidelines) -* [Testing](#testing) -* [Finalising your pull request](#finalising-your-pull-request) -* [For any doubts](#for-any-doubts) - -## Testing and reporting bugs -We really appreciate your usage of this project. -Whenever you find a bug, it would be nice to check, if this isn't a feature to us. :wink: -Currently, we are lacking sophisticated documentation, but we will work on it. -If you still think it's a bug, please raise an [issue](https://guides.github.com/features/issues/) for us. -Considering the following aspects in your inquiry, assists us in helping you: - -* **Is there already an issue addressing your problem?** -* Try to **locate the error** as precise as possible. -* What has to be done to **reproduce the error**? -* **Provide stack trace, logs etc.** and further helpful information -* **What would do you expect to happen?** -* Mark the issue with the **label _bug_**. - -## Suggest extensions -We use issues as well to keep track of enhancement suggestions. -Considering the following aspects, assists us in understanding your needs properly: - -* **Is there already an issue addressing your request?** -* **What would do you desire for?** -* If possible provide an **example or sketch**. -* Show a **use case**, that should be as versatile as possible. -* Mark the issue with the **label _enhancement_**. - -## Contributing code -If you intend to produce some lines of code, pick an issue and get some hands on! - -### Branching and handing in pull requests -We try to follow a branch naming strategy of the form `/#-`. -If for example [Prof. Dr. rer. hort. Klaus-Dieter Brokkoli](https://www.instagram.com/prof_broccoli/) would like to add some work on node models reported in issue 4711, he would open a branch `kb/#4711-extendingNodeModels`. -Please hand in a _draft_ pull request as early as possible to allow other to keep track on your changes. -Before opening it for review, please [finalise your pull request](#finalising-your-pull-request). - -### General (software) design guidelines -In order to maintain a consistent project, we thought of some general design guidlines, we kindly ask you to take care of: - -* We :heart: **immutability**. Therefore, please don't provide setters and use proper instantiation instead. -* `double a = b * pow(x, j)`? :hand: Please **be expressive** in what you code! -* Document your code with **javadoc**. - -### Testing -Ensure the proper function of your code by [test driven development (TDD)](https://www.guru99.com/test-driven-development.html). - -### Finalising your pull request -Some automated checks assist us in delivering a pretty fair quality of software. -Before marking the pull request as 'ready to review', take these precautionary actions: - -* Are all tests passing? -* Is your code properly formatted? - -## For any doubts -... please contact -* Chris (@ckittl), -* Johannes (@johanneshiry), -* Thomas (@t-ober) or -* Debopama (@sensarmad) - -We are happy to help! :smiley: +# Contributing to powerFactory2psdm +Welcome dear fellow of sophisticated power system modelling! :wave: +And thank you for considering your contribution to this project! +With this document we would like to give you some orientation on how you can contribute. + +## Table of contents +* [Testing and reporting bugs](#testing-and-reporting-bugs) +* [Suggest extensions](#suggest-extensions) +* [Contributing code](#contributing-code) +* [Branching and handing in pull requests](#branching-and-handing-in-pull-requests) +* [General (software) design guidelines](#general-software-design-guidelines) +* [Testing](#testing) +* [Finalising your pull request](#finalising-your-pull-request) +* [For any doubts](#for-any-doubts) + +## Testing and reporting bugs +We really appreciate your usage of this project. +Whenever you find a bug, it would be nice to check, if this isn't a feature to us. :wink: +Currently, we are lacking sophisticated documentation, but we will work on it. +If you still think it's a bug, please raise an [issue](https://guides.github.com/features/issues/) for us. +Considering the following aspects in your inquiry, assists us in helping you: + +* **Is there already an issue addressing your problem?** +* Try to **locate the error** as precise as possible. +* What has to be done to **reproduce the error**? +* **Provide stack trace, logs etc.** and further helpful information +* **What would do you expect to happen?** +* Mark the issue with the **label _bug_**. + +## Suggest extensions +We use issues as well to keep track of enhancement suggestions. +Considering the following aspects, assists us in understanding your needs properly: + +* **Is there already an issue addressing your request?** +* **What would do you desire for?** +* If possible provide an **example or sketch**. +* Show a **use case**, that should be as versatile as possible. +* Mark the issue with the **label _enhancement_**. + +## Contributing code +If you intend to produce some lines of code, pick an issue and get some hands on! + +### Branching and handing in pull requests +We try to follow a branch naming strategy of the form `/#-`. +If for example [Prof. Dr. rer. hort. Klaus-Dieter Brokkoli](https://www.instagram.com/prof_broccoli/) would like to add some work on node models reported in issue 4711, he would open a branch `kb/#4711-extendingNodeModels`. +Please hand in a _draft_ pull request as early as possible to allow other to keep track on your changes. +Before opening it for review, please [finalise your pull request](#finalising-your-pull-request). + +### General (software) design guidelines +In order to maintain a consistent project, we thought of some general design guidlines, we kindly ask you to take care of: + +* We :heart: **immutability**. Therefore, please don't provide setters and use proper instantiation instead. +* `double a = b * pow(x, j)`? :hand: Please **be expressive** in what you code! +* Document your code with **javadoc**. + +### Testing +Ensure the proper function of your code by [test driven development (TDD)](https://www.guru99.com/test-driven-development.html). + +### Finalising your pull request +Some automated checks assist us in delivering a pretty fair quality of software. +Before marking the pull request as 'ready to review', take these precautionary actions: + +* Are all tests passing? +* Is your code properly formatted? + +## For any doubts +... please contact +* Chris (@ckittl), +* Johannes (@johanneshiry), +* Thomas (@t-ober) or +* Debopama (@sensarmad) + +We are happy to help! :smiley: diff --git a/README.md b/README.md index 7de92d47..10ca5eb5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![License](https://img.shields.io/github/license/ie3-institute/powerFactory2psdm)](https://github.com/ie3-institute/powerFactory2psdm/blob/main/LICENSE) - -# powerFactory2psdm - -Convert PowerFactory projects to [PowerSystemDataModel](https://github.com/ie3-institute/PowerSystemDataModel). +[![License](https://img.shields.io/github/license/ie3-institute/powerFactory2psdm)](https://github.com/ie3-institute/powerFactory2psdm/blob/main/LICENSE) + +# powerFactory2psdm + +Convert PowerFactory projects to [PowerSystemDataModel](https://github.com/ie3-institute/PowerSystemDataModel). diff --git a/build.gradle b/build.gradle index 1737f9c4..ce2ee79e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,143 +1,143 @@ -plugins { - id 'groovy' // groovy support - id 'java' // java support - id 'scala' // scala support - id 'pmd' //code check, working on source code - id 'com.diffplug.spotless' version '5.14.3'//code format - id "org.sonarqube" version "3.3" // sonarqube - id 'org.scoverage' version '7.0.0' // Code coverage plugin for scala - id "com.github.maiflai.scalatest" version "0.31" // run scalatest without specific spec task -} - -ext { - javaVersion = JavaVersion.VERSION_11 - scalaVersion = '2.13' - scalaBinaryVersion = '2.13.6' - slf4jVersion = '1.7.32' - circeVersion = '0.14.1' - scriptsLocation = 'gradle' + File.separator + 'scripts' + File.separator //location of script plugins -} - -apply from: scriptsLocation + 'pmd.gradle' -apply from: scriptsLocation + 'spotless.gradle' -apply from: scriptsLocation + 'checkJavaVersion.gradle' -apply from: scriptsLocation + 'scoverage.gradle' -apply from: scriptsLocation + 'sonarqube.gradle' - -configurations { - scalaCompilerPlugin -} - -repositories { - //searches in Maven Central - mavenCentral() - - // allows github repos as dependencies - maven { url 'https://www.jitpack.io' } - - // sonatype snapshot repo - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } -} - -dependencies { - - // json parsing - implementation "io.circe:circe-generic_${scalaVersion}:${circeVersion}" - implementation "io.circe:circe-parser_${scalaVersion}:${circeVersion}" - - // https://mvnrepository.com/artifact/org.scalameta/scalafmt-interfaces - implementation group: 'org.scalameta', name: 'scalafmt-interfaces', version: '3.0.2' - // https://mvnrepository.com/artifact/org.scalameta/scalafmt-dynamic - implementation group: 'org.scalameta', name: "scalafmt-dynamic_${scalaVersion}", version: '3.0.1' - - - implementation('com.github.ie3-institute:PowerSystemDataModel:2.0.1') { - exclude group: 'org.slf4j', module: 'slf4j-api' - /* Exclude our own nested dependencies */ - exclude group: 'com.github.ie3-institute' - } - - // Graphs - implementation group: 'org.jgrapht', name: 'jgrapht-core', version: '1.5.1' - - // config // - //implementation 'com.typesafe:config:+' - implementation "com.github.pureconfig:pureconfig_${scalaVersion}:0.16.0" - - // cmd args parser // - implementation "com.github.scopt:scopt_${scalaVersion}:+" - - // ie³ internal repository - implementation 'com.github.ie3-institute:PowerSystemUtils:1.5.3' - - // logging - implementation "org.slf4j:slf4j-api:${slf4jVersion}" // slf4j wrapper - implementation 'com.lmax:disruptor:3.4.4' // async logging - implementation 'org.apache.logging.log4j:log4j-api:+' // log4j - implementation 'org.apache.logging.log4j:log4j-core:+' // log4j - implementation 'org.apache.logging.log4j:log4j-slf4j-impl:+' // log4j -> slf4j - - implementation "com.typesafe.scala-logging:scala-logging_${scalaVersion}:3.9.4" // akka scala logging - implementation 'com.typesafe.scala-logging:scala-logging-slf4j_2.11:2.1.2'// scala logging - implementation "org.slf4j:log4j-over-slf4j:${slf4jVersion}" // slf4j -> log4j - - // CORE Scala // - implementation "org.scala-lang:scala-library:${scalaBinaryVersion}" - - // TEST Scala // - testImplementation "org.scalatest:scalatest_${scalaVersion}:3.2.9" - testImplementation 'com.vladsch.flexmark:flexmark-all:0.35.10' - - // Linter Scala // - implementation "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat - scalaCompilerPlugin "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat -} - -/* scapegoat hook configuration - * https://github.com/sksamuel/scapegoat - * using compileScala instead of tasks.withType(ScalaCompile) prevents applying scapegoat to scala test classes - * see https://docs.gradle.org/current/userguide/scala_plugin.html#sec:configure_scala_classpath for details - */ -compileScala { - scalaCompileOptions.additionalParameters = [ - "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, - "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/src/", - "-P:scapegoat:disabledInspections:VariableShadowing", - "-P:scapegoat:ignoredFiles:.*/PowerFactoryGrid.scala" // see scapegoat-sbt page for this param - ] -} - -// separate scapegoat report for test classes -compileTestScala { - scalaCompileOptions.additionalParameters = [ - "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, - "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/testsrc/", - "-P:scapegoat:disabledInspections:VariableShadowing" - ] -} -group = 'edu.ie3' -version = '0.1-SNAPSHOT' -description = 'powerFactory2psdm' -sourceCompatibility = javaVersion -targetCompatibility = javaVersion - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -tasks.withType(ScalaCompile) { - options.forkOptions.jvmArgs += [ - '-Xmx4096m', - '-Xss4m', - '-Xms2048m', - '-XX:+UseParallelGC', - '-XX:MaxInlineLevel=20' - ] - options.compilerArgs += [ - '-Xmx4096m', - '-Xss4m', - '-Xms2048m', - '-XX:+UseParallelGC', - '-XX:MaxInlineLevel=20' - ] -} +plugins { + id 'groovy' // groovy support + id 'java' // java support + id 'scala' // scala support + id 'pmd' //code check, working on source code + id 'com.diffplug.spotless' version '5.14.3'//code format + id "org.sonarqube" version "3.3" // sonarqube + id 'org.scoverage' version '7.0.0' // Code coverage plugin for scala + id "com.github.maiflai.scalatest" version "0.31" // run scalatest without specific spec task +} + +ext { + javaVersion = JavaVersion.VERSION_11 + scalaVersion = '2.13' + scalaBinaryVersion = '2.13.6' + slf4jVersion = '1.7.32' + circeVersion = '0.14.1' + scriptsLocation = 'gradle' + File.separator + 'scripts' + File.separator //location of script plugins +} + +apply from: scriptsLocation + 'pmd.gradle' +apply from: scriptsLocation + 'spotless.gradle' +apply from: scriptsLocation + 'checkJavaVersion.gradle' +apply from: scriptsLocation + 'scoverage.gradle' +apply from: scriptsLocation + 'sonarqube.gradle' + +configurations { + scalaCompilerPlugin +} + +repositories { + //searches in Maven Central + mavenCentral() + + // allows github repos as dependencies + maven { url 'https://www.jitpack.io' } + + // sonatype snapshot repo + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } +} + +dependencies { + + // json parsing + implementation "io.circe:circe-generic_${scalaVersion}:${circeVersion}" + implementation "io.circe:circe-parser_${scalaVersion}:${circeVersion}" + + // https://mvnrepository.com/artifact/org.scalameta/scalafmt-interfaces + implementation group: 'org.scalameta', name: 'scalafmt-interfaces', version: '3.0.2' + // https://mvnrepository.com/artifact/org.scalameta/scalafmt-dynamic + implementation group: 'org.scalameta', name: "scalafmt-dynamic_${scalaVersion}", version: '3.0.1' + + + implementation('com.github.ie3-institute:PowerSystemDataModel:2.0.1') { + exclude group: 'org.slf4j', module: 'slf4j-api' + /* Exclude our own nested dependencies */ + exclude group: 'com.github.ie3-institute' + } + + // Graphs + implementation group: 'org.jgrapht', name: 'jgrapht-core', version: '1.5.1' + + // config // + //implementation 'com.typesafe:config:+' + implementation "com.github.pureconfig:pureconfig_${scalaVersion}:0.16.0" + + // cmd args parser // + implementation "com.github.scopt:scopt_${scalaVersion}:+" + + // ie³ internal repository + implementation 'com.github.ie3-institute:PowerSystemUtils:1.5.3' + + // logging + implementation "org.slf4j:slf4j-api:${slf4jVersion}" // slf4j wrapper + implementation 'com.lmax:disruptor:3.4.4' // async logging + implementation 'org.apache.logging.log4j:log4j-api:+' // log4j + implementation 'org.apache.logging.log4j:log4j-core:+' // log4j + implementation 'org.apache.logging.log4j:log4j-slf4j-impl:+' // log4j -> slf4j + + implementation "com.typesafe.scala-logging:scala-logging_${scalaVersion}:3.9.4" // akka scala logging + implementation 'com.typesafe.scala-logging:scala-logging-slf4j_2.11:2.1.2'// scala logging + implementation "org.slf4j:log4j-over-slf4j:${slf4jVersion}" // slf4j -> log4j + + // CORE Scala // + implementation "org.scala-lang:scala-library:${scalaBinaryVersion}" + + // TEST Scala // + testImplementation "org.scalatest:scalatest_${scalaVersion}:3.2.9" + testImplementation 'com.vladsch.flexmark:flexmark-all:0.35.10' + + // Linter Scala // + implementation "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat + scalaCompilerPlugin "com.sksamuel.scapegoat:scalac-scapegoat-plugin_${scalaBinaryVersion}:1.4.9" // scala scapegoat +} + +/* scapegoat hook configuration + * https://github.com/sksamuel/scapegoat + * using compileScala instead of tasks.withType(ScalaCompile) prevents applying scapegoat to scala test classes + * see https://docs.gradle.org/current/userguide/scala_plugin.html#sec:configure_scala_classpath for details + */ +compileScala { + scalaCompileOptions.additionalParameters = [ + "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, + "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/src/", + "-P:scapegoat:disabledInspections:VariableShadowing", + "-P:scapegoat:ignoredFiles:.*/PowerFactoryGrid.scala" // see scapegoat-sbt page for this param + ] +} + +// separate scapegoat report for test classes +compileTestScala { + scalaCompileOptions.additionalParameters = [ + "-Xplugin:" + configurations.scalaCompilerPlugin.asPath, + "-P:scapegoat:dataDir:" + buildDir + "/reports/scapegoat/testsrc/", + "-P:scapegoat:disabledInspections:VariableShadowing" + ] +} +group = 'edu.ie3' +version = '0.1-SNAPSHOT' +description = 'powerFactory2psdm' +sourceCompatibility = javaVersion +targetCompatibility = javaVersion + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +tasks.withType(ScalaCompile) { + options.forkOptions.jvmArgs += [ + '-Xmx4096m', + '-Xss4m', + '-Xms2048m', + '-XX:+UseParallelGC', + '-XX:MaxInlineLevel=20' + ] + options.compilerArgs += [ + '-Xmx4096m', + '-Xss4m', + '-Xms2048m', + '-XX:+UseParallelGC', + '-XX:MaxInlineLevel=20' + ] +} diff --git a/gradle/scripts/checkJavaVersion.gradle b/gradle/scripts/checkJavaVersion.gradle index 68cf0674..665708ad 100644 --- a/gradle/scripts/checkJavaVersion.gradle +++ b/gradle/scripts/checkJavaVersion.gradle @@ -1,14 +1,14 @@ -// Enforces the correct Java version, as some parts of the project may malfunction under a wrong version -// If this task fails, try changing your JAVA_HOME to the required version -tasks.register("checkJavaVersion") { - group = 'Verification' - description = 'Enforces correct Java version' - - doLast { - def foundVersion = JavaVersion.current() - if (foundVersion != javaVersion) - throw new IllegalStateException("Wrong Java version: required is " - + javaVersion + ", but found " + foundVersion) - } -} -compileJava.dependsOn(checkJavaVersion) +// Enforces the correct Java version, as some parts of the project may malfunction under a wrong version +// If this task fails, try changing your JAVA_HOME to the required version +tasks.register("checkJavaVersion") { + group = 'Verification' + description = 'Enforces correct Java version' + + doLast { + def foundVersion = JavaVersion.current() + if (foundVersion != javaVersion) + throw new IllegalStateException("Wrong Java version: required is " + + javaVersion + ", but found " + foundVersion) + } +} +compileJava.dependsOn(checkJavaVersion) diff --git a/gradle/scripts/pmd.gradle b/gradle/scripts/pmd.gradle index aa3c1478..46bb0c56 100644 --- a/gradle/scripts/pmd.gradle +++ b/gradle/scripts/pmd.gradle @@ -1,7 +1,7 @@ -// pmd is a code check tool, working on source code - -pmd { - consoleOutput = true - toolVersion = "6.21.0" - rulesMinimumPriority = 2 -} +// pmd is a code check tool, working on source code + +pmd { + consoleOutput = true + toolVersion = "6.21.0" + rulesMinimumPriority = 2 +} diff --git a/gradle/scripts/scoverage.gradle b/gradle/scripts/scoverage.gradle index ceef437b..9317b61d 100644 --- a/gradle/scripts/scoverage.gradle +++ b/gradle/scripts/scoverage.gradle @@ -1,12 +1,12 @@ -/* - * Scala code coverage gradle plugin see https://github.com/scoverage/gradle-scoverage#configuration and - * https://github.com/scoverage/gradle-scoverage/issues/109 for details - */ -scoverage { - scoverageScalaVersion = scalaBinaryVersion - coverageOutputHTML = false - coverageOutputXML = true - coverageOutputCobertura = false - minimumRate = 0.6 // minimum code coverage - coverageDebug = false -} +/* + * Scala code coverage gradle plugin see https://github.com/scoverage/gradle-scoverage#configuration and + * https://github.com/scoverage/gradle-scoverage/issues/109 for details + */ +scoverage { + scoverageScalaVersion = scalaBinaryVersion + coverageOutputHTML = false + coverageOutputXML = true + coverageOutputCobertura = false + minimumRate = 0.6 // minimum code coverage + coverageDebug = false +} diff --git a/gradle/scripts/sonarqube.gradle b/gradle/scripts/sonarqube.gradle index 591e72c8..1857368d 100644 --- a/gradle/scripts/sonarqube.gradle +++ b/gradle/scripts/sonarqube.gradle @@ -1,35 +1,35 @@ -sonarqube { - properties { - // general stuff - property 'sonar.projectName', 'powerfactory2psdm' // project name - property 'sonar.verbose', 'true' // verbose mode - property 'sonar.sourceEncoding', 'UTF-8' // encoding - property 'sonar.sources', [ - 'src/main/resources', - 'src/main/scala'] // src dirs - property "sonar.tests", [ - 'src/test/resources', - 'src/test/scala'] // test src dirs - // reports stuff (for all languages) - property 'sonar.junit.reportPaths', [ - 'build/test-results/test'] // Comma-delimited list of paths to Surefire XML-format reports. - // scapegoat report dir - property "sonar.scala.scapegoat.reportPaths", [ - "build/reports/scapegoat/src/scapegoat-scalastyle.xml", - "build/reports/scapegoat/testsrc/scapegoat-scalastyle.xml"] // Comma-delimited list of paths to Scapegoat reports in the Scalastyle format - // scala specific stuff - property 'sonar.scala.coverage.reportPaths', 'build/reports/scoverage/scoverage.xml' - // remove auto generated RawGridModel file from sonarqube analysis - property 'sonar.exclusions', [ - "src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" - ] - } -} - -// forces sonarqube to execute integration tests -project.tasks["sonarqube"].dependsOn "pmdMain" -project.tasks["sonarqube"].dependsOn "pmdTest" -project.tasks["sonarqube"].dependsOn "check" - -project.tasks["sonarqube"].dependsOn "reportScoverage" -project.tasks["sonarqube"].dependsOn "checkScoverage" +sonarqube { + properties { + // general stuff + property 'sonar.projectName', 'powerfactory2psdm' // project name + property 'sonar.verbose', 'true' // verbose mode + property 'sonar.sourceEncoding', 'UTF-8' // encoding + property 'sonar.sources', [ + 'src/main/resources', + 'src/main/scala'] // src dirs + property "sonar.tests", [ + 'src/test/resources', + 'src/test/scala'] // test src dirs + // reports stuff (for all languages) + property 'sonar.junit.reportPaths', [ + 'build/test-results/test'] // Comma-delimited list of paths to Surefire XML-format reports. + // scapegoat report dir + property "sonar.scala.scapegoat.reportPaths", [ + "build/reports/scapegoat/src/scapegoat-scalastyle.xml", + "build/reports/scapegoat/testsrc/scapegoat-scalastyle.xml"] // Comma-delimited list of paths to Scapegoat reports in the Scalastyle format + // scala specific stuff + property 'sonar.scala.coverage.reportPaths', 'build/reports/scoverage/scoverage.xml' + // remove auto generated RawGridModel file from sonarqube analysis + property 'sonar.exclusions', [ + "src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" + ] + } +} + +// forces sonarqube to execute integration tests +project.tasks["sonarqube"].dependsOn "pmdMain" +project.tasks["sonarqube"].dependsOn "pmdTest" +project.tasks["sonarqube"].dependsOn "check" + +project.tasks["sonarqube"].dependsOn "reportScoverage" +project.tasks["sonarqube"].dependsOn "checkScoverage" diff --git a/gradle/scripts/spotless.gradle b/gradle/scripts/spotless.gradle index 9fa8559a..bdb11fd7 100644 --- a/gradle/scripts/spotless.gradle +++ b/gradle/scripts/spotless.gradle @@ -1,45 +1,45 @@ -// spotless is a code formatter - -spotless { - def ie3LicHead = '/*\n' + - ' * © $YEAR. TU Dortmund University,\n' + - ' * Institute of Energy Systems, Energy Efficiency and Energy Economics,\n' + - ' * Research group Distribution grid planning and operation\n' + - ' */\n\n' - - //sets a license header, removes unused imports and formats conforming to the google java format - java { - removeUnusedImports() // removes any unused imports - googleJavaFormat() - licenseHeader ie3LicHead - } - - /* cf. https://github.com/diffplug/spotless/tree/master/plugin-gradle */ - groovy { - licenseHeader ie3LicHead - excludeJava() // excludes all Java sources within the Groovy source dirs from formatting - // the Groovy Eclipse formatter extends the Java Eclipse formatter, - // so it formats Java files by default (unless `excludeJava` is used). - greclipse() - } - - groovyGradle { - // same as groovy, but for .gradle (defaults to '*.gradle') - target '*.gradle', 'gradle/scripts/*.gradle' - greclipse() - } - - //sets a license header, removes unused imports and formats conforming to the scala fmt formatter - scala { - scalafmt() - licenseHeader ie3LicHead, "package.*\\n" - } - - // removes unnecessary whitespace, indents with tabs and ends on new line for gradle, md and gitignore files and config-XMLs - format 'misc', { - target '**/*.md', '**/.gitignore', 'configs/**' - trimTrailingWhitespace() - indentWithTabs() - endWithNewline() - } -} +// spotless is a code formatter + +spotless { + def ie3LicHead = '/*\n' + + ' * © $YEAR. TU Dortmund University,\n' + + ' * Institute of Energy Systems, Energy Efficiency and Energy Economics,\n' + + ' * Research group Distribution grid planning and operation\n' + + ' */\n\n' + + //sets a license header, removes unused imports and formats conforming to the google java format + java { + removeUnusedImports() // removes any unused imports + googleJavaFormat() + licenseHeader ie3LicHead + } + + /* cf. https://github.com/diffplug/spotless/tree/master/plugin-gradle */ + groovy { + licenseHeader ie3LicHead + excludeJava() // excludes all Java sources within the Groovy source dirs from formatting + // the Groovy Eclipse formatter extends the Java Eclipse formatter, + // so it formats Java files by default (unless `excludeJava` is used). + greclipse() + } + + groovyGradle { + // same as groovy, but for .gradle (defaults to '*.gradle') + target '*.gradle', 'gradle/scripts/*.gradle' + greclipse() + } + + //sets a license header, removes unused imports and formats conforming to the scala fmt formatter + scala { + scalafmt() + licenseHeader ie3LicHead, "package.*\\n" + } + + // removes unnecessary whitespace, indents with tabs and ends on new line for gradle, md and gitignore files and config-XMLs + format 'misc', { + target '**/*.md', '**/.gitignore', 'configs/**' + trimTrailingWhitespace() + indentWithTabs() + endWithNewline() + } +} diff --git a/settings.gradle b/settings.gradle index 072b7f8c..1fa5047a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,10 +1,10 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.8.3/userguide/multi_project_builds.html - */ - -rootProject.name = 'powerFactory2psdm' +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.8.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'powerFactory2psdm' diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala index b0d99106..66475d8f 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConfigValidator.scala @@ -1,150 +1,150 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.config - -import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ - Fixed, - GenerationMethod, - ModelConfigs, - NormalDistribution, - PvConfig, - PvModelGeneration, - UniformDistribution -} -import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException - -import scala.util.{Failure, Success, Try} - -object ConfigValidator { - - /** Checks the parsed [[ConversionConfig]] for general soundness. - * @param config - * the parsed config - */ - def validate(config: ConversionConfig): Unit = { - validateModelConfigs(config.modelConfigs) - } - - private[config] def validateModelConfigs(modelConfigs: ModelConfigs): Unit = { - validatePvConfig(modelConfigs.pvConfig) - } - - private[config] def validatePvConfig(pvConfig: PvConfig): Unit = { - Seq(pvConfig.conversionMode) ++ pvConfig.individualConfigs - .getOrElse(Nil) - .map(conf => conf.conversionMode) - .collect { case pvModelGeneration: PvModelGeneration => - pvModelGeneration - } - .map(validatePvModelGenerationParams) - } - - private[config] def validatePvModelGenerationParams( - params: PvModelGeneration - ): Unit = { - validateGenerationMethod(params.albedo, 0, 1) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The albedo of the plants surrounding: ${params.albedo} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.azimuth, -90, 90) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The azimuth of the plant: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.etaConv, 0, 100) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The efficiency of the plants inverter: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.kG, 0, 1) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The PV generator correction factor (kG): ${params.kG} isn't valid. Exception: ${exc.getMessage}" - ) - } - validateGenerationMethod(params.kT, 0, 1) match { - case Success(_) => - case Failure(exc) => - throw ConversionConfigException( - s"The PV temperature correction factor (kT): ${params.kT} isn't valid. Exception: ${exc.getMessage}" - ) - } - } - - private[config] def validateGenerationMethod( - genMethod: GenerationMethod, - lowerBound: Double, - upperBound: Double - ): Try[Unit] = - genMethod match { - case Fixed(value) => - checkForBoundViolation(value, lowerBound, upperBound) - case UniformDistribution(min, max) => - if (min > max) - return Failure( - ConversionConfigException( - s"The minimum value: $min exceeds the maximum value: $max" - ) - ) - if (min < lowerBound && max > upperBound) - return lowerUpperBoundViolation(min, max, lowerBound, upperBound) - else if (min < lowerBound) return lowerBoundViolation(min, lowerBound) - else if (max > upperBound) return upperBoundViolation(max, upperBound) - Success(()) - case NormalDistribution(mean, _) => - checkForBoundViolation(mean, lowerBound, upperBound) - } - - private[config] def checkForBoundViolation( - value: Double, - lowerBound: Double, - upperBound: Double - ): Try[Unit] = { - if (value < lowerBound) return lowerBoundViolation(value, lowerBound) - if (value > upperBound) return upperBoundViolation(value, upperBound) - Success(()) - } - - private[config] def lowerBoundViolation( - value: Double, - lowerBound: Double - ): Failure[Unit] = Failure( - ConversionConfigException( - s"The parameters value: $value lies below the lower bound: $lowerBound" - ) - ) - - private[config] def upperBoundViolation( - value: Double, - upperBound: Double - ): Failure[Unit] = Failure( - ConversionConfigException( - s"The parameters value: $value exceeds the upper bound: $upperBound" - ) - ) - - private[config] def lowerUpperBoundViolation( - min: Double, - max: Double, - lowerBound: Double, - upperBound: Double - ): Failure[Unit] = - Failure( - ConversionConfigException( - s"The minimum: $min and maximum: $max of the uniform distribution lie below the lower bound: $lowerBound and above the upper bound: $upperBound " - ) - ) - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.config + +import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ + Fixed, + GenerationMethod, + ModelConfigs, + NormalDistribution, + PvConfig, + PvModelGeneration, + UniformDistribution +} +import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException + +import scala.util.{Failure, Success, Try} + +object ConfigValidator { + + /** Checks the parsed [[ConversionConfig]] for general soundness. + * @param config + * the parsed config + */ + def validate(config: ConversionConfig): Unit = { + validateModelConfigs(config.modelConfigs) + } + + private[config] def validateModelConfigs(modelConfigs: ModelConfigs): Unit = { + validatePvConfig(modelConfigs.pvConfig) + } + + private[config] def validatePvConfig(pvConfig: PvConfig): Unit = { + Seq(pvConfig.conversionMode) ++ pvConfig.individualConfigs + .getOrElse(Nil) + .map(conf => conf.conversionMode) + .collect { case pvModelGeneration: PvModelGeneration => + pvModelGeneration + } + .map(validatePvModelGenerationParams) + } + + private[config] def validatePvModelGenerationParams( + params: PvModelGeneration + ): Unit = { + validateGenerationMethod(params.albedo, 0, 1) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The albedo of the plants surrounding: ${params.albedo} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.azimuth, -90, 90) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The azimuth of the plant: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.etaConv, 0, 100) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The efficiency of the plants inverter: ${params.azimuth} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.kG, 0, 1) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The PV generator correction factor (kG): ${params.kG} isn't valid. Exception: ${exc.getMessage}" + ) + } + validateGenerationMethod(params.kT, 0, 1) match { + case Success(_) => + case Failure(exc) => + throw ConversionConfigException( + s"The PV temperature correction factor (kT): ${params.kT} isn't valid. Exception: ${exc.getMessage}" + ) + } + } + + private[config] def validateGenerationMethod( + genMethod: GenerationMethod, + lowerBound: Double, + upperBound: Double + ): Try[Unit] = + genMethod match { + case Fixed(value) => + checkForBoundViolation(value, lowerBound, upperBound) + case UniformDistribution(min, max) => + if (min > max) + return Failure( + ConversionConfigException( + s"The minimum value: $min exceeds the maximum value: $max" + ) + ) + if (min < lowerBound && max > upperBound) + return lowerUpperBoundViolation(min, max, lowerBound, upperBound) + else if (min < lowerBound) return lowerBoundViolation(min, lowerBound) + else if (max > upperBound) return upperBoundViolation(max, upperBound) + Success(()) + case NormalDistribution(mean, _) => + checkForBoundViolation(mean, lowerBound, upperBound) + } + + private[config] def checkForBoundViolation( + value: Double, + lowerBound: Double, + upperBound: Double + ): Try[Unit] = { + if (value < lowerBound) return lowerBoundViolation(value, lowerBound) + if (value > upperBound) return upperBoundViolation(value, upperBound) + Success(()) + } + + private[config] def lowerBoundViolation( + value: Double, + lowerBound: Double + ): Failure[Unit] = Failure( + ConversionConfigException( + s"The parameters value: $value lies below the lower bound: $lowerBound" + ) + ) + + private[config] def upperBoundViolation( + value: Double, + upperBound: Double + ): Failure[Unit] = Failure( + ConversionConfigException( + s"The parameters value: $value exceeds the upper bound: $upperBound" + ) + ) + + private[config] def lowerUpperBoundViolation( + min: Double, + max: Double, + lowerBound: Double, + upperBound: Double + ): Failure[Unit] = + Failure( + ConversionConfigException( + s"The minimum: $min and maximum: $max of the uniform distribution lie below the lower bound: $lowerBound and above the upper bound: $upperBound " + ) + ) + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala index 8498ed62..76522664 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/config/ConversionConfig.scala @@ -1,57 +1,57 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.config - -import edu.ie3.powerFactory2psdm.config.ConversionConfig.ModelConfigs - -final case class ConversionConfig(modelConfigs: ModelConfigs) - -object ConversionConfig { - - case class ModelConfigs( - pvConfig: PvConfig - ) - - case class PvConfig( - conversionMode: PvConversionMode, - individualConfigs: Option[List[IndividualPvConfig]] - ) - - case class IndividualPvConfig( - ids: Set[String], - conversionMode: PvConversionMode - ) - - sealed trait PvConversionMode - - case object PvFixedFeedIn extends PvConversionMode - - case class PvModelGeneration( - albedo: GenerationMethod, - azimuth: GenerationMethod, - etaConv: GenerationMethod, - kG: GenerationMethod, - kT: GenerationMethod - ) extends PvConversionMode - - sealed trait GenerationMethod - - case class Fixed( - value: Double - ) extends GenerationMethod - - case class UniformDistribution( - lowerBound: Double, - upperBound: Double - ) extends GenerationMethod - - case class NormalDistribution( - mean: Double, - standardDeviation: Double - ) extends GenerationMethod - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.config + +import edu.ie3.powerFactory2psdm.config.ConversionConfig.ModelConfigs + +final case class ConversionConfig(modelConfigs: ModelConfigs) + +object ConversionConfig { + + case class ModelConfigs( + pvConfig: PvConfig + ) + + case class PvConfig( + conversionMode: PvConversionMode, + individualConfigs: Option[List[IndividualPvConfig]] + ) + + case class IndividualPvConfig( + ids: Set[String], + conversionMode: PvConversionMode + ) + + sealed trait PvConversionMode + + case object PvFixedFeedIn extends PvConversionMode + + case class PvModelGeneration( + albedo: GenerationMethod, + azimuth: GenerationMethod, + etaConv: GenerationMethod, + kG: GenerationMethod, + kT: GenerationMethod + ) extends PvConversionMode + + sealed trait GenerationMethod + + case class Fixed( + value: Double + ) extends GenerationMethod + + case class UniformDistribution( + lowerBound: Double, + upperBound: Double + ) extends GenerationMethod + + case class NormalDistribution( + mean: Double, + standardDeviation: Double + ) extends GenerationMethod + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala index 891ac23c..c2519226 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverter.scala @@ -1,36 +1,36 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.input.NodeInput -import edu.ie3.util.geo.GeoUtils -import org.locationtech.jts.geom.{Coordinate, Point} - -case object CoordinateConverter { - - /** Converts optional lat and lon values of PowerFactory elements to the - * expected position description of the PSDM. - * - * @param maybeLat - * optional lat value - * @param maybeLon - * optional lon value - * @return - * position as described by a [[Point]] - */ - def convert(maybeLat: Option[Double], maybeLon: Option[Double]): Point = { - maybeLat.zip(maybeLon) match { - // if no coord is specifically set in the power factory grid, lat and lon are 0.0 - case Some((0.0, 0.0)) => - NodeInput.DEFAULT_GEO_POSITION - case Some((lat, lon)) => - GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint(new Coordinate(lon, lat)) - case None => - NodeInput.DEFAULT_GEO_POSITION - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.input.NodeInput +import edu.ie3.util.geo.GeoUtils +import org.locationtech.jts.geom.{Coordinate, Point} + +case object CoordinateConverter { + + /** Converts optional lat and lon values of PowerFactory elements to the + * expected position description of the PSDM. + * + * @param maybeLat + * optional lat value + * @param maybeLon + * optional lon value + * @return + * position as described by a [[Point]] + */ + def convert(maybeLat: Option[Double], maybeLon: Option[Double]): Point = { + maybeLat.zip(maybeLon) match { + // if no coord is specifically set in the power factory grid, lat and lon are 0.0 + case Some((0.0, 0.0)) => + NodeInput.DEFAULT_GEO_POSITION + case Some((lat, lon)) => + GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint(new Coordinate(lon, lat)) + case None => + NodeInput.DEFAULT_GEO_POSITION + } + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala index 39cc8f9a..12f495e9 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridConverter.scala @@ -1,51 +1,51 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.input.NodeInput -import edu.ie3.powerFactory2psdm.model.entity.Subnet -import edu.ie3.powerFactory2psdm.model.{PreprocessedPfGridModel, RawPfGridModel} - -/** Functionalities to transform an exported and then parsed PowerFactory grid - * to the PSDM. - */ -case object GridConverter { - - def convert(pfGrid: RawPfGridModel) = { - val grid = PreprocessedPfGridModel.build(pfGrid) - val gridElements = convertGridElements(grid) - } - - /** Converts the grid elements of the PowerFactory grid - * - * @param rawGrid - * the raw parsed PowerFactoryGrid - */ - def convertGridElements( - grid: PreprocessedPfGridModel - ): Unit = { - val graph = - GridGraphBuilder.build(grid.nodes, grid.lines ++ grid.switches) - val nodeId2node = grid.nodes.map(node => (node.id, node)).toMap - val subnets = SubnetBuilder.buildSubnets(graph, nodeId2node) - val psdmNodes = subnets.flatMap(subnet => convertNodesOfSubnet(subnet)) - } - - /** Converts all nodes within a subnet to PSDM [[NodeInput]] - * - * @param subnet - * the subnet with reference to all PF nodes that live within - * @return - * list of all converted [[NodeInput]] - */ - def convertNodesOfSubnet( - subnet: Subnet - ): List[NodeInput] = - subnet.nodes - .map(node => NodeConverter.convertNode(node, subnet.id, subnet.voltLvl)) - .toList -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.input.NodeInput +import edu.ie3.powerFactory2psdm.model.entity.Subnet +import edu.ie3.powerFactory2psdm.model.{PreprocessedPfGridModel, RawPfGridModel} + +/** Functionalities to transform an exported and then parsed PowerFactory grid + * to the PSDM. + */ +case object GridConverter { + + def convert(pfGrid: RawPfGridModel) = { + val grid = PreprocessedPfGridModel.build(pfGrid) + val gridElements = convertGridElements(grid) + } + + /** Converts the grid elements of the PowerFactory grid + * + * @param rawGrid + * the raw parsed PowerFactoryGrid + */ + def convertGridElements( + grid: PreprocessedPfGridModel + ): Unit = { + val graph = + GridGraphBuilder.build(grid.nodes, grid.lines ++ grid.switches) + val nodeId2node = grid.nodes.map(node => (node.id, node)).toMap + val subnets = SubnetBuilder.buildSubnets(graph, nodeId2node) + val psdmNodes = subnets.flatMap(subnet => convertNodesOfSubnet(subnet)) + } + + /** Converts all nodes within a subnet to PSDM [[NodeInput]] + * + * @param subnet + * the subnet with reference to all PF nodes that live within + * @return + * list of all converted [[NodeInput]] + */ + def convertNodesOfSubnet( + subnet: Subnet + ): List[NodeInput] = + subnet.nodes + .map(node => NodeConverter.convertNode(node, subnet.id, subnet.voltLvl)) + .toList +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala index 28c98df7..e20aa63a 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilder.scala @@ -1,64 +1,64 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException -import edu.ie3.powerFactory2psdm.model.entity.{Edge, Node} -import org.jgrapht.graph._ - -/** Builds a graph representation of the powerfactory grid - */ -object GridGraphBuilder { - - /** Builds up the graph topology of the grid. All nodes (represented by their - * ids) and the connection between nodes by edges (lines and switches) are - * added to the graph. The resulting subgraphs inside the graph represent the - * subnets of the grid. - * - * @param nodes - * nodes of the power factory grid - * @param edges - * edges (lines and switches) of the power factory grid - * @return - * [[Multigraph]] of all the ids of the nodes and their connection - */ - def build[I <: Edge]( - nodes: List[Node], - edges: List[I] - ): AsUnmodifiableGraph[String, DefaultEdge] = { - - val graph = new Multigraph[String, DefaultEdge](classOf[DefaultEdge]) - nodes.foreach(node => graph.addVertex(node.id)) - edges.map(edge => { - graph.addEdge(edge.nodeAId, edge.nodeBId) - }) - new AsUnmodifiableGraph(graph) - } - - /** Unpacks the optional ids of two busses, connected by an edge. - * - * @param edgeId - * id of edge connecting busses with bus1Id and bus2Id - * @param maybeBus1Id - * Option of id of bus 1 - * @param maybeBus2Id - * Option of id of bus 2 - * @return - * Tuple of the ids of bus 1 and bus 2 - */ - def unpackConnectedBusses( - edgeId: String, - maybeBus1Id: Option[String], - maybeBus2Id: Option[String] - ): (String, String) = maybeBus1Id.zip(maybeBus2Id) match { - case Some((bus1Id, bus2Id)) => (bus1Id, bus2Id) - case None => - throw ElementConfigurationException( - s"Exception occurred while adding an edge. Exc: Edge with id: $edgeId is missing at least one connected node" - ) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException +import edu.ie3.powerFactory2psdm.model.entity.{Edge, Node} +import org.jgrapht.graph._ + +/** Builds a graph representation of the powerfactory grid + */ +object GridGraphBuilder { + + /** Builds up the graph topology of the grid. All nodes (represented by their + * ids) and the connection between nodes by edges (lines and switches) are + * added to the graph. The resulting subgraphs inside the graph represent the + * subnets of the grid. + * + * @param nodes + * nodes of the power factory grid + * @param edges + * edges (lines and switches) of the power factory grid + * @return + * [[Multigraph]] of all the ids of the nodes and their connection + */ + def build[I <: Edge]( + nodes: List[Node], + edges: List[I] + ): AsUnmodifiableGraph[String, DefaultEdge] = { + + val graph = new Multigraph[String, DefaultEdge](classOf[DefaultEdge]) + nodes.foreach(node => graph.addVertex(node.id)) + edges.map(edge => { + graph.addEdge(edge.nodeAId, edge.nodeBId) + }) + new AsUnmodifiableGraph(graph) + } + + /** Unpacks the optional ids of two busses, connected by an edge. + * + * @param edgeId + * id of edge connecting busses with bus1Id and bus2Id + * @param maybeBus1Id + * Option of id of bus 1 + * @param maybeBus2Id + * Option of id of bus 2 + * @return + * Tuple of the ids of bus 1 and bus 2 + */ + def unpackConnectedBusses( + edgeId: String, + maybeBus1Id: Option[String], + maybeBus2Id: Option[String] + ): (String, String) = maybeBus1Id.zip(maybeBus2Id) match { + case Some((bus1Id, bus2Id)) => (bus1Id, bus2Id) + case None => + throw ElementConfigurationException( + s"Exception occurred while adding an edge. Exc: Edge with id: $edgeId is missing at least one connected node" + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala index 7df28947..19359ce4 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/NodeConverter.scala @@ -1,71 +1,71 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.OperationTime -import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} -import edu.ie3.datamodel.models.voltagelevels.VoltageLevel -import edu.ie3.powerFactory2psdm.exception.pf.GridConfigurationException -import edu.ie3.powerFactory2psdm.model.entity.Node -import edu.ie3.util.quantities.PowerSystemUnits.PU -import org.locationtech.jts.geom.Point -import tech.units.indriya.quantity.Quantities - -import java.util.UUID - -object NodeConverter { - - /** Converts a PowerFactory node into a PSDM node. - * - * @param id - * id of the PF node to convert - * @param id2node - * Map of ids and their associated [[Node]] s - * @param subnet - * subnet the PF node lives in - * @return - * a PSDM [[NodeInput]] - */ - def convertNode( - node: Node, - subnetId: Int, - voltLvl: VoltageLevel - ): NodeInput = { - val vTarget = Quantities.getQuantity(node.vTarget, PU) - val geoPosition: Point = CoordinateConverter.convert(node.lat, node.lon) - val slack = isSlack(node) - new NodeInput( - UUID.randomUUID(), - node.id, - OperatorInput.NO_OPERATOR_ASSIGNED, - OperationTime.notLimited(), - vTarget, - slack, - geoPosition, - voltLvl, - subnetId - ) - } - - /** Checks if a node is a slack node by checking if there is an external grid - * connected to the node. - * - * @param node - * node to check - * @return - * true or false - */ - private def isSlack(node: Node): Boolean = - node.conElms.filter(_.pfCls == "ElmXnet") match { - case Seq(_) => true - case Nil => false - case _ => - throw GridConfigurationException( - s"There is more than one external grid connected to Node: ${node.id}" - ) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.OperationTime +import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} +import edu.ie3.datamodel.models.voltagelevels.VoltageLevel +import edu.ie3.powerFactory2psdm.exception.pf.GridConfigurationException +import edu.ie3.powerFactory2psdm.model.entity.Node +import edu.ie3.util.quantities.PowerSystemUnits.PU +import org.locationtech.jts.geom.Point +import tech.units.indriya.quantity.Quantities + +import java.util.UUID + +object NodeConverter { + + /** Converts a PowerFactory node into a PSDM node. + * + * @param id + * id of the PF node to convert + * @param id2node + * Map of ids and their associated [[Node]] s + * @param subnet + * subnet the PF node lives in + * @return + * a PSDM [[NodeInput]] + */ + def convertNode( + node: Node, + subnetId: Int, + voltLvl: VoltageLevel + ): NodeInput = { + val vTarget = Quantities.getQuantity(node.vTarget, PU) + val geoPosition: Point = CoordinateConverter.convert(node.lat, node.lon) + val slack = isSlack(node) + new NodeInput( + UUID.randomUUID(), + node.id, + OperatorInput.NO_OPERATOR_ASSIGNED, + OperationTime.notLimited(), + vTarget, + slack, + geoPosition, + voltLvl, + subnetId + ) + } + + /** Checks if a node is a slack node by checking if there is an external grid + * connected to the node. + * + * @param node + * node to check + * @return + * true or false + */ + private def isSlack(node: Node): Boolean = + node.conElms.filter(_.pfCls == "ElmXnet") match { + case Seq(_) => true + case Nil => false + case _ => + throw GridConfigurationException( + s"There is more than one external grid connected to Node: ${node.id}" + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala index 6b653711..eb2bf391 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilder.scala @@ -1,95 +1,95 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.datamodel.models.StandardUnits -import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - ElementConfigurationException, - GridConfigurationException -} -import edu.ie3.powerFactory2psdm.model.entity -import edu.ie3.powerFactory2psdm.model.entity.{Node, Subnet} -import org.jgrapht.alg.connectivity.BiconnectivityInspector -import org.jgrapht.graph.{AsUnmodifiableGraph, DefaultEdge} -import tech.units.indriya.quantity.Quantities.getQuantity - -import scala.jdk.CollectionConverters.CollectionHasAsScala - -object SubnetBuilder extends LazyLogging { - - /** Takes the grid graph and builds up the different [[Subnet]] s from it. - * - * @param gridGraph - * the built grid graph represented by the UUIDS of the nodes and the lines - * connecting them - * @param id2Node - * the mapping between UUID and the corresponding node - * @return - * the list of all subnets of the grid - */ - def buildSubnets( - gridGraph: AsUnmodifiableGraph[String, DefaultEdge], - id2Node: Map[String, Node] - ): List[Subnet] = { - new BiconnectivityInspector( - gridGraph - ).getConnectedComponents.asScala.toList.zipWithIndex map { - case (subgraph, index) => - buildSubnet( - index, - subgraph.vertexSet().asScala.toSet, - id2Node - ) - } - } - - /** Builds a [[Subnet]] after checking if all nodes have the same nominal - * voltage - * - * @param subnetId - * id of the subnet - * @param nodeIds - * UUIDS of all nodes that live in the subnet - * @param id2node - * mapping between UUID and node - * @return - * the built [[Subnet]] - */ - def buildSubnet( - subnetId: Int, - nodeIds: Set[String], - id2node: Map[String, Node] - ): Subnet = { - val nodes = nodeIds.map(id => - id2node.getOrElse( - id, - throw ConversionException(s"Can't find node id $id in id2node map") - ) - ) - val nomVoltage = nodes.headOption - .getOrElse( - throw GridConfigurationException("There are no nodes in the subnet!") - ) - .nominalVoltage - val divergences = nodes - .filter(node => Math.abs(nomVoltage - node.nominalVoltage) > 0.001) - .map { node => - node.id + " -> " + node.nominalVoltage - } - if (divergences.nonEmpty) - throw ElementConfigurationException( - s"There are the following divergences from the nominal voltage $nomVoltage : $divergences" - ) - val voltLvl = GermanVoltageLevelUtils.parse( - getQuantity(nomVoltage, StandardUnits.RATED_VOLTAGE_MAGNITUDE) - ) - entity.Subnet(subnetId, nodes, voltLvl) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.datamodel.models.StandardUnits +import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + ElementConfigurationException, + GridConfigurationException +} +import edu.ie3.powerFactory2psdm.model.entity +import edu.ie3.powerFactory2psdm.model.entity.{Node, Subnet} +import org.jgrapht.alg.connectivity.BiconnectivityInspector +import org.jgrapht.graph.{AsUnmodifiableGraph, DefaultEdge} +import tech.units.indriya.quantity.Quantities.getQuantity + +import scala.jdk.CollectionConverters.CollectionHasAsScala + +object SubnetBuilder extends LazyLogging { + + /** Takes the grid graph and builds up the different [[Subnet]] s from it. + * + * @param gridGraph + * the built grid graph represented by the UUIDS of the nodes and the lines + * connecting them + * @param id2Node + * the mapping between UUID and the corresponding node + * @return + * the list of all subnets of the grid + */ + def buildSubnets( + gridGraph: AsUnmodifiableGraph[String, DefaultEdge], + id2Node: Map[String, Node] + ): List[Subnet] = { + new BiconnectivityInspector( + gridGraph + ).getConnectedComponents.asScala.toList.zipWithIndex map { + case (subgraph, index) => + buildSubnet( + index, + subgraph.vertexSet().asScala.toSet, + id2Node + ) + } + } + + /** Builds a [[Subnet]] after checking if all nodes have the same nominal + * voltage + * + * @param subnetId + * id of the subnet + * @param nodeIds + * UUIDS of all nodes that live in the subnet + * @param id2node + * mapping between UUID and node + * @return + * the built [[Subnet]] + */ + def buildSubnet( + subnetId: Int, + nodeIds: Set[String], + id2node: Map[String, Node] + ): Subnet = { + val nodes = nodeIds.map(id => + id2node.getOrElse( + id, + throw ConversionException(s"Can't find node id $id in id2node map") + ) + ) + val nomVoltage = nodes.headOption + .getOrElse( + throw GridConfigurationException("There are no nodes in the subnet!") + ) + .nominalVoltage + val divergences = nodes + .filter(node => Math.abs(nomVoltage - node.nominalVoltage) > 0.001) + .map { node => + node.id + " -> " + node.nominalVoltage + } + if (divergences.nonEmpty) + throw ElementConfigurationException( + s"There are the following divergences from the nominal voltage $nomVoltage : $divergences" + ) + val voltLvl = GermanVoltageLevelUtils.parse( + getQuantity(nomVoltage, StandardUnits.RATED_VOLTAGE_MAGNITUDE) + ) + entity.Subnet(subnetId, nodes, voltLvl) + } +} 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 f3eb1c0f..ebec8a1b 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverter.scala @@ -1,63 +1,63 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types -import edu.ie3.datamodel.models.input.connector.`type`.LineTypeInput -import edu.ie3.powerFactory2psdm.model.entity.types.LineType -import tech.units.indriya.quantity.Quantities -import edu.ie3.util.quantities.PowerSystemUnits.{ - KILOVOLT, - OHM_PER_KILOMETRE, - SIEMENS_PER_KILOMETRE -} -import tech.units.indriya.unit.Units.AMPERE - -import java.util.UUID -import javax.measure.MetricPrefix - -/** Functionality to translate a [[LineType]] to a [[LineTypeInput]] - */ -object LineTypeConverter { - - def convert(input: LineType): LineTypeInput = { - val b = Quantities.getQuantity( - input.b, - MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) - ) - val g = Quantities.getQuantity( - input.g, - MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) - ) - val r = Quantities.getQuantity( - input.r, - OHM_PER_KILOMETRE - ) - val x = Quantities.getQuantity( - input.x, - OHM_PER_KILOMETRE - ) - val iMax = Quantities.getQuantity( - input.iMax, - MetricPrefix.KILO(AMPERE) - ) - val vRated = - Quantities.getQuantity( - input.vRated, - KILOVOLT - ) - - new LineTypeInput( - UUID.randomUUID(), - input.id, - b, - g, - r, - x, - iMax, - vRated - ) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types +import edu.ie3.datamodel.models.input.connector.`type`.LineTypeInput +import edu.ie3.powerFactory2psdm.model.entity.types.LineType +import tech.units.indriya.quantity.Quantities +import edu.ie3.util.quantities.PowerSystemUnits.{ + KILOVOLT, + OHM_PER_KILOMETRE, + SIEMENS_PER_KILOMETRE +} +import tech.units.indriya.unit.Units.AMPERE + +import java.util.UUID +import javax.measure.MetricPrefix + +/** Functionality to translate a [[LineType]] to a [[LineTypeInput]] + */ +object LineTypeConverter { + + def convert(input: LineType): LineTypeInput = { + val b = Quantities.getQuantity( + input.b, + MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) + ) + val g = Quantities.getQuantity( + input.g, + MetricPrefix.MICRO(SIEMENS_PER_KILOMETRE) + ) + val r = Quantities.getQuantity( + input.r, + OHM_PER_KILOMETRE + ) + val x = Quantities.getQuantity( + input.x, + OHM_PER_KILOMETRE + ) + val iMax = Quantities.getQuantity( + input.iMax, + MetricPrefix.KILO(AMPERE) + ) + val vRated = + Quantities.getQuantity( + input.vRated, + KILOVOLT + ) + + new LineTypeInput( + UUID.randomUUID(), + input.id, + b, + g, + r, + x, + iMax, + vRated + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala index 6a670113..1ba607ea 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverter.scala @@ -1,87 +1,87 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types - -import edu.ie3.datamodel.models.input.connector.`type`.Transformer2WTypeInput -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - ElementConfigurationException -} -import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W -import tech.units.indriya.quantity.Quantities -import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS, VOLT} -import edu.ie3.util.quantities.PowerSystemUnits.{DEGREE_GEOM, VOLTAMPERE} - -import math.{pow, sqrt} -import java.util.UUID -import javax.measure.MetricPrefix - -object TransformerType2WConverter { - - def convert(input: TransformerType2W): Transformer2WTypeInput = { - - val sRated = input.sRated * 1e6 - val vRatedA = input.vRatedA * 1e3 - val vRatedB = input.vRatedB * 1e3 - val pCu = input.pCu * 1e3 - val pFe = input.pFe * 1e3 - val uk = (input.uk / 100) * vRatedA - val iRated = sRated / (math.sqrt(3) * vRatedA) - val iNoLoadNom = (input.iNoLoad / 100) * iRated - - // short circuit experiment - val rSc = pCu / (3 * pow(iRated, 2)) - val zSc = (uk / sqrt(3)) / iRated - if (rSc > zSc) { - throw ConversionException( - s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + - s"parameters. The short circuit resistance can't exceed the short circuit impedance." - ) - } - val xSc = sqrt(pow(zSc, 2) - pow(rSc, 2)) - - // no load experiment - val yNoLoad = iNoLoadNom / (vRatedA / sqrt(3)) - val gNoLoad = pFe / pow(vRatedA, 2) - if (gNoLoad > yNoLoad) { - throw ConversionException( - s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + - s"The no load conductance can't exceed the no load admittance." - ) - } - val bNoLoad = sqrt((pow(yNoLoad, 2) - pow(gNoLoad, 2)).doubleValue) - - val tapSide = input.tapSide match { - case 0 => false - case 1 => true - case _ => - throw ElementConfigurationException( - s"The tap side of the transformer-type ${input.id} is neither 0 nor 1 - I am confused!" - ) - } - - new Transformer2WTypeInput( - UUID.randomUUID(), - input.id, - Quantities.getQuantity(rSc, OHM), - Quantities.getQuantity(xSc, OHM), - Quantities.getQuantity(input.sRated, MetricPrefix.MEGA(VOLTAMPERE)), - Quantities.getQuantity(vRatedA, VOLT), - Quantities.getQuantity(vRatedB, VOLT), - Quantities.getQuantity(gNoLoad, SIEMENS), - Quantities.getQuantity(bNoLoad, SIEMENS), - Quantities.getQuantity(input.dV, PERCENT), - Quantities.getQuantity(input.dPhi, DEGREE_GEOM), - tapSide, - input.tapNeutr.toInt, - input.tapMin.toInt, - input.tapMax.toInt - ) - - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types + +import edu.ie3.datamodel.models.input.connector.`type`.Transformer2WTypeInput +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + ElementConfigurationException +} +import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W +import tech.units.indriya.quantity.Quantities +import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS, VOLT} +import edu.ie3.util.quantities.PowerSystemUnits.{DEGREE_GEOM, VOLTAMPERE} + +import math.{pow, sqrt} +import java.util.UUID +import javax.measure.MetricPrefix + +object TransformerType2WConverter { + + def convert(input: TransformerType2W): Transformer2WTypeInput = { + + val sRated = input.sRated * 1e6 + val vRatedA = input.vRatedA * 1e3 + val vRatedB = input.vRatedB * 1e3 + val pCu = input.pCu * 1e3 + val pFe = input.pFe * 1e3 + val uk = (input.uk / 100) * vRatedA + val iRated = sRated / (math.sqrt(3) * vRatedA) + val iNoLoadNom = (input.iNoLoad / 100) * iRated + + // short circuit experiment + val rSc = pCu / (3 * pow(iRated, 2)) + val zSc = (uk / sqrt(3)) / iRated + if (rSc > zSc) { + throw ConversionException( + s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + + s"parameters. The short circuit resistance can't exceed the short circuit impedance." + ) + } + val xSc = sqrt(pow(zSc, 2) - pow(rSc, 2)) + + // no load experiment + val yNoLoad = iNoLoadNom / (vRatedA / sqrt(3)) + val gNoLoad = pFe / pow(vRatedA, 2) + if (gNoLoad > yNoLoad) { + throw ConversionException( + s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + + s"The no load conductance can't exceed the no load admittance." + ) + } + val bNoLoad = sqrt((pow(yNoLoad, 2) - pow(gNoLoad, 2)).doubleValue) + + val tapSide = input.tapSide match { + case 0 => false + case 1 => true + case _ => + throw ElementConfigurationException( + s"The tap side of the transformer-type ${input.id} is neither 0 nor 1 - I am confused!" + ) + } + + new Transformer2WTypeInput( + UUID.randomUUID(), + input.id, + Quantities.getQuantity(rSc, OHM), + Quantities.getQuantity(xSc, OHM), + Quantities.getQuantity(input.sRated, MetricPrefix.MEGA(VOLTAMPERE)), + Quantities.getQuantity(vRatedA, VOLT), + Quantities.getQuantity(vRatedB, VOLT), + Quantities.getQuantity(gNoLoad, SIEMENS), + Quantities.getQuantity(bNoLoad, SIEMENS), + Quantities.getQuantity(input.dV, PERCENT), + Quantities.getQuantity(input.dPhi, DEGREE_GEOM), + tapSide, + input.tapNeutr.toInt, + input.tapMin.toInt, + input.tapMax.toInt + ) + + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala index eb92572e..c14694b3 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/ConversionConfigException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.io - -final case class ConversionConfigException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends IoException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.io + +final case class ConversionConfigException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends IoException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala index c73d555e..ae6b275d 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/GridParsingException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.io - -final case class GridParsingException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends IoException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.io + +final case class GridParsingException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends IoException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala index 78c35adb..125413fa 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/io/IoException.scala @@ -1,14 +1,14 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.io - -/** Base class for grouping io related exceptions - */ -class IoException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends Exception(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.io + +/** Base class for grouping io related exceptions + */ +class IoException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends Exception(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala index 469fc70e..73c9d421 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ConversionException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class ConversionException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class ConversionException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala index cb30a2ea..054a4dd3 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/ElementConfigurationException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -case class ElementConfigurationException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +case class ElementConfigurationException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala index be2a8919..b06fb556 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/GridConfigurationException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -case class GridConfigurationException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +case class GridConfigurationException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala index b68d7d10..ce5d8a24 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingGridElementException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class MissingGridElementException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class MissingGridElementException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala index 17eb0a36..ed1bc3ce 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/MissingParameterException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class MissingParameterException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class MissingParameterException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala index 382ac7e4..22d6445c 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/PfException.scala @@ -1,14 +1,14 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -/** Base class for grouping power factory related exceptions - */ -class PfException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends Exception(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +/** Base class for grouping power factory related exceptions + */ +class PfException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends Exception(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala index 60e62407..47d4c17a 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/exception/pf/TestException.scala @@ -1,12 +1,12 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.exception.pf - -final case class TestException( - private val msg: String, - private val cause: Throwable = None.orNull -) extends PfException(msg, cause) +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.exception.pf + +final case class TestException( + private val msg: String, + private val cause: Throwable = None.orNull +) extends PfException(msg, cause) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala b/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala index 33f5c1c8..15d8ef37 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/io/PfGridParser.scala @@ -1,35 +1,35 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.io - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.model.RawPfGridModel -import io.circe.parser.decode -import io.circe.generic.auto._ -import io.circe.parser._ - -import scala.io.Source -import java.io.File - -object PfGridParser extends LazyLogging { - - def parse(gridFile: String): Option[RawPfGridModel] = { - val source = - Source.fromFile(gridFile) - val jsonString = - try source.mkString - finally source.close - - decode[RawPfGridModel](jsonString) match { - case Left(error) => - logger.error(error.getMessage()) - None - case Right(decodingResult) => - Some(decodingResult) - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.io + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.model.RawPfGridModel +import io.circe.parser.decode +import io.circe.generic.auto._ +import io.circe.parser._ + +import scala.io.Source +import java.io.File + +object PfGridParser extends LazyLogging { + + def parse(gridFile: String): Option[RawPfGridModel] = { + val source = + Source.fromFile(gridFile) + val jsonString = + try source.mkString + finally source.close + + decode[RawPfGridModel](jsonString) match { + case Left(error) => + logger.error(error.getMessage()) + None + case Right(decodingResult) => + Some(decodingResult) + } + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala b/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala index 371335cd..d7fc2604 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/main/RunConversion.scala @@ -1,41 +1,41 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.main - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.config.{ConfigValidator, ConversionConfig} -import edu.ie3.powerFactory2psdm.converter.GridConverter -import edu.ie3.powerFactory2psdm.io.PfGridParser - -import java.io.File -import edu.ie3.powerFactory2psdm.exception.io.GridParsingException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel -import pureconfig.ConfigSource -import pureconfig.generic.auto._ - -object RunConversion extends LazyLogging { - - def main(args: Array[String]): Unit = { - - logger.info("Parsing the config") - val config = - ConfigSource - .file("src/test/resources/application.conf") - .at("conversion-config") - .loadOrThrow[ConversionConfig] - ConfigValidator.validate(config) - val exportedGridFile = - s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" - logger.info("Parsing the json grid file.") - val pfGrid: RawPfGridModel = PfGridParser - .parse(exportedGridFile) - .getOrElse( - throw GridParsingException("Parsing the Json grid file failed") - ) - val psdmGrid = GridConverter.convert(pfGrid) - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.main + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.config.{ConfigValidator, ConversionConfig} +import edu.ie3.powerFactory2psdm.converter.GridConverter +import edu.ie3.powerFactory2psdm.io.PfGridParser + +import java.io.File +import edu.ie3.powerFactory2psdm.exception.io.GridParsingException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel +import pureconfig.ConfigSource +import pureconfig.generic.auto._ + +object RunConversion extends LazyLogging { + + def main(args: Array[String]): Unit = { + + logger.info("Parsing the config") + val config = + ConfigSource + .file("src/test/resources/application.conf") + .at("conversion-config") + .loadOrThrow[ConversionConfig] + ConfigValidator.validate(config) + val exportedGridFile = + s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" + logger.info("Parsing the json grid file.") + val pfGrid: RawPfGridModel = PfGridParser + .parse(exportedGridFile) + .getOrElse( + throw GridParsingException("Parsing the Json grid file failed") + ) + val psdmGrid = GridConverter.convert(pfGrid) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala index 4239c5fc..cddbf9be 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModel.scala @@ -1,203 +1,203 @@ -/* - * © 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 - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - GridConfigurationException, - MissingParameterException -} -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ - LineTypes, - Lines, - Nodes, - ProjectSettings, - Switches, - TrafoTypes2w, - Trafos2w -} -import edu.ie3.powerFactory2psdm.model.entity.{Line, Node, Switch} -import edu.ie3.powerFactory2psdm.model.entity.types.{ - LineType, - TransformerType2W -} -import edu.ie3.powerFactory2psdm.model.setting.{ConversionPrefixes, UnitSystem} - -/** Representation of the grid which is to be converted to a PSDM - * [[edu.ie3.datamodel.models.input.container.GridContainer]]. The - * preprocessing is done in order to unpack and check Optionals of the - * [[RawPfGridModel]], check for duplicated ids as well as allow for building - * inheritance relationships of all the custom classes of grid elements (e.g. - * nodes, lines, generators) since these can not sensibly be actualized by the - * [[SchemaGenerator]]. - * - * @param nodes - * electrical nodes of the grid - * @param lineTypes - * line types of the grid - * @param lines - * electrical lines of the grid - * @param switches - * electrical switches of the grid - * @param transformerTypes2W - * two-winding transformer types - * @param conversionPrefixes - * conversion prefixes for certain model parameters - */ -final case class PreprocessedPfGridModel( - nodes: List[Node], - lineTypes: List[LineType], - lines: List[Line], - switches: List[Switch], - transformerTypes2W: List[TransformerType2W], - conversionPrefixes: ConversionPrefixes -) - -object PreprocessedPfGridModel extends LazyLogging { - - /** Builds the preprocessed grid model from the unprocessed generated grid - * model. - * - * @param rawGrid - * the raw PowerFactory grid model as generated by the [[SchemaGenerator]] - * @return - * the preprocessed PowerFactory grid model - */ - def build(rawGrid: RawPfGridModel): PreprocessedPfGridModel = { - val projectSettings = extractProjectSettings(rawGrid.projectSettings) - checkUnitSystem( - projectSettings.unitSystem.getOrElse( - throw ConversionException( - "There is no unit system defined." - ) - ) - ) - val conversionPrefixes = buildConversionPrefixes(projectSettings) - val rawNodes = rawGrid.nodes.getOrElse( - throw GridConfigurationException("There are no nodes in the grid.") - ) - val rawLines = rawGrid.lines.getOrElse({ - logger.debug("There are no lines in the grid.") - List.empty[Lines] - }) - val rawLineTypes = rawGrid.lineTypes.getOrElse({ - logger.debug("There are no lines in the grid.") - List.empty[LineTypes] - }) - val rawSwitches = rawGrid.switches.getOrElse({ - logger.debug("There are no switches in the grid.") - List.empty[Switches] - }) - val rawTrafos2W = rawGrid.trafos2w.getOrElse({ - logger.debug("There are no switches in the grid.") - List.empty[Trafos2w] - }) - val rawTrafoTpyes2W = rawGrid.trafoTypes2w.getOrElse({ - logger.debug("There are no 2w trafo types in the grid.") - List.empty[TrafoTypes2w] - }) - - val models = - rawNodes ++ rawLines ++ rawLineTypes ++ rawSwitches ++ rawTrafos2W ++ rawTrafoTpyes2W - val modelIds = models.map { - case node: Nodes => - node.id.getOrElse( - throw MissingParameterException(s"Node $node has no defined id") - ) - case line: Lines => - line.id.getOrElse( - throw MissingParameterException(s"Line $line has no defined id") - ) - case lineType: LineTypes => - lineType.id.getOrElse( - throw MissingParameterException( - s"Line type $lineType has no defined id" - ) - ) - case switch: Switches => - switch.id.getOrElse( - throw MissingParameterException(s"Switch $switch has no defined id") - ) - case trafo2w: Trafos2w => - trafo2w.id.getOrElse( - throw MissingParameterException( - s"Transformer $trafo2w has no defined id" - ) - ) - case trafoTypes2w: TrafoTypes2w => - trafoTypes2w.id.getOrElse( - throw MissingParameterException( - s"Transformer type $trafoTypes2w has no defined id" - ) - ) - } - val uniqueIds = modelIds.distinct - if (uniqueIds.size < modelIds.size) { - val duplicateIds = modelIds.diff(uniqueIds) - throw GridConfigurationException( - s"Can't build grid as there are grid elements with duplicated ids: $duplicateIds" - ) - } - - val nodes = rawNodes.map(Node.build) - val lines = rawLines.map(line => Line.build(line)) - val lineTypes = rawLineTypes.map(LineType.build) - val switches = rawSwitches.flatMap(Switch.maybeBuild) - val trafoTypes2W = rawTrafoTpyes2W.map(TransformerType2W.build) - - PreprocessedPfGridModel( - nodes, - lineTypes, - lines, - switches, - trafoTypes2W, - conversionPrefixes - ) - } - - private def extractProjectSettings( - rawSettings: Option[List[ProjectSettings]] - ): ProjectSettings = { - rawSettings match { - case Some(List(settings)) => settings - case Some(List(_, _, _*)) => - throw ConversionException( - "There are multiple project settings defined." - ) - case None => - throw ConversionException("There are no project settings defined.") - } - } - - private def checkUnitSystem(unitSystem: Double): Unit = { - unitSystem match { - case UnitSystem.metric => () - case _ => - throw ConversionException( - "Conversion is currently only implemented for the metric unit system" - ) - } - } - - private def buildConversionPrefixes( - settings: ProjectSettings - ): ConversionPrefixes = { - ConversionPrefixes( - settings.prefixPQS.getOrElse( - throw MissingParameterException( - "The projects settings miss the prefix specification for active/reactive/apparent power values" - ) - ), - settings.prefixLength.getOrElse( - throw MissingParameterException( - "The project settings miss the prefix specification for line length." - ) - ) - ) - } -} +/* + * © 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 + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + GridConfigurationException, + MissingParameterException +} +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ + LineTypes, + Lines, + Nodes, + ProjectSettings, + Switches, + TrafoTypes2w, + Trafos2w +} +import edu.ie3.powerFactory2psdm.model.entity.{Line, Node, Switch} +import edu.ie3.powerFactory2psdm.model.entity.types.{ + LineType, + TransformerType2W +} +import edu.ie3.powerFactory2psdm.model.setting.{ConversionPrefixes, UnitSystem} + +/** Representation of the grid which is to be converted to a PSDM + * [[edu.ie3.datamodel.models.input.container.GridContainer]]. The + * preprocessing is done in order to unpack and check Optionals of the + * [[RawPfGridModel]], check for duplicated ids as well as allow for building + * inheritance relationships of all the custom classes of grid elements (e.g. + * nodes, lines, generators) since these can not sensibly be actualized by the + * [[SchemaGenerator]]. + * + * @param nodes + * electrical nodes of the grid + * @param lineTypes + * line types of the grid + * @param lines + * electrical lines of the grid + * @param switches + * electrical switches of the grid + * @param transformerTypes2W + * two-winding transformer types + * @param conversionPrefixes + * conversion prefixes for certain model parameters + */ +final case class PreprocessedPfGridModel( + nodes: List[Node], + lineTypes: List[LineType], + lines: List[Line], + switches: List[Switch], + transformerTypes2W: List[TransformerType2W], + conversionPrefixes: ConversionPrefixes +) + +object PreprocessedPfGridModel extends LazyLogging { + + /** Builds the preprocessed grid model from the unprocessed generated grid + * model. + * + * @param rawGrid + * the raw PowerFactory grid model as generated by the [[SchemaGenerator]] + * @return + * the preprocessed PowerFactory grid model + */ + def build(rawGrid: RawPfGridModel): PreprocessedPfGridModel = { + val projectSettings = extractProjectSettings(rawGrid.projectSettings) + checkUnitSystem( + projectSettings.unitSystem.getOrElse( + throw ConversionException( + "There is no unit system defined." + ) + ) + ) + val conversionPrefixes = buildConversionPrefixes(projectSettings) + val rawNodes = rawGrid.nodes.getOrElse( + throw GridConfigurationException("There are no nodes in the grid.") + ) + val rawLines = rawGrid.lines.getOrElse({ + logger.debug("There are no lines in the grid.") + List.empty[Lines] + }) + val rawLineTypes = rawGrid.lineTypes.getOrElse({ + logger.debug("There are no lines in the grid.") + List.empty[LineTypes] + }) + val rawSwitches = rawGrid.switches.getOrElse({ + logger.debug("There are no switches in the grid.") + List.empty[Switches] + }) + val rawTrafos2W = rawGrid.trafos2w.getOrElse({ + logger.debug("There are no switches in the grid.") + List.empty[Trafos2w] + }) + val rawTrafoTpyes2W = rawGrid.trafoTypes2w.getOrElse({ + logger.debug("There are no 2w trafo types in the grid.") + List.empty[TrafoTypes2w] + }) + + val models = + rawNodes ++ rawLines ++ rawLineTypes ++ rawSwitches ++ rawTrafos2W ++ rawTrafoTpyes2W + val modelIds = models.map { + case node: Nodes => + node.id.getOrElse( + throw MissingParameterException(s"Node $node has no defined id") + ) + case line: Lines => + line.id.getOrElse( + throw MissingParameterException(s"Line $line has no defined id") + ) + case lineType: LineTypes => + lineType.id.getOrElse( + throw MissingParameterException( + s"Line type $lineType has no defined id" + ) + ) + case switch: Switches => + switch.id.getOrElse( + throw MissingParameterException(s"Switch $switch has no defined id") + ) + case trafo2w: Trafos2w => + trafo2w.id.getOrElse( + throw MissingParameterException( + s"Transformer $trafo2w has no defined id" + ) + ) + case trafoTypes2w: TrafoTypes2w => + trafoTypes2w.id.getOrElse( + throw MissingParameterException( + s"Transformer type $trafoTypes2w has no defined id" + ) + ) + } + val uniqueIds = modelIds.distinct + if (uniqueIds.size < modelIds.size) { + val duplicateIds = modelIds.diff(uniqueIds) + throw GridConfigurationException( + s"Can't build grid as there are grid elements with duplicated ids: $duplicateIds" + ) + } + + val nodes = rawNodes.map(Node.build) + val lines = rawLines.map(line => Line.build(line)) + val lineTypes = rawLineTypes.map(LineType.build) + val switches = rawSwitches.flatMap(Switch.maybeBuild) + val trafoTypes2W = rawTrafoTpyes2W.map(TransformerType2W.build) + + PreprocessedPfGridModel( + nodes, + lineTypes, + lines, + switches, + trafoTypes2W, + conversionPrefixes + ) + } + + private def extractProjectSettings( + rawSettings: Option[List[ProjectSettings]] + ): ProjectSettings = { + rawSettings match { + case Some(List(settings)) => settings + case Some(List(_, _, _*)) => + throw ConversionException( + "There are multiple project settings defined." + ) + case None => + throw ConversionException("There are no project settings defined.") + } + } + + private def checkUnitSystem(unitSystem: Double): Unit = { + unitSystem match { + case UnitSystem.metric => () + case _ => + throw ConversionException( + "Conversion is currently only implemented for the metric unit system" + ) + } + } + + private def buildConversionPrefixes( + settings: ProjectSettings + ): ConversionPrefixes = { + ConversionPrefixes( + settings.prefixPQS.getOrElse( + throw MissingParameterException( + "The projects settings miss the prefix specification for active/reactive/apparent power values" + ) + ), + settings.prefixLength.getOrElse( + throw MissingParameterException( + "The project settings miss the prefix specification for line length." + ) + ) + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala index 4943250f..c2aae419 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/RawPfGridModel.scala @@ -1,128 +1,128 @@ -/* - * © 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 - -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ - ExtGrid, - LineTypes, - Lines, - Loads, - LoadsLV, - LoadsMV, - Nodes, - PowerPlants, - ProjectSettings, - Pvs, - StatGen, - Switches, - TrafoTypes2w, - TrafoTypes3w, - Trafos2w, - Trafos3w -} - -final case class RawPfGridModel( - trafos2w: Option[List[Trafos2w]], - loadsMV: Option[List[LoadsMV]], - nodes: Option[List[Nodes]], - projectSettings: Option[List[ProjectSettings]], - powerPlants: Option[List[PowerPlants]], - trafoTypes3w: Option[List[TrafoTypes3w]], - pvs: Option[List[Pvs]], - lineTypes: Option[List[LineTypes]], - switches: Option[List[Switches]], - loadsLV: Option[List[LoadsLV]], - statGen: Option[List[StatGen]], - loads: Option[List[Loads]], - trafos3w: Option[List[Trafos3w]], - extGrid: Option[List[ExtGrid]], - trafoTypes2w: Option[List[TrafoTypes2w]], - lines: Option[List[Lines]] -) - -object RawPfGridModel { - - final case class Switches( - id: Option[String], - bus1Id: Option[String], - bus2Id: Option[String] - ) - - final case class Pvs() - - final case class ConElms(id: Option[String], pfCls: Option[String]) - - final case class Loads(id: Option[String]) - - final case class LineTypes( - bline: Option[Double], - gline: Option[Double], - id: Option[String], - sline: Option[Double], - uline: Option[Double], - xline: Option[Double], - rline: Option[Double] - ) - - final case class StatGen(id: Option[String]) - - final case class Lines( - id: Option[String], - bus1Id: Option[String], - bus2Id: Option[String] - ) - - final case class PowerPlants(id: Option[String]) - - final case class Trafos3w() - - final case class ExtGrid(id: Option[String]) - - final case class TrafoTypes2w( - nntap0: Option[Double], - pfe: Option[Double], - uktr: Option[Double], - id: Option[String], - ntpmn: Option[Double], - dutap: Option[Double], - strn: Option[Double], - utrn_l: Option[Double], - curmg: Option[Double], - tap_side: Option[Double], - ntpmx: Option[Double], - pcutr: Option[Double], - phitr: Option[Double], - utrn_h: Option[Double] - ) - - final case class ProjectSettings( - unitSystem: Option[Double], - prefixPQS: Option[String], - prefixLength: Option[String] - ) - - final case class LoadsLV(id: Option[String]) - - final case class Nodes( - vtarget: Option[Double], - conElms: Option[List[Option[ConElms]]], - GPSlat: Option[Double], - id: Option[String], - GPSlon: Option[Double], - uknom: Option[Double] - ) - - final case class Trafos2w( - id: Option[String], - conElms: Option[List[Option[ConElms]]] - ) - - final case class TrafoTypes3w() - - final case class LoadsMV(id: Option[String]) - -} +/* + * © 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 + +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ + ExtGrid, + LineTypes, + Lines, + Loads, + LoadsLV, + LoadsMV, + Nodes, + PowerPlants, + ProjectSettings, + Pvs, + StatGen, + Switches, + TrafoTypes2w, + TrafoTypes3w, + Trafos2w, + Trafos3w +} + +final case class RawPfGridModel( + trafos2w: Option[List[Trafos2w]], + loadsMV: Option[List[LoadsMV]], + nodes: Option[List[Nodes]], + projectSettings: Option[List[ProjectSettings]], + powerPlants: Option[List[PowerPlants]], + trafoTypes3w: Option[List[TrafoTypes3w]], + pvs: Option[List[Pvs]], + lineTypes: Option[List[LineTypes]], + switches: Option[List[Switches]], + loadsLV: Option[List[LoadsLV]], + statGen: Option[List[StatGen]], + loads: Option[List[Loads]], + trafos3w: Option[List[Trafos3w]], + extGrid: Option[List[ExtGrid]], + trafoTypes2w: Option[List[TrafoTypes2w]], + lines: Option[List[Lines]] +) + +object RawPfGridModel { + + final case class Switches( + id: Option[String], + bus1Id: Option[String], + bus2Id: Option[String] + ) + + final case class Pvs() + + final case class ConElms(id: Option[String], pfCls: Option[String]) + + final case class Loads(id: Option[String]) + + final case class LineTypes( + bline: Option[Double], + gline: Option[Double], + id: Option[String], + sline: Option[Double], + uline: Option[Double], + xline: Option[Double], + rline: Option[Double] + ) + + final case class StatGen(id: Option[String]) + + final case class Lines( + id: Option[String], + bus1Id: Option[String], + bus2Id: Option[String] + ) + + final case class PowerPlants(id: Option[String]) + + final case class Trafos3w() + + final case class ExtGrid(id: Option[String]) + + final case class TrafoTypes2w( + nntap0: Option[Double], + pfe: Option[Double], + uktr: Option[Double], + id: Option[String], + ntpmn: Option[Double], + dutap: Option[Double], + strn: Option[Double], + utrn_l: Option[Double], + curmg: Option[Double], + tap_side: Option[Double], + ntpmx: Option[Double], + pcutr: Option[Double], + phitr: Option[Double], + utrn_h: Option[Double] + ) + + final case class ProjectSettings( + unitSystem: Option[Double], + prefixPQS: Option[String], + prefixLength: Option[String] + ) + + final case class LoadsLV(id: Option[String]) + + final case class Nodes( + vtarget: Option[Double], + conElms: Option[List[Option[ConElms]]], + GPSlat: Option[Double], + id: Option[String], + GPSlon: Option[Double], + uknom: Option[Double] + ) + + final case class Trafos2w( + id: Option[String], + conElms: Option[List[Option[ConElms]]] + ) + + final case class TrafoTypes3w() + + final case class LoadsMV(id: Option[String]) + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala index 5e5ad46d..d5919ee3 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/ConnectedElement.scala @@ -1,38 +1,38 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ConElms - -/** Data type that represents a ConnectedElement - * - * @param id - * identifier - * @param pfCls - * PowerFactory class that element represents - */ -case class ConnectedElement( - id: String, - pfCls: String -) extends EntityModel - -object ConnectedElement { - def build(conElm: ConElms): ConnectedElement = { - val id = conElm.id.getOrElse( - throw MissingParameterException( - s"There is no id for the connected element: $conElm" - ) - ) - val pfCls = conElm.pfCls.getOrElse( - throw MissingParameterException( - s"There is no PowerFactory class mentioned for connected element: $conElm" - ) - ) - ConnectedElement(id, pfCls) - } -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ConElms + +/** Data type that represents a ConnectedElement + * + * @param id + * identifier + * @param pfCls + * PowerFactory class that element represents + */ +case class ConnectedElement( + id: String, + pfCls: String +) extends EntityModel + +object ConnectedElement { + def build(conElm: ConElms): ConnectedElement = { + val id = conElm.id.getOrElse( + throw MissingParameterException( + s"There is no id for the connected element: $conElm" + ) + ) + val pfCls = conElm.pfCls.getOrElse( + throw MissingParameterException( + s"There is no PowerFactory class mentioned for connected element: $conElm" + ) + ) + ConnectedElement(id, pfCls) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala index 3f09a921..0a635f82 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Edge.scala @@ -1,14 +1,14 @@ -/* - * © 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 - -/** Denotes an element that connects two nodes within a Subnet - */ -trait Edge { - val nodeAId: String - val nodeBId: String -} +/* + * © 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 + +/** Denotes an element that connects two nodes within a Subnet + */ +trait Edge { + val nodeAId: String + val nodeBId: String +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala index 286eeb43..fcd0cd5c 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/EntityModel.scala @@ -1,16 +1,16 @@ -/* - * © 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 - -/** Common Entity parameters - */ -trait EntityModel { - - /** Id of the entity - */ - val id: String -} +/* + * © 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 + +/** Common Entity parameters + */ +trait EntityModel { + + /** Id of the entity + */ + val id: String +} 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 1d6a612c..31db92b8 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Line.scala @@ -1,46 +1,46 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines - -/** Electrical line - * - * @param id - * identifier - * @param nodeAId - * id of connected node - * @param nodeBId - * id of connected node - */ -final case class Line( - id: String, - nodeAId: String, - nodeBId: String -) extends EntityModel - with Edge - -object Line { - - def build(rawLine: Lines): Line = { - val id = rawLine.id.getOrElse( - throw MissingParameterException(s"There is no id for line $rawLine") - ) - val nodeAId = rawLine.bus1Id.getOrElse( - throw MissingParameterException(s"Line: $id has no defined node a") - ) - val nodeBId = rawLine.bus2Id.getOrElse( - throw MissingParameterException(s"Line: $id has no defined node b") - ) - Line( - id, - nodeAId, - nodeBId - ) - } -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines + +/** Electrical line + * + * @param id + * identifier + * @param nodeAId + * id of connected node + * @param nodeBId + * id of connected node + */ +final case class Line( + id: String, + nodeAId: String, + nodeBId: String +) extends EntityModel + with Edge + +object Line { + + def build(rawLine: Lines): Line = { + val id = rawLine.id.getOrElse( + throw MissingParameterException(s"There is no id for line $rawLine") + ) + val nodeAId = rawLine.bus1Id.getOrElse( + throw MissingParameterException(s"Line: $id has no defined node a") + ) + val nodeBId = rawLine.bus2Id.getOrElse( + throw MissingParameterException(s"Line: $id has no defined node b") + ) + Line( + id, + nodeAId, + nodeBId + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala index a2e5649c..51b7fd3e 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Node.scala @@ -1,75 +1,75 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Nodes - -/** Electrical node - * - * @param id - * identifier - * @param nominalVoltage - * nominal voltage in kV - * @param vTarget - * target voltage in p.u. - * @param lat - * latitude - * @param lon - * longitude - * @param conElms - * connected elements to the node - */ -final case class Node( - id: String, - nominalVoltage: Double, - vTarget: Double, - lat: Option[Double], - lon: Option[Double], - conElms: List[ConnectedElement] -) extends EntityModel - -object Node { - - /** Build a [[Node]] from a raw [[Nodes]] - * - * @param rawNode - * raw schema generated node - * @return - * [[Node]] - */ - def build(rawNode: Nodes): Node = { - val id = rawNode.id.getOrElse( - throw MissingParameterException(s"There is no id for node $rawNode") - ) - val nominalVoltage = rawNode.uknom.getOrElse( - throw MissingParameterException( - s"Node: $id has no defined nominal voltage" - ) - ) - val vTarget = rawNode.vtarget.getOrElse( - throw MissingParameterException( - s"Node: $id has no defined target voltage" - ) - ) - val conElms = rawNode.conElms - .getOrElse( - throw MissingParameterException(s"Node: $id has no connected elements") - ) - .flatten - .map(conElm => ConnectedElement.build(conElm)) - - Node( - id, - nominalVoltage, - vTarget, - rawNode.GPSlat, - rawNode.GPSlon, - conElms - ) - } -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Nodes + +/** Electrical node + * + * @param id + * identifier + * @param nominalVoltage + * nominal voltage in kV + * @param vTarget + * target voltage in p.u. + * @param lat + * latitude + * @param lon + * longitude + * @param conElms + * connected elements to the node + */ +final case class Node( + id: String, + nominalVoltage: Double, + vTarget: Double, + lat: Option[Double], + lon: Option[Double], + conElms: List[ConnectedElement] +) extends EntityModel + +object Node { + + /** Build a [[Node]] from a raw [[Nodes]] + * + * @param rawNode + * raw schema generated node + * @return + * [[Node]] + */ + def build(rawNode: Nodes): Node = { + val id = rawNode.id.getOrElse( + throw MissingParameterException(s"There is no id for node $rawNode") + ) + val nominalVoltage = rawNode.uknom.getOrElse( + throw MissingParameterException( + s"Node: $id has no defined nominal voltage" + ) + ) + val vTarget = rawNode.vtarget.getOrElse( + throw MissingParameterException( + s"Node: $id has no defined target voltage" + ) + ) + val conElms = rawNode.conElms + .getOrElse( + throw MissingParameterException(s"Node: $id has no connected elements") + ) + .flatten + .map(conElm => ConnectedElement.build(conElm)) + + Node( + id, + nominalVoltage, + vTarget, + rawNode.GPSlat, + rawNode.GPSlon, + conElms + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala index f2136e39..e156eba2 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Subnet.scala @@ -1,20 +1,20 @@ -/* - * © 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.datamodel.models.voltagelevels.VoltageLevel - -/** Data type that wraps necessary information for the different subnets - * - * @param id - * of the subnet - * @param nodes - * UUIDS of the nodes inside the subnet - * @param voltLvl - * voltage level that the nodes live in - */ -final case class Subnet(id: Int, nodes: Set[Node], voltLvl: VoltageLevel) +/* + * © 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.datamodel.models.voltagelevels.VoltageLevel + +/** Data type that wraps necessary information for the different subnets + * + * @param id + * of the subnet + * @param nodes + * UUIDS of the nodes inside the subnet + * @param voltLvl + * voltage level that the nodes live in + */ +final case class Subnet(id: Int, nodes: Set[Node], voltLvl: VoltageLevel) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala index a005cf38..e104209d 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/Switch.scala @@ -1,67 +1,67 @@ -/* - * © 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 com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches - -/** Electrical Switch - * - * @param id - * identifier - * @param nodeAId - * id of one of the connected nodes - * @param nodeBId - * id of one of the connected nodes - */ -final case class Switch( - id: String, - nodeAId: String, - nodeBId: String -) extends EntityModel - with Edge - -object Switch extends LazyLogging { - - /** Builds an Option that probably contains a [[Switch]] - * - * Note: We are not throwing an Exception if one connection of the switches - * is missing, since unused singly connected switches in PowerFactoryGrid - * substations are a thing. In that case we don't build it and return None. - * - * @param rawSwitch - * the schema generated switch - * @return - * a [[Switch]] - */ - def maybeBuild(rawSwitch: Switches): Option[Switch] = { - val id = rawSwitch.id.getOrElse( - throw MissingParameterException(s"There is no id for switch $rawSwitch") - ) - (rawSwitch.bus1Id, rawSwitch.bus2Id) match { - case (Some(bus1Id), Some(bus2Id)) => - Some( - Switch( - id, - bus1Id, - bus2Id - ) - ) - case (None, Some(_)) | (Some(_), None) => - logger.warn( - s"Switch: $id is not being built, as it's only connected to a single node" - ) - None - case _ => - throw MissingParameterException( - s"Switch: $id is not connected to any node" - ) - } - } -} +/* + * © 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 com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches + +/** Electrical Switch + * + * @param id + * identifier + * @param nodeAId + * id of one of the connected nodes + * @param nodeBId + * id of one of the connected nodes + */ +final case class Switch( + id: String, + nodeAId: String, + nodeBId: String +) extends EntityModel + with Edge + +object Switch extends LazyLogging { + + /** Builds an Option that probably contains a [[Switch]] + * + * Note: We are not throwing an Exception if one connection of the switches + * is missing, since unused singly connected switches in PowerFactoryGrid + * substations are a thing. In that case we don't build it and return None. + * + * @param rawSwitch + * the schema generated switch + * @return + * a [[Switch]] + */ + def maybeBuild(rawSwitch: Switches): Option[Switch] = { + val id = rawSwitch.id.getOrElse( + throw MissingParameterException(s"There is no id for switch $rawSwitch") + ) + (rawSwitch.bus1Id, rawSwitch.bus2Id) match { + case (Some(bus1Id), Some(bus2Id)) => + Some( + Switch( + id, + bus1Id, + bus2Id + ) + ) + case (None, Some(_)) | (Some(_), None) => + logger.warn( + s"Switch: $id is not being built, as it's only connected to a single node" + ) + None + case _ => + throw MissingParameterException( + s"Switch: $id is not connected to any node" + ) + } + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala index c55325d0..014bce44 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/LineType.scala @@ -1,89 +1,89 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes -import edu.ie3.powerFactory2psdm.model.entity.EntityModel - -/** Electrical line - * - * @param id - * identifier - * @param vRated - * rated voltage in kA - * @param iMax - * thermal current in kA - * @param r - * specific resistance in Ohm/km - * @param x - * specific reactance in Ohm/km - * @param b - * phase-to-ground conductance in micro Siemens/km - * @param g - * phase-to-ground conductance in micro Siemens/km - */ -final case class LineType( - id: String, - vRated: Double, - iMax: Double, - r: Double, - x: Double, - b: Double, - g: Double -) extends EntityModel - -object LineType { - - def build(rawLineType: LineTypes): LineType = { - val id = rawLineType.id.getOrElse( - throw MissingParameterException( - s"There is no id for line type $rawLineType" - ) - ) - val vRated = rawLineType.uline.getOrElse( - throw MissingParameterException( - s"There is no rated voltage defined for line type: $id" - ) - ) - val iMax = rawLineType.sline.getOrElse( - throw MissingParameterException( - s"There is no maximum thermal current defined for line type: $id" - ) - ) - val r = rawLineType.rline.getOrElse( - throw MissingParameterException( - s"There is no specific resistance defined for line type: $id" - ) - ) - val x = rawLineType.xline.getOrElse( - throw MissingParameterException( - s"There is no specific reactance defined for line type: $id" - ) - ) - val b = rawLineType.bline.getOrElse( - throw MissingParameterException( - s"There is no phase-to-ground conductance defined for line type: $id" - ) - ) - val g = rawLineType.gline.getOrElse( - throw MissingParameterException( - s"There is no phase-to-ground susceptance defined for line type: $id" - ) - ) - - LineType( - id, - vRated, - iMax, - r, - x, - b, - g - ) - } -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes +import edu.ie3.powerFactory2psdm.model.entity.EntityModel + +/** Electrical line + * + * @param id + * identifier + * @param vRated + * rated voltage in kA + * @param iMax + * thermal current in kA + * @param r + * specific resistance in Ohm/km + * @param x + * specific reactance in Ohm/km + * @param b + * phase-to-ground conductance in micro Siemens/km + * @param g + * phase-to-ground conductance in micro Siemens/km + */ +final case class LineType( + id: String, + vRated: Double, + iMax: Double, + r: Double, + x: Double, + b: Double, + g: Double +) extends EntityModel + +object LineType { + + def build(rawLineType: LineTypes): LineType = { + val id = rawLineType.id.getOrElse( + throw MissingParameterException( + s"There is no id for line type $rawLineType" + ) + ) + val vRated = rawLineType.uline.getOrElse( + throw MissingParameterException( + s"There is no rated voltage defined for line type: $id" + ) + ) + val iMax = rawLineType.sline.getOrElse( + throw MissingParameterException( + s"There is no maximum thermal current defined for line type: $id" + ) + ) + val r = rawLineType.rline.getOrElse( + throw MissingParameterException( + s"There is no specific resistance defined for line type: $id" + ) + ) + val x = rawLineType.xline.getOrElse( + throw MissingParameterException( + s"There is no specific reactance defined for line type: $id" + ) + ) + val b = rawLineType.bline.getOrElse( + throw MissingParameterException( + s"There is no phase-to-ground conductance defined for line type: $id" + ) + ) + val g = rawLineType.gline.getOrElse( + throw MissingParameterException( + s"There is no phase-to-ground susceptance defined for line type: $id" + ) + ) + + LineType( + id, + vRated, + iMax, + r, + x, + b, + g + ) + } +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala index 8b725dc3..bb421809 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/entity/types/TransformerType2W.scala @@ -1,166 +1,166 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w -import edu.ie3.powerFactory2psdm.model.entity.EntityModel - -/** Transformer type - * - * @param id - * Identifier - * @param sRated - * Rated apparent power in MVA - * @param vRatedA - * Rated voltage of the high voltage winding in kV - * @param vRatedB - * Rated voltage of the low voltage winding in kV - * @param dV - * Voltage magnitude deviation per tap position in % - * @param dPhi - * Voltage angle deviation per tap position in ° - * @param tapSide - * Selection of winding, where the tap changer is installed (0 = OS, 1 = US). - * @param tapNeutr - * Neutral tap position - * @param tapMin - * Minimum available tap position - * @param tapMax - * Maximum available tap position - * @param uk - * Short circuit voltage in % - * @param iNoLoad - * No load current in % - * @param pFe - * Iron losses in kW - * @param pCu - * Copper losses in kW - */ -final case class TransformerType2W( - id: String, - sRated: Double, - vRatedA: Double, - vRatedB: Double, - dV: Double, - dPhi: Double, - tapSide: Double, - tapNeutr: Double, - tapMin: Double, - tapMax: Double, - uk: Double, - iNoLoad: Double, - pFe: Double, - pCu: Double -) extends EntityModel - -object TransformerType2W { - - def build(rawType: TrafoTypes2w): TransformerType2W = { - val id = rawType.id.getOrElse( - throw MissingParameterException( - s"There is no id for transformer-type: $rawType" - ) - ) - - val sRated = rawType.strn.getOrElse( - throw MissingParameterException( - s"There is no rated apparent power for transformer-type: $id" - ) - ) - - val vRatedA = rawType.utrn_h.getOrElse( - throw MissingParameterException( - s"There is no voltage of high winding side for transformer-type: $id" - ) - ) - - val vRatedB = rawType.utrn_l.getOrElse( - throw MissingParameterException( - s"There is no voltage of low winding side for transformer-type: $id" - ) - ) - - val dV = rawType.dutap.getOrElse( - throw MissingParameterException( - s"There is no voltage magnitude deviation per tap position for transfomer type: $id" - ) - ) - - val dPhi = rawType.phitr.getOrElse( - throw MissingParameterException( - s"There is no voltage angle deviation per tap position for transformer-type: $id" - ) - ) - - val tapSide = rawType.tap_side.getOrElse( - throw MissingParameterException( - s"There is no selection of winding where tap changer is installed for transformer-type: $id" - ) - ) - - val tapNeutr = rawType.nntap0.getOrElse( - throw MissingParameterException( - s"There is no neutral tap position defined for transformer-type: $id" - ) - ) - - val tapMin = rawType.ntpmn.getOrElse( - throw MissingParameterException( - s"There is no minmum tap position defined for transformer-type: $id" - ) - ) - - val tapMax = rawType.ntpmx.getOrElse( - throw MissingParameterException( - s"There is no maximum tap position defined for transformer-type: $id" - ) - ) - - val uk = rawType.uktr.getOrElse( - throw MissingParameterException( - s"There is no short circuit voltage defined for transformer-type: $id" - ) - ) - - val iNoLoad = rawType.curmg.getOrElse( - throw MissingParameterException( - s"There is no no load current defined for transformer-type: $id" - ) - ) - - val pFe = rawType.pfe.getOrElse( - throw MissingParameterException( - s"There is no iron loss defined for transformer-type: $id" - ) - ) - - val pCu = rawType.pcutr.getOrElse( - throw MissingParameterException( - s"There is no iron loss defined for transformer-type: $id" - ) - ) - - TransformerType2W( - id, - sRated, - vRatedA, - vRatedB, - dV, - dPhi, - tapSide, - tapNeutr, - tapMin, - tapMax, - uk, - iNoLoad, - pFe, - pCu - ) - } - -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w +import edu.ie3.powerFactory2psdm.model.entity.EntityModel + +/** Transformer type + * + * @param id + * Identifier + * @param sRated + * Rated apparent power in MVA + * @param vRatedA + * Rated voltage of the high voltage winding in kV + * @param vRatedB + * Rated voltage of the low voltage winding in kV + * @param dV + * Voltage magnitude deviation per tap position in % + * @param dPhi + * Voltage angle deviation per tap position in ° + * @param tapSide + * Selection of winding, where the tap changer is installed (0 = OS, 1 = US). + * @param tapNeutr + * Neutral tap position + * @param tapMin + * Minimum available tap position + * @param tapMax + * Maximum available tap position + * @param uk + * Short circuit voltage in % + * @param iNoLoad + * No load current in % + * @param pFe + * Iron losses in kW + * @param pCu + * Copper losses in kW + */ +final case class TransformerType2W( + id: String, + sRated: Double, + vRatedA: Double, + vRatedB: Double, + dV: Double, + dPhi: Double, + tapSide: Double, + tapNeutr: Double, + tapMin: Double, + tapMax: Double, + uk: Double, + iNoLoad: Double, + pFe: Double, + pCu: Double +) extends EntityModel + +object TransformerType2W { + + def build(rawType: TrafoTypes2w): TransformerType2W = { + val id = rawType.id.getOrElse( + throw MissingParameterException( + s"There is no id for transformer-type: $rawType" + ) + ) + + val sRated = rawType.strn.getOrElse( + throw MissingParameterException( + s"There is no rated apparent power for transformer-type: $id" + ) + ) + + val vRatedA = rawType.utrn_h.getOrElse( + throw MissingParameterException( + s"There is no voltage of high winding side for transformer-type: $id" + ) + ) + + val vRatedB = rawType.utrn_l.getOrElse( + throw MissingParameterException( + s"There is no voltage of low winding side for transformer-type: $id" + ) + ) + + val dV = rawType.dutap.getOrElse( + throw MissingParameterException( + s"There is no voltage magnitude deviation per tap position for transfomer type: $id" + ) + ) + + val dPhi = rawType.phitr.getOrElse( + throw MissingParameterException( + s"There is no voltage angle deviation per tap position for transformer-type: $id" + ) + ) + + val tapSide = rawType.tap_side.getOrElse( + throw MissingParameterException( + s"There is no selection of winding where tap changer is installed for transformer-type: $id" + ) + ) + + val tapNeutr = rawType.nntap0.getOrElse( + throw MissingParameterException( + s"There is no neutral tap position defined for transformer-type: $id" + ) + ) + + val tapMin = rawType.ntpmn.getOrElse( + throw MissingParameterException( + s"There is no minmum tap position defined for transformer-type: $id" + ) + ) + + val tapMax = rawType.ntpmx.getOrElse( + throw MissingParameterException( + s"There is no maximum tap position defined for transformer-type: $id" + ) + ) + + val uk = rawType.uktr.getOrElse( + throw MissingParameterException( + s"There is no short circuit voltage defined for transformer-type: $id" + ) + ) + + val iNoLoad = rawType.curmg.getOrElse( + throw MissingParameterException( + s"There is no no load current defined for transformer-type: $id" + ) + ) + + val pFe = rawType.pfe.getOrElse( + throw MissingParameterException( + s"There is no iron loss defined for transformer-type: $id" + ) + ) + + val pCu = rawType.pcutr.getOrElse( + throw MissingParameterException( + s"There is no iron loss defined for transformer-type: $id" + ) + ) + + TransformerType2W( + id, + sRated, + vRatedA, + vRatedB, + dV, + dPhi, + tapSide, + tapNeutr, + tapMin, + tapMax, + uk, + iNoLoad, + pFe, + pCu + ) + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala index 3864ebee..01ec0a68 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/ConversionPrefixes.scala @@ -1,53 +1,53 @@ -/* - * © 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.setting - -/** For the active, reactive and apparent power of loads and the length of lines - * the unit system within power factory can be adjusted. This class holds the - * proper prefix values for their conversion. - * - * @param pqsPrefix - * prefix value for active, reactive and apparent power of loads - * @param lengthPrefix - * prefix value for length of lines - */ -class ConversionPrefixes private ( - pqsPrefix: Double, - lengthPrefix: Double -) { - def loadPQSPrefixValue(): Double = pqsPrefix - def lineLengthPrefixValue(): Double = lengthPrefix -} - -object ConversionPrefixes { - def apply( - pqsPrefSymbol: String, - lengthPrefSymbol: String - ): ConversionPrefixes = { - new ConversionPrefixes( - getMetricPrefixBySymbol(pqsPrefSymbol), - getMetricPrefixBySymbol(lengthPrefSymbol) - ) - } - - private def getMetricPrefixBySymbol(symbol: String): Double = symbol match { - case "a" => 1e-18 - case "f" => 1e-15 - case "p" => 1e-12 - case "n" => 1e-9 - case "u" => 1e-6 - case "m" => 1e-3 - case "" => 1 - case "k" => 1e3 - case "M" => 1e6 - case "G" => 1e9 - case "T" => 1e12 - case "P" => 1e15 - case "E" => 1e18 - } - -} +/* + * © 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.setting + +/** For the active, reactive and apparent power of loads and the length of lines + * the unit system within power factory can be adjusted. This class holds the + * proper prefix values for their conversion. + * + * @param pqsPrefix + * prefix value for active, reactive and apparent power of loads + * @param lengthPrefix + * prefix value for length of lines + */ +class ConversionPrefixes private ( + pqsPrefix: Double, + lengthPrefix: Double +) { + def loadPQSPrefixValue(): Double = pqsPrefix + def lineLengthPrefixValue(): Double = lengthPrefix +} + +object ConversionPrefixes { + def apply( + pqsPrefSymbol: String, + lengthPrefSymbol: String + ): ConversionPrefixes = { + new ConversionPrefixes( + getMetricPrefixBySymbol(pqsPrefSymbol), + getMetricPrefixBySymbol(lengthPrefSymbol) + ) + } + + private def getMetricPrefixBySymbol(symbol: String): Double = symbol match { + case "a" => 1e-18 + case "f" => 1e-15 + case "p" => 1e-12 + case "n" => 1e-9 + case "u" => 1e-6 + case "m" => 1e-3 + case "" => 1 + case "k" => 1e3 + case "M" => 1e6 + case "G" => 1e9 + case "T" => 1e12 + case "P" => 1e15 + case "E" => 1e18 + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala index d39d5f6d..7090557b 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/model/setting/UnitSystem.scala @@ -1,16 +1,16 @@ -/* - * © 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.setting - -/** Enumeration of the different unit system options within the project - * settings. - */ -object UnitSystem extends Enumeration { - val metric = 0 - val englishTransportation = 1d - val englishIndustry = 2d -} +/* + * © 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.setting + +/** Enumeration of the different unit system options within the project + * settings. + */ +object UnitSystem extends Enumeration { + val metric = 0 + val englishTransportation = 1d + val englishIndustry = 2d +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala index 6a78e9cc..a29db96d 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala @@ -1,31 +1,32 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import java.nio.file.{Files, Paths} -import org.scalafmt.interfaces.Scalafmt -import java.io.File - -object Formatter { - - def format(str: String, fmtPath: Option[String]): String = { - val classLoader = this.getClass.getClassLoader - val scalafmt = Scalafmt.create(classLoader) - val configFile = new File(classLoader.getResource(".scalafmt.conf").getFile) - val defaultConfigPath = - Paths.get(configFile.getPath) - val defaultConfig = - if (Files.exists(defaultConfigPath)) defaultConfigPath else Paths.get("") - val config = fmtPath.fold(defaultConfig)(Paths.get(_)) - val result = scalafmt - .withRespectVersion(false) - .format(config, Paths.get("Nil.scala"), str) - scalafmt.clear() - result - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import java.nio.file.{Files, Paths} +import org.scalafmt.interfaces.Scalafmt + +import java.io.File + +object Formatter { + + def format(str: String, fmtPath: Option[String]): String = { + val classLoader = this.getClass.getClassLoader + val scalafmt = Scalafmt.create(classLoader) + val configFile = new File(classLoader.getResource(".scalafmt.conf").getFile) + val defaultConfigPath = + Paths.get(configFile.getPath) + val defaultConfig = + if (Files.exists(defaultConfigPath)) defaultConfigPath else Paths.get("") + val config = fmtPath.fold(defaultConfig)(Paths.get(_)) + val result = scalafmt + .withRespectVersion(false) + .format(config, Paths.get("Nil.scala"), str) + scalafmt.clear() + result + } + +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala index 3ea0ef39..3c33b16e 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/GridPreparator.scala @@ -1,50 +1,50 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.powerFactory2psdm.model.RawPfGridModel -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches - -object GridPreparator extends LazyLogging { - - /** Perform preparation of the [[RawPfGridModel]] before the actual conversion - * can happen. - * - * @param pfGrid - * the [[RawPfGridModel]] to prepare - * @return - * the prepared [[RawPfGridModel]] - */ - def prepare(pfGrid: RawPfGridModel): RawPfGridModel = { - val filteredSwitches = removeSinglyConnectedSwitches(pfGrid.switches) - pfGrid.copy(switches = filteredSwitches) - } - - /** Removes [[Switches]] from a [[RawPfGridModel]] that are only connected to - * a single node. - * - * @param maybeSwitches - * @return - */ - def removeSinglyConnectedSwitches( - maybeSwitches: Option[List[RawPfGridModel.Switches]] - ): Option[List[RawPfGridModel.Switches]] = - maybeSwitches.map(switches => { - val (fullyConnected, singlyConnected) = - switches.partition(isFullyConnectedSwitch) - singlyConnected.foreach(switch => - logger.debug( - s"Removed switch with id: ${switch.id.getOrElse("NO_ID")}, since it only has a single connection." - ) - ) - fullyConnected - }) - - def isFullyConnectedSwitch(switch: Switches): Boolean = - switch.bus1Id.isDefined & switch.bus2Id.isDefined -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.powerFactory2psdm.model.RawPfGridModel +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches + +object GridPreparator extends LazyLogging { + + /** Perform preparation of the [[RawPfGridModel]] before the actual conversion + * can happen. + * + * @param pfGrid + * the [[RawPfGridModel]] to prepare + * @return + * the prepared [[RawPfGridModel]] + */ + def prepare(pfGrid: RawPfGridModel): RawPfGridModel = { + val filteredSwitches = removeSinglyConnectedSwitches(pfGrid.switches) + pfGrid.copy(switches = filteredSwitches) + } + + /** Removes [[Switches]] from a [[RawPfGridModel]] that are only connected to + * a single node. + * + * @param maybeSwitches + * @return + */ + def removeSinglyConnectedSwitches( + maybeSwitches: Option[List[RawPfGridModel.Switches]] + ): Option[List[RawPfGridModel.Switches]] = + maybeSwitches.map(switches => { + val (fullyConnected, singlyConnected) = + switches.partition(isFullyConnectedSwitch) + singlyConnected.foreach(switch => + logger.debug( + s"Removed switch with id: ${switch.id.getOrElse("NO_ID")}, since it only has a single connection." + ) + ) + fullyConnected + }) + + def isFullyConnectedSwitch(switch: Switches): Boolean = + switch.bus1Id.isDefined & switch.bus2Id.isDefined +} diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala index 014aec9c..d0c3021d 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/SchemaGenerator.scala @@ -1,420 +1,420 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import java.io.{File, PrintWriter} -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.util.StringUtils -import io.circe.Json.Folder -import io.circe.{Json, JsonNumber, JsonObject} -import io.circe.parser._ - -import scala.io.Source - -object SchemaGenerator extends LazyLogging { - def main(args: Array[String]): Unit = { - val source = - Source.fromFile( - s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" - ) - val jsonString = - try source.mkString - finally source.close - run(jsonString) - } - - def run(jsonString: String): Unit = { - run( - jsonString, - "RawGridModel", - "edu.ie3.powerFactory2psdm.model.powerfactory" - ).foreach(formatedClassString => { - val pw = new PrintWriter( - new File( - s"${new File(".").getCanonicalPath}/src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" - ) - ) - pw.write(formatedClassString) - pw.close() - }) - } - - def run( - jsonString: String, - className: String, - `package`: String - ): Option[String] = - parse(jsonString) match { - case Left(error) => - logger.error( - s"Exception during json file parsing: '${error.getMessage()}'" - ) - None - case Right(json) => - generateClass( - json, - className, - `package` - ).map(Formatter.format(_, None)) - } - - private def generateClass( - json: Json, - className: String, - `package`: String - ): Option[String] = { - - json.asObject match { - case Some(jsonObject) if !jsonObject.isEmpty => - val classes: Iterable[SimpleClass] = - json - .foldWith(ClassFolder(className, `package`)) - .flatMap(_.classes) - .groupBy(_.name) - .flatMap { - case (name, elems) if elems.size > 1 => - // duplicated class name, merge the fields and provide default None for all of them - Vector(SimpleClass(name, elems.flatMap(_.fields).distinct)) - case (_, elems) => elems - } - - val wrapperClass = - s""" - | final case class ${this.className(className)}( - | ${jsonObject.toMap.keys - .map(key => s"$key: Option[List[${this.className(key)}]]") - .mkString(",\n")} - | ) - |""".stripMargin - - val importStatement = - s""" - | import ${`package`}.${this.className(className)}.{ - | ${jsonObject.toMap.keys - .map(key => this.className(key)) - .mkString(",\n")} - | } - |""".stripMargin - - val packageStatement = - s"package ${`package`}" - - val wrapperObj = - s""" - | object ${this.className(className)}{ - | - | ${classes.map(_.toString).mkString("")} - | } - |""".stripMargin - - Some(packageStatement + importStatement + wrapperClass + wrapperObj) - case _ => - None - } - - } - - private def className(name: String) = - StringUtils.snakeCaseToCamelCase(StringUtils.cleanString(name)).capitalize - - final case class FieldMeta( - rawFieldType: String, - rawFieldName: String, - collectionStack: Int - ) { - - def fieldString: String = - simpleString(rawFieldName, rawFieldType, collectionStack) - - private def simpleString( - name: String, - `type`: String, - collectionStack: Int - ): String = - if (collectionStack == 0) { - s"$name: Option[${`type`}]" - } else { - s"$name:" + (0 until collectionStack) - .foldLeft("Option[")((cur, _) => - cur + s"List[Option[" - ) + `type` + "]" * (collectionStack * 2 + 1) - } - } - - final case class ComplexClass( - name: String, - fields: Iterable[FieldMeta], - classes: Iterable[SimpleClass] = Vector.empty, - cStack: Int = 0, - isObj: Boolean = false - ) - - final case class SimpleClass( - name: String, - fields: Iterable[String] - ) { - - private def caseClassString(name: String, fields: String) = - s""" - |final case class ${className(name)} ($fields) - |""".stripMargin - - override def toString: String = caseClassString(name, fields.mkString(",")) - } - - final case class ClassFolder( - name: String, - `package`: String, - isRoot: Boolean = true, - collectionStack: Int = 0, - isObj: Boolean = false, - defaultOnNullType: String = "String" - ) extends Folder[Vector[ComplexClass]] { - - override def onNull: Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta(defaultOnNullType, name, collectionStack) - ) - ) - ) - - override def onBoolean(value: Boolean): Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta("Boolean", name, collectionStack) - ) - ) - ) - - override def onNumber(value: JsonNumber): Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta("Double", name, collectionStack) - ) - ) - ) - - override def onString(value: String): Vector[ComplexClass] = - Vector( - ComplexClass( - name, - Vector( - FieldMeta("String", name, collectionStack) - ) - ) - ) - - override def onArray(value: Vector[Json]): Vector[ComplexClass] = - value - .flatMap( - _.foldWith( - this.copy(isRoot = false, collectionStack = collectionStack + 1) - ) - ) match { - case array - if array.isEmpty => // if empty default to collection with default type - Vector( - ComplexClass( - name, - Vector( - FieldMeta(defaultOnNullType, name, collectionStack + 1) - ) - ) - ) - case nonEmptyArray => - nonEmptyArray.distinct // keep only uniques - } - - override def onObject(value: JsonObject): Vector[ComplexClass] = { - - val fieldsOrClasses1 = value.toMap - .map { case (objName, jsonObjs) => - ( - objName, - jsonObjs.asArray match { - case Some(objArr) => - // filter multiple json objects - objArr - .flatMap( - _.foldWith( - this.copy( - name = objName, - isRoot = false, - collectionStack = 1, - isObj = true - ) - ) - ) - case _ => - jsonObjs.foldWith( - this.copy( - name = objName, - isRoot = false, - collectionStack = 0, - isObj = true - ) - ) - } - ) - } - - val fieldsOrClasses = fieldsOrClasses1.flatMap { - case (className, cplxClasses) if isRoot && cplxClasses.isEmpty => - // empty case class @ root level - Vector( - ComplexClass( - name, - Vector.empty, - Vector(SimpleClass(className, Vector.empty)) - ) - ) - case (className, cplxClasses) if isRoot && cplxClasses.nonEmpty => - // case class @ root level - collapseClasses( - cplxClasses, - defaultOnNullType, - collectionStack, - isObj - ).map(cplxClass => - ComplexClass( - name, - Vector.empty, - cplxClasses.flatMap(_.classes) ++ Vector( - SimpleClass(className, cplxClass.fields.map(_.fieldString)) - ) - ) - ) - - case (cName, cplxClasses) - if cplxClasses.nonEmpty && - cplxClasses.head.isObj && - !isRoot => - // complex nested case class - Vector( - ComplexClass( - name, - Vector( - FieldMeta(className(cName), cName, cplxClasses.head.cStack) - ), - cplxClasses.flatMap(_.classes) :+ SimpleClass( - cName, - cplxClasses.head.fields.map(_.fieldString) - ) - ) - ) - case (_, cplxClasses) => - // if not root level and not an object, map the field vals - Vector( - ComplexClass( - name, - collapseSameFieldTypes( - cplxClasses.flatMap(_.fields), - defaultOnNullType - ), - cplxClasses.flatMap(_.classes) - ) - ) - } - - Vector( - ComplexClass( - name, - fieldsOrClasses - .flatMap(_.fields) - .filterNot(_.fieldString.isBlank) - .filterNot(_.fieldString.isEmpty), - fieldsOrClasses.flatMap(_.classes), - collectionStack, - isObj - ) - ) - } - } - - private def collapseSameFieldTypes( - fields: Iterable[FieldMeta], - defaultOnNullType: String - ): Iterable[FieldMeta] = { - // check if field types contains a non default value - // if any use this one, of not, keep string - fields.filterNot(_.rawFieldType.equals(defaultOnNullType)).toSet match { - case noneDefaultType if noneDefaultType.size == 1 => - noneDefaultType.headOption - case noneDefaultType if noneDefaultType.size > 1 => - throw new IllegalArgumentException( - s"More than one field type identified: ${noneDefaultType.mkString(",")}" - ) - case empty if empty.isEmpty => - // we just filtered the defaultOnNullType and the vector is empty - // => field is the same as the defaultType and we can just return - fields - } - } - - private def collapseClasses( - classes: Seq[ComplexClass], - defaultOnNullType: String, - collectionStack: Int, - isObj: Boolean - ): Iterable[ComplexClass] = classes.distinct.groupBy(_.name).flatMap { - case (_, distClasses) - if distClasses.size == 1 => // all classes are equal, return only one of them - distClasses - case ( - name, - distClasses - ) => // multiple classes with same name but maybe different fields, try to merge them ... - // merge and collapse fields - val allFields = distClasses - .flatMap(_.fields) - .groupMap(fieldMeta => - (fieldMeta.rawFieldName, fieldMeta.collectionStack) - )(_.rawFieldType) - .flatMap { case ((name, colStack), types) => - types.distinct.filterNot(_.equals(defaultOnNullType)) match { - case noneDefaultType if noneDefaultType.size == 1 => - noneDefaultType.headOption.map(fieldType => - FieldMeta( - fieldType, - name, - colStack - ) - ) - case noneDefaultType if noneDefaultType.size > 1 => - throw new IllegalArgumentException( - s"More than one field type identified: ${noneDefaultType.mkString(",")}" - ) - case empty - if empty.isEmpty => // if default type is given we end up here, as we filtered all default types - Some( - FieldMeta( - defaultOnNullType, - name, - colStack - ) - ) - } - } - Iterable( - ComplexClass( - name, - allFields, - distClasses.flatMap(_.classes), - collectionStack, - isObj - ) - ) - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import java.io.{File, PrintWriter} +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.util.StringUtils +import io.circe.Json.Folder +import io.circe.{Json, JsonNumber, JsonObject} +import io.circe.parser._ + +import scala.io.Source + +object SchemaGenerator extends LazyLogging { + def main(args: Array[String]): Unit = { + val source = + Source.fromFile( + s"${new File(".").getCanonicalPath}/src/main/python/pfGridExport/pfGrid.json" + ) + val jsonString = + try source.mkString + finally source.close + run(jsonString) + } + + def run(jsonString: String): Unit = { + run( + jsonString, + "RawGridModel", + "edu.ie3.powerFactory2psdm.model.powerfactory" + ).foreach(formatedClassString => { + val pw = new PrintWriter( + new File( + s"${new File(".").getCanonicalPath}/src/main/scala/edu/ie3/powerFactory2psdm/model/powerfactory/RawGridModel.scala" + ) + ) + pw.write(formatedClassString) + pw.close() + }) + } + + def run( + jsonString: String, + className: String, + `package`: String + ): Option[String] = + parse(jsonString) match { + case Left(error) => + logger.error( + s"Exception during json file parsing: '${error.getMessage()}'" + ) + None + case Right(json) => + generateClass( + json, + className, + `package` + ).map(Formatter.format(_, None)) + } + + private def generateClass( + json: Json, + className: String, + `package`: String + ): Option[String] = { + + json.asObject match { + case Some(jsonObject) if !jsonObject.isEmpty => + val classes: Iterable[SimpleClass] = + json + .foldWith(ClassFolder(className, `package`)) + .flatMap(_.classes) + .groupBy(_.name) + .flatMap { + case (name, elems) if elems.size > 1 => + // duplicated class name, merge the fields and provide default None for all of them + Vector(SimpleClass(name, elems.flatMap(_.fields).distinct)) + case (_, elems) => elems + } + + val wrapperClass = + s""" + | final case class ${this.className(className)}( + | ${jsonObject.toMap.keys + .map(key => s"$key: Option[List[${this.className(key)}]]") + .mkString(",\n")} + | ) + |""".stripMargin + + val importStatement = + s""" + | import ${`package`}.${this.className(className)}.{ + | ${jsonObject.toMap.keys + .map(key => this.className(key)) + .mkString(",\n")} + | } + |""".stripMargin + + val packageStatement = + s"package ${`package`}" + + val wrapperObj = + s""" + | object ${this.className(className)}{ + | + | ${classes.map(_.toString).mkString("")} + | } + |""".stripMargin + + Some(packageStatement + importStatement + wrapperClass + wrapperObj) + case _ => + None + } + + } + + private def className(name: String) = + StringUtils.snakeCaseToCamelCase(StringUtils.cleanString(name)).capitalize + + final case class FieldMeta( + rawFieldType: String, + rawFieldName: String, + collectionStack: Int + ) { + + def fieldString: String = + simpleString(rawFieldName, rawFieldType, collectionStack) + + private def simpleString( + name: String, + `type`: String, + collectionStack: Int + ): String = + if (collectionStack == 0) { + s"$name: Option[${`type`}]" + } else { + s"$name:" + (0 until collectionStack) + .foldLeft("Option[")((cur, _) => + cur + s"List[Option[" + ) + `type` + "]" * (collectionStack * 2 + 1) + } + } + + final case class ComplexClass( + name: String, + fields: Iterable[FieldMeta], + classes: Iterable[SimpleClass] = Vector.empty, + cStack: Int = 0, + isObj: Boolean = false + ) + + final case class SimpleClass( + name: String, + fields: Iterable[String] + ) { + + private def caseClassString(name: String, fields: String) = + s""" + |final case class ${className(name)} ($fields) + |""".stripMargin + + override def toString: String = caseClassString(name, fields.mkString(",")) + } + + final case class ClassFolder( + name: String, + `package`: String, + isRoot: Boolean = true, + collectionStack: Int = 0, + isObj: Boolean = false, + defaultOnNullType: String = "String" + ) extends Folder[Vector[ComplexClass]] { + + override def onNull: Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta(defaultOnNullType, name, collectionStack) + ) + ) + ) + + override def onBoolean(value: Boolean): Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta("Boolean", name, collectionStack) + ) + ) + ) + + override def onNumber(value: JsonNumber): Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta("Double", name, collectionStack) + ) + ) + ) + + override def onString(value: String): Vector[ComplexClass] = + Vector( + ComplexClass( + name, + Vector( + FieldMeta("String", name, collectionStack) + ) + ) + ) + + override def onArray(value: Vector[Json]): Vector[ComplexClass] = + value + .flatMap( + _.foldWith( + this.copy(isRoot = false, collectionStack = collectionStack + 1) + ) + ) match { + case array + if array.isEmpty => // if empty default to collection with default type + Vector( + ComplexClass( + name, + Vector( + FieldMeta(defaultOnNullType, name, collectionStack + 1) + ) + ) + ) + case nonEmptyArray => + nonEmptyArray.distinct // keep only uniques + } + + override def onObject(value: JsonObject): Vector[ComplexClass] = { + + val fieldsOrClasses1 = value.toMap + .map { case (objName, jsonObjs) => + ( + objName, + jsonObjs.asArray match { + case Some(objArr) => + // filter multiple json objects + objArr + .flatMap( + _.foldWith( + this.copy( + name = objName, + isRoot = false, + collectionStack = 1, + isObj = true + ) + ) + ) + case _ => + jsonObjs.foldWith( + this.copy( + name = objName, + isRoot = false, + collectionStack = 0, + isObj = true + ) + ) + } + ) + } + + val fieldsOrClasses = fieldsOrClasses1.flatMap { + case (className, cplxClasses) if isRoot && cplxClasses.isEmpty => + // empty case class @ root level + Vector( + ComplexClass( + name, + Vector.empty, + Vector(SimpleClass(className, Vector.empty)) + ) + ) + case (className, cplxClasses) if isRoot && cplxClasses.nonEmpty => + // case class @ root level + collapseClasses( + cplxClasses, + defaultOnNullType, + collectionStack, + isObj + ).map(cplxClass => + ComplexClass( + name, + Vector.empty, + cplxClasses.flatMap(_.classes) ++ Vector( + SimpleClass(className, cplxClass.fields.map(_.fieldString)) + ) + ) + ) + + case (cName, cplxClasses) + if cplxClasses.nonEmpty && + cplxClasses.head.isObj && + !isRoot => + // complex nested case class + Vector( + ComplexClass( + name, + Vector( + FieldMeta(className(cName), cName, cplxClasses.head.cStack) + ), + cplxClasses.flatMap(_.classes) :+ SimpleClass( + cName, + cplxClasses.head.fields.map(_.fieldString) + ) + ) + ) + case (_, cplxClasses) => + // if not root level and not an object, map the field vals + Vector( + ComplexClass( + name, + collapseSameFieldTypes( + cplxClasses.flatMap(_.fields), + defaultOnNullType + ), + cplxClasses.flatMap(_.classes) + ) + ) + } + + Vector( + ComplexClass( + name, + fieldsOrClasses + .flatMap(_.fields) + .filterNot(_.fieldString.isBlank) + .filterNot(_.fieldString.isEmpty), + fieldsOrClasses.flatMap(_.classes), + collectionStack, + isObj + ) + ) + } + } + + private def collapseSameFieldTypes( + fields: Iterable[FieldMeta], + defaultOnNullType: String + ): Iterable[FieldMeta] = { + // check if field types contains a non default value + // if any use this one, of not, keep string + fields.filterNot(_.rawFieldType.equals(defaultOnNullType)).toSet match { + case noneDefaultType if noneDefaultType.size == 1 => + noneDefaultType.headOption + case noneDefaultType if noneDefaultType.size > 1 => + throw new IllegalArgumentException( + s"More than one field type identified: ${noneDefaultType.mkString(",")}" + ) + case empty if empty.isEmpty => + // we just filtered the defaultOnNullType and the vector is empty + // => field is the same as the defaultType and we can just return + fields + } + } + + private def collapseClasses( + classes: Seq[ComplexClass], + defaultOnNullType: String, + collectionStack: Int, + isObj: Boolean + ): Iterable[ComplexClass] = classes.distinct.groupBy(_.name).flatMap { + case (_, distClasses) + if distClasses.size == 1 => // all classes are equal, return only one of them + distClasses + case ( + name, + distClasses + ) => // multiple classes with same name but maybe different fields, try to merge them ... + // merge and collapse fields + val allFields = distClasses + .flatMap(_.fields) + .groupMap(fieldMeta => + (fieldMeta.rawFieldName, fieldMeta.collectionStack) + )(_.rawFieldType) + .flatMap { case ((name, colStack), types) => + types.distinct.filterNot(_.equals(defaultOnNullType)) match { + case noneDefaultType if noneDefaultType.size == 1 => + noneDefaultType.headOption.map(fieldType => + FieldMeta( + fieldType, + name, + colStack + ) + ) + case noneDefaultType if noneDefaultType.size > 1 => + throw new IllegalArgumentException( + s"More than one field type identified: ${noneDefaultType.mkString(",")}" + ) + case empty + if empty.isEmpty => // if default type is given we end up here, as we filtered all default types + Some( + FieldMeta( + defaultOnNullType, + name, + colStack + ) + ) + } + } + Iterable( + ComplexClass( + name, + allFields, + distClasses.flatMap(_.classes), + collectionStack, + isObj + ) + ) + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala b/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala index bd75fb28..7a8d583a 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/common/ConverterTestData.scala @@ -1,321 +1,321 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.common - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.datamodel.models.input.connector.`type`.{ - LineTypeInput, - Transformer2WTypeInput -} -import edu.ie3.datamodel.models.{OperationTime, StandardUnits, UniqueEntity} -import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} -import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils.LV -import edu.ie3.powerFactory2psdm.config.ConversionConfig - -import java.io.File -import edu.ie3.powerFactory2psdm.exception.io.GridParsingException -import edu.ie3.powerFactory2psdm.exception.pf.TestException -import edu.ie3.powerFactory2psdm.io.PfGridParser -import edu.ie3.powerFactory2psdm.model.entity.{ - ConnectedElement, - EntityModel, - Node, - Subnet -} -import edu.ie3.powerFactory2psdm.model.entity.types.{ - LineType, - TransformerType2W -} -import edu.ie3.powerFactory2psdm.model.PreprocessedPfGridModel -import edu.ie3.util.quantities.PowerSystemUnits.PU -import org.locationtech.jts.geom.{Coordinate, GeometryFactory} -import pureconfig.ConfigSource -import tech.units.indriya.quantity.Quantities -import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS} -import edu.ie3.util.quantities.PowerSystemUnits.{ - DEGREE_GEOM, - KILOVOLT, - VOLTAMPERE -} -import pureconfig.generic.auto._ - -import java.util.UUID -import javax.measure.MetricPrefix - -object ConverterTestData extends LazyLogging { - - val config: ConversionConfig = - ConfigSource.default.at("conversion-config").loadOrThrow[ConversionConfig] - - /** Case class to denote a consistent pair of input and expected output of a - * conversion - * - * @param input - * Input model - * @param result - * Resulting, converted model - * @tparam I - * Type of input model - * @tparam R - * Type of result class - */ - final case class ConversionPair[I <: EntityModel, R <: UniqueEntity]( - input: I, - result: R - ) { - def getPair: (I, R) = (input, result) - } - - logger.warn("Building the grid model") - - val testGridFile = - s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" - - val testGrid: PreprocessedPfGridModel = PreprocessedPfGridModel.build( - PfGridParser - .parse(testGridFile) - .getOrElse( - throw GridParsingException( - s"Couldn't parse the grid file $testGridFile" - ) - ) - ) - - val id2node: Map[String, Node] = - testGrid.nodes.map(node => (node.id, node)).toMap - - val bus1Id = "Grid.ElmNet\\Bus_0001.ElmTerm" - val bus2Id = "Grid.ElmNet\\Bus_0002.ElmTerm" - val bus3Id = "Grid.ElmNet\\Bus_0003.ElmTerm" - val bus4Id = "Grid.ElmNet\\Bus_0004.ElmTerm" - val bus5Id = "Grid.ElmNet\\Bus_0005.ElmTerm" - val bus6Id = "Grid.ElmNet\\Bus_0006.ElmTerm" - val bus7Id = "Grid.ElmNet\\Bus_0007.ElmTerm" - val bus8Id = "Grid.ElmNet\\Bus_0008.ElmTerm" - val bus9Id = "Grid.ElmNet\\Bus_0009.ElmTerm" - val bus10Id = "Grid.ElmNet\\Bus_0010.ElmTerm" - val bus11Id = "Grid.ElmNet\\Bus_0011.ElmTerm" - val bus12Id = "Grid.ElmNet\\Bus_0012.ElmTerm" - val bus13Id = "Grid.ElmNet\\Bus_0013.ElmTerm" - val bus14Id = "Grid.ElmNet\\Bus_0014.ElmTerm" - val bus15Id = "Grid.ElmNet\\Bus_0015.ElmTerm" - val busOns1Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\1.ElmTerm" - val busOns2Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\2.ElmTerm" - val busOnsLv = - "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\ON_Station_Lower.ElmTerm" - - val subnet1Ids: Set[String] = - Set( - bus1Id, - bus2Id, - bus3Id, - bus4Id, - bus5Id - ) - - val subnet2Ids: Set[String] = Set(bus7Id) - - val subnet3Ids: Set[String] = Set(bus8Id) - - val subnet4Ids: Set[String] = - Set( - bus6Id, - bus9Id, - bus10Id, - bus11Id, - bus12Id, - bus13Id, - bus14Id, - bus15Id, - busOns1Id, - busOns2Id, - busOnsLv - ) - - val geometryFactory = new GeometryFactory() - - val nodes = Map( - "someNode" -> ConversionPair( - Node( - "someNode", - 0.4, - 1.0, - Some(11.1123), - Some(52.1425), - List( - ConnectedElement( - "someConnectedElement", - "ElmLne" - ) - ) - ), - new NodeInput( - UUID.randomUUID(), - "someNode", - OperatorInput.NO_OPERATOR_ASSIGNED, - OperationTime.notLimited(), - Quantities.getQuantity(1d, PU), - false, - geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), - LV, - 1 - ) - ), - "someSlackNode" -> ConversionPair( - Node( - "someSlackNode", - 0.4, - 1.0, - Some(11.1123), - Some(52.1425), - List( - ConnectedElement( - "someConnectedElement", - "ElmXnet" - ) - ) - ), - new NodeInput( - UUID.randomUUID(), - "someSlackNode", - OperatorInput.NO_OPERATOR_ASSIGNED, - OperationTime.notLimited(), - Quantities.getQuantity(1d, PU), - true, - geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), - LV, - 2 - ) - ) - ) - - def getNodePair(key: String): ConversionPair[Node, NodeInput] = { - nodes.getOrElse( - key, - throw TestException( - s"Cannot find input/result pair for ${Node.getClass.getSimpleName} with key: $key " - ) - ) - } - - val subnets = Map( - "someSubnet" -> Subnet( - 1, - Set(getNodePair("someNode").input), - LV - ) - ) - - def getSubnet(key: String): Subnet = subnets.getOrElse( - key, - throw TestException(s"Cannot find subnet with key: $key") - ) - - val lineTypes = Map( - "someLineType" -> - ConversionPair( - LineType( - "someLineType", - 132.0, - 1.0, - 6.753542423248291, - 20.61956214904785, - 151.51515197753906, - 1.543 - ), - new LineTypeInput( - UUID.randomUUID(), - "someLineType", - Quantities.getQuantity( - 151.51515197753906, - StandardUnits.ADMITTANCE_PER_LENGTH - ), - Quantities.getQuantity( - 1.543, - StandardUnits.ADMITTANCE_PER_LENGTH - ), - Quantities.getQuantity( - 6.753542423248291, - StandardUnits.IMPEDANCE_PER_LENGTH - ), - Quantities.getQuantity( - 20.61956214904785, - StandardUnits.IMPEDANCE_PER_LENGTH - ), - Quantities.getQuantity( - 1000, - StandardUnits.ELECTRIC_CURRENT_MAGNITUDE - ), - Quantities.getQuantity( - 132.0, - StandardUnits.RATED_VOLTAGE_MAGNITUDE - ) - ) - ) - ) - - def getLineType(key: String): ConversionPair[LineType, LineTypeInput] = { - lineTypes.getOrElse( - key, - throw TestException( - s"Cannot find input/result pair for ${LineType.getClass.getSimpleName} with key: $key " - ) - ) - } - - val transformerTypes = Map( - "SomeTrafo2wType" -> ConversionPair( - TransformerType2W( - id = "SomeTrafo2wType", - sRated = 40d, - vRatedA = 110d, - vRatedB = 10d, - dV = 2.5, - dPhi = 5d, - tapSide = 0, - tapNeutr = 0, - tapMin = -10, - tapMax = 10, - uk = 5, - iNoLoad = 1, - pFe = 10, - pCu = 6 - ), - new Transformer2WTypeInput( - UUID.randomUUID(), - "SomeTrafo2wType", - Quantities.getQuantity(45.375, MetricPrefix.MILLI(OHM)), - Quantities.getQuantity(15.1249319, OHM), - Quantities.getQuantity(40d, MetricPrefix.MEGA(VOLTAMPERE)), - Quantities.getQuantity(110d, KILOVOLT), - Quantities.getQuantity(10d, KILOVOLT), - Quantities.getQuantity(826.4462809, MetricPrefix.NANO(SIEMENS)), - Quantities - .getQuantity(33047.519046, MetricPrefix.NANO(SIEMENS)) - .to(MetricPrefix.NANO(SIEMENS)), - Quantities.getQuantity(2.5, PERCENT), - Quantities.getQuantity(5d, DEGREE_GEOM), - false, - 0, - -10, - 10 - ) - ) - ) - - def getTransformer2wType( - key: String - ): ConversionPair[TransformerType2W, Transformer2WTypeInput] = { - transformerTypes.getOrElse( - key, - throw TestException( - s"Cannot find input/result pair for ${TransformerType2W.getClass.getSimpleName} with key: $key " - ) - ) - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.common + +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.datamodel.models.input.connector.`type`.{ + LineTypeInput, + Transformer2WTypeInput +} +import edu.ie3.datamodel.models.{OperationTime, StandardUnits, UniqueEntity} +import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} +import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils.LV +import edu.ie3.powerFactory2psdm.config.ConversionConfig + +import java.io.File +import edu.ie3.powerFactory2psdm.exception.io.GridParsingException +import edu.ie3.powerFactory2psdm.exception.pf.TestException +import edu.ie3.powerFactory2psdm.io.PfGridParser +import edu.ie3.powerFactory2psdm.model.entity.{ + ConnectedElement, + EntityModel, + Node, + Subnet +} +import edu.ie3.powerFactory2psdm.model.entity.types.{ + LineType, + TransformerType2W +} +import edu.ie3.powerFactory2psdm.model.PreprocessedPfGridModel +import edu.ie3.util.quantities.PowerSystemUnits.PU +import org.locationtech.jts.geom.{Coordinate, GeometryFactory} +import pureconfig.ConfigSource +import tech.units.indriya.quantity.Quantities +import tech.units.indriya.unit.Units.{OHM, PERCENT, SIEMENS} +import edu.ie3.util.quantities.PowerSystemUnits.{ + DEGREE_GEOM, + KILOVOLT, + VOLTAMPERE +} +import pureconfig.generic.auto._ + +import java.util.UUID +import javax.measure.MetricPrefix + +object ConverterTestData extends LazyLogging { + + val config: ConversionConfig = + ConfigSource.default.at("conversion-config").loadOrThrow[ConversionConfig] + + /** Case class to denote a consistent pair of input and expected output of a + * conversion + * + * @param input + * Input model + * @param result + * Resulting, converted model + * @tparam I + * Type of input model + * @tparam R + * Type of result class + */ + final case class ConversionPair[I <: EntityModel, R <: UniqueEntity]( + input: I, + result: R + ) { + def getPair: (I, R) = (input, result) + } + + logger.warn("Building the grid model") + + val testGridFile = + s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" + + val testGrid: PreprocessedPfGridModel = PreprocessedPfGridModel.build( + PfGridParser + .parse(testGridFile) + .getOrElse( + throw GridParsingException( + s"Couldn't parse the grid file $testGridFile" + ) + ) + ) + + val id2node: Map[String, Node] = + testGrid.nodes.map(node => (node.id, node)).toMap + + val bus1Id = "Grid.ElmNet\\Bus_0001.ElmTerm" + val bus2Id = "Grid.ElmNet\\Bus_0002.ElmTerm" + val bus3Id = "Grid.ElmNet\\Bus_0003.ElmTerm" + val bus4Id = "Grid.ElmNet\\Bus_0004.ElmTerm" + val bus5Id = "Grid.ElmNet\\Bus_0005.ElmTerm" + val bus6Id = "Grid.ElmNet\\Bus_0006.ElmTerm" + val bus7Id = "Grid.ElmNet\\Bus_0007.ElmTerm" + val bus8Id = "Grid.ElmNet\\Bus_0008.ElmTerm" + val bus9Id = "Grid.ElmNet\\Bus_0009.ElmTerm" + val bus10Id = "Grid.ElmNet\\Bus_0010.ElmTerm" + val bus11Id = "Grid.ElmNet\\Bus_0011.ElmTerm" + val bus12Id = "Grid.ElmNet\\Bus_0012.ElmTerm" + val bus13Id = "Grid.ElmNet\\Bus_0013.ElmTerm" + val bus14Id = "Grid.ElmNet\\Bus_0014.ElmTerm" + val bus15Id = "Grid.ElmNet\\Bus_0015.ElmTerm" + val busOns1Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\1.ElmTerm" + val busOns2Id = "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\2.ElmTerm" + val busOnsLv = + "Grid.ElmNet\\Ortsnetzstation.ElmTrfstat\\ON_Station_Lower.ElmTerm" + + val subnet1Ids: Set[String] = + Set( + bus1Id, + bus2Id, + bus3Id, + bus4Id, + bus5Id + ) + + val subnet2Ids: Set[String] = Set(bus7Id) + + val subnet3Ids: Set[String] = Set(bus8Id) + + val subnet4Ids: Set[String] = + Set( + bus6Id, + bus9Id, + bus10Id, + bus11Id, + bus12Id, + bus13Id, + bus14Id, + bus15Id, + busOns1Id, + busOns2Id, + busOnsLv + ) + + val geometryFactory = new GeometryFactory() + + val nodes = Map( + "someNode" -> ConversionPair( + Node( + "someNode", + 0.4, + 1.0, + Some(11.1123), + Some(52.1425), + List( + ConnectedElement( + "someConnectedElement", + "ElmLne" + ) + ) + ), + new NodeInput( + UUID.randomUUID(), + "someNode", + OperatorInput.NO_OPERATOR_ASSIGNED, + OperationTime.notLimited(), + Quantities.getQuantity(1d, PU), + false, + geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), + LV, + 1 + ) + ), + "someSlackNode" -> ConversionPair( + Node( + "someSlackNode", + 0.4, + 1.0, + Some(11.1123), + Some(52.1425), + List( + ConnectedElement( + "someConnectedElement", + "ElmXnet" + ) + ) + ), + new NodeInput( + UUID.randomUUID(), + "someSlackNode", + OperatorInput.NO_OPERATOR_ASSIGNED, + OperationTime.notLimited(), + Quantities.getQuantity(1d, PU), + true, + geometryFactory.createPoint(new Coordinate(11.1123, 52.1425)), + LV, + 2 + ) + ) + ) + + def getNodePair(key: String): ConversionPair[Node, NodeInput] = { + nodes.getOrElse( + key, + throw TestException( + s"Cannot find input/result pair for ${Node.getClass.getSimpleName} with key: $key " + ) + ) + } + + val subnets = Map( + "someSubnet" -> Subnet( + 1, + Set(getNodePair("someNode").input), + LV + ) + ) + + def getSubnet(key: String): Subnet = subnets.getOrElse( + key, + throw TestException(s"Cannot find subnet with key: $key") + ) + + val lineTypes = Map( + "someLineType" -> + ConversionPair( + LineType( + "someLineType", + 132.0, + 1.0, + 6.753542423248291, + 20.61956214904785, + 151.51515197753906, + 1.543 + ), + new LineTypeInput( + UUID.randomUUID(), + "someLineType", + Quantities.getQuantity( + 151.51515197753906, + StandardUnits.ADMITTANCE_PER_LENGTH + ), + Quantities.getQuantity( + 1.543, + StandardUnits.ADMITTANCE_PER_LENGTH + ), + Quantities.getQuantity( + 6.753542423248291, + StandardUnits.IMPEDANCE_PER_LENGTH + ), + Quantities.getQuantity( + 20.61956214904785, + StandardUnits.IMPEDANCE_PER_LENGTH + ), + Quantities.getQuantity( + 1000, + StandardUnits.ELECTRIC_CURRENT_MAGNITUDE + ), + Quantities.getQuantity( + 132.0, + StandardUnits.RATED_VOLTAGE_MAGNITUDE + ) + ) + ) + ) + + def getLineType(key: String): ConversionPair[LineType, LineTypeInput] = { + lineTypes.getOrElse( + key, + throw TestException( + s"Cannot find input/result pair for ${LineType.getClass.getSimpleName} with key: $key " + ) + ) + } + + val transformerTypes = Map( + "SomeTrafo2wType" -> ConversionPair( + TransformerType2W( + id = "SomeTrafo2wType", + sRated = 40d, + vRatedA = 110d, + vRatedB = 10d, + dV = 2.5, + dPhi = 5d, + tapSide = 0, + tapNeutr = 0, + tapMin = -10, + tapMax = 10, + uk = 5, + iNoLoad = 1, + pFe = 10, + pCu = 6 + ), + new Transformer2WTypeInput( + UUID.randomUUID(), + "SomeTrafo2wType", + Quantities.getQuantity(45.375, MetricPrefix.MILLI(OHM)), + Quantities.getQuantity(15.1249319, OHM), + Quantities.getQuantity(40d, MetricPrefix.MEGA(VOLTAMPERE)), + Quantities.getQuantity(110d, KILOVOLT), + Quantities.getQuantity(10d, KILOVOLT), + Quantities.getQuantity(826.4462809, MetricPrefix.NANO(SIEMENS)), + Quantities + .getQuantity(33047.519046, MetricPrefix.NANO(SIEMENS)) + .to(MetricPrefix.NANO(SIEMENS)), + Quantities.getQuantity(2.5, PERCENT), + Quantities.getQuantity(5d, DEGREE_GEOM), + false, + 0, + -10, + 10 + ) + ) + ) + + def getTransformer2wType( + key: String + ): ConversionPair[TransformerType2W, Transformer2WTypeInput] = { + transformerTypes.getOrElse( + key, + throw TestException( + s"Cannot find input/result pair for ${TransformerType2W.getClass.getSimpleName} with key: $key " + ) + ) + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala index 5bad54c9..8f38a5dc 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/config/ConfigValidatorSpec.scala @@ -1,117 +1,117 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.config - -import edu.ie3.powerFactory2psdm.common.ConverterTestData -import edu.ie3.powerFactory2psdm.config.ConfigValidator.{ - lowerBoundViolation, - lowerUpperBoundViolation, - upperBoundViolation, - validatePvModelGenerationParams -} -import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ - Fixed, - PvFixedFeedIn, - PvModelGeneration, - UniformDistribution -} -import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException -import edu.ie3.powerFactory2psdm.exception.pf.TestException -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class ConfigValidatorSpec extends Matchers with AnyWordSpecLike { - - "A ConfigValidator " should { - - val pvModelGeneration: PvModelGeneration = - ConverterTestData.config.modelConfigs.pvConfig.conversionMode match { - case PvFixedFeedIn => - throw TestException( - "The test pv config is supposed to be configured for PvModelGeneration" - ) - case x: PvModelGeneration => x - } - - "validate a conversion config" in { - ConfigValidator.validate(ConverterTestData.config) - } - - "validate model configs" in { - ConfigValidator.validateModelConfigs( - ConverterTestData.config.modelConfigs - ) - } - - "validate pv configs" in { - ConfigValidator.validatePvConfig( - ConverterTestData.config.modelConfigs.pvConfig - ) - } - - "validate correct pv params" in { - validatePvModelGenerationParams(pvModelGeneration) - } - - "throw an exception for invalid pv param albedo" in { - val faultyParams = pvModelGeneration.copy(albedo = Fixed(1.1)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The albedo of the plants surrounding: ${faultyParams.albedo} isn't valid. Exception: ${upperBoundViolation(1.1, 1.0).exception.getMessage}" - } - - "throw an exception for invalid pv param azimuth" in { - val faultyParams = - pvModelGeneration.copy(azimuth = UniformDistribution(-91, 92)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: ${lowerUpperBoundViolation(-91, 92, -90, 90).exception.getMessage}" - } - - "throw an exception for min/max error of pv param azimuth" in { - val faultyParams = - pvModelGeneration.copy(azimuth = UniformDistribution(20, -10)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: The minimum value: 20.0 exceeds the maximum value: -10.0" - } - - "throw an exception for invalid pv param etaConv" in { - val faultyParams = pvModelGeneration.copy(etaConv = Fixed(101)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The efficiency of the plants inverter: ${faultyParams.azimuth} isn't valid. Exception: ${upperBoundViolation(101, 100).exception.getMessage}" - } - - "throw an exception for invalid pv param kG" in { - val faultyParams = pvModelGeneration.copy(kG = Fixed(-0.1)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The PV generator correction factor (kG): ${faultyParams.kG} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" - } - - "throw an exception for invalid pv param kT" in { - val faultyParams = pvModelGeneration.copy(kT = Fixed(-0.1)) - val exc = - intercept[ConversionConfigException]( - validatePvModelGenerationParams(faultyParams) - ) - exc.getMessage shouldBe s"The PV temperature correction factor (kT): ${faultyParams.kT} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" - } - - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.config + +import edu.ie3.powerFactory2psdm.common.ConverterTestData +import edu.ie3.powerFactory2psdm.config.ConfigValidator.{ + lowerBoundViolation, + lowerUpperBoundViolation, + upperBoundViolation, + validatePvModelGenerationParams +} +import edu.ie3.powerFactory2psdm.config.ConversionConfig.{ + Fixed, + PvFixedFeedIn, + PvModelGeneration, + UniformDistribution +} +import edu.ie3.powerFactory2psdm.exception.io.ConversionConfigException +import edu.ie3.powerFactory2psdm.exception.pf.TestException +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class ConfigValidatorSpec extends Matchers with AnyWordSpecLike { + + "A ConfigValidator " should { + + val pvModelGeneration: PvModelGeneration = + ConverterTestData.config.modelConfigs.pvConfig.conversionMode match { + case PvFixedFeedIn => + throw TestException( + "The test pv config is supposed to be configured for PvModelGeneration" + ) + case x: PvModelGeneration => x + } + + "validate a conversion config" in { + ConfigValidator.validate(ConverterTestData.config) + } + + "validate model configs" in { + ConfigValidator.validateModelConfigs( + ConverterTestData.config.modelConfigs + ) + } + + "validate pv configs" in { + ConfigValidator.validatePvConfig( + ConverterTestData.config.modelConfigs.pvConfig + ) + } + + "validate correct pv params" in { + validatePvModelGenerationParams(pvModelGeneration) + } + + "throw an exception for invalid pv param albedo" in { + val faultyParams = pvModelGeneration.copy(albedo = Fixed(1.1)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The albedo of the plants surrounding: ${faultyParams.albedo} isn't valid. Exception: ${upperBoundViolation(1.1, 1.0).exception.getMessage}" + } + + "throw an exception for invalid pv param azimuth" in { + val faultyParams = + pvModelGeneration.copy(azimuth = UniformDistribution(-91, 92)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: ${lowerUpperBoundViolation(-91, 92, -90, 90).exception.getMessage}" + } + + "throw an exception for min/max error of pv param azimuth" in { + val faultyParams = + pvModelGeneration.copy(azimuth = UniformDistribution(20, -10)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The azimuth of the plant: ${faultyParams.azimuth} isn't valid. Exception: The minimum value: 20.0 exceeds the maximum value: -10.0" + } + + "throw an exception for invalid pv param etaConv" in { + val faultyParams = pvModelGeneration.copy(etaConv = Fixed(101)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The efficiency of the plants inverter: ${faultyParams.azimuth} isn't valid. Exception: ${upperBoundViolation(101, 100).exception.getMessage}" + } + + "throw an exception for invalid pv param kG" in { + val faultyParams = pvModelGeneration.copy(kG = Fixed(-0.1)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The PV generator correction factor (kG): ${faultyParams.kG} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" + } + + "throw an exception for invalid pv param kT" in { + val faultyParams = pvModelGeneration.copy(kT = Fixed(-0.1)) + val exc = + intercept[ConversionConfigException]( + validatePvModelGenerationParams(faultyParams) + ) + exc.getMessage shouldBe s"The PV temperature correction factor (kT): ${faultyParams.kT} isn't valid. Exception: ${lowerBoundViolation(-0.1, 0).exception.getMessage}" + } + + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala index e6c9d3c8..df6bbd03 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/CoordinateConverterSpec.scala @@ -1,43 +1,43 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.input.NodeInput -import edu.ie3.util.geo.GeoUtils -import org.locationtech.jts.geom.{Coordinate, Point} -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class CoordinateConverterSpec extends Matchers with AnyWordSpecLike { - - "A coordinate converter" should { - - "convert the given values to the proper coordinate" in { - val maybeTestLat = Some(40.415634765229235) - val maybeTestLon = Some(-3.7071948736763316) - val actual = CoordinateConverter.convert(maybeTestLat, maybeTestLon) - val expected = GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint( - new Coordinate(-3.7071948736763316, 40.415634765229235) - ) - actual shouldBe expected - } - - "return the default geo position for default PowerFactory values" in { - val maybeDefaultLat = Some(0.0) - val maybeDefaultLon = Some(0.0) - CoordinateConverter.convert( - maybeDefaultLat, - maybeDefaultLon - ) shouldBe NodeInput.DEFAULT_GEO_POSITION - } - - "return the default geo position when given None" in { - val actual = CoordinateConverter.convert(None, None) - actual shouldBe NodeInput.DEFAULT_GEO_POSITION - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.input.NodeInput +import edu.ie3.util.geo.GeoUtils +import org.locationtech.jts.geom.{Coordinate, Point} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class CoordinateConverterSpec extends Matchers with AnyWordSpecLike { + + "A coordinate converter" should { + + "convert the given values to the proper coordinate" in { + val maybeTestLat = Some(40.415634765229235) + val maybeTestLon = Some(-3.7071948736763316) + val actual = CoordinateConverter.convert(maybeTestLat, maybeTestLon) + val expected = GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint( + new Coordinate(-3.7071948736763316, 40.415634765229235) + ) + actual shouldBe expected + } + + "return the default geo position for default PowerFactory values" in { + val maybeDefaultLat = Some(0.0) + val maybeDefaultLon = Some(0.0) + CoordinateConverter.convert( + maybeDefaultLat, + maybeDefaultLon + ) shouldBe NodeInput.DEFAULT_GEO_POSITION + } + + "return the default geo position when given None" in { + val actual = CoordinateConverter.convert(None, None) + actual shouldBe NodeInput.DEFAULT_GEO_POSITION + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala index a55eb75f..748b9758 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/GridGraphBuilderSpec.scala @@ -1,75 +1,75 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ - subnet1Ids, - subnet2Ids, - subnet3Ids, - subnet4Ids, - testGrid -} -import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException -import org.jgrapht.alg.connectivity.BiconnectivityInspector -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import scala.jdk.CollectionConverters._ - -class GridGraphBuilderSpec extends Matchers with AnyWordSpecLike { - - "The GridGraphBuilder" should { - - val gridGraph = GridGraphBuilder.build( - testGrid.nodes, - testGrid.lines ++ testGrid.switches - ) - val inspect = new BiconnectivityInspector(gridGraph) - val vertexSets = inspect.getConnectedComponents.asScala - .map(graph => graph.vertexSet()) - - "add the correct number of nodes to the gridGraph" in { - gridGraph.vertexSet().size shouldBe testGrid.nodes.size - } - - "add the correct number of edges to the gridGraph" in { - gridGraph - .edgeSet() - .size shouldBe (testGrid.lines ++ testGrid.switches).size - } - - "generate the correct number of subnets" in { - inspect.getConnectedComponents.size shouldBe 5 - } - - "aggregate all nodes of subnet 1 in one of the subgraphs" in { - vertexSets.contains(subnet1Ids.asJava) shouldBe true - } - - "aggregate all nodes of subnet 2 in one of the subgraphs" in { - vertexSets.contains(subnet2Ids.asJava) shouldBe true - } - - "aggregate all nodes of subnet 3 in one of the subgraphs" in { - vertexSets.contains(subnet3Ids.asJava) shouldBe true - } - - "aggregate all nodes of subnet 4 in one of the subgraphs" in { - vertexSets.contains(subnet4Ids.asJava) shouldBe true - } - - "throw an Exception when trying to unpack busses of singly connected edge" in { - - val thrown = intercept[ElementConfigurationException]( - GridGraphBuilder.unpackConnectedBusses("myEdge", Some("bus1"), None) - ) - - thrown.getMessage shouldBe "Exception occurred while adding an edge. " + - "Exc: Edge with id: myEdge is missing at least one connected node" - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ + subnet1Ids, + subnet2Ids, + subnet3Ids, + subnet4Ids, + testGrid +} +import edu.ie3.powerFactory2psdm.exception.pf.ElementConfigurationException +import org.jgrapht.alg.connectivity.BiconnectivityInspector +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import scala.jdk.CollectionConverters._ + +class GridGraphBuilderSpec extends Matchers with AnyWordSpecLike { + + "The GridGraphBuilder" should { + + val gridGraph = GridGraphBuilder.build( + testGrid.nodes, + testGrid.lines ++ testGrid.switches + ) + val inspect = new BiconnectivityInspector(gridGraph) + val vertexSets = inspect.getConnectedComponents.asScala + .map(graph => graph.vertexSet()) + + "add the correct number of nodes to the gridGraph" in { + gridGraph.vertexSet().size shouldBe testGrid.nodes.size + } + + "add the correct number of edges to the gridGraph" in { + gridGraph + .edgeSet() + .size shouldBe (testGrid.lines ++ testGrid.switches).size + } + + "generate the correct number of subnets" in { + inspect.getConnectedComponents.size shouldBe 5 + } + + "aggregate all nodes of subnet 1 in one of the subgraphs" in { + vertexSets.contains(subnet1Ids.asJava) shouldBe true + } + + "aggregate all nodes of subnet 2 in one of the subgraphs" in { + vertexSets.contains(subnet2Ids.asJava) shouldBe true + } + + "aggregate all nodes of subnet 3 in one of the subgraphs" in { + vertexSets.contains(subnet3Ids.asJava) shouldBe true + } + + "aggregate all nodes of subnet 4 in one of the subgraphs" in { + vertexSets.contains(subnet4Ids.asJava) shouldBe true + } + + "throw an Exception when trying to unpack busses of singly connected edge" in { + + val thrown = intercept[ElementConfigurationException]( + GridGraphBuilder.unpackConnectedBusses("myEdge", Some("bus1"), None) + ) + + thrown.getMessage shouldBe "Exception occurred while adding an edge. " + + "Exc: Edge with id: myEdge is missing at least one connected node" + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala index b87d1256..7c97542d 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/NodeConverterSpec.scala @@ -1,52 +1,52 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils -import edu.ie3.powerFactory2psdm.common.ConverterTestData.getNodePair -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class NodeConverterSpec extends Matchers with AnyWordSpecLike { - - "The node converter" should { - - "convert a correctly configured pf node to a correctly configured PSDM Node" in { - val conversionPair = getNodePair("someNode") - val input = conversionPair.input - val expected = conversionPair.result - - val actual = NodeConverter.convertNode( - input, - 1, - GermanVoltageLevelUtils.LV - ) - actual.getId shouldBe expected.getId - actual.getVoltLvl shouldBe expected.getVoltLvl - actual.getSubnet shouldBe expected.getSubnet - actual.isSlack shouldBe expected.isSlack - } - - } - - "convert a correctly configured slack pf node to a correctly configured slack PSDM Node" in { - val conversionPair = getNodePair("someSlackNode") - val input = conversionPair.input - val expected = conversionPair.result - - val actual = NodeConverter.convertNode( - input, - 2, - GermanVoltageLevelUtils.LV - ) - actual.getId shouldBe expected.getId - actual.getVoltLvl shouldBe expected.getVoltLvl - actual.getSubnet shouldBe expected.getSubnet - actual.isSlack shouldBe expected.isSlack - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils +import edu.ie3.powerFactory2psdm.common.ConverterTestData.getNodePair +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class NodeConverterSpec extends Matchers with AnyWordSpecLike { + + "The node converter" should { + + "convert a correctly configured pf node to a correctly configured PSDM Node" in { + val conversionPair = getNodePair("someNode") + val input = conversionPair.input + val expected = conversionPair.result + + val actual = NodeConverter.convertNode( + input, + 1, + GermanVoltageLevelUtils.LV + ) + actual.getId shouldBe expected.getId + actual.getVoltLvl shouldBe expected.getVoltLvl + actual.getSubnet shouldBe expected.getSubnet + actual.isSlack shouldBe expected.isSlack + } + + } + + "convert a correctly configured slack pf node to a correctly configured slack PSDM Node" in { + val conversionPair = getNodePair("someSlackNode") + val input = conversionPair.input + val expected = conversionPair.result + + val actual = NodeConverter.convertNode( + input, + 2, + GermanVoltageLevelUtils.LV + ) + actual.getId shouldBe expected.getId + actual.getVoltLvl shouldBe expected.getVoltLvl + actual.getSubnet shouldBe expected.getSubnet + actual.isSlack shouldBe expected.isSlack + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala index 21328eaa..4a2f53f2 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/SubnetBuilderSpec.scala @@ -1,65 +1,65 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ - id2node, - subnet1Ids, - subnet2Ids, - testGrid -} -import edu.ie3.powerFactory2psdm.exception.pf.{ - ElementConfigurationException, - TestException -} -import edu.ie3.powerFactory2psdm.model.entity.Subnet -import org.jgrapht.alg.connectivity.BiconnectivityInspector -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class SubnetBuilderSpec extends Matchers with AnyWordSpecLike { - - "The SubnetBuilder" should { - - val gridGraph = GridGraphBuilder.build( - testGrid.nodes, - testGrid.lines ++ testGrid.switches - ) - val subgraphs = - new BiconnectivityInspector(gridGraph).getConnectedComponents - - "build a subnet for each subgraph" in { - SubnetBuilder - .buildSubnets(gridGraph, id2node) - .size shouldBe subgraphs.size - } - - "throw an exception if at least one of the nodes has a deviating nominal voltage" in { - val nodeId = "Grid.ElmNet\\Bus_0003.ElmTerm" - val node = id2node.getOrElse( - nodeId, - throw TestException(s"No node with id $nodeId in the id2node map") - ) - val faultyNode = node.copy(nominalVoltage = 131.0) - val updatedMap = id2node.updated(nodeId, faultyNode) - intercept[ElementConfigurationException] { - SubnetBuilder.buildSubnet(1, subnet1Ids, updatedMap) - }.getMessage shouldBe (s"There are the following divergences from the nominal voltage 132.0 : HashSet($nodeId -> 131.0)") - } - - "identify the correct voltage level id for the voltage level " in { - val subnet1: Subnet = - SubnetBuilder.buildSubnet(1, subnet1Ids, id2node) - subnet1.voltLvl.getId shouldBe "Hochspannung" - val subnet2: Subnet = - SubnetBuilder.buildSubnet(2, subnet2Ids, id2node) - subnet2.voltLvl.getId shouldBe "Niederspannung" - } - - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.{ + id2node, + subnet1Ids, + subnet2Ids, + testGrid +} +import edu.ie3.powerFactory2psdm.exception.pf.{ + ElementConfigurationException, + TestException +} +import edu.ie3.powerFactory2psdm.model.entity.Subnet +import org.jgrapht.alg.connectivity.BiconnectivityInspector +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class SubnetBuilderSpec extends Matchers with AnyWordSpecLike { + + "The SubnetBuilder" should { + + val gridGraph = GridGraphBuilder.build( + testGrid.nodes, + testGrid.lines ++ testGrid.switches + ) + val subgraphs = + new BiconnectivityInspector(gridGraph).getConnectedComponents + + "build a subnet for each subgraph" in { + SubnetBuilder + .buildSubnets(gridGraph, id2node) + .size shouldBe subgraphs.size + } + + "throw an exception if at least one of the nodes has a deviating nominal voltage" in { + val nodeId = "Grid.ElmNet\\Bus_0003.ElmTerm" + val node = id2node.getOrElse( + nodeId, + throw TestException(s"No node with id $nodeId in the id2node map") + ) + val faultyNode = node.copy(nominalVoltage = 131.0) + val updatedMap = id2node.updated(nodeId, faultyNode) + intercept[ElementConfigurationException] { + SubnetBuilder.buildSubnet(1, subnet1Ids, updatedMap) + }.getMessage shouldBe (s"There are the following divergences from the nominal voltage 132.0 : HashSet($nodeId -> 131.0)") + } + + "identify the correct voltage level id for the voltage level " in { + val subnet1: Subnet = + SubnetBuilder.buildSubnet(1, subnet1Ids, id2node) + subnet1.voltLvl.getId shouldBe "Hochspannung" + val subnet2: Subnet = + SubnetBuilder.buildSubnet(2, subnet2Ids, id2node) + subnet2.voltLvl.getId shouldBe "Niederspannung" + } + + } + +} 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 79815793..2d2bc8ea 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/LineTypeConverterSpec.scala @@ -1,32 +1,32 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.getLineType -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class LineTypeConverterSpec extends Matchers with AnyWordSpecLike { - - "The line type converter" should { - val conversionPair = getLineType("someLineType") - - "convert a line type correctly" in { - val actual = LineTypeConverter.convert(conversionPair.input) - val expected = conversionPair.result - - actual.getB shouldBe expected.getB - actual.getG shouldBe expected.getG - actual.getR shouldBe expected.getR - actual.getX shouldBe expected.getX - actual.getiMax shouldBe expected.getiMax - actual.getvRated shouldBe expected.getvRated - } - - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.getLineType +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class LineTypeConverterSpec extends Matchers with AnyWordSpecLike { + + "The line type converter" should { + val conversionPair = getLineType("someLineType") + + "convert a line type correctly" in { + val actual = LineTypeConverter.convert(conversionPair.input) + val expected = conversionPair.result + + actual.getB shouldBe expected.getB + actual.getG shouldBe expected.getG + actual.getR shouldBe expected.getR + actual.getX shouldBe expected.getX + actual.getiMax shouldBe expected.getiMax + actual.getvRated shouldBe expected.getvRated + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala index 46366f01..4d681ad5 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/converter/types/TransformerType2WConverterSpec.scala @@ -1,67 +1,67 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.converter.types - -import edu.ie3.powerFactory2psdm.common.ConverterTestData.getTransformer2wType -import edu.ie3.powerFactory2psdm.exception.pf.ConversionException -import edu.ie3.scalatest.QuantityMatchers -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import math.abs - -class TransformerType2WConverterSpec - extends Matchers - with AnyWordSpecLike - with QuantityMatchers { - - "A Transformer2wTypeConverter" should { - val conversionPair = getTransformer2wType("SomeTrafo2wType") - val input = conversionPair.input - val expected = conversionPair.result - implicit val quantityTolerance: Double = 1e-6 - - "convert a transformer type correctly" in { - val actual = TransformerType2WConverter.convert(input) - - actual.getId shouldBe expected.getId - - actual.getvRatedA should equalWithTolerance(expected.getvRatedA) - actual.getvRatedB should equalWithTolerance(expected.getvRatedB) - actual.getdV should equalWithTolerance(expected.getdV) - actual.getdPhi should equalWithTolerance(expected.getdPhi) - - actual.getrSc should equalWithTolerance(expected.getrSc) - actual.getxSc should equalWithTolerance(expected.getxSc) - actual.getgM should equalWithTolerance(expected.getgM) - - actual.isTapSide shouldBe expected.isTapSide - actual.getTapNeutr shouldBe expected.getTapNeutr - actual.getTapMin shouldBe expected.getTapMin - actual.getTapMax shouldBe expected.getTapMax - } - - "throw an exception, when the input model does not allow to calculate short circuit parameters correctly" in { - val invalidInput = input.copy(pCu = 3000) - val thrown = intercept[ConversionException]( - TransformerType2WConverter.convert(invalidInput) - ) - thrown.getMessage shouldBe s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + - s"parameters. The short circuit resistance can't exceed the short circuit impedance." - } - - "throw an exception, when the input model does not allow to calculate no load circuit parameters correctly" in { - val invalidInput = input.copy(pFe = 1000) - val thrown = intercept[ConversionException]( - TransformerType2WConverter.convert(invalidInput) - ) - thrown.getMessage shouldBe s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + - s"The no load conductance can't exceed the no load admittance." - } - - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.converter.types + +import edu.ie3.powerFactory2psdm.common.ConverterTestData.getTransformer2wType +import edu.ie3.powerFactory2psdm.exception.pf.ConversionException +import edu.ie3.scalatest.QuantityMatchers +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import math.abs + +class TransformerType2WConverterSpec + extends Matchers + with AnyWordSpecLike + with QuantityMatchers { + + "A Transformer2wTypeConverter" should { + val conversionPair = getTransformer2wType("SomeTrafo2wType") + val input = conversionPair.input + val expected = conversionPair.result + implicit val quantityTolerance: Double = 1e-6 + + "convert a transformer type correctly" in { + val actual = TransformerType2WConverter.convert(input) + + actual.getId shouldBe expected.getId + + actual.getvRatedA should equalWithTolerance(expected.getvRatedA) + actual.getvRatedB should equalWithTolerance(expected.getvRatedB) + actual.getdV should equalWithTolerance(expected.getdV) + actual.getdPhi should equalWithTolerance(expected.getdPhi) + + actual.getrSc should equalWithTolerance(expected.getrSc) + actual.getxSc should equalWithTolerance(expected.getxSc) + actual.getgM should equalWithTolerance(expected.getgM) + + actual.isTapSide shouldBe expected.isTapSide + actual.getTapNeutr shouldBe expected.getTapNeutr + actual.getTapMin shouldBe expected.getTapMin + actual.getTapMax shouldBe expected.getTapMax + } + + "throw an exception, when the input model does not allow to calculate short circuit parameters correctly" in { + val invalidInput = input.copy(pCu = 3000) + val thrown = intercept[ConversionException]( + TransformerType2WConverter.convert(invalidInput) + ) + thrown.getMessage shouldBe s"Short circuit experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty " + + s"parameters. The short circuit resistance can't exceed the short circuit impedance." + } + + "throw an exception, when the input model does not allow to calculate no load circuit parameters correctly" in { + val invalidInput = input.copy(pFe = 1000) + val thrown = intercept[ConversionException]( + TransformerType2WConverter.convert(invalidInput) + ) + thrown.getMessage shouldBe s"No load experiment calculations of 2w transformer type: ${input.id} is not possible due to faulty parameters." + + s"The no load conductance can't exceed the no load admittance." + } + + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala index 589ed2e3..49d58472 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/PreprocessedPfGridModelSpec.scala @@ -1,94 +1,94 @@ -/* - * © 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 - -import edu.ie3.powerFactory2psdm.exception.pf.{ - ConversionException, - MissingParameterException -} -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ProjectSettings -import edu.ie3.powerFactory2psdm.model.setting.ConversionPrefixes -import org.scalatest.PrivateMethodTester -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class PreprocessedPfGridModelSpec - extends Matchers - with AnyWordSpecLike - with PrivateMethodTester { - - "A GridModel" should { - val settings = ProjectSettings(Some(0), Some("k"), Some("k")) - - "extract project settings" in { - val extractProjectSettings = - PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) - val extractedSettings = - PreprocessedPfGridModel invokePrivate extractProjectSettings( - Some(List(settings)) - ) - extractedSettings shouldBe settings - } - - "throw an exception if there are multiple project settings" in { - val extractProjectSettings = - PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) - val exc = intercept[ConversionException]( - PreprocessedPfGridModel invokePrivate extractProjectSettings( - Some(List(settings, settings)) - ) - ) - exc.getMessage shouldBe "There are multiple project settings defined." - } - - "throw an exception if there are no project settings" in { - val extractProjectSettings = - PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) - val exc = intercept[ConversionException]( - PreprocessedPfGridModel invokePrivate extractProjectSettings( - None - ) - ) - exc.getMessage shouldBe "There are no project settings defined." - } - - "build conversion prefixes" in { - val buildConversionPrefixes = - PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) - val conversionPrefixes = - PreprocessedPfGridModel invokePrivate buildConversionPrefixes( - settings - ) - conversionPrefixes.loadPQSPrefixValue shouldBe 1e3 - conversionPrefixes.lineLengthPrefixValue shouldBe 1e3 - } - - "throw an exception when the pqsPrefix is missing" in { - val buildConversionPrefixes = - PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) - val exc = intercept[MissingParameterException]( - PreprocessedPfGridModel invokePrivate buildConversionPrefixes( - settings.copy(prefixPQS = None) - ) - ) - exc.getMessage shouldBe "The projects settings miss the prefix specification for active/reactive/apparent power values" - } - - "throw an exception when the prefixLength is missing" in { - val buildConversionPrefixes = - PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) - val exc = intercept[MissingParameterException]( - PreprocessedPfGridModel invokePrivate buildConversionPrefixes( - settings.copy(prefixLength = None) - ) - ) - exc.getMessage shouldBe "The project settings miss the prefix specification for line length." - } - - } - -} +/* + * © 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 + +import edu.ie3.powerFactory2psdm.exception.pf.{ + ConversionException, + MissingParameterException +} +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.ProjectSettings +import edu.ie3.powerFactory2psdm.model.setting.ConversionPrefixes +import org.scalatest.PrivateMethodTester +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class PreprocessedPfGridModelSpec + extends Matchers + with AnyWordSpecLike + with PrivateMethodTester { + + "A GridModel" should { + val settings = ProjectSettings(Some(0), Some("k"), Some("k")) + + "extract project settings" in { + val extractProjectSettings = + PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) + val extractedSettings = + PreprocessedPfGridModel invokePrivate extractProjectSettings( + Some(List(settings)) + ) + extractedSettings shouldBe settings + } + + "throw an exception if there are multiple project settings" in { + val extractProjectSettings = + PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) + val exc = intercept[ConversionException]( + PreprocessedPfGridModel invokePrivate extractProjectSettings( + Some(List(settings, settings)) + ) + ) + exc.getMessage shouldBe "There are multiple project settings defined." + } + + "throw an exception if there are no project settings" in { + val extractProjectSettings = + PrivateMethod[ProjectSettings](Symbol("extractProjectSettings")) + val exc = intercept[ConversionException]( + PreprocessedPfGridModel invokePrivate extractProjectSettings( + None + ) + ) + exc.getMessage shouldBe "There are no project settings defined." + } + + "build conversion prefixes" in { + val buildConversionPrefixes = + PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) + val conversionPrefixes = + PreprocessedPfGridModel invokePrivate buildConversionPrefixes( + settings + ) + conversionPrefixes.loadPQSPrefixValue shouldBe 1e3 + conversionPrefixes.lineLengthPrefixValue shouldBe 1e3 + } + + "throw an exception when the pqsPrefix is missing" in { + val buildConversionPrefixes = + PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) + val exc = intercept[MissingParameterException]( + PreprocessedPfGridModel invokePrivate buildConversionPrefixes( + settings.copy(prefixPQS = None) + ) + ) + exc.getMessage shouldBe "The projects settings miss the prefix specification for active/reactive/apparent power values" + } + + "throw an exception when the prefixLength is missing" in { + val buildConversionPrefixes = + PrivateMethod[ConversionPrefixes](Symbol("buildConversionPrefixes")) + val exc = intercept[MissingParameterException]( + PreprocessedPfGridModel invokePrivate buildConversionPrefixes( + settings.copy(prefixLength = None) + ) + ) + exc.getMessage shouldBe "The project settings miss the prefix specification for line length." + } + + } + +} 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 06012200..ff845ef9 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/LineSpec.scala @@ -1,54 +1,54 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class LineSpec extends Matchers with AnyWordSpecLike { - "A Line " should { - - val id = "SomeLine.ElmLne" - - val input = Lines( - id = Some(id), - bus1Id = Some("SomeBusA"), - bus2Id = Some("SomeBusB") - ) - - "throw an exception if the id is missing" in { - val line = input.copy(id = None) - val exc = intercept[MissingParameterException](Line.build(line)) - 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)) - 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)) - exc.getMessage shouldBe s"Line: $id has no defined node b" - } - - "build a fully configured line correctly" in { - val line = Line.build(input) - line.id shouldBe "SomeLine.ElmLne" - line.nodeAId shouldBe "SomeBusA" - line.nodeBId shouldBe "SomeBusB" - } - - } - -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Lines +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class LineSpec extends Matchers with AnyWordSpecLike { + "A Line " should { + + val id = "SomeLine.ElmLne" + + val input = Lines( + id = Some(id), + bus1Id = Some("SomeBusA"), + bus2Id = Some("SomeBusB") + ) + + "throw an exception if the id is missing" in { + val line = input.copy(id = None) + val exc = intercept[MissingParameterException](Line.build(line)) + 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)) + 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)) + exc.getMessage shouldBe s"Line: $id has no defined node b" + } + + "build a fully configured line correctly" in { + val line = Line.build(input) + line.id shouldBe "SomeLine.ElmLne" + line.nodeAId shouldBe "SomeBusA" + line.nodeBId shouldBe "SomeBusB" + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala index eb079313..a9b59199 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/NodeSpec.scala @@ -1,75 +1,75 @@ -/* - * © 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.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ConElms, Nodes} -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class NodeSpec extends Matchers with AnyWordSpecLike { - "A Node " should { - - val conElm = ConElms( - Some( - "SomeLine.ElmLne" - ), - Some( - "ElmLne" - ) - ) - - val id = "SomeBus.ElmTerm" - - val input = Nodes( - id = Some(id), - vtarget = Some(1.0), - uknom = Some(132.0), - conElms = Some(List(Some(conElm))), - GPSlat = Some(1.0), - GPSlon = Some(1.0) - ) - - "throw an exception if the id is missing" in { - val node = input.copy(id = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"There is no id for node $node" - } - - "throw an exception if the nominal voltage is missing" in { - val id = "BrokenBus1.ElmTerm" - val node = input.copy(id = Some(id), uknom = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"Node: $id has no defined nominal voltage" - } - - "throw an exception if the target voltage is missing" in { - val id = "BrokenBus2.ElmTerm" - val node = input.copy(id = Some(id), vtarget = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"Node: $id has no defined target voltage" - } - - "throw an exception if there is no connected element" in { - val id = "BrokenBus3.ElmTerm" - val node = input.copy(id = Some(id), conElms = None) - val exc = intercept[MissingParameterException](Node.build(node)) - exc.getMessage shouldBe s"Node: $id has no connected elements" - } - - "build a fully configured node correctly" in { - val node = Node.build(input) - node.id shouldBe "SomeBus.ElmTerm" - node.vTarget shouldBe 1.0 - node.nominalVoltage shouldBe 132.0 - node.lat shouldBe Some(1.0) - node.lon shouldBe Some(1.0) - } - - } - -} +/* + * © 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.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.{ConElms, Nodes} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class NodeSpec extends Matchers with AnyWordSpecLike { + "A Node " should { + + val conElm = ConElms( + Some( + "SomeLine.ElmLne" + ), + Some( + "ElmLne" + ) + ) + + val id = "SomeBus.ElmTerm" + + val input = Nodes( + id = Some(id), + vtarget = Some(1.0), + uknom = Some(132.0), + conElms = Some(List(Some(conElm))), + GPSlat = Some(1.0), + GPSlon = Some(1.0) + ) + + "throw an exception if the id is missing" in { + val node = input.copy(id = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"There is no id for node $node" + } + + "throw an exception if the nominal voltage is missing" in { + val id = "BrokenBus1.ElmTerm" + val node = input.copy(id = Some(id), uknom = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"Node: $id has no defined nominal voltage" + } + + "throw an exception if the target voltage is missing" in { + val id = "BrokenBus2.ElmTerm" + val node = input.copy(id = Some(id), vtarget = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"Node: $id has no defined target voltage" + } + + "throw an exception if there is no connected element" in { + val id = "BrokenBus3.ElmTerm" + val node = input.copy(id = Some(id), conElms = None) + val exc = intercept[MissingParameterException](Node.build(node)) + exc.getMessage shouldBe s"Node: $id has no connected elements" + } + + "build a fully configured node correctly" in { + val node = Node.build(input) + node.id shouldBe "SomeBus.ElmTerm" + node.vTarget shouldBe 1.0 + node.nominalVoltage shouldBe 132.0 + node.lat shouldBe Some(1.0) + node.lon shouldBe Some(1.0) + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala index a81c9e7d..8f7bed41 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/entity/SwitchSpec.scala @@ -1,57 +1,57 @@ -/* - * © 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.{ - MissingParameterException, - TestException -} -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class SwitchSpec extends Matchers with AnyWordSpecLike { - "A switch " should { - - val id = "Some_Switch.ElmCoup" - - val input = Switches( - id = Some(id), - bus1Id = Some("SomeBusA"), - bus2Id = Some("SomeBusB") - ) - - "throw an exception when building if the id is missing" in { - val switch = input.copy(id = None) - val exc = intercept[MissingParameterException](Switch.maybeBuild(switch)) - exc.getMessage shouldBe s"There is no id for switch $switch" - } - - "return None when building if the bus1Id is missing" in { - val id = "Broken_Switch1.ElmLne" - val switch = Switch.maybeBuild(input.copy(id = Some(id), bus1Id = None)) - switch shouldBe None - } - - "return None when building if the bus2Id is missing" in { - val id = "Broken_Switch2.ElmLne" - val switch = Switch.maybeBuild(input.copy(id = Some(id), bus2Id = None)) - switch shouldBe None - } - - "build a fully configured switch correctly" in { - val switch = Switch - .maybeBuild(input) - .getOrElse(throw TestException("We shouldn't get None here!")) - switch.id shouldBe "Some_Switch.ElmCoup" - switch.nodeAId shouldBe "SomeBusA" - switch.nodeBId shouldBe "SomeBusB" - } - - } - -} +/* + * © 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.{ + MissingParameterException, + TestException +} +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class SwitchSpec extends Matchers with AnyWordSpecLike { + "A switch " should { + + val id = "Some_Switch.ElmCoup" + + val input = Switches( + id = Some(id), + bus1Id = Some("SomeBusA"), + bus2Id = Some("SomeBusB") + ) + + "throw an exception when building if the id is missing" in { + val switch = input.copy(id = None) + val exc = intercept[MissingParameterException](Switch.maybeBuild(switch)) + exc.getMessage shouldBe s"There is no id for switch $switch" + } + + "return None when building if the bus1Id is missing" in { + val id = "Broken_Switch1.ElmLne" + val switch = Switch.maybeBuild(input.copy(id = Some(id), bus1Id = None)) + switch shouldBe None + } + + "return None when building if the bus2Id is missing" in { + val id = "Broken_Switch2.ElmLne" + val switch = Switch.maybeBuild(input.copy(id = Some(id), bus2Id = None)) + switch shouldBe None + } + + "build a fully configured switch correctly" in { + val switch = Switch + .maybeBuild(input) + .getOrElse(throw TestException("We shouldn't get None here!")) + switch.id shouldBe "Some_Switch.ElmCoup" + switch.nodeAId shouldBe "SomeBusA" + switch.nodeBId shouldBe "SomeBusB" + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala index 0d2af587..24e151d3 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/LineTypeSpec.scala @@ -1,75 +1,75 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes -import edu.ie3.powerFactory2psdm.model.entity.types.LineType -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class LineTypeSpec extends Matchers with AnyWordSpecLike { - "A line type" should { - - val input = LineTypes( - id = Some("testLineType"), - uline = Some(132.0), - sline = Some(1.0), - rline = Some(6.753542423248291), - xline = Some(20.61956214904785), - bline = Some(151.51515197753906), - gline = Some(1.543) - ) - "throw an exception when building if the id is missing" in { - val lineType = input.copy(id = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no id for line type $lineType" - } - - "throw an exception when building if no rated voltage is defined" in { - val id = "BrokenLineType1" - val lineType = input.copy(id = Some(id), uline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no rated voltage defined for line type: $id" - } - - "throw an exception when building no thermal current is defined" in { - val id = "BrokenLineType2" - val lineType = input.copy(id = Some(id), sline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no maximum thermal current defined for line type: $id" - } - - "throw an exception when building if no specific resistance is defined" in { - val id = "BrokenLineType3" - val lineType = input.copy(id = Some(id), rline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no specific resistance defined for line type: $id" - } - - "throw an exception when building if no specific reactance is defined" in { - val id = "BrokenLineType4" - val lineType = input.copy(id = Some(id), xline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no specific reactance defined for line type: $id" - } - - "throw an exception when building if no phase-to-ground conductance is defined" in { - val id = "BrokenLineType5" - val lineType = input.copy(id = Some(id), bline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no phase-to-ground conductance defined for line type: $id" - } - - "throw an exception when building if no phase-to-ground susceptance is defined" in { - val id = "BrokenLineType6" - val lineType = input.copy(id = Some(id), gline = None) - val exc = intercept[MissingParameterException](LineType.build(lineType)) - exc.getMessage shouldBe s"There is no phase-to-ground susceptance defined for line type: $id" - } - } -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.LineTypes +import edu.ie3.powerFactory2psdm.model.entity.types.LineType +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class LineTypeSpec extends Matchers with AnyWordSpecLike { + "A line type" should { + + val input = LineTypes( + id = Some("testLineType"), + uline = Some(132.0), + sline = Some(1.0), + rline = Some(6.753542423248291), + xline = Some(20.61956214904785), + bline = Some(151.51515197753906), + gline = Some(1.543) + ) + "throw an exception when building if the id is missing" in { + val lineType = input.copy(id = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no id for line type $lineType" + } + + "throw an exception when building if no rated voltage is defined" in { + val id = "BrokenLineType1" + val lineType = input.copy(id = Some(id), uline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no rated voltage defined for line type: $id" + } + + "throw an exception when building no thermal current is defined" in { + val id = "BrokenLineType2" + val lineType = input.copy(id = Some(id), sline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no maximum thermal current defined for line type: $id" + } + + "throw an exception when building if no specific resistance is defined" in { + val id = "BrokenLineType3" + val lineType = input.copy(id = Some(id), rline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no specific resistance defined for line type: $id" + } + + "throw an exception when building if no specific reactance is defined" in { + val id = "BrokenLineType4" + val lineType = input.copy(id = Some(id), xline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no specific reactance defined for line type: $id" + } + + "throw an exception when building if no phase-to-ground conductance is defined" in { + val id = "BrokenLineType5" + val lineType = input.copy(id = Some(id), bline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no phase-to-ground conductance defined for line type: $id" + } + + "throw an exception when building if no phase-to-ground susceptance is defined" in { + val id = "BrokenLineType6" + val lineType = input.copy(id = Some(id), gline = None) + val exc = intercept[MissingParameterException](LineType.build(lineType)) + exc.getMessage shouldBe s"There is no phase-to-ground susceptance defined for line type: $id" + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala index b5bec997..fc289b41 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/model/types/TransformerType2WSpec.scala @@ -1,170 +1,170 @@ -/* - * © 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.types - -import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w -import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class TransformerType2WSpec extends Matchers with AnyWordSpecLike { - - "A Transformer2wTypeSpec" should { - - val id = "Some2wTransformerType" - - val input = TrafoTypes2w( - id = Some(id), - strn = Some(40), - utrn_h = Some(10), - utrn_l = Some(0.4), - dutap = Some(1.2), - phitr = Some(12), - tap_side = Some(0), - nntap0 = Some(-1), - ntpmn = Some(-3), - ntpmx = Some(5), - uktr = Some(5.1345), - curmg = Some(1), - pfe = Some(10), - pcutr = Some(13) - ) - - "throw an exception when trying to build a TransformerType without an id" in { - val faulty1 = input.copy(id = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty1) - ) - exc.getMessage shouldBe s"There is no id for transformer-type: $faulty1" - } - - "throw an exception when trying to build a TransformerType without parameter strn" in { - val faulty2 = input.copy(strn = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty2) - ) - exc.getMessage shouldBe s"There is no rated apparent power for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter utrn_h" in { - val faulty3 = input.copy(utrn_h = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty3) - ) - exc.getMessage shouldBe s"There is no voltage of high winding side for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter utrn_l" in { - val faulty4 = input.copy(utrn_l = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty4) - ) - exc.getMessage shouldBe s"There is no voltage of low winding side for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter dutap" in { - val faulty5 = input.copy(dutap = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty5) - ) - exc.getMessage shouldBe s"There is no voltage magnitude deviation per tap position for transfomer type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter dV" in { - val faulty6 = input.copy(phitr = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty6) - ) - exc.getMessage shouldBe s"There is no voltage angle deviation per tap position for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter tap_side" in { - val faulty7 = input.copy(tap_side = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty7) - ) - exc.getMessage shouldBe s"There is no selection of winding where tap changer is installed for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter nntap0" in { - val faulty8 = input.copy(nntap0 = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty8) - ) - exc.getMessage shouldBe s"There is no neutral tap position defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter ntpmn" in { - val faulty9 = input.copy(ntpmn = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty9) - ) - exc.getMessage shouldBe s"There is no minmum tap position defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter ntpmx" in { - val faulty10 = input.copy(ntpmx = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty10) - ) - exc.getMessage shouldBe s"There is no maximum tap position defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter uktr" in { - val faulty11 = input.copy(uktr = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty11) - ) - exc.getMessage shouldBe s"There is no short circuit voltage defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter curmg" in { - val faulty12 = input.copy(curmg = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty12) - ) - exc.getMessage shouldBe s"There is no no load current defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter pfe" in { - val faulty13 = input.copy(pfe = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty13) - ) - exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" - } - - "throw an exception when trying to build a TransformerType without parameter pcutr" in { - val faulty13 = input.copy(pcutr = None) - val exc = intercept[MissingParameterException]( - TransformerType2W.build(faulty13) - ) - exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" - } - - "build a correctly configured TransformerType2w" in { - val actual = TransformerType2W.build(input) - actual.id shouldBe "Some2wTransformerType" - actual.sRated shouldBe 40 - actual.vRatedA shouldBe 10 - actual.vRatedB shouldBe 0.4 - actual.dV shouldBe 1.2 - actual.dPhi shouldBe 12 - actual.tapSide shouldBe 0 - actual.tapNeutr shouldBe -1 - actual.tapMin shouldBe -3 - actual.tapMax shouldBe 5 - actual.uk shouldBe 5.1345 - actual.iNoLoad shouldBe 1 - actual.pFe shouldBe 10 - actual.pCu shouldBe 13 - } - - } - -} +/* + * © 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.types + +import edu.ie3.powerFactory2psdm.exception.pf.MissingParameterException +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.TrafoTypes2w +import edu.ie3.powerFactory2psdm.model.entity.types.TransformerType2W +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class TransformerType2WSpec extends Matchers with AnyWordSpecLike { + + "A Transformer2wTypeSpec" should { + + val id = "Some2wTransformerType" + + val input = TrafoTypes2w( + id = Some(id), + strn = Some(40), + utrn_h = Some(10), + utrn_l = Some(0.4), + dutap = Some(1.2), + phitr = Some(12), + tap_side = Some(0), + nntap0 = Some(-1), + ntpmn = Some(-3), + ntpmx = Some(5), + uktr = Some(5.1345), + curmg = Some(1), + pfe = Some(10), + pcutr = Some(13) + ) + + "throw an exception when trying to build a TransformerType without an id" in { + val faulty1 = input.copy(id = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty1) + ) + exc.getMessage shouldBe s"There is no id for transformer-type: $faulty1" + } + + "throw an exception when trying to build a TransformerType without parameter strn" in { + val faulty2 = input.copy(strn = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty2) + ) + exc.getMessage shouldBe s"There is no rated apparent power for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter utrn_h" in { + val faulty3 = input.copy(utrn_h = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty3) + ) + exc.getMessage shouldBe s"There is no voltage of high winding side for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter utrn_l" in { + val faulty4 = input.copy(utrn_l = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty4) + ) + exc.getMessage shouldBe s"There is no voltage of low winding side for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter dutap" in { + val faulty5 = input.copy(dutap = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty5) + ) + exc.getMessage shouldBe s"There is no voltage magnitude deviation per tap position for transfomer type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter dV" in { + val faulty6 = input.copy(phitr = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty6) + ) + exc.getMessage shouldBe s"There is no voltage angle deviation per tap position for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter tap_side" in { + val faulty7 = input.copy(tap_side = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty7) + ) + exc.getMessage shouldBe s"There is no selection of winding where tap changer is installed for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter nntap0" in { + val faulty8 = input.copy(nntap0 = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty8) + ) + exc.getMessage shouldBe s"There is no neutral tap position defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter ntpmn" in { + val faulty9 = input.copy(ntpmn = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty9) + ) + exc.getMessage shouldBe s"There is no minmum tap position defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter ntpmx" in { + val faulty10 = input.copy(ntpmx = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty10) + ) + exc.getMessage shouldBe s"There is no maximum tap position defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter uktr" in { + val faulty11 = input.copy(uktr = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty11) + ) + exc.getMessage shouldBe s"There is no short circuit voltage defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter curmg" in { + val faulty12 = input.copy(curmg = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty12) + ) + exc.getMessage shouldBe s"There is no no load current defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter pfe" in { + val faulty13 = input.copy(pfe = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty13) + ) + exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" + } + + "throw an exception when trying to build a TransformerType without parameter pcutr" in { + val faulty13 = input.copy(pcutr = None) + val exc = intercept[MissingParameterException]( + TransformerType2W.build(faulty13) + ) + exc.getMessage shouldBe s"There is no iron loss defined for transformer-type: $id" + } + + "build a correctly configured TransformerType2w" in { + val actual = TransformerType2W.build(input) + actual.id shouldBe "Some2wTransformerType" + actual.sRated shouldBe 40 + actual.vRatedA shouldBe 10 + actual.vRatedB shouldBe 0.4 + actual.dV shouldBe 1.2 + actual.dPhi shouldBe 12 + actual.tapSide shouldBe 0 + actual.tapNeutr shouldBe -1 + actual.tapMin shouldBe -3 + actual.tapMax shouldBe 5 + actual.uk shouldBe 5.1345 + actual.iNoLoad shouldBe 1 + actual.pFe shouldBe 10 + actual.pCu shouldBe 13 + } + + } + +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala index 9dddd124..05747c44 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/util/GridPreparatorSpec.scala @@ -1,45 +1,45 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class GridPreparatorSpec extends Matchers with AnyWordSpecLike { - - "The grid preparator" should { - - "filter out a singly connected switch" in { - val singlyConnectedSwitch = Switches( - Some("singlyConnectedSwitch"), - Some("someNodeId"), - None - ) - val anotherSinglyConnectedSwitch = Switches( - Some("anotherSinglyConnectedSwitch"), - None, - Some("someNodeId") - ) - val fullyConnectedSwitch = Switches( - Some("fullyConnectedSwitch"), - Some("someNodeId"), - Some("aDifferentNodeId") - ) - val filteredSwitches = GridPreparator.removeSinglyConnectedSwitches( - Some( - List( - singlyConnectedSwitch, - anotherSinglyConnectedSwitch, - fullyConnectedSwitch - ) - ) - ) - filteredSwitches shouldEqual Some(List(fullyConnectedSwitch)) - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import edu.ie3.powerFactory2psdm.model.RawPfGridModel.Switches +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class GridPreparatorSpec extends Matchers with AnyWordSpecLike { + + "The grid preparator" should { + + "filter out a singly connected switch" in { + val singlyConnectedSwitch = Switches( + Some("singlyConnectedSwitch"), + Some("someNodeId"), + None + ) + val anotherSinglyConnectedSwitch = Switches( + Some("anotherSinglyConnectedSwitch"), + None, + Some("someNodeId") + ) + val fullyConnectedSwitch = Switches( + Some("fullyConnectedSwitch"), + Some("someNodeId"), + Some("aDifferentNodeId") + ) + val filteredSwitches = GridPreparator.removeSinglyConnectedSwitches( + Some( + List( + singlyConnectedSwitch, + anotherSinglyConnectedSwitch, + fullyConnectedSwitch + ) + ) + ) + filteredSwitches shouldEqual Some(List(fullyConnectedSwitch)) + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala index 9897b7a0..8697be9c 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorIT.scala @@ -1,67 +1,67 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import edu.ie3.powerFactory2psdm.util.SchemaGenerator.run -import edu.ie3.util.io.FileIOUtils -import org.scalatest.BeforeAndAfterAll -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import java.io.{File, PrintWriter} -import scala.io.Source -import scala.reflect.runtime.universe -import scala.util.{Try, Using} - -class SchemaGeneratorIT - extends Matchers - with AnyWordSpecLike - with BeforeAndAfterAll { - - // create test dir - protected val testTmpDir: String = System.getProperty( - "user.dir" - ) + File.separator + "test" + File.separator + "tmp_" + this.getClass.getSimpleName - - override protected def beforeAll(): Unit = new File(testTmpDir).mkdirs() - - override protected def afterAll(): Unit = - FileIOUtils.deleteRecursively(testTmpDir) - - "A SchemaGenerator" should { - - "generate the schema from a test file correctly" in { - - val source = - Source.fromFile( - s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" - ) - val jsonString = - try source.mkString - finally source.close - - val outputFile = new File( - s"$testTmpDir${File.separator}PowerFactoryGrid.scala" - ) - - SchemaGenerator - .run( - jsonString, - "PowerFactoryGrid", - "edu.ie3.powerFactory2psdm.model.powerfactory" - ) - .foreach(formatedClassString => { - val pw = new PrintWriter( - outputFile - ) - pw.write(formatedClassString) - pw.close() - }) - assert(outputFile.exists()) - } - } -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import edu.ie3.powerFactory2psdm.util.SchemaGenerator.run +import edu.ie3.util.io.FileIOUtils +import org.scalatest.BeforeAndAfterAll +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import java.io.{File, PrintWriter} +import scala.io.Source +import scala.reflect.runtime.universe +import scala.util.{Try, Using} + +class SchemaGeneratorIT + extends Matchers + with AnyWordSpecLike + with BeforeAndAfterAll { + + // create test dir + protected val testTmpDir: String = System.getProperty( + "user.dir" + ) + File.separator + "test" + File.separator + "tmp_" + this.getClass.getSimpleName + + override protected def beforeAll(): Unit = new File(testTmpDir).mkdirs() + + override protected def afterAll(): Unit = + FileIOUtils.deleteRecursively(testTmpDir) + + "A SchemaGenerator" should { + + "generate the schema from a test file correctly" in { + + val source = + Source.fromFile( + s"${new File(".").getCanonicalPath}/src/test/resources/pfGrids/exampleGrid.json" + ) + val jsonString = + try source.mkString + finally source.close + + val outputFile = new File( + s"$testTmpDir${File.separator}PowerFactoryGrid.scala" + ) + + SchemaGenerator + .run( + jsonString, + "PowerFactoryGrid", + "edu.ie3.powerFactory2psdm.model.powerfactory" + ) + .foreach(formatedClassString => { + val pw = new PrintWriter( + outputFile + ) + pw.write(formatedClassString) + pw.close() + }) + assert(outputFile.exists()) + } + } +} diff --git a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala index 60a5e597..d191176c 100644 --- a/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala +++ b/src/test/scala/edu/ie3/powerFactory2psdm/util/SchemaGeneratorSpec.scala @@ -1,538 +1,538 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.powerFactory2psdm.util - -import org.scalatest.PrivateMethodTester -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -class SchemaGeneratorSpec - extends Matchers - with AnyWordSpecLike - with PrivateMethodTester { - - private val className = "PowerFactoryGrid" - private val packageName = "edu.ie3.powerFactory2psdm.model.powerfactory" - - private def genCode(json: String): Option[String] = - SchemaGenerator.run(json, className, packageName) - - "A SchemaGenerator" should { - - "not generate code if the json object is empty" in { - - val json = - """ - | {} - |""".stripMargin - - genCode(json) shouldBe None - } - - "generate a valid class for an empty top level collection" in { - - val json = - """ - |{ - | "lines": [] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines() - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for multiple empty top level collections" in { - val json = - """ - |{ - | "lines": [], - | "nodes": [] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ - | Lines, - | Nodes - |} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]], - | nodes: Option[List[Nodes]] - |) - | - |object PowerFactoryGrid { - | - | final case class Nodes() - | - | final case class Lines() - | - |} - |""".stripMargin - ) - } - - "generate a valid class for a top level collection with one simple attribute" in { - - val json = - """ - |{ - | "lines": [ - | { - | "length": 1 - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines(length: Option[Double]) - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for a top level collection with multiple simple attribute" in { - - val json = - """ - |{ - | "lines": [ - | { - | "length": 1, - | "AccessTime": 0.0, - | "GPSlat": 0.0, - | "GPSlon": 0.0, - | "Vtarget": 11.0, - | "cpSubstat": null - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines( - | AccessTime: Option[Double], - | GPSlat: Option[Double], - | GPSlon: Option[Double], - | length: Option[Double], - | cpSubstat: Option[String], - | Vtarget: Option[Double] - | ) - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for a top level collection with nested class fields" in { - - val json = - """ - |{ - | "lines": [ - | { - | "obj": { - | "a": "x", - | "b": "y" - | } - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Obj(a: Option[String], b: Option[String]) - | - | final case class Lines(obj: Option[Obj]) - | - |} - |""".stripMargin - ) - - } - } - - "generate a valid class if we have multiple nested complex fields that are nested in several objects" in { - - val json = - """ - |{ - | "lines": [ - | { - | "conElms": [ - | { - | "loc_name": "Klemmleiste(1)", - | "pfCls": "ElmTerm" - | }, - | { - | "loc_name": "Klemmleiste", - | "pfCls": "ElmTerm" - | } - | ] - | } - | ], - | "trafos3w": [ - | { - | "conElms": [ - | { - | "loc_name": "Klemmleiste OS", - | "pfCls": "ElmTerm" - | }, - | { - | "loc_name": "Klemmleiste MS", - | "pfCls": "ElmTerm" - | }, - | { - | "loc_name": "Klemmleiste MS2", - | "pfCls": "ElmTerm" - | } - | ] - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ - | Lines, - | Trafos3w - |} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]], - | trafos3w: Option[List[Trafos3w]] - |) - | - |object PowerFactoryGrid { - | - | final case class Trafos3w(conElms: Option[List[Option[ConElms]]]) - | - | final case class ConElms(loc_name: Option[String], pfCls: Option[String]) - | - | final case class Lines(conElms: Option[List[Option[ConElms]]]) - | - |} - |""".stripMargin - ) - - } - - "generate a valid class for a complex json" in { - - val json = - """ - |{ - | "nodes": [ - | { - | "AccessTime": 0.0, - | "GPSlat": 0.0, - | "GPSlon": 0.0, - | "Vtarget": 11.0, - | "cpSubstat": null, - | "bar": { - | "a": "asd", - | "b": "ass" - | }, - | "foo": { - | }, - | "cpZone": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | }, - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | } - | ], - | "nestedArray": [ - | [] - | ], - | "loc_name": "Klemmleiste MS2", - | "root_id": null, - | "uknom": 11.0, - | "vmax": 1.0499999523162842, - | "vmin": 0.0, - | "vtarget": 1.0 - | }, - | { - | "AccessTime": 0.0, - | "GPSlat": 0.0, - | "GPSlon": 0.0, - | "Vtarget": 11.0, - | "cpSubstat": null, - | "cpZone": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | }, - | { - | "loc_name": "myZone", - | "additionalParam": "myParam", - | "leet": [ - | { - | "loc_name": "myZone", - | "additionalParam": "myParam" - | } - | ] - | } - | ], - | "nestedArray": [ - | [] - | ], - | "loc_name": "Klemmleiste MS2", - | "root_id": null, - | "uknom": 11.0, - | "vmax": 1.0499999523162842, - | "vmin": 0.0, - | "vtarget": 1.0 - | } - | ], - | "lines": [ - | { - | "length": 1 - | } - | ] - |} - |""".stripMargin - - genCode(json) shouldBe Some( - """|package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ - | Nodes, - | Lines - |} - | - |final case class PowerFactoryGrid( - | nodes: Option[List[Nodes]], - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class CpZone( - | loc_name: Option[String], - | additionalParam: Option[String], - | leet: Option[List[Option[Leet]]] - | ) - | - | final case class Nodes( - | loc_name: Option[String], - | root_id: Option[String], - | AccessTime: Option[Double], - | GPSlon: Option[Double], - | nestedArray: Option[List[Option[List[Option[String]]]]], - | cpZone: Option[List[Option[CpZone]]], - | vmin: Option[Double], - | GPSlat: Option[Double], - | foo: Option[Foo], - | cpSubstat: Option[String], - | Vtarget: Option[Double], - | uknom: Option[Double], - | bar: Option[Bar], - | vtarget: Option[Double], - | vmax: Option[Double] - | ) - | - | final case class Leet( - | loc_name: Option[String], - | additionalParam: Option[String] - | ) - | - | final case class Bar(a: Option[String], b: Option[String]) - | - | final case class Lines(length: Option[Double]) - | - | final case class Foo() - | - |} - |""".stripMargin - ) - - } - - "generate valid GPSCoords for simple case" in { - val json = - """ - |{ - | "lines": [ - | { - | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", - | "GPScoords": [ - | [ - | 51.4843281, - | 7.4116482 - | ], - | [ - | 52.2895, - | 12.8273 - | ], - | [ - | 52.4895, - | 13.8273 - | ] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" - | } - | ] - |}""".stripMargin - - genCode(json) shouldBe Some( - """package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines( - | id: Option[String], - | GPScoords: Option[List[Option[List[Option[Double]]]]], - | bus1Id: Option[String], - | bus2Id: Option[String] - | ) - | - |} - |""".stripMargin - ) - } - - "generate valid GPSCoords if field values differ" in { - val json = - """ - |{ - | "lines": [ - | { - | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", - | "GPScoords": [ - | [] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" - | }, - | { - | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", - | "GPScoords": [ - | [ - | 51.4843281, - | 7.4116482 - | ], - | [ - | 52.2895, - | 12.8273 - | ], - | [ - | 52.4895, - | 13.8273 - | ] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" - | }, - | { - | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", - | "GPScoords": [ - | [] - | ], - | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", - | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" - | } - | ] - |}""".stripMargin - - genCode(json) shouldBe Some( - """package edu.ie3.powerFactory2psdm.model.powerfactory - |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} - | - |final case class PowerFactoryGrid( - | lines: Option[List[Lines]] - |) - | - |object PowerFactoryGrid { - | - | final case class Lines( - | id: Option[String], - | GPScoords: Option[List[Option[List[Option[Double]]]]], - | bus1Id: Option[String], - | bus2Id: Option[String] - | ) - | - |} - |""".stripMargin - ) - } - -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.powerFactory2psdm.util + +import org.scalatest.PrivateMethodTester +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +class SchemaGeneratorSpec + extends Matchers + with AnyWordSpecLike + with PrivateMethodTester { + + private val className = "PowerFactoryGrid" + private val packageName = "edu.ie3.powerFactory2psdm.model.powerfactory" + + private def genCode(json: String): Option[String] = + SchemaGenerator.run(json, className, packageName) + + "A SchemaGenerator" should { + + "not generate code if the json object is empty" in { + + val json = + """ + | {} + |""".stripMargin + + genCode(json) shouldBe None + } + + "generate a valid class for an empty top level collection" in { + + val json = + """ + |{ + | "lines": [] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines() + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for multiple empty top level collections" in { + val json = + """ + |{ + | "lines": [], + | "nodes": [] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ + | Lines, + | Nodes + |} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]], + | nodes: Option[List[Nodes]] + |) + | + |object PowerFactoryGrid { + | + | final case class Nodes() + | + | final case class Lines() + | + |} + |""".stripMargin + ) + } + + "generate a valid class for a top level collection with one simple attribute" in { + + val json = + """ + |{ + | "lines": [ + | { + | "length": 1 + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines(length: Option[Double]) + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for a top level collection with multiple simple attribute" in { + + val json = + """ + |{ + | "lines": [ + | { + | "length": 1, + | "AccessTime": 0.0, + | "GPSlat": 0.0, + | "GPSlon": 0.0, + | "Vtarget": 11.0, + | "cpSubstat": null + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines( + | AccessTime: Option[Double], + | GPSlat: Option[Double], + | GPSlon: Option[Double], + | length: Option[Double], + | cpSubstat: Option[String], + | Vtarget: Option[Double] + | ) + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for a top level collection with nested class fields" in { + + val json = + """ + |{ + | "lines": [ + | { + | "obj": { + | "a": "x", + | "b": "y" + | } + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Obj(a: Option[String], b: Option[String]) + | + | final case class Lines(obj: Option[Obj]) + | + |} + |""".stripMargin + ) + + } + } + + "generate a valid class if we have multiple nested complex fields that are nested in several objects" in { + + val json = + """ + |{ + | "lines": [ + | { + | "conElms": [ + | { + | "loc_name": "Klemmleiste(1)", + | "pfCls": "ElmTerm" + | }, + | { + | "loc_name": "Klemmleiste", + | "pfCls": "ElmTerm" + | } + | ] + | } + | ], + | "trafos3w": [ + | { + | "conElms": [ + | { + | "loc_name": "Klemmleiste OS", + | "pfCls": "ElmTerm" + | }, + | { + | "loc_name": "Klemmleiste MS", + | "pfCls": "ElmTerm" + | }, + | { + | "loc_name": "Klemmleiste MS2", + | "pfCls": "ElmTerm" + | } + | ] + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ + | Lines, + | Trafos3w + |} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]], + | trafos3w: Option[List[Trafos3w]] + |) + | + |object PowerFactoryGrid { + | + | final case class Trafos3w(conElms: Option[List[Option[ConElms]]]) + | + | final case class ConElms(loc_name: Option[String], pfCls: Option[String]) + | + | final case class Lines(conElms: Option[List[Option[ConElms]]]) + | + |} + |""".stripMargin + ) + + } + + "generate a valid class for a complex json" in { + + val json = + """ + |{ + | "nodes": [ + | { + | "AccessTime": 0.0, + | "GPSlat": 0.0, + | "GPSlon": 0.0, + | "Vtarget": 11.0, + | "cpSubstat": null, + | "bar": { + | "a": "asd", + | "b": "ass" + | }, + | "foo": { + | }, + | "cpZone": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | }, + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | } + | ], + | "nestedArray": [ + | [] + | ], + | "loc_name": "Klemmleiste MS2", + | "root_id": null, + | "uknom": 11.0, + | "vmax": 1.0499999523162842, + | "vmin": 0.0, + | "vtarget": 1.0 + | }, + | { + | "AccessTime": 0.0, + | "GPSlat": 0.0, + | "GPSlon": 0.0, + | "Vtarget": 11.0, + | "cpSubstat": null, + | "cpZone": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | }, + | { + | "loc_name": "myZone", + | "additionalParam": "myParam", + | "leet": [ + | { + | "loc_name": "myZone", + | "additionalParam": "myParam" + | } + | ] + | } + | ], + | "nestedArray": [ + | [] + | ], + | "loc_name": "Klemmleiste MS2", + | "root_id": null, + | "uknom": 11.0, + | "vmax": 1.0499999523162842, + | "vmin": 0.0, + | "vtarget": 1.0 + | } + | ], + | "lines": [ + | { + | "length": 1 + | } + | ] + |} + |""".stripMargin + + genCode(json) shouldBe Some( + """|package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{ + | Nodes, + | Lines + |} + | + |final case class PowerFactoryGrid( + | nodes: Option[List[Nodes]], + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class CpZone( + | loc_name: Option[String], + | additionalParam: Option[String], + | leet: Option[List[Option[Leet]]] + | ) + | + | final case class Nodes( + | loc_name: Option[String], + | root_id: Option[String], + | AccessTime: Option[Double], + | GPSlon: Option[Double], + | nestedArray: Option[List[Option[List[Option[String]]]]], + | cpZone: Option[List[Option[CpZone]]], + | vmin: Option[Double], + | GPSlat: Option[Double], + | foo: Option[Foo], + | cpSubstat: Option[String], + | Vtarget: Option[Double], + | uknom: Option[Double], + | bar: Option[Bar], + | vtarget: Option[Double], + | vmax: Option[Double] + | ) + | + | final case class Leet( + | loc_name: Option[String], + | additionalParam: Option[String] + | ) + | + | final case class Bar(a: Option[String], b: Option[String]) + | + | final case class Lines(length: Option[Double]) + | + | final case class Foo() + | + |} + |""".stripMargin + ) + + } + + "generate valid GPSCoords for simple case" in { + val json = + """ + |{ + | "lines": [ + | { + | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", + | "GPScoords": [ + | [ + | 51.4843281, + | 7.4116482 + | ], + | [ + | 52.2895, + | 12.8273 + | ], + | [ + | 52.4895, + | 13.8273 + | ] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" + | } + | ] + |}""".stripMargin + + genCode(json) shouldBe Some( + """package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines( + | id: Option[String], + | GPScoords: Option[List[Option[List[Option[Double]]]]], + | bus1Id: Option[String], + | bus2Id: Option[String] + | ) + | + |} + |""".stripMargin + ) + } + + "generate valid GPSCoords if field values differ" in { + val json = + """ + |{ + | "lines": [ + | { + | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", + | "GPScoords": [ + | [] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" + | }, + | { + | "id": "Grid.ElmNet\\Line_0001_0002/1.ElmLne", + | "GPScoords": [ + | [ + | 51.4843281, + | 7.4116482 + | ], + | [ + | 52.2895, + | 12.8273 + | ], + | [ + | 52.4895, + | 13.8273 + | ] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0001.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0002.ElmTerm" + | }, + | { + | "id": "Grid.ElmNet\\Line_0009_0014.ElmLne", + | "GPScoords": [ + | [] + | ], + | "bus1Id": "Grid.ElmNet\\Bus_0014.ElmTerm", + | "bus2Id": "Grid.ElmNet\\Bus_0009.ElmTerm" + | } + | ] + |}""".stripMargin + + genCode(json) shouldBe Some( + """package edu.ie3.powerFactory2psdm.model.powerfactory + |import edu.ie3.powerFactory2psdm.model.powerfactory.PowerFactoryGrid.{Lines} + | + |final case class PowerFactoryGrid( + | lines: Option[List[Lines]] + |) + | + |object PowerFactoryGrid { + | + | final case class Lines( + | id: Option[String], + | GPScoords: Option[List[Option[List[Option[Double]]]]], + | bus1Id: Option[String], + | bus2Id: Option[String] + | ) + | + |} + |""".stripMargin + ) + } + +} diff --git a/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala b/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala index df4b6fbc..3ff3b991 100644 --- a/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala +++ b/src/test/scala/edu/ie3/scalatest/QuantityMatchers.scala @@ -1,43 +1,43 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.scalatest - -import edu.ie3.util.quantities.QuantityUtil -import javax.measure.Quantity -import org.scalatest.matchers.{MatchResult, Matcher} - -/** Trait, to simplify test coding, that is reliant on [[Quantity]] s - */ -trait QuantityMatchers { - class QuantityMatcher[Q <: Quantity[Q]](right: Quantity[Q], tolerance: Double) - extends Matcher[Quantity[Q]] - with QuantityMatchers { - override def apply(left: Quantity[Q]): MatchResult = MatchResult( - QuantityUtil.equals(left, right, tolerance), - QuantityMatchers.assembleRawFailureMessage(left, right, tolerance), - QuantityMatchers.assembleNegatedFailureMessage(left, right, tolerance) - ) - } - - def equalWithTolerance[Q <: Quantity[Q]]( - right: Quantity[Q] - )(implicit quantityTolerance: Double = 1e-10) = - new QuantityMatcher(right, quantityTolerance) -} - -case object QuantityMatchers extends QuantityMatchers { - private def assembleRawFailureMessage[Q <: Quantity[Q]]( - lhs: Quantity[Q], - rhs: Quantity[Q], - tolerance: Double - ) = s"The quantities $lhs and $rhs differ more than $tolerance in value" - private def assembleNegatedFailureMessage[Q <: Quantity[Q]]( - lhs: Quantity[Q], - rhs: Quantity[Q], - tolerance: Double - ) = s"The quantities $lhs and $rhs differ less than $tolerance in value" -} +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.scalatest + +import edu.ie3.util.quantities.QuantityUtil +import javax.measure.Quantity +import org.scalatest.matchers.{MatchResult, Matcher} + +/** Trait, to simplify test coding, that is reliant on [[Quantity]] s + */ +trait QuantityMatchers { + class QuantityMatcher[Q <: Quantity[Q]](right: Quantity[Q], tolerance: Double) + extends Matcher[Quantity[Q]] + with QuantityMatchers { + override def apply(left: Quantity[Q]): MatchResult = MatchResult( + QuantityUtil.equals(left, right, tolerance), + QuantityMatchers.assembleRawFailureMessage(left, right, tolerance), + QuantityMatchers.assembleNegatedFailureMessage(left, right, tolerance) + ) + } + + def equalWithTolerance[Q <: Quantity[Q]]( + right: Quantity[Q] + )(implicit quantityTolerance: Double = 1e-10) = + new QuantityMatcher(right, quantityTolerance) +} + +case object QuantityMatchers extends QuantityMatchers { + private def assembleRawFailureMessage[Q <: Quantity[Q]]( + lhs: Quantity[Q], + rhs: Quantity[Q], + tolerance: Double + ) = s"The quantities $lhs and $rhs differ more than $tolerance in value" + private def assembleNegatedFailureMessage[Q <: Quantity[Q]]( + lhs: Quantity[Q], + rhs: Quantity[Q], + tolerance: Double + ) = s"The quantities $lhs and $rhs differ less than $tolerance in value" +} From 59baed12ec30fc2daff9f9e462c2c6c3c8993678 Mon Sep 17 00:00:00 2001 From: t-ober <63147366+t-ober@users.noreply.github.com> Date: Wed, 8 Sep 2021 13:32:48 +0200 Subject: [PATCH 7/8] use lf line endings --- .gitattributes | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.gitattributes b/.gitattributes index 00a51aff..e52aea20 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,2 @@ -# -# https://help.github.com/articles/dealing-with-line-endings/ -# -# These are explicitly windows files and should use crlf -*.bat text eol=crlf - +# use LF line endings within the project +* text eol=lf From 79be511e97b5aefa82ff32322f73585dcf5c00d9 Mon Sep 17 00:00:00 2001 From: t-ober <63147366+t-ober@users.noreply.github.com> Date: Wed, 8 Sep 2021 13:34:12 +0200 Subject: [PATCH 8/8] clean up --- src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala index a29db96d..8b586de2 100644 --- a/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala +++ b/src/main/scala/edu/ie3/powerFactory2psdm/util/Formatter.scala @@ -8,7 +8,6 @@ package edu.ie3.powerFactory2psdm.util import java.nio.file.{Files, Paths} import org.scalafmt.interfaces.Scalafmt - import java.io.File object Formatter {